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
;
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 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 static final String USERNAME_A
= "USER.1";
27 static final String USERNAME_B
= "USER.2";
29 static final PartialAddresses ADDR_A
= new PartialAddresses(SERVICE_ID_A
, PNI_A
, NUMBER_A
, USERNAME_A
);
30 static final PartialAddresses ADDR_B
= new PartialAddresses(SERVICE_ID_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
)),
58 Set
.of(rec(1, ADDR_A
.PNI
), rec(1000000, ADDR_A
.ACI_NUM
))),
59 new T(Set
.of(rec(1, ADDR_A
.ACI_PNI
)), ADDR_A
.ACI_NUM
, Set
.of(rec(1, ADDR_A
.FULL
))),
61 new T(Set
.of(rec(1, ADDR_A
.FULL
)), ADDR_A
.PNI_NUM
, Set
.of(rec(1, ADDR_A
.FULL
))),
62 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
))),
63 new T(Set
.of(rec(1, ADDR_A
.PNI
)), ADDR_A
.PNI_NUM
, Set
.of(rec(1, ADDR_A
.PNI_NUM
))),
64 new T(Set
.of(rec(1, ADDR_A
.NUM
)), ADDR_A
.PNI_NUM
, Set
.of(rec(1, ADDR_A
.PNI_NUM
))),
65 new T(Set
.of(rec(1, ADDR_A
.ACI_NUM
)),
67 Set
.of(rec(1, ADDR_A
.ACI
), rec(1000000, ADDR_A
.PNI_NUM
))),
68 new T(Set
.of(rec(1, ADDR_A
.PNI_NUM
)), ADDR_A
.PNI_NUM
, Set
.of(rec(1, ADDR_A
.PNI_NUM
))),
69 new T(Set
.of(rec(1, ADDR_A
.ACI_PNI
)), ADDR_A
.PNI_NUM
, Set
.of(rec(1, ADDR_A
.FULL
))),
71 new T(Set
.of(rec(1, ADDR_A
.FULL
)), ADDR_A
.ACI_PNI
, Set
.of(rec(1, ADDR_A
.FULL
))),
72 new T(Set
.of(rec(1, ADDR_A
.ACI
)), ADDR_A
.ACI_PNI
, Set
.of(rec(1, ADDR_A
.ACI_PNI
))),
73 new T(Set
.of(rec(1, ADDR_A
.PNI
)), ADDR_A
.ACI_PNI
, Set
.of(rec(1, ADDR_A
.ACI_PNI
))),
74 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
))),
75 new T(Set
.of(rec(1, ADDR_A
.ACI_NUM
)), ADDR_A
.ACI_PNI
, Set
.of(rec(1, ADDR_A
.FULL
))),
76 new T(Set
.of(rec(1, ADDR_A
.PNI_NUM
)), ADDR_A
.ACI_PNI
, Set
.of(rec(1, ADDR_A
.FULL
))),
77 new T(Set
.of(rec(1, ADDR_A
.ACI_PNI
)), ADDR_A
.ACI_PNI
, Set
.of(rec(1, ADDR_A
.ACI_PNI
))),
79 new T(Set
.of(rec(1, ADDR_A
.FULL
)), ADDR_B
.FULL
, Set
.of(rec(1, ADDR_A
.FULL
), rec(1000000, ADDR_B
.FULL
))),
81 new T(Set
.of(rec(1, ADDR_A
.FULL
)), ADDR_A
.ACI_USERNAME
, Set
.of(rec(1, ADDR_A
.FULL_USERNAME
))),
82 new T(Set
.of(rec(1, ADDR_A
.ACI
)), ADDR_A
.ACI_USERNAME
, Set
.of(rec(1, ADDR_A
.ACI_USERNAME
))),
83 new T(Set
.of(rec(1, ADDR_A
.ACI_NUM
)), ADDR_A
.ACI_USERNAME
, Set
.of(rec(1, ADDR_A
.ACI_NUM_USERNAME
))),
86 static final T
[] testInstancesTwo
= new T
[]{
87 new T(Set
.of(rec(1, ADDR_A
.ACI
), rec(2, ADDR_A
.PNI
)), ADDR_A
.FULL
, Set
.of(rec(1, ADDR_A
.FULL
))),
88 new T(Set
.of(rec(1, ADDR_A
.ACI
), rec(2, ADDR_A
.NUM
)), ADDR_A
.FULL
, Set
.of(rec(1, ADDR_A
.FULL
))),
89 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
))),
90 new T(Set
.of(rec(1, ADDR_A
.PNI
), rec(2, ADDR_A
.NUM
)), ADDR_A
.FULL
, Set
.of(rec(1, ADDR_A
.FULL
))),
91 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
))),
92 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
))),
94 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
))),
95 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
))),
96 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
))),
98 new T(Set
.of(rec(1, ADDR_A
.ACI
), rec(2, ADDR_A
.PNI_NUM
)),
100 Set
.of(rec(1, ADDR_A
.ACI
), rec(2, ADDR_A
.PNI_NUM
))),
101 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
))),
102 new T(Set
.of(rec(1, ADDR_A
.PNI
), rec(2, ADDR_A
.ACI_NUM
)),
104 Set
.of(rec(1, ADDR_A
.PNI_NUM
), rec(2, ADDR_A
.ACI
))),
105 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
))),
107 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
))),
108 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
))),
109 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
))),
112 static final T
[] testInstancesThree
= new T
[]{
113 new T(Set
.of(rec(1, ADDR_A
.ACI
), rec(2, ADDR_A
.PNI
), rec(3, ADDR_A
.NUM
)),
115 Set
.of(rec(1, ADDR_A
.FULL
))),
116 new T(Set
.of(rec(1, ADDR_A
.ACI
.withIdentifiersFrom(ADDR_B
.PNI
)), rec(2, ADDR_A
.PNI
), rec(3, ADDR_A
.NUM
)),
118 Set
.of(rec(1, ADDR_A
.FULL
))),
119 new T(Set
.of(rec(1, ADDR_A
.ACI
.withIdentifiersFrom(ADDR_B
.NUM
)), rec(2, ADDR_A
.PNI
), rec(3, ADDR_A
.NUM
)),
121 Set
.of(rec(1, ADDR_A
.FULL
))),
122 new T(Set
.of(rec(1, ADDR_A
.ACI
), rec(2, ADDR_A
.PNI
), rec(3, ADDR_A
.NUM
.withIdentifiersFrom(ADDR_B
.ACI
))),
124 Set
.of(rec(1, ADDR_A
.FULL
), rec(3, ADDR_B
.ACI
))),
125 new T(Set
.of(rec(1, ADDR_A
.ACI
), rec(2, ADDR_A
.PNI
.withIdentifiersFrom(ADDR_B
.ACI
)), rec(3, ADDR_A
.NUM
)),
127 Set
.of(rec(1, ADDR_A
.FULL
), rec(2, ADDR_B
.ACI
))),
132 void resolveRecipientTrustedLocked_NoneExisting(T test
) throws Exception
{
133 final var testStore
= new TestStore(test
.input
);
134 MergeRecipientHelper
.resolveRecipientTrustedLocked(testStore
, test
.request
);
135 assertEquals(test
.output
, testStore
.getRecipients());
138 private static Stream
<Arguments
> resolveRecipientTrustedLocked_NoneExisting() {
139 return Arrays
.stream(testInstancesNone
).map(Arguments
::of
);
144 void resolveRecipientTrustedLocked_SingleExisting(T test
) throws Exception
{
145 final var testStore
= new TestStore(test
.input
);
146 MergeRecipientHelper
.resolveRecipientTrustedLocked(testStore
, test
.request
);
147 assertEquals(test
.output
, testStore
.getRecipients());
150 private static Stream
<Arguments
> resolveRecipientTrustedLocked_SingleExisting() {
151 return Arrays
.stream(testInstancesSingle
).map(Arguments
::of
);
156 void resolveRecipientTrustedLocked_TwoExisting(T test
) throws Exception
{
157 final var testStore
= new TestStore(test
.input
);
158 MergeRecipientHelper
.resolveRecipientTrustedLocked(testStore
, test
.request
);
159 assertEquals(test
.output
, testStore
.getRecipients());
162 private static Stream
<Arguments
> resolveRecipientTrustedLocked_TwoExisting() {
163 return Arrays
.stream(testInstancesTwo
).map(Arguments
::of
);
168 void resolveRecipientTrustedLocked_ThreeExisting(T test
) throws Exception
{
169 final var testStore
= new TestStore(test
.input
);
170 MergeRecipientHelper
.resolveRecipientTrustedLocked(testStore
, test
.request
);
171 assertEquals(test
.output
, testStore
.getRecipients());
174 private static Stream
<Arguments
> resolveRecipientTrustedLocked_ThreeExisting() {
175 return Arrays
.stream(testInstancesThree
).map(Arguments
::of
);
178 private static RecipientWithAddress
rec(long recipientId
, RecipientAddress address
) {
179 return new RecipientWithAddress(new RecipientId(recipientId
, null), address
);
183 Set
<RecipientWithAddress
> input
, RecipientAddress request
, Set
<RecipientWithAddress
> output
187 public String
toString() {
188 return "T{#input=%s, request=%s_%s_%s, #output=%s}".formatted(input
.size(),
189 request
.serviceId().isPresent() ?
"SVI" : "",
190 request
.pni().isPresent() ?
"PNI" : "",
191 request
.number().isPresent() ?
"NUM" : "",
196 static class TestStore
implements MergeRecipientHelper
.Store
{
198 final Set
<RecipientWithAddress
> recipients
;
199 long nextRecipientId
= 1000000;
201 TestStore(final Set
<RecipientWithAddress
> recipients
) {
202 this.recipients
= new HashSet
<>(recipients
);
205 public Set
<RecipientWithAddress
> getRecipients() {
210 public Set
<RecipientWithAddress
> findAllByAddress(final RecipientAddress address
) {
211 return recipients
.stream().filter(r
-> r
.address().matches(address
)).collect(Collectors
.toSet());
215 public RecipientId
addNewRecipient(final RecipientAddress address
) {
216 final var recipientId
= new RecipientId(nextRecipientId
++, null);
217 recipients
.add(new RecipientWithAddress(recipientId
, address
));
222 public void updateRecipientAddress(
223 final RecipientId recipientId
, final RecipientAddress address
225 recipients
.removeIf(r
-> r
.id().equals(recipientId
));
226 recipients
.add(new RecipientWithAddress(recipientId
, address
));
230 public void removeRecipientAddress(final RecipientId recipientId
) {
231 recipients
.removeIf(r
-> r
.id().equals(recipientId
));
235 private record PartialAddresses(
236 RecipientAddress FULL
,
237 RecipientAddress FULL_USERNAME
,
238 RecipientAddress ACI
,
239 RecipientAddress PNI
,
240 RecipientAddress NUM
,
241 RecipientAddress ACI_NUM
,
242 RecipientAddress ACI_NUM_USERNAME
,
243 RecipientAddress PNI_NUM
,
244 RecipientAddress ACI_PNI
,
245 RecipientAddress ACI_USERNAME
248 PartialAddresses(ServiceId serviceId
, PNI pni
, String number
, String username
) {
249 this(new RecipientAddress(serviceId
, pni
, number
),
250 new RecipientAddress(serviceId
, pni
, number
, username
),
251 new RecipientAddress(serviceId
, null, null),
252 new RecipientAddress(null, pni
, null),
253 new RecipientAddress(null, null, number
),
254 new RecipientAddress(serviceId
, null, number
),
255 new RecipientAddress(serviceId
, null, number
, username
),
256 new RecipientAddress(null, pni
, number
),
257 new RecipientAddress(serviceId
, pni
, null),
258 new RecipientAddress(serviceId
, null, null, username
));