Skip to content

Commit 2b69c11

Browse files
committed
Add integ test to compile and check AWS endpoints
Also adds to CI/CD to test every night. Loads AWS modeled endpoints, makes sure compiling to BDD works, endpoint test cases work, and other things like bytecode round-trip works and disassembler works.
1 parent 1576330 commit 2b69c11

2 files changed

Lines changed: 181 additions & 0 deletions

File tree

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: Endpoint Ruleset Compatibility
2+
3+
on:
4+
# Run daily at 3 AM UTC
5+
schedule:
6+
- cron: '0 3 * * *'
7+
8+
# Allow manual trigger
9+
workflow_dispatch:
10+
11+
permissions:
12+
contents: read
13+
14+
jobs:
15+
endpoint-compatibility:
16+
runs-on: ubuntu-latest
17+
name: AWS Endpoint Ruleset Compatibility
18+
19+
steps:
20+
- uses: actions/checkout@v6
21+
22+
- name: Checkout api-models-aws
23+
uses: actions/checkout@v6
24+
with:
25+
repository: aws/api-models-aws
26+
path: api-models-aws
27+
28+
- name: Set up JDK 21
29+
uses: actions/setup-java@v5
30+
with:
31+
java-version: 21
32+
distribution: 'corretto'
33+
34+
- name: Cache compiled buildscripts
35+
uses: actions/cache@v5
36+
with:
37+
key: ${{ runner.os }}-gradle-${{ hashFiles('buildSrc/**/*.kts') }}
38+
path: |
39+
./buildSrc/build
40+
41+
- name: Setup Gradle
42+
uses: gradle/actions/setup-gradle@v5
43+
with:
44+
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
45+
gradle-home-cache-includes: |
46+
caches
47+
48+
- name: Run endpoint ruleset compatibility tests
49+
env:
50+
API_MODELS_AWS_DIR: ${{ github.workspace }}/api-models-aws
51+
run: |
52+
./gradlew :client:client-rulesengine:integ --tests "*.AwsEndpointRulesetTest" --stacktrace
53+
54+
- uses: actions/upload-artifact@v6
55+
if: failure()
56+
with:
57+
name: endpoint-compatibility-report
58+
path: '**/build/reports/tests'
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package software.amazon.smithy.java.client.rulesengine;
7+
8+
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
9+
import static org.junit.jupiter.api.Assertions.assertNotNull;
10+
import static org.junit.jupiter.api.Assertions.assertTrue;
11+
12+
import java.io.IOException;
13+
import java.nio.file.Files;
14+
import java.nio.file.Path;
15+
import java.nio.file.Paths;
16+
import java.util.List;
17+
import java.util.stream.Stream;
18+
import org.junit.jupiter.api.condition.EnabledIf;
19+
import org.junit.jupiter.params.ParameterizedTest;
20+
import org.junit.jupiter.params.provider.MethodSource;
21+
import software.amazon.smithy.model.Model;
22+
import software.amazon.smithy.model.loader.ModelAssembler;
23+
import software.amazon.smithy.model.shapes.ServiceShape;
24+
import software.amazon.smithy.rulesengine.language.EndpointRuleSet;
25+
import software.amazon.smithy.rulesengine.language.evaluation.TestEvaluator;
26+
import software.amazon.smithy.rulesengine.logic.bdd.NodeReversal;
27+
import software.amazon.smithy.rulesengine.logic.bdd.SiftingOptimization;
28+
import software.amazon.smithy.rulesengine.logic.cfg.Cfg;
29+
import software.amazon.smithy.rulesengine.traits.EndpointBddTrait;
30+
import software.amazon.smithy.rulesengine.traits.EndpointRuleSetTrait;
31+
import software.amazon.smithy.rulesengine.traits.EndpointTestCase;
32+
import software.amazon.smithy.rulesengine.traits.EndpointTestsTrait;
33+
34+
/**
35+
* Tests that every AWS service endpoint ruleset can be compiled to BDD bytecode,
36+
* evaluated against its endpoint test cases, round-tripped through serialization,
37+
* and disassembled without errors.
38+
*
39+
* <p>Set the {@code API_MODELS_AWS_DIR} environment variable to the root of a local
40+
* checkout of api-models-aws to enable this test.
41+
*/
42+
class AwsEndpointRulesetTest {
43+
44+
private static Path getModelsDir() {
45+
var dir = System.getenv("API_MODELS_AWS_DIR");
46+
return dir != null ? Paths.get(dir, "models") : null;
47+
}
48+
49+
static boolean modelsAvailable() {
50+
var dir = getModelsDir();
51+
return dir != null && Files.isDirectory(dir);
52+
}
53+
54+
static Stream<Path> awsModels() throws IOException {
55+
var modelsDir = getModelsDir();
56+
try (var paths = Files.find(modelsDir,
57+
4,
58+
(p, a) -> p.toString().endsWith(".json")
59+
&& p.getParent().getParent().getFileName().toString().equals("service"))) {
60+
return paths.sorted().toList().stream();
61+
}
62+
}
63+
64+
@EnabledIf("modelsAvailable")
65+
@ParameterizedTest(name = "{0}")
66+
@MethodSource("awsModels")
67+
void compileEvaluateAndRoundTrip(Path modelPath) {
68+
// Load the model
69+
Model model = Model.assembler()
70+
.addImport(modelPath)
71+
.putProperty(ModelAssembler.ALLOW_UNKNOWN_TRAITS, true)
72+
.discoverModels()
73+
.assemble()
74+
.unwrap();
75+
76+
// Find the service with endpoint rules
77+
ServiceShape service = model.getServiceShapes()
78+
.stream()
79+
.filter(s -> s.hasTrait(EndpointRuleSetTrait.class))
80+
.findFirst()
81+
.orElse(null);
82+
if (service == null) {
83+
// Skip models without endpoint rules
84+
return;
85+
}
86+
87+
EndpointRuleSet ruleSet = service.expectTrait(EndpointRuleSetTrait.class).getEndpointRuleSet();
88+
List<EndpointTestCase> testCases = service.hasTrait(EndpointTestsTrait.class)
89+
? service.expectTrait(EndpointTestsTrait.class).getTestCases()
90+
: List.of();
91+
92+
// Evaluate test cases against the original rules (if these fail, no point in testing the BDD)
93+
for (EndpointTestCase testCase : testCases) {
94+
TestEvaluator.evaluate(ruleSet, testCase);
95+
}
96+
97+
// Compile to BDD
98+
Cfg cfg = Cfg.from(ruleSet);
99+
EndpointBddTrait bddTrait = EndpointBddTrait.from(cfg);
100+
EndpointBddTrait optimizedTrait = SiftingOptimization.builder().cfg(cfg).build().apply(bddTrait);
101+
EndpointBddTrait finalTrait = new NodeReversal().apply(optimizedTrait);
102+
103+
// Evaluate test cases against the BDD
104+
for (EndpointTestCase testCase : testCases) {
105+
TestEvaluator.evaluate(finalTrait, testCase);
106+
}
107+
108+
// Compile BDD to bytecode
109+
var builder = new RulesEngineBuilder();
110+
Bytecode compiled = builder.compile(finalTrait);
111+
assertNotNull(compiled);
112+
assertTrue(compiled.getBytecode().length > 0);
113+
114+
// Round-trip: serialize then load
115+
byte[] serialized = compiled.getBytecode();
116+
Bytecode loaded = builder.load(serialized);
117+
assertArrayEquals(serialized, loaded.getBytecode(), "Round-trip mismatch");
118+
119+
// Disassemble without error
120+
String disassembly = loaded.toString();
121+
assertTrue(disassembly.contains("=== Bytecode Program ==="));
122+
}
123+
}

0 commit comments

Comments
 (0)