Skip to content

Commit 28d81c4

Browse files
committed
feat: add integration tests for Java generator
Implements integration test coverage for the Java generator following the standardized Dagger-based testing pattern. Changes: - test/integration/cmd/java/run.go - Dagger test runner for Java - test/java-integration/pom.xml - Maven configuration with OpenFeature SDK - test/java-integration/src/main/java/dev/openfeature/Main.java - Test program - test/integration/cmd/run.go - Added Java test to main runner - Makefile - Added test-integration-java target The test: 1. Builds the CLI from source 2. Generates Java feature flag client code 3. Compiles and executes the generated code with Maven 4. Validates all flag types (boolean, string, int, double, object) 5. Runs as part of CI suite via test-integration target Acceptance criteria met: ✅ Java generator covered by integration test ✅ Generated Java code compiles and passes execution ✅ Test integrated into CI suite ✅ Follows documented integration testing structure Closes #115 Signed-off-by: vikasrao23 <vikasrao23@users.noreply.github.com>
1 parent 144eb0f commit 28d81c4

File tree

5 files changed

+267
-1
lines changed

5 files changed

+267
-1
lines changed

Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ help:
1010
@echo " test-integration - Run all integration tests"
1111
@echo " test-integration-csharp - Run C# integration tests"
1212
@echo " test-integration-go - Run Go integration tests"
13+
@echo " test-integration-java - Run Java integration tests"
1314
@echo " test-integration-nodejs - Run NodeJS integration tests"
1415
@echo " generate - Generate all code (API clients, docs, schema)"
1516
@echo " generate-api - Generate API clients from OpenAPI specs"
@@ -67,6 +68,11 @@ test-integration-angular:
6768
@echo "Running Angular integration test with Dagger..."
6869
@go run ./test/integration/cmd/angular/run.go
6970

71+
.PHONY: test-integration-java
72+
test-integration-java:
73+
@echo "Running Java integration test with Dagger..."
74+
@go run ./test/integration/cmd/java/run.go
75+
7076
.PHONY: test-integration
7177
test-integration:
7278
@echo "Running all integration tests with Dagger..."

test/integration/cmd/java/run.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os"
7+
"path/filepath"
8+
9+
"dagger.io/dagger"
10+
"github.com/open-feature/cli/test/integration"
11+
)
12+
13+
// Test implements the integration test for the Java generator
14+
type Test struct {
15+
// ProjectDir is the absolute path to the root of the project
16+
ProjectDir string
17+
// TestDir is the absolute path to the test directory
18+
TestDir string
19+
}
20+
21+
// New creates a new Test
22+
func New(projectDir, testDir string) *Test {
23+
return &Test{
24+
ProjectDir: projectDir,
25+
TestDir: testDir,
26+
}
27+
}
28+
29+
// Run executes the Java integration test using Dagger
30+
func (t *Test) Run(ctx context.Context, client *dagger.Client) (*dagger.Container, error) {
31+
// Source code container
32+
source := client.Host().Directory(t.ProjectDir)
33+
testFiles := client.Host().Directory(t.TestDir, dagger.HostDirectoryOpts{
34+
Include: []string{"pom.xml", "src/**/*.java"},
35+
})
36+
37+
// Build the CLI
38+
cli := client.Container().
39+
From("golang:1.24-alpine").
40+
WithExec([]string{"apk", "add", "--no-cache", "git"}).
41+
WithDirectory("/src", source).
42+
WithWorkdir("/src").
43+
WithExec([]string{"go", "mod", "tidy"}).
44+
WithExec([]string{"go", "mod", "download"}).
45+
WithExec([]string{"go", "build", "-o", "cli", "./cmd/openfeature"})
46+
47+
// Generate Java client
48+
generated := cli.WithExec([]string{
49+
"./cli", "generate", "java",
50+
"--manifest=/src/sample/sample_manifest.json",
51+
"--output=/tmp/generated",
52+
"--package-name=dev.openfeature.generated",
53+
})
54+
55+
// Get generated files
56+
generatedFiles := generated.Directory("/tmp/generated")
57+
58+
// Test Java compilation with the generated files
59+
javaContainer := client.Container().
60+
From("maven:3.9-eclipse-temurin-21-alpine").
61+
WithWorkdir("/app").
62+
WithDirectory("/app", testFiles).
63+
WithDirectory("/app/src/main/java/dev/openfeature/generated", generatedFiles).
64+
WithExec([]string{"mvn", "clean", "compile", "-B", "-q"}).
65+
WithExec([]string{"mvn", "exec:java", "-Dexec.mainClass=dev.openfeature.Main", "-q"})
66+
67+
return javaContainer, nil
68+
}
69+
70+
// Name returns the name of the integration test
71+
func (t *Test) Name() string {
72+
return "java"
73+
}
74+
75+
func main() {
76+
ctx := context.Background()
77+
78+
// Get project root
79+
projectDir, err := filepath.Abs(os.Getenv("PWD"))
80+
if err != nil {
81+
fmt.Fprintf(os.Stderr, "Failed to get project dir: %v\n", err)
82+
os.Exit(1)
83+
}
84+
85+
// Get test directory
86+
testDir, err := filepath.Abs(filepath.Join(projectDir, "test/java-integration"))
87+
if err != nil {
88+
fmt.Fprintf(os.Stderr, "Failed to get test dir: %v\n", err)
89+
os.Exit(1)
90+
}
91+
92+
// Create and run the Java integration test
93+
test := New(projectDir, testDir)
94+
95+
if err := integration.RunTest(ctx, test); err != nil {
96+
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
97+
os.Exit(1)
98+
}
99+
}

