1 package org
.asamk
.signal
.manager
.storage
.recipients
;
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
.ACI
;
7 import org
.whispersystems
.signalservice
.api
.push
.ServiceId
.PNI
;
9 import java
.util
.Arrays
;
10 import java
.util
.HashSet
;
12 import java
.util
.UUID
;
13 import java
.util
.stream
.Collectors
;
14 import java
.util
.stream
.Stream
;
16 import static org
.junit
.jupiter
.api
.Assertions
.assertEquals
;
18 class MergeRecipientHelperTest
{
20 static final ACI ACI_A
= ACI
.from(UUID
.randomUUID());
21 static final ACI ACI_B
= 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 static final String USERNAME_A
= "USER.1";
27 static final String USERNAME_B
= "USER.2";
29 static final PartialAddresses ADDR_A
= new PartialAddresses(ACI_A
, PNI_A
, NUMBER_A
, USERNAME_A
);
30 static final PartialAddresses ADDR_B
= new PartialAddresses(ACI_B
, PNI_B
, NUMBER_B
, USERNAME_B
);
32 static final T
[] testInstancesNone
= new T
[]{
33 new T(Set
.of(), ADDR_A
.FULL
, Set
.of(rec(1000000, ADDR_A
.FULL
))),
34 new T(Set
.of(), ADDR_A
.ACI_NUM
, Set
.of(rec(1000000, ADDR_A
.ACI_NUM
))),
35 new T(Set
.of(), ADDR_A
.ACI_PNI
, Set
.of(rec(1000000, ADDR_A
.ACI_PNI
))),
36 new T(Set
.of(), ADDR_A
.PNI_NUM
, Set
.of(rec(1000000, ADDR_A
.PNI_NUM
))),
37 new T(Set
.of(), ADDR_A
.ACI_USERNAME
, Set
.of(rec(1000000, ADDR_A
.ACI_USERNAME
))),
38 new T(Set
.of(), ADDR_A
.FULL_USERNAME
, Set
.of(rec(1000000, ADDR_A
.FULL_USERNAME
))),
41 static final T
[] testInstancesSingle
= new T
[]{
42 new T(Set
.of(rec(1, ADDR_A
.FULL
)), ADDR_A
.FULL
, Set
.of(rec(1, ADDR_A
.FULL
))),
43 new T(Set
.of(rec(1, ADDR_A
.FULL_USERNAME
)), ADDR_A
.FULL
, Set
.of(rec(1, ADDR_A
.FULL_USERNAME
))),
44 new T(Set
.of(rec(1, ADDR_A
.ACI
)), ADDR_A
.FULL
, Set
.of(rec(1, ADDR_A
.FULL
))),
45 new T(Set
.of(rec(1, ADDR_A
.PNI
)), ADDR_A
.FULL
, Set
.of(rec(1, ADDR_A
.FULL
))),
46 new T(Set
.of(rec(1, ADDR_A
.NUM
)), ADDR_A
.FULL
, Set
.of(rec(1, ADDR_A
.FULL
))),
47 new T(Set
.of(rec(1, ADDR_A
.ACI_NUM
)), ADDR_A
.FULL
, Set
.of(rec(1, ADDR_A
.FULL
))),
48 new T(Set
.of(rec(1, ADDR_A
.PNI_NUM
)), ADDR_A
.FULL
, Set
.of(rec(1, ADDR_A
.FULL
))),
49 new T(Set
.of(rec(1, ADDR_A
.ACI_PNI
)), ADDR_A
.FULL
, Set
.of(rec(1, ADDR_A
.FULL
))),
51 new T(Set
.of(rec(1, ADDR_A
.FULL
)), ADDR_A
.ACI_NUM
, Set
.of(rec(1, ADDR_A
.FULL
))),
52 new T(Set
.of(rec(1, ADDR_A
.ACI
)), ADDR_A
.ACI_NUM
, Set
.of(rec(1, ADDR_A
.ACI_NUM
))),
53 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
))),
54 new T(Set
.of(rec(1, ADDR_A
.NUM
)), ADDR_A
.ACI_NUM
, Set
.of(rec(1, ADDR_A
.ACI_NUM
))),
55 new T(Set
.of(rec(1, ADDR_A
.ACI_NUM
)), ADDR_A
.ACI_NUM
, Set
.of(rec(1, ADDR_A
.ACI_NUM
))),
56 new T(Set
.of(rec(1, ADDR_A
.PNI_NUM
)), ADDR_A
.ACI_NUM
, Set
.of(rec(1, ADDR_A
.FULL
))),
57 new T(Set
.of(rec(1, ADDR_A
.ACI_PNI
)), ADDR_A
.ACI_NUM
, Set
.of(rec(1, ADDR_A
.FULL
))),
59 new T(Set
.of(rec(1, ADDR_A
.FULL
)), ADDR_A
.PNI_NUM
, Set
.of(rec(1, ADDR_A
.FULL
))),
60 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
))),
61 new T(Set
.of(rec(1, ADDR_A
.PNI
)), ADDR_A
.PNI_NUM
, Set
.of(rec(1, ADDR_A
.PNI_NUM
))),
62 new T(Set
.of(rec(1, ADDR_A
.NUM
)), ADDR_A
.PNI_NUM
, Set
.of(rec(1, ADDR_A
.PNI_NUM
))),
63 new T(Set
.of(rec(1, ADDR_A
.ACI_NUM
)),
65 Set
.of(rec(1, ADDR_A
.ACI
), rec(1000000, ADDR_A
.PNI_NUM
))),
66 new T(Set
.of(rec(1, ADDR_A
.PNI_NUM
)), ADDR_A
.PNI_NUM
, Set
.of(rec(1, ADDR_A
.PNI_NUM
))),
67 new T(Set
.of(rec(1, ADDR_A
.ACI_PNI
)), ADDR_A
.PNI_NUM
, Set
.of(rec(1, ADDR_A
.FULL
))),
69 new T(Set
.of(rec(1, ADDR_A
.FULL
)), ADDR_A
.ACI_PNI
, Set
.of(rec(1, ADDR_A
.FULL
))),
70 new T(Set
.of(rec(1, ADDR_A
.ACI
)), ADDR_A
.ACI_PNI
, Set
.of(rec(1, ADDR_A
.ACI_PNI
))),
71 new T(Set
.of(rec(1, ADDR_A
.PNI
)), ADDR_A
.ACI_PNI
, Set
.of(rec(1, ADDR_A
.ACI_PNI
))),
72 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
))),
73 new T(Set
.of(rec(1, ADDR_A
.ACI_NUM
)), ADDR_A
.ACI_PNI
, Set
.of(rec(1, ADDR_A
.FULL
))),
74 new T(Set
.of(rec(1, ADDR_A
.PNI_NUM
)), ADDR_A
.ACI_PNI
, Set
.of(rec(1, ADDR_A
.FULL
))),
75 new T(Set
.of(rec(1, ADDR_A
.ACI_PNI
)), ADDR_A
.ACI_PNI
, Set
.of(rec(1, ADDR_A
.ACI_PNI
))),
77 new T(Set
.of(rec(1, ADDR_A
.FULL
)), ADDR_B
.FULL
, Set
.of(rec(1, ADDR_A
.FULL
), rec(1000000, ADDR_B
.FULL
))),
79 new T(Set
.of(rec(1, ADDR_A
.FULL
)), ADDR_A
.ACI_USERNAME
, Set
.of(rec(1, ADDR_A
.FULL_USERNAME
))),
80 new T(Set
.of(rec(1, ADDR_A
.ACI
)), ADDR_A
.ACI_USERNAME
, Set
.of(rec(1, ADDR_A
.ACI_USERNAME
))),
81 new T(Set
.of(rec(1, ADDR_A
.ACI_NUM
)), ADDR_A
.ACI_USERNAME
, Set
.of(rec(1, ADDR_A
.ACI_NUM_USERNAME
))),
84 static final T
[] testInstancesTwo
= new T
[]{
85 new T(Set
.of(rec(1, ADDR_A
.ACI
), rec(2, ADDR_A
.PNI
)), ADDR_A
.FULL
, Set
.of(rec(1, ADDR_A
.FULL
))),
86 new T(Set
.of(rec(1, ADDR_A
.ACI
), rec(2, ADDR_A
.NUM
)), ADDR_A
.FULL
, Set
.of(rec(1, ADDR_A
.FULL
))),
87 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
))),
88 new T(Set
.of(rec(1, ADDR_A
.PNI
), rec(2, ADDR_A
.NUM
)), ADDR_A
.FULL
, Set
.of(rec(1, ADDR_A
.FULL
))),
89 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
))),
90 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
))),
92 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
))),
93 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
))),
94 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
))),
96 new T(Set
.of(rec(1, ADDR_A
.ACI
), rec(2, ADDR_A
.PNI_NUM
)),
98 Set
.of(rec(1, ADDR_A
.ACI
), rec(2, ADDR_A
.PNI_NUM
))),
99 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
))),
100 new T(Set
.of(rec(1, ADDR_A
.PNI
), rec(2, ADDR_A
.ACI_NUM
)),
102 Set
.of(rec(1, ADDR_A
.PNI_NUM
), rec(2, ADDR_A
.ACI
))),
103 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
))),
105 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
))),
106 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
))),
107 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
))),
110 static final T
[] testInstancesThree
= new T
[]{
111 new T(Set
.of(rec(1, ADDR_A
.ACI
), rec(2, ADDR_A
.PNI
), rec(3, ADDR_A
.NUM
)),
113 Set
.of(rec(1, ADDR_A
.FULL
))),
114 new T(Set
.of(rec(1, ADDR_A
.ACI
.withIdentifiersFrom(ADDR_B
.PNI
)), rec(2, ADDR_A
.PNI
), rec(3, ADDR_A
.NUM
)),
116 Set
.of(rec(1, ADDR_A
.FULL
))),
117 new T(Set
.of(rec(1, ADDR_A
.ACI
.withIdentifiersFrom(ADDR_B
.NUM
)), rec(2, ADDR_A
.PNI
), rec(3, ADDR_A
.NUM
)),
119 Set
.of(rec(1, ADDR_A
.FULL
))),
120 new T(Set
.of(rec(1, ADDR_A
.ACI
), rec(2, ADDR_A
.PNI
), rec(3, ADDR_A
.NUM
.withIdentifiersFrom(ADDR_B
.ACI
))),
122 Set
.of(rec(1, ADDR_A
.FULL
), rec(3, ADDR_B
.ACI
))),
123 new T(Set
.of(rec(1, ADDR_A
.ACI
), rec(2, ADDR_A
.PNI
.withIdentifiersFrom(ADDR_B
.ACI
)), rec(3, ADDR_A
.NUM
)),
125 Set
.of(rec(1, ADDR_A
.FULL
), rec(2, ADDR_B
.ACI
))),
130 void resolveRecipientTrustedLocked_NoneExisting(T test
) throws Exception
{
131 final var testStore
= new TestStore(test
.input
);
132 MergeRecipientHelper
.resolveRecipientTrustedLocked(testStore
, test
.request
);
133 assertEquals(test
.output
, testStore
.getRecipients());
136 private static Stream
<Arguments
> resolveRecipientTrustedLocked_NoneExisting() {
137 return Arrays
.stream(testInstancesNone
).map(Arguments
::of
);
142 void resolveRecipientTrustedLocked_SingleExisting(T test
) throws Exception
{
143 final var testStore
= new TestStore(test
.input
);
144 MergeRecipientHelper
.resolveRecipientTrustedLocked(testStore
, test
.request
);
145 assertEquals(test
.output
, testStore
.getRecipients());
148 private static Stream
<Arguments
> resolveRecipientTrustedLocked_SingleExisting() {
149 return Arrays
.stream(testInstancesSingle
).map(Arguments
::of
);
154 void resolveRecipientTrustedLocked_TwoExisting(T test
) throws Exception
{
155 final var testStore
= new TestStore(test
.input
);
156 MergeRecipientHelper
.resolveRecipientTrustedLocked(testStore
, test
.request
);
157 assertEquals(test
.output
, testStore
.getRecipients());
160 private static Stream
<Arguments
> resolveRecipientTrustedLocked_TwoExisting() {
161 return Arrays
.stream(testInstancesTwo
).map(Arguments
::of
);
166 void resolveRecipientTrustedLocked_ThreeExisting(T test
) throws Exception
{
167 final var testStore
= new TestStore(test
.input
);
168 MergeRecipientHelper
.resolveRecipientTrustedLocked(testStore
, test
.request
);
169 assertEquals(test
.output
, testStore
.getRecipients());
172 private static Stream
<Arguments
> resolveRecipientTrustedLocked_ThreeExisting() {
173 return Arrays
.stream(testInstancesThree
).map(Arguments
::of
);
176 private static RecipientWithAddress
rec(long recipientId
, RecipientAddress address
) {
177 return new RecipientWithAddress(new RecipientId(recipientId
, null), address
);
181 Set
<RecipientWithAddress
> input
, RecipientAddress request
, Set
<RecipientWithAddress
> output
185 public String
toString() {
186 return "T{#input=%s, request=%s_%s_%s, #output=%s}".formatted(input
.size(),
187 request
.aci().isPresent() ?
"ACI" : "",
188 request
.pni().isPresent() ?
"PNI" : "",
189 request
.number().isPresent() ?
"NUM" : "",
194 static class TestStore
implements MergeRecipientHelper
.Store
{
196 final Set
<RecipientWithAddress
> recipients
;
197 long nextRecipientId
= 1000000;
199 TestStore(final Set
<RecipientWithAddress
> recipients
) {
200 this.recipients
= new HashSet
<>(recipients
);
203 public Set
<RecipientWithAddress
> getRecipients() {
208 public Set
<RecipientWithAddress
> findAllByAddress(final RecipientAddress address
) {
209 return recipients
.stream().filter(r
-> r
.address().matches(address
)).collect(Collectors
.toSet());
213 public RecipientId
addNewRecipient(final RecipientAddress address
) {
214 final var recipientId
= new RecipientId(nextRecipientId
++, null);
215 recipients
.add(new RecipientWithAddress(recipientId
, address
));
220 public void updateRecipientAddress(final RecipientId recipientId
, final RecipientAddress address
) {
221 recipients
.removeIf(r
-> r
.id().equals(recipientId
));
222 recipients
.add(new RecipientWithAddress(recipientId
, address
));
226 public void removeRecipientAddress(final RecipientId recipientId
) {
227 recipients
.removeIf(r
-> r
.id().equals(recipientId
));
231 private record PartialAddresses(
232 RecipientAddress FULL
,
233 RecipientAddress FULL_USERNAME
,
234 RecipientAddress ACI
,
235 RecipientAddress PNI
,
236 RecipientAddress NUM
,
237 RecipientAddress ACI_NUM
,
238 RecipientAddress ACI_NUM_USERNAME
,
239 RecipientAddress PNI_NUM
,
240 RecipientAddress ACI_PNI
,
241 RecipientAddress ACI_USERNAME
244 PartialAddresses(ACI aci
, PNI pni
, String number
, String username
) {
245 this(new RecipientAddress(aci
, pni
, number
),
246 new RecipientAddress(aci
, pni
, number
, username
),
247 new RecipientAddress(aci
, null, null),
248 new RecipientAddress(null, pni
, null),
249 new RecipientAddress(null, null, number
),
250 new RecipientAddress(aci
, null, number
),
251 new RecipientAddress(aci
, null, number
, username
),
252 new RecipientAddress(null, pni
, number
),
253 new RecipientAddress(aci
, pni
, null),
254 new RecipientAddress(aci
, null, null, username
));