1 package civitas.bboard.server.controllers.tests;
2
3 import static org.junit.jupiter.api.Assertions.assertEquals;
4 import static org.junit.jupiter.api.Assertions.assertThrows;
5 import static org.mockito.Mockito.verify;
6
7 import java.io.IOException;
8 import java.math.BigInteger;
9
10 import org.bouncycastle.crypto.CryptoException;
11 import org.junit.jupiter.api.DisplayName;
12 import org.junit.jupiter.api.Test;
13 import org.mockito.InjectMocks;
14 import org.mockito.Mock;
15 import org.slf4j.MarkerFactory;
16
17 import civitas.bboard.common.BBPostRepository;
18 import civitas.bboard.common.tests.BBPostTestData;
19 import civitas.bboard.server.GetBoardForId;
20 import civitas.bboard.server.controllers.CommunicableException;
21 import civitas.bboard.server.controllers.PostController;
22 import civitas.bboard.server.controllers.PostDTO;
23 import civitas.bboard.server.electioncache.UpdateCache;
24 import civitas.common.CheckAccess;
25 import civitas.common.LoggerService;
26 import civitas.common.Operation;
27 import civitas.common.board.tests.BulletinBoardTestData;
28 import civitas.common.tests.EnvironmentState;
29 import civitas.common.tests.RandomAwareTestBase;
30 import civitas.crypto.messagedigest.CryptoHash;
31 import civitas.crypto.rsapublickey.VerifyPublicKeySignature;
32 import jakarta.xml.bind.JAXBException;
33
34 class PostControllerTest extends RandomAwareTestBase implements BulletinBoardTestData, BBPostTestData {
35
36 @InjectMocks
37 PostController postController;
38
39 @Mock
40 CheckAccess checkAccess;
41
42 @Mock
43 GetBoardForId getBoardForId;
44
45 @Mock
46 UpdateCache updateCache;
47
48 @Mock
49 BBPostRepository bBPostRepository;
50
51 @Mock
52 CryptoHash cryptoHash;
53
54 @Mock
55 LoggerService logger;
56
57 @Mock
58 VerifyPublicKeySignature verifyPublicKeySignature;
59
60 @Test
61 @DisplayName(
62 """
63 records a post to a bulletin board and returns the time of recording
64 - checks access right
65 - verifies the signature
66 - retrieves the last post for the serial and hash
67 - computes the hash using the previous hash and the signature
68 - records the hash, serial and current time in the post and saves it
69 - updates the election cache
70 - logs the transaction with its meta and board id
71 """)
72 void test() throws CommunicableException, JAXBException, IOException, CryptoException {
73 assertEquals(
74 CURRENT_TIME,
75 postController.apply(
76 BULLETIN_BOARD_ID,
77 new PostDTO(
78 BOARD_CLOSED_CONTENT_COMMITMENT_META,
79 BOARD_CLOSED_CONTENT_COMMITMENT_XML,
80 BOARD_CLOSED_CONTENT_COMMITMENT_SIGNATURE)));
81 verify(checkAccess)
82 .apply(
83 Operation.POST,
84 BOARD_CLOSED_CONTENT_COMMITMENT_SIGNATURE.getSignerPubKey(),
85 BOARD_CLOSED_CONTENT_COMMITMENT_META + BULLETIN_BOARD_ID);
86 verify(verifyPublicKeySignature)
87 .apply(BOARD_CLOSED_CONTENT_COMMITMENT_SIGNATURE, BOARD_CLOSED_CONTENT_COMMITMENT_XML.getBytes());
88 verify(getBoardForId).apply(BULLETIN_BOARD_ID, true);
89 verify(updateCache)
90 .apply(
91 BULLETIN_BOARD_ID,
92 BOARD_CLOSED_CONTENT_COMMITMENT_META,
93 BOARD_CLOSED_CONTENT_COMMITMENT_XML,
94 CURRENT_TIME);
95 verify(bBPostRepository).findByBbidOrderBySerialDesc(BULLETIN_BOARD_ID);
96 verify(cryptoHash)
97 .apply(
98 BBPOST.hash,
99 BigInteger.valueOf(CURRENT_TIME).toByteArray(),
100 BOARD_CLOSED_CONTENT_COMMITMENT_SIGNATURE.signatureBytes);
101 verify(logger)
102 .apply(MarkerFactory.getMarker("bbs_post"), BOARD_CLOSED_CONTENT_COMMITMENT_META + BULLETIN_BOARD_ID);
103 verify(bBPostRepository).save(NEXT_POST);
104 }
105
106 @Test
107 @DisplayName("if the signature does not check, a CommunicableException is thrown")
108 void test1() {
109 assertThrows(
110 CommunicableException.class,
111 () -> postController.apply(
112 BULLETIN_BOARD_ID,
113 new PostDTO(
114 BOARD_CLOSED_CONTENT_COMMITMENT_META,
115 BOARD_CLOSED_CONTENT_COMMITMENT_XML,
116 BOARD_CLOSED_CONTENT_COMMITMENT_SIGNATURE_BAD)));
117 }
118
119 @Test
120 @DisplayName("if the signer is not authorized to post, a SecurityException is thrown")
121 void test2() {
122 assertThrows(
123 SecurityException.class,
124 () -> postController.apply(
125 BULLETIN_BOARD_ID,
126 new PostDTO(
127 BOARD_CLOSED_CONTENT_COMMITMENT_META,
128 BOARD_CLOSED_CONTENT_COMMITMENT_XML,
129 BOARD_CLOSED_CONTENT_COMMITMENT_SIGNATURE_BAD_ACTOR)));
130 }
131
132 @Test
133 @DisplayName("if there is no previous post the hash uses only the time and signature, and the serial is 1 ")
134 void test3() throws CommunicableException {
135 given(EnvironmentState.EMPTY_BOARD);
136 Long actual = postController.apply(
137 BULLETIN_BOARD_ID,
138 new PostDTO(
139 BOARD_CLOSED_CONTENT_COMMITMENT_META,
140 BOARD_CLOSED_CONTENT_COMMITMENT_XML,
141 BOARD_CLOSED_CONTENT_COMMITMENT_SIGNATURE));
142 verify(cryptoHash)
143 .apply(
144 new byte[0],
145 BigInteger.valueOf(CURRENT_TIME).toByteArray(),
146 BOARD_CLOSED_CONTENT_COMMITMENT_SIGNATURE.signatureBytes);
147 verify(bBPostRepository).save(FIRST_POST);
148 assertEquals(CURRENT_TIME, actual);
149 }
150 }