Skip to content

Commit 62fcb33

Browse files
committed
First draft of new test structure
1 parent 1930c20 commit 62fcb33

File tree

4 files changed

+460
-0
lines changed

4 files changed

+460
-0
lines changed
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
package com.microsoft.aad.msal4j;
2+
3+
import com.fasterxml.jackson.databind.JsonNode;
4+
5+
import java.util.ArrayList;
6+
import java.util.HashMap;
7+
import java.util.List;
8+
import java.util.Map;
9+
10+
import static com.microsoft.aad.msal4j.ManagedIdentitySourceType.SERVICE_FABRIC;
11+
import static org.junit.jupiter.api.Assertions.assertEquals;
12+
13+
import com.microsoft.aad.msal4j.Shortcuts.TestConfig;
14+
import com.microsoft.aad.msal4j.Shortcuts.TestObject;
15+
import com.microsoft.aad.msal4j.Shortcuts.TestAction;
16+
import org.slf4j.Logger;
17+
import org.slf4j.LoggerFactory;
18+
19+
public class RunnerHelper {
20+
private static final Logger LOG = LoggerFactory.getLogger(RunnerHelper.class);
21+
22+
/**
23+
* Create Managed Identity applications from the test configuration.
24+
* This method processes the "arrange" section of the test configuration.
25+
*/
26+
static Map<String, ManagedIdentityApplication> createAppsFromConfig(TestConfig config) {
27+
Map<String, ManagedIdentityApplication> apps = new HashMap<>();
28+
29+
for (String appName : config.getAllArrangeObjects()) {
30+
TestObject appObject = config.getArrangeObject(appName);
31+
if ("ManagedIdentityClient".equals(appObject.getType())) {
32+
ManagedIdentityId identityId = createManagedIdentityId(appObject);
33+
List<String> capabilities = extractClientCapabilities(appObject);
34+
IEnvironmentVariables envVars = createEnvironmentVariables(config);
35+
// TODO: other application properties
36+
37+
ManagedIdentityApplication app = ManagedIdentityApplication.builder(identityId)
38+
.clientCapabilities(capabilities)
39+
.build();
40+
41+
ManagedIdentityApplication.setEnvironmentVariables(envVars);
42+
43+
apps.put(appName, app);
44+
} //TODO: Confidential and public clients
45+
}
46+
47+
return apps;
48+
}
49+
50+
/**
51+
* Execute an action and return the result
52+
* This method uses the "act" section of the test configuration.
53+
*/
54+
static IAuthenticationResult executeAction(ManagedIdentityApplication app, TestAction action) throws Exception {
55+
if (action.getMethodName().equals("AcquireTokenForManagedIdentity")) {
56+
LOG.info(String.format("Executing action: %s", action.getMethodName()));
57+
58+
ManagedIdentityParameters params = buildManagedIdentityParameters(action);
59+
60+
IAuthenticationResult result = app.acquireTokenForManagedIdentity(params).get();
61+
62+
LOG.info("Action result:");
63+
LOG.info(String.format("Access Token: %s", result.accessToken()));
64+
LOG.info(String.format("ID Token : %s", result.idToken()));
65+
LOG.info(String.format("Account : %s", result.account()));
66+
LOG.info(String.format("Token Source: %s", result.metadata().tokenSource()));
67+
68+
return result;
69+
} else {
70+
//TODO: other token calls and apps
71+
throw new UnsupportedOperationException("Unsupported action: " + action.getMethodName());
72+
}
73+
}
74+
75+
/**
76+
* Validate assertions against a result.
77+
* This method uses the "assert" section of the test configuration.
78+
*/
79+
static void validateAssertions(IAuthenticationResult result, Map<String, JsonNode> assertions) {
80+
assertions.forEach((key, value) -> {
81+
switch (key) {
82+
case "token_source":
83+
LOG.info("Validating token source");
84+
validateTokenSource(value.asText(), result);
85+
break;
86+
//TODO: other assertions
87+
default:
88+
// Optional: Handle unknown assertion types
89+
break;
90+
}
91+
});
92+
}
93+
94+
/**
95+
* Create managed identity ID from test object
96+
*/
97+
static ManagedIdentityId createManagedIdentityId(TestObject appObject) {
98+
String idType = appObject.getProperty("managed_identity").get("ManagedIdentityIdType").asText();
99+
100+
if ("SystemAssigned".equals(idType)) {
101+
return ManagedIdentityId.systemAssigned();
102+
} else {
103+
// TODO: handle user assertions
104+
return null;
105+
}
106+
}
107+
108+
/**
109+
* Extract client capabilities from test object
110+
*/
111+
static List<String> extractClientCapabilities(TestObject testObject) {
112+
List<String> capabilities = new ArrayList<>();
113+
JsonNode capabilitiesNode = testObject.getProperty("client_capabilities");
114+
115+
if (capabilitiesNode != null && capabilitiesNode.isArray()) {
116+
capabilitiesNode.forEach(node -> capabilities.add(node.asText()));
117+
}
118+
119+
LOG.info(String.format("Extracted client capabilities: %s", capabilities));
120+
121+
return capabilities;
122+
}
123+
124+
//TODO: Re-used from other Managed Identity tests, specific to this proof-of-concept but should be more generic
125+
static IEnvironmentVariables createEnvironmentVariables(TestConfig config) {
126+
return new EnvironmentVariablesHelper(
127+
SERVICE_FABRIC,
128+
config.getEnvironmentVariable("IDENTITY_ENDPOINT"));
129+
}
130+
131+
/**
132+
* Build parameters for token acquisition
133+
*/
134+
static ManagedIdentityParameters buildManagedIdentityParameters(TestAction action) {
135+
String resource = action.getParameter("resource").asText();
136+
137+
LOG.info(String.format("Building ManagedIdentityParameters with resource: %s", resource));
138+
139+
ManagedIdentityParameters.ManagedIdentityParametersBuilder builder =
140+
ManagedIdentityParameters.builder(resource);
141+
142+
// Add optional claims challenge
143+
if (action.hasParameter("claims_challenge")) {
144+
builder.claims(action.getParameter("claims_challenge").asText());
145+
}
146+
147+
//TODO: other parameters
148+
149+
return builder.build();
150+
}
151+
152+
/**
153+
* Validate token source assertion, either cache or identity provider
154+
*/
155+
static void validateTokenSource(String expectedSource, IAuthenticationResult result) {
156+
TokenSource expected = "identity_provider".equals(expectedSource) ?
157+
TokenSource.IDENTITY_PROVIDER : TokenSource.CACHE;
158+
LOG.info(String.format("Expected token source: %s", expected));
159+
LOG.info(String.format("Actual token source : %s", result.metadata().tokenSource()));
160+
161+
assertEquals(expected, result.metadata().tokenSource());
162+
}
163+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package com.microsoft.aad.msal4j;
2+
3+
import com.fasterxml.jackson.databind.JsonNode;
4+
5+
import java.util.HashMap;
6+
import java.util.Map;
7+
8+
import com.fasterxml.jackson.databind.ObjectMapper;
9+
import com.microsoft.aad.msal4j.Shortcuts.TestConfig;
10+
import com.microsoft.aad.msal4j.Shortcuts.TestObject;
11+
import com.microsoft.aad.msal4j.Shortcuts.TestStep;
12+
import com.microsoft.aad.msal4j.Shortcuts.TestAction;
13+
14+
//TODO: Too specific for the test case used in this proof-of-concept, should be able to reuse the regular JsonHelper class
15+
class RunnerJsonHelper {
16+
private static final ObjectMapper mapper = new ObjectMapper();
17+
18+
/**
19+
* Parse test configuration from JSON string
20+
*/
21+
static TestConfig parseTestConfig(String jsonContent) {
22+
try {
23+
return JsonParser.parseConfig(mapper.readTree(jsonContent));
24+
} catch (Exception e) {
25+
throw new RuntimeException("Failed to parse test configuration: " + e.getMessage(), e);
26+
}
27+
}
28+
29+
/**
30+
* Helper class for parsing JSON into test configuration
31+
*/
32+
private static class JsonParser {
33+
static TestConfig parseConfig(JsonNode rootNode) {
34+
TestConfig.Builder builder = new TestConfig.Builder()
35+
.type(rootNode.path("type").asText())
36+
.version(rootNode.path("ver").asInt());
37+
38+
parseEnvironment(rootNode.path("env"), builder);
39+
parseArrangement(rootNode.path("arrange"), builder);
40+
parseSteps(rootNode.path("steps"), builder);
41+
42+
return builder.build();
43+
}
44+
45+
private static void parseEnvironment(JsonNode envNode, TestConfig.Builder builder) {
46+
envNode.fields().forEachRemaining(entry ->
47+
builder.addEnvironmentVariable(entry.getKey(), entry.getValue().asText()));
48+
}
49+
50+
private static void parseArrangement(JsonNode arrangeNode, TestConfig.Builder builder) {
51+
arrangeNode.fields().forEachRemaining(appEntry -> {
52+
String appName = appEntry.getKey();
53+
JsonNode appNode = appEntry.getValue();
54+
55+
appNode.fields().forEachRemaining(classEntry -> {
56+
String classType = classEntry.getKey();
57+
JsonNode classNode = classEntry.getValue();
58+
59+
Map<String, JsonNode> properties = new HashMap<>();
60+
classNode.fields().forEachRemaining(prop ->
61+
properties.put(prop.getKey(), prop.getValue()));
62+
63+
builder.addArrangedObject(appName,
64+
new TestObject(appName, classType, properties));
65+
});
66+
});
67+
}
68+
69+
private static void parseSteps(JsonNode stepsNode, TestConfig.Builder builder) {
70+
for (JsonNode stepNode : stepsNode) {
71+
TestAction action = parseAction(stepNode.path("act"));
72+
Map<String, JsonNode> assertions = parseAssertions(stepNode.path("assert"));
73+
74+
if (action != null) {
75+
builder.addStep(new TestStep(action, assertions));
76+
}
77+
}
78+
}
79+
80+
private static TestAction parseAction(JsonNode actNode) {
81+
if (actNode.isMissingNode()) return null;
82+
83+
String actorKey = actNode.fieldNames().next();
84+
String[] actorParts = actorKey.split("\\.", 2);
85+
86+
Map<String, JsonNode> parameters = new HashMap<>();
87+
actNode.get(actorKey).fields().forEachRemaining(entry ->
88+
parameters.put(entry.getKey(), entry.getValue()));
89+
90+
return new TestAction(actorParts[0], actorParts[1], parameters);
91+
}
92+
93+
private static Map<String, JsonNode> parseAssertions(JsonNode assertNode) {
94+
Map<String, JsonNode> assertions = new HashMap<>();
95+
96+
if (!assertNode.isMissingNode()) {
97+
assertNode.fields().forEachRemaining(entry ->
98+
assertions.put(entry.getKey(), entry.getValue()));
99+
}
100+
101+
return assertions;
102+
}
103+
}
104+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.microsoft.aad.msal4j;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import com.microsoft.aad.msal4j.Shortcuts.TestConfig;
6+
import com.microsoft.aad.msal4j.Shortcuts.TestStep;
7+
8+
import java.util.Map;
9+
10+
class RunnerTest {
11+
12+
@Test
13+
void testManagedIdentityWithJsonConfig() throws Exception {
14+
//TODO: get test cases list from the server
15+
TestConfig config = RunnerJsonHelper.parseTestConfig(Shortcuts.getTestConfigJson());
16+
Map<String, ManagedIdentityApplication> apps = RunnerHelper.createAppsFromConfig(config);
17+
18+
for (ManagedIdentityApplication app : apps.values()) {
19+
//Execute the "steps" section of the test config
20+
for (TestStep step : config.getSteps()) {
21+
//Execute the "act" section of the test config
22+
IAuthenticationResult result = RunnerHelper.executeAction(app, step.getAction());
23+
24+
//Execute the "assert" section of the test config
25+
RunnerHelper.validateAssertions(result, step.getAssertions());
26+
}
27+
}
28+
}
29+
}

0 commit comments

Comments
 (0)