Skip to content

V3 Identity Map test #106

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
<dependency>
<groupId>com.uid2</groupId>
<artifactId>uid2-client</artifactId>
<version>4.3.22</version>
<version>4.6.8-alpha-29-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
Expand Down
19 changes: 19 additions & 0 deletions src/test/java/app/component/Operator.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.time.Clock;
import java.time.Instant;
import java.util.Base64;
import java.util.List;

public class Operator extends App {
public enum Type {
Expand Down Expand Up @@ -260,12 +261,30 @@ public DecryptionResponse v2TokenDecrypt(String token) throws UID2ClientExceptio
return dspClient.decrypt(token);
}

// Need to use the manual mapping for error cases - SDK won't allow creating input with bad emails or disable optout check
public JsonNode v2IdentityMap(String payload) throws Exception {
V2Envelope envelope = v2CreateEnvelope(payload, getClientApiSecret());
String encryptedResponse = HttpClient.post(getBaseUrl() + "/v2/identity/map", envelope.envelope(), getClientApiKey());
return v2DecryptEncryptedResponse(encryptedResponse, envelope.nonce(), getClientApiSecret());
}

public IdentityMapResponse v2IdentityMap(IdentityMapInput input) {
IdentityMapClient identityMapClient = new IdentityMapClient(getBaseUrl(), CLIENT_API_KEY, CLIENT_API_SECRET);
return identityMapClient.generateIdentityMap(input);
}

// Need to use the manual mapping for error cases - SDK won't allow creating input with bad emails
public JsonNode v3IdentityMap(String payload) throws Exception {
V2Envelope envelope = v2CreateEnvelope(payload, getClientApiSecret());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can get rid of all the getClientApiKey() or getClientApiSecret() calls and use the consts directly

String encryptedResponse = HttpClient.post(getBaseUrl() + "/v3/identity/map", envelope.envelope(), getClientApiKey());
return v2DecryptEncryptedResponse(encryptedResponse, envelope.nonce(), getClientApiSecret());
}

public IdentityMapV3Response v3IdentityMap(IdentityMapV3Input input) {
IdentityMapV3Client identityMapV3Client = new IdentityMapV3Client(getBaseUrl(), CLIENT_API_KEY, CLIENT_API_SECRET);
return identityMapV3Client.generateIdentityMap(input);
}

public JsonNode v2IdentityBuckets(String payload) throws Exception {
V2Envelope envelope = v2CreateEnvelope(payload, CLIENT_API_SECRET);
String encryptedResponse = HttpClient.post(getBaseUrl() + "/v2/identity/buckets", envelope.envelope(), CLIENT_API_KEY);
Expand Down
7 changes: 0 additions & 7 deletions src/test/java/app/component/Validator.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

public class Validator extends App {
private final PublisherUid2Helper publisherHelper;
private final UID2Client dspClient;
private final Headers standardHeaders;
private final MediaType FORM = MediaType.get("application/x-www-form-urlencoded");;

Expand All @@ -20,12 +19,6 @@ public Validator(String host, Integer port, String name, String clientApiKey, St
.add("Authorization", "Bearer " + clientApiKey)
.add("X-UID2-Client-Version: java-e2e-test")
.build();

dspClient = new UID2Client(
this.getBaseUrl(),
clientApiKey,
clientSecret,
IdentityScope.UID2);
}

public Response triggerGenerateTokenFromEmail(String email) throws IOException {
Expand Down
6 changes: 3 additions & 3 deletions src/test/java/suite/E2ELocalFullTestSuite.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
BasicTest.class,
CoreTest.class,
CoreRefreshTest.class,
V2ApiOperatorTest.class,
V2ApiOperatorPublicOnlyTest.class,
V2ApiOperatorLocalOnlyTest.class,
OperatorTest.class,
OperatorPublicOnlyTest.class,
OperatorLocalOnlyTest.class,
OptoutTest.class,
V2ApiValidatorTest.class
})
Expand Down
4 changes: 2 additions & 2 deletions src/test/java/suite/E2EPrivateOperatorTestSuite.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
import org.junit.platform.suite.api.SelectClasses;
import org.junit.platform.suite.api.Suite;
import suite.basic.BasicTest;
import suite.operator.V2ApiOperatorTest;
import suite.operator.OperatorTest;

@Suite
@SelectClasses({
BasicTest.class,
V2ApiOperatorTest.class
OperatorTest.class
})
public class E2EPrivateOperatorTestSuite {
}
6 changes: 3 additions & 3 deletions src/test/java/suite/E2EPublicOperatorTestSuite.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
BasicTest.class,
CoreTest.class,
CoreRefreshTest.class,
V2ApiOperatorTest.class,
V2ApiOperatorPublicOnlyTest.class,
V2ApiOperatorLocalOnlyTest.class,
OperatorTest.class,
OperatorPublicOnlyTest.class,
OperatorLocalOnlyTest.class,
OptoutTest.class
})
public class E2EPublicOperatorTestSuite {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
import app.component.App;
import app.component.Operator;
import com.uid2.client.*;
import common.Const;
import common.EnvUtil;
import org.junit.jupiter.api.condition.EnabledIf;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
Expand All @@ -18,7 +16,7 @@
import static org.junit.jupiter.api.Assertions.assertTrue;

@EnabledIf("common.EnabledCondition#isLocal")
public class V2ApiOperatorLocalOnlyTest {
public class OperatorLocalOnlyTest {
@ParameterizedTest(name = "{index} ==> Sender {0} encrypts with {1}, recipient {2} decrypts with {3}, expected result is {4}")
@MethodSource({
"suite.operator.TestData#sharingArgs",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;

@SuppressWarnings("unused")
public class V2ApiOperatorPublicOnlyTest {
public class OperatorPublicOnlyTest {
private static final String EMAIL_OPTOUT_ID = "optout@unifiedid.com";
private static final String PHONE_OPTOUT_ID = "+00000000001";

Expand Down Expand Up @@ -81,6 +83,24 @@ public void testV2IdentityMapSpecialOptoutNoParam(String label, Operator operato
assertThat(response.get("body").get("unmapped").get(0).get("reason").asText()).isEqualTo("optout");
}

@ParameterizedTest(name = "/v3/identity/map - OPTOUT EMAIL, NO OPTOUT PARAM - {0} - {2} - Old Participant: {5}")
@MethodSource({
"suite.operator.TestData#tokenEmailArgsSpecialOptout",
"suite.operator.TestData#tokenPhoneArgsSpecialOptout"
})
public void testV3IdentityMapSpecialOptout(String label, Operator operator, String operatorName, String type, String identity) throws Exception {
if (isPrivateOperator(operator)) {
return;
}

// We need all properties to be there for Identity Map V3, so default all to empty
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need all properties to be present

// In JSON if a property appears multiple times, the last value wins
String payload = "{\"email\":[], \"email_hash\":[], \"phone\":[], \"phone_hash\":[], \"" + type + "\": [\"" + identity + "\"]}";
JsonNode response = operator.v3IdentityMap(payload);

assertThat(response.get("body").get(type).get(0).get("e").asText()).isEqualTo("optout");
}

@EnabledIf("common.EnabledCondition#isLocal")
@ParameterizedTest(name = "/v2/token/client-generate - {0} - {2}")
@MethodSource({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@
import org.junit.jupiter.params.provider.MethodSource;

import java.time.Duration;
import java.time.Instant;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;

@SuppressWarnings("unused")
public class V2ApiOperatorTest {
public class OperatorTest {
/*
TODO:
/v2/token/generate - Add failure case
Expand All @@ -31,6 +32,7 @@ public class V2ApiOperatorTest {

private static final ObjectMapper OBJECT_MAPPER = Mapper.getInstance();
private static final String CLIENT_SITE_ID = EnvUtil.getEnv(Const.Config.Operator.CLIENT_SITE_ID);
private static final int RAW_UID_SIZE = 44;

@ParameterizedTest(name = "/v2/token/generate - {0} - {2}")
@MethodSource({
Expand Down Expand Up @@ -96,39 +98,108 @@ public void testV2TokenValidate(String label, Operator operator, String operator

@ParameterizedTest(name = "/v2/identity/map - {0} - {2}")
@MethodSource({
"suite.operator.TestData#identityMapBatchEmailArgs",
"suite.operator.TestData#identityMapBatchPhoneArgs",
"suite.operator.TestData#identityMapBatchBadEmailArgs",
"suite.operator.TestData#identityMapBatchBadPhoneArgs"
})
public void testV2IdentityMap(String label, Operator operator, String operatorName, String payload) throws Exception {
public void testV2IdentityMapUnmapped(String label, Operator operator, String operatorName, String payload) throws Exception {
JsonNode response = operator.v2IdentityMap(payload);

// TODO: Assert the value
assertThat(response.at("/status").asText()).isEqualTo("success");
assertThat(response.at("/body/unmapped/0/reason").asText()).isEqualTo("invalid identifier");
}

@ParameterizedTest(name = "/v2/identity/map - {0} - {2}")
@MethodSource({
"suite.operator.TestData#identityMapBigBatchArgs"
"suite.operator.TestData#identityMapBatchEmailArgs",
"suite.operator.TestData#identityMapBatchPhoneArgs",
})
public void testV2IdentityMapLargeBatch(String label, Operator operator, String operatorName, String payload, List<String> diis) {
assertTimeoutPreemptively(Duration.ofSeconds(5), () -> { // Validate we didn't make mapping too slow.
JsonNode response = operator.v2IdentityMap(payload);
public void testV2IdentityMapMapped(String label, Operator operator, String operatorName, String payload) throws Exception {
JsonNode response = operator.v2IdentityMap(payload);

assertThat(response.at("/status").asText()).isEqualTo("success");
// TODO: Assert the value
assertThat(response.at("/status").asText()).isEqualTo("success");
}

var mapped = response.at("/body/mapped");
assertThat(mapped.size()).isEqualTo(10_000);
@ParameterizedTest(name = "/v2/identity/map - {0} - {2}")
@MethodSource({"suite.operator.TestData#identityMapArgs"})
public void testV2IdentityMap(
String label,
Operator operator,
String operatorName,
IdentityMapInput input,
List<String> diis
) {
assertTimeoutPreemptively(Duration.ofSeconds(5), () -> { // Validate we didn't make mapping too slow.
var response = operator.v2IdentityMap(input);

assertThat(response.isSuccess()).isTrue();

assertThat(response.getUnmappedIdentities()).isEmpty();

var allMappedDiis = response.getMappedIdentities();
assertThat(allMappedDiis.size()).isEqualTo(10_000);

for (var dii : diis) {
var mappedDii = allMappedDiis.get(dii);
assertThat(mappedDii).isNotNull();
assertThat(mappedDii.getRawUid().length()).isEqualTo(RAW_UID_SIZE);
assertThat(mappedDii.getBucketId()).isNotBlank();
}
});
}

for (int i = 0; i < 10_000; i++) {
assertThat(mapped.get(i).get("identifier").asText()).isEqualTo(diis.get(i));
assertThat(mapped.get(i).get("advertising_id").asText()).isNotNull().isNotEmpty();
assertThat(mapped.get(i).get("bucket_id").asText()).isNotNull().isNotEmpty();
@ParameterizedTest(name = "/v3/identity/map - {0} - {2}")
@MethodSource({"suite.operator.TestData#identityMapV3Args"})
public void testV3IdentityMapLargeBatch(
String label,
Operator operator,
String operatorName,
IdentityMapV3Input input,
List<String> diis
) {
assertTimeoutPreemptively(Duration.ofSeconds(5), () -> { // Validate we didn't make mapping too slow.
var response = operator.v3IdentityMap(input);

assertThat(response.isSuccess()).isTrue();

assertThat(response.getUnmappedIdentities()).isEmpty();

var allMappedDiis = response.getMappedIdentities();
assertThat(allMappedDiis.size()).isEqualTo(10_000);

for (var dii : diis) {
var mappedDii = allMappedDiis.get(dii);
assertThat(mappedDii).isNotNull();

// Current UID should always be there and should have correct length
assertThat(mappedDii.getCurrentRawUid().length()).isEqualTo(RAW_UID_SIZE);

// Previous UID is there for 90 days after rotation only, then it's null.
// If it's there, it should have the correct size
assertThat(mappedDii.getPreviousRawUid()).satisfiesAnyOf(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't want to do an assertion based on the dii's last updated?

uid -> assertThat(uid).isNull(),
uid -> assertThat(uid).hasSize(RAW_UID_SIZE)
);

// Sanity check that refresh from is a date not too far in the past.
// If it is, either there is an Operator issue or salt rotation hasn't been running for a long time.
assertThat(mappedDii.getRefreshFrom()).isAfter(Instant.now().minus(Duration.ofDays(10)));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to let the test pass if refresh from is in the past at all?

}
});
}

@ParameterizedTest(name = "/v3/identity/map - {0} - {2}")
@MethodSource({
"suite.operator.TestData#identityMapV3BatchBadEmailArgs",
"suite.operator.TestData#identityMapV3BatchBadPhoneArgs"
})
public void testV3IdentityMapUnmapped(String label, Operator operator, String operatorName, String payload, String section) throws Exception {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe rename section to identityType?

JsonNode response = operator.v3IdentityMap(payload);

assertThat(response.at("/status").asText()).isEqualTo("success");
assertThat(response.at("/body/" + section + "/0/e").asText()).isEqualTo("invalid identifier");
}


@ParameterizedTest(name = "/v2/identity/map - VALIDATE EMAIL - {0} - {2}")
@MethodSource({
Expand Down
Loading
Loading