]> nmode's Git Repositories - signal-cli/blob - lib/src/test/java/org/asamk/signal/manager/storage/recipients/MergeRecipientHelperTest.java
770054f32aba08ac401d0981dd6eb3dc9b6e2f7a
[signal-cli] / lib / src / test / java / org / asamk / signal / manager / storage / recipients / MergeRecipientHelperTest.java
1 package org.asamk.signal.manager.storage.recipients;
2
3 import org.junit.jupiter.params.ParameterizedTest;
4 import org.junit.jupiter.params.provider.Arguments;
5 import org.junit.jupiter.params.provider.MethodSource;
6 import org.whispersystems.signalservice.api.push.ServiceId;
7 import org.whispersystems.signalservice.api.push.ServiceId.PNI;
8
9 import java.util.Arrays;
10 import java.util.HashSet;
11 import java.util.Set;
12 import java.util.UUID;
13 import java.util.stream.Collectors;
14 import java.util.stream.Stream;
15
16 import static org.junit.jupiter.api.Assertions.assertEquals;
17
18 class MergeRecipientHelperTest {
19
20 static final ServiceId SERVICE_ID_A = ServiceId.ACI.from(UUID.randomUUID());
21 static final ServiceId SERVICE_ID_B = ServiceId.ACI.from(UUID.randomUUID());
22 static final PNI PNI_A = PNI.from(UUID.randomUUID());
23 static final PNI PNI_B = PNI.from(UUID.randomUUID());
24 static final String NUMBER_A = "+AAA";
25 static final String NUMBER_B = "+BBB";
26
27 static final PartialAddresses ADDR_A = new PartialAddresses(SERVICE_ID_A, PNI_A, NUMBER_A);
28 static final PartialAddresses ADDR_B = new PartialAddresses(SERVICE_ID_B, PNI_B, NUMBER_B);
29
30 static final T[] testInstancesNone = new T[]{
31 new T(Set.of(), ADDR_A.FULL, Set.of(rec(1000000, ADDR_A.FULL))),
32 new T(Set.of(), ADDR_A.ACI_NUM, Set.of(rec(1000000, ADDR_A.ACI_NUM))),
33 new T(Set.of(), ADDR_A.ACI_PNI, Set.of(rec(1000000, ADDR_A.ACI_PNI))),
34 new T(Set.of(), ADDR_A.PNI_NUM, Set.of(rec(1000000, ADDR_A.PNI_NUM))),
35 };
36
37 static final T[] testInstancesSingle = new T[]{
38 new T(Set.of(rec(1, ADDR_A.FULL)), ADDR_A.FULL, Set.of(rec(1, ADDR_A.FULL))),
39 new T(Set.of(rec(1, ADDR_A.ACI)), ADDR_A.FULL, Set.of(rec(1, ADDR_A.FULL))),
40 new T(Set.of(rec(1, ADDR_A.PNI)), ADDR_A.FULL, Set.of(rec(1, ADDR_A.FULL))),
41 new T(Set.of(rec(1, ADDR_A.NUM)), ADDR_A.FULL, Set.of(rec(1, ADDR_A.FULL))),
42 new T(Set.of(rec(1, ADDR_A.ACI_NUM)), ADDR_A.FULL, Set.of(rec(1, ADDR_A.FULL))),
43 new T(Set.of(rec(1, ADDR_A.PNI_NUM)), ADDR_A.FULL, Set.of(rec(1, ADDR_A.FULL))),
44 new T(Set.of(rec(1, ADDR_A.ACI_PNI)), ADDR_A.FULL, Set.of(rec(1, ADDR_A.FULL))),
45
46 new T(Set.of(rec(1, ADDR_A.FULL)), ADDR_A.ACI_NUM, Set.of(rec(1, ADDR_A.FULL))),
47 new T(Set.of(rec(1, ADDR_A.ACI)), ADDR_A.ACI_NUM, Set.of(rec(1, ADDR_A.ACI_NUM))),
48 new T(Set.of(rec(1, ADDR_A.PNI)), ADDR_A.ACI_NUM, Set.of(rec(1, ADDR_A.PNI), rec(1000000, ADDR_A.ACI_NUM))),
49 new T(Set.of(rec(1, ADDR_A.NUM)), ADDR_A.ACI_NUM, Set.of(rec(1, ADDR_A.ACI_NUM))),
50 new T(Set.of(rec(1, ADDR_A.ACI_NUM)), ADDR_A.ACI_NUM, Set.of(rec(1, ADDR_A.ACI_NUM))),
51 new T(Set.of(rec(1, ADDR_A.PNI_NUM)),
52 ADDR_A.ACI_NUM,
53 Set.of(rec(1, ADDR_A.PNI), rec(1000000, ADDR_A.ACI_NUM))),
54 new T(Set.of(rec(1, ADDR_A.ACI_PNI)), ADDR_A.ACI_NUM, Set.of(rec(1, ADDR_A.FULL))),
55
56 new T(Set.of(rec(1, ADDR_A.FULL)), ADDR_A.PNI_NUM, Set.of(rec(1, ADDR_A.FULL))),
57 new T(Set.of(rec(1, ADDR_A.ACI)), ADDR_A.PNI_NUM, Set.of(rec(1, ADDR_A.ACI), rec(1000000, ADDR_A.PNI_NUM))),
58 new T(Set.of(rec(1, ADDR_A.PNI)), ADDR_A.PNI_NUM, Set.of(rec(1, ADDR_A.PNI_NUM))),
59 new T(Set.of(rec(1, ADDR_A.NUM)), ADDR_A.PNI_NUM, Set.of(rec(1, ADDR_A.PNI_NUM))),
60 new T(Set.of(rec(1, ADDR_A.ACI_NUM)),
61 ADDR_A.PNI_NUM,
62 Set.of(rec(1, ADDR_A.ACI), rec(1000000, ADDR_A.PNI_NUM))),
63 new T(Set.of(rec(1, ADDR_A.PNI_NUM)), ADDR_A.PNI_NUM, Set.of(rec(1, ADDR_A.PNI_NUM))),
64 new T(Set.of(rec(1, ADDR_A.ACI_PNI)), ADDR_A.PNI_NUM, Set.of(rec(1, ADDR_A.FULL))),
65
66 new T(Set.of(rec(1, ADDR_A.FULL)), ADDR_A.ACI_PNI, Set.of(rec(1, ADDR_A.FULL))),
67 new T(Set.of(rec(1, ADDR_A.ACI)), ADDR_A.ACI_PNI, Set.of(rec(1, ADDR_A.ACI_PNI))),
68 new T(Set.of(rec(1, ADDR_A.PNI)), ADDR_A.ACI_PNI, Set.of(rec(1, ADDR_A.ACI_PNI))),
69 new T(Set.of(rec(1, ADDR_A.NUM)), ADDR_A.ACI_PNI, Set.of(rec(1, ADDR_A.NUM), rec(1000000, ADDR_A.ACI_PNI))),
70 new T(Set.of(rec(1, ADDR_A.ACI_NUM)), ADDR_A.ACI_PNI, Set.of(rec(1, ADDR_A.FULL))),
71 new T(Set.of(rec(1, ADDR_A.PNI_NUM)), ADDR_A.ACI_PNI, Set.of(rec(1, ADDR_A.FULL))),
72 new T(Set.of(rec(1, ADDR_A.ACI_PNI)), ADDR_A.ACI_PNI, Set.of(rec(1, ADDR_A.ACI_PNI))),
73
74 new T(Set.of(rec(1, ADDR_A.FULL)), ADDR_B.FULL, Set.of(rec(1, ADDR_A.FULL), rec(1000000, ADDR_B.FULL))),
75 };
76
77 static final T[] testInstancesTwo = new T[]{
78 new T(Set.of(rec(1, ADDR_A.ACI), rec(2, ADDR_A.PNI)), ADDR_A.FULL, Set.of(rec(1, ADDR_A.FULL))),
79 new T(Set.of(rec(1, ADDR_A.ACI), rec(2, ADDR_A.NUM)), ADDR_A.FULL, Set.of(rec(1, ADDR_A.FULL))),
80 new T(Set.of(rec(1, ADDR_A.ACI), rec(2, ADDR_A.PNI_NUM)), ADDR_A.FULL, Set.of(rec(1, ADDR_A.FULL))),
81 new T(Set.of(rec(1, ADDR_A.PNI), rec(2, ADDR_A.NUM)), ADDR_A.FULL, Set.of(rec(1, ADDR_A.FULL))),
82 new T(Set.of(rec(1, ADDR_A.PNI), rec(2, ADDR_A.ACI_NUM)), ADDR_A.FULL, Set.of(rec(2, ADDR_A.FULL))),
83 new T(Set.of(rec(1, ADDR_A.NUM), rec(2, ADDR_A.ACI_PNI)), ADDR_A.FULL, Set.of(rec(2, ADDR_A.FULL))),
84
85 new T(Set.of(rec(1, ADDR_A.ACI), rec(2, ADDR_A.NUM)), ADDR_A.ACI_NUM, Set.of(rec(1, ADDR_A.ACI_NUM))),
86 new T(Set.of(rec(1, ADDR_A.ACI), rec(2, ADDR_A.PNI_NUM)), ADDR_A.ACI_NUM, Set.of(rec(1, ADDR_A.FULL))),
87 new T(Set.of(rec(1, ADDR_A.NUM), rec(2, ADDR_A.ACI_PNI)), ADDR_A.ACI_NUM, Set.of(rec(2, ADDR_A.FULL))),
88
89 new T(Set.of(rec(1, ADDR_A.ACI), rec(2, ADDR_A.PNI_NUM)),
90 ADDR_A.PNI_NUM,
91 Set.of(rec(1, ADDR_A.ACI), rec(2, ADDR_A.PNI_NUM))),
92 new T(Set.of(rec(1, ADDR_A.PNI), rec(2, ADDR_A.NUM)), ADDR_A.PNI_NUM, Set.of(rec(1, ADDR_A.PNI_NUM))),
93 new T(Set.of(rec(1, ADDR_A.PNI), rec(2, ADDR_A.ACI_NUM)),
94 ADDR_A.PNI_NUM,
95 Set.of(rec(1, ADDR_A.PNI_NUM), rec(2, ADDR_A.ACI))),
96 new T(Set.of(rec(1, ADDR_A.NUM), rec(2, ADDR_A.ACI_PNI)), ADDR_A.PNI_NUM, Set.of(rec(2, ADDR_A.FULL))),
97
98 new T(Set.of(rec(1, ADDR_A.ACI), rec(2, ADDR_A.PNI)), ADDR_A.ACI_PNI, Set.of(rec(1, ADDR_A.ACI_PNI))),
99 new T(Set.of(rec(1, ADDR_A.ACI), rec(2, ADDR_A.PNI_NUM)), ADDR_A.ACI_PNI, Set.of(rec(1, ADDR_A.FULL))),
100 new T(Set.of(rec(1, ADDR_A.PNI), rec(2, ADDR_A.ACI_NUM)), ADDR_A.ACI_PNI, Set.of(rec(2, ADDR_A.FULL))),
101 };
102
103 static final T[] testInstancesThree = new T[]{
104 new T(Set.of(rec(1, ADDR_A.ACI), rec(2, ADDR_A.PNI), rec(3, ADDR_A.NUM)),
105 ADDR_A.FULL,
106 Set.of(rec(1, ADDR_A.FULL))),
107 new T(Set.of(rec(1, ADDR_A.ACI.withIdentifiersFrom(ADDR_B.PNI)), rec(2, ADDR_A.PNI), rec(3, ADDR_A.NUM)),
108 ADDR_A.FULL,
109 Set.of(rec(1, ADDR_A.FULL))),
110 new T(Set.of(rec(1, ADDR_A.ACI.withIdentifiersFrom(ADDR_B.NUM)), rec(2, ADDR_A.PNI), rec(3, ADDR_A.NUM)),
111 ADDR_A.FULL,
112 Set.of(rec(1, ADDR_A.FULL))),
113 new T(Set.of(rec(1, ADDR_A.ACI), rec(2, ADDR_A.PNI), rec(3, ADDR_A.NUM.withIdentifiersFrom(ADDR_B.ACI))),
114 ADDR_A.FULL,
115 Set.of(rec(1, ADDR_A.FULL), rec(3, ADDR_B.ACI))),
116 new T(Set.of(rec(1, ADDR_A.ACI), rec(2, ADDR_A.PNI.withIdentifiersFrom(ADDR_B.ACI)), rec(3, ADDR_A.NUM)),
117 ADDR_A.FULL,
118 Set.of(rec(1, ADDR_A.FULL), rec(2, ADDR_B.ACI))),
119 };
120
121 @ParameterizedTest
122 @MethodSource
123 void resolveRecipientTrustedLocked_NoneExisting(T test) throws Exception {
124 final var testStore = new TestStore(test.input);
125 MergeRecipientHelper.resolveRecipientTrustedLocked(testStore, test.request);
126 assertEquals(test.output, testStore.getRecipients());
127 }
128
129 private static Stream<Arguments> resolveRecipientTrustedLocked_NoneExisting() {
130 return Arrays.stream(testInstancesNone).map(Arguments::of);
131 }
132
133 @ParameterizedTest
134 @MethodSource
135 void resolveRecipientTrustedLocked_SingleExisting(T test) throws Exception {
136 final var testStore = new TestStore(test.input);
137 MergeRecipientHelper.resolveRecipientTrustedLocked(testStore, test.request);
138 assertEquals(test.output, testStore.getRecipients());
139 }
140
141 private static Stream<Arguments> resolveRecipientTrustedLocked_SingleExisting() {
142 return Arrays.stream(testInstancesSingle).map(Arguments::of);
143 }
144
145 @ParameterizedTest
146 @MethodSource
147 void resolveRecipientTrustedLocked_TwoExisting(T test) throws Exception {
148 final var testStore = new TestStore(test.input);
149 MergeRecipientHelper.resolveRecipientTrustedLocked(testStore, test.request);
150 assertEquals(test.output, testStore.getRecipients());
151 }
152
153 private static Stream<Arguments> resolveRecipientTrustedLocked_TwoExisting() {
154 return Arrays.stream(testInstancesTwo).map(Arguments::of);
155 }
156
157 @ParameterizedTest
158 @MethodSource
159 void resolveRecipientTrustedLocked_ThreeExisting(T test) throws Exception {
160 final var testStore = new TestStore(test.input);
161 MergeRecipientHelper.resolveRecipientTrustedLocked(testStore, test.request);
162 assertEquals(test.output, testStore.getRecipients());
163 }
164
165 private static Stream<Arguments> resolveRecipientTrustedLocked_ThreeExisting() {
166 return Arrays.stream(testInstancesThree).map(Arguments::of);
167 }
168
169 private static RecipientWithAddress rec(long recipientId, RecipientAddress address) {
170 return new RecipientWithAddress(new RecipientId(recipientId, null), address);
171 }
172
173 record T(
174 Set<RecipientWithAddress> input, RecipientAddress request, Set<RecipientWithAddress> output
175 ) {
176
177 @Override
178 public String toString() {
179 return "T{#input=%s, request=%s_%s_%s, #output=%s}".formatted(input.size(),
180 request.serviceId().isPresent() ? "SVI" : "",
181 request.pni().isPresent() ? "PNI" : "",
182 request.number().isPresent() ? "NUM" : "",
183 output.size());
184 }
185 }
186
187 static class TestStore implements MergeRecipientHelper.Store {
188
189 final Set<RecipientWithAddress> recipients;
190 long nextRecipientId = 1000000;
191
192 TestStore(final Set<RecipientWithAddress> recipients) {
193 this.recipients = new HashSet<>(recipients);
194 }
195
196 public Set<RecipientWithAddress> getRecipients() {
197 return recipients;
198 }
199
200 @Override
201 public Set<RecipientWithAddress> findAllByAddress(final RecipientAddress address) {
202 return recipients.stream().filter(r -> r.address().matches(address)).collect(Collectors.toSet());
203 }
204
205 @Override
206 public RecipientId addNewRecipient(final RecipientAddress address) {
207 final var recipientId = new RecipientId(nextRecipientId++, null);
208 recipients.add(new RecipientWithAddress(recipientId, address));
209 return recipientId;
210 }
211
212 @Override
213 public void updateRecipientAddress(
214 final RecipientId recipientId, final RecipientAddress address
215 ) {
216 recipients.removeIf(r -> r.id().equals(recipientId));
217 recipients.add(new RecipientWithAddress(recipientId, address));
218 }
219
220 @Override
221 public void removeRecipientAddress(final RecipientId recipientId) {
222 recipients.removeIf(r -> r.id().equals(recipientId));
223 }
224 }
225
226 private record PartialAddresses(
227 RecipientAddress FULL,
228 RecipientAddress ACI,
229 RecipientAddress PNI,
230 RecipientAddress NUM,
231 RecipientAddress ACI_NUM,
232 RecipientAddress PNI_NUM,
233 RecipientAddress ACI_PNI
234 ) {
235
236 PartialAddresses(ServiceId serviceId, PNI pni, String number) {
237 this(new RecipientAddress(serviceId, pni, number),
238 new RecipientAddress(serviceId, null, null),
239 new RecipientAddress(null, pni, null),
240 new RecipientAddress(null, null, number),
241 new RecipientAddress(serviceId, null, number),
242 new RecipientAddress(null, pni, number),
243 new RecipientAddress(serviceId, pni, null));
244 }
245 }
246 }