test/integration/cmd/run.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,14 @@ func main() {
4545
os.Exit(1)
4646
}
4747

48-
// Add more tests here as they are available
48+
// Run the Java integration test
49+
javaCmd := exec.Command("go", "run", "github.com/open-feature/cli/test/integration/cmd/java")
50+
javaCmd.Stdout = os.Stdout
51+
javaCmd.Stderr = os.Stderr
52+
if err := javaCmd.Run(); err != nil {
53+
fmt.Fprintf(os.Stderr, "Error running Java integration test: %v\n", err)
54+
os.Exit(1)
55+
}
4956

5057
fmt.Println("=== All integration tests passed successfully ===")
5158
}

test/java-integration/pom.xml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<groupId>dev.openfeature</groupId>
8+
<artifactId>cli-java-integration-test</artifactId>
9+
<version>1.0-SNAPSHOT</version>
10+
11+
<properties>
12+
<maven.compiler.source>11</maven.compiler.source>
13+
<maven.compiler.target>11</maven.compiler.target>
14+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
15+
</properties>
16+
17+
<dependencies>
18+
<!-- OpenFeature Java SDK -->
19+
<dependency>
20+
<groupId>dev.openfeature</groupId>
21+
<artifactId>sdk</artifactId>
22+
<version>1.14.0</version>
23+
</dependency>
24+
</dependencies>
25+
26+
<build>
27+
<plugins>
28+
<plugin>
29+
<groupId>org.apache.maven.plugins</groupId>
30+
<artifactId>maven-compiler-plugin</artifactId>
31+
<version>3.11.0</version>
32+
<configuration>
33+
<source>11</source>
34+
<target>11</target>
35+
</configuration>
36+
</plugin>
37+
<plugin>
38+
<groupId>org.codehaus.mojo</groupId>
39+
<artifactId>exec-maven-plugin</artifactId>
40+
<version>3.1.0</version>
41+
<configuration>
42+
<mainClass>dev.openfeature.Main</mainClass>
43+
</configuration>
44+
</plugin>
45+
</plugins>
46+
</build>
47+
</project>
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package dev.openfeature;
2+
3+
import dev.openfeature.generated.*;
4+
import dev.openfeature.sdk.*;
5+
import dev.openfeature.sdk.providers.memory.Flag;
6+
import dev.openfeature.sdk.providers.memory.InMemoryProvider;
7+
8+
import java.util.HashMap;
9+
import java.util.Map;
10+
11+
public class Main {
12+
public static void main(String[] args) {
13+
try {
14+
run();
15+
System.out.println("Generated Java code compiles successfully!");
16+
} catch (Exception e) {
17+
System.err.println("Error: " + e.getMessage());
18+
e.printStackTrace();
19+
System.exit(1);
20+
}
21+
}
22+
23+
private static void run() throws Exception {
24+
// Set up the in-memory provider with test flags
25+
Map<String, Flag<?>> flags = new HashMap<>();
26+
27+
flags.put("discountPercentage", Flag.builder()
28+
.variant("default", 0.15)
29+
.defaultVariant("default")
30+
.build());
31+
32+
flags.put("enableFeatureA", Flag.builder()
33+
.variant("default", false)
34+
.defaultVariant("default")
35+
.build());
36+
37+
flags.put("greetingMessage", Flag.builder()
38+
.variant("default", "Hello there!")
39+
.defaultVariant("default")
40+
.build());
41+
42+
flags.put("usernameMaxLength", Flag.builder()
43+
.variant("default", 50)
44+
.defaultVariant("default")
45+
.build());
46+
47+
Map<String, Object> themeConfig = new HashMap<>();
48+
themeConfig.put("primaryColor", "#007bff");
49+
themeConfig.put("secondaryColor", "#6c757d");
50+
51+
flags.put("themeCustomization", Flag.builder()
52+
.variant("default", new Value(themeConfig))
53+
.defaultVariant("default")
54+
.build());
55+
56+
InMemoryProvider provider = new InMemoryProvider(flags);
57+
58+
// Set the provider
59+
OpenFeatureAPI.getInstance().setProviderAndWait(provider);
60+
61+
Client client = OpenFeatureAPI.getInstance().getClient();
62+
MutableContext evalContext = new MutableContext();
63+
64+
// Use the generated code for all flag evaluations
65+
Boolean enableFeatureA = EnableFeatureA.value(client, evalContext);
66+
System.out.println("enableFeatureA: " + enableFeatureA);
67+
FlagEvaluationDetails<Boolean> enableFeatureADetails = EnableFeatureA.valueWithDetails(client, evalContext);
68+
if (enableFeatureADetails.getErrorCode() != null) {
69+
throw new Exception("Error evaluating boolean flag");
70+
}
71+
72+
Double discount = DiscountPercentage.value(client, evalContext);
73+
System.out.printf("Discount Percentage: %.2f%n", discount);
74+
FlagEvaluationDetails<Double> discountDetails = DiscountPercentage.valueWithDetails(client, evalContext);
75+
if (discountDetails.getErrorCode() != null) {
76+
throw new Exception("Failed to get discount");
77+
}
78+
79+
String greetingMessage = GreetingMessage.value(client, evalContext);
80+
System.out.println("greetingMessage: " + greetingMessage);
81+
FlagEvaluationDetails<String> greetingDetails = GreetingMessage.valueWithDetails(client, evalContext);
82+
if (greetingDetails.getErrorCode() != null) {
83+
throw new Exception("Error evaluating string flag");
84+
}
85+
86+
Integer usernameMaxLength = UsernameMaxLength.value(client, evalContext);
87+
System.out.println("usernameMaxLength: " + usernameMaxLength);
88+
FlagEvaluationDetails<Integer> usernameDetails = UsernameMaxLength.valueWithDetails(client, evalContext);
89+
if (usernameDetails.getErrorCode() != null) {
90+
throw new Exception("Error evaluating int flag");
91+
}
92+
93+
Value themeCustomization = ThemeCustomization.value(client, evalContext);
94+
FlagEvaluationDetails<Value> themeDetails = ThemeCustomization.valueWithDetails(client, evalContext);
95+
if (themeDetails.getErrorCode() != null) {
96+
throw new Exception("Error evaluating object flag");
97+
}
98+
System.out.println("themeCustomization: " + themeCustomization);
99+
100+
// Test the getKey() method functionality for all flags
101+
System.out.println("enableFeatureA flag key: " + EnableFeatureA.getKey());
102+
System.out.println("discountPercentage flag key: " + DiscountPercentage.getKey());
103+
System.out.println("greetingMessage flag key: " + GreetingMessage.getKey());
104+
System.out.println("usernameMaxLength flag key: " + UsernameMaxLength.getKey());
105+
System.out.println("themeCustomization flag key: " + ThemeCustomization.getKey());
106+
}
107+
}

0 commit comments

Comments
 (0)