diff --git a/Makefile b/Makefile index b889d85..b05df5c 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,7 @@ help: @echo " test-integration - Run all integration tests" @echo " test-integration-csharp - Run C# integration tests" @echo " test-integration-go - Run Go integration tests" + @echo " test-integration-java - Run Java integration tests" @echo " test-integration-nodejs - Run NodeJS integration tests" @echo " generate - Generate all code (API clients, docs, schema)" @echo " generate-api - Generate API clients from OpenAPI specs" @@ -67,6 +68,11 @@ test-integration-angular: @echo "Running Angular integration test with Dagger..." @go run ./test/integration/cmd/angular/run.go +.PHONY: test-integration-java +test-integration-java: + @echo "Running Java integration test with Dagger..." + @go run ./test/integration/cmd/java/run.go + .PHONY: test-integration test-integration: @echo "Running all integration tests with Dagger..." diff --git a/test/integration/cmd/java/run.go b/test/integration/cmd/java/run.go new file mode 100644 index 0000000..b5c67cb --- /dev/null +++ b/test/integration/cmd/java/run.go @@ -0,0 +1,95 @@ +package main + +import ( + "context" + "fmt" + "os" + "path/filepath" + + "dagger.io/dagger" + "github.com/open-feature/cli/test/integration" +) + +// Test implements the integration test for the Java generator +type Test struct { + // ProjectDir is the absolute path to the root of the project + ProjectDir string + // TestDir is the absolute path to the test directory + TestDir string +} + +// New creates a new Test +func New(projectDir, testDir string) *Test { + return &Test{ + ProjectDir: projectDir, + TestDir: testDir, + } +} + +// Run executes the Java integration test using Dagger +func (t *Test) Run(ctx context.Context, client *dagger.Client) (*dagger.Container, error) { + // Source code container + source := client.Host().Directory(t.ProjectDir) + testFiles := client.Host().Directory(t.TestDir, dagger.HostDirectoryOpts{ + Include: []string{"pom.xml", "src/**/*.java"}, + }) + + // Build the CLI + cli := client.Container(). + From("golang:1.24-alpine"). + WithExec([]string{"apk", "add", "--no-cache", "git"}). + WithDirectory("/src", source). + WithWorkdir("/src"). + WithExec([]string{"go", "mod", "tidy"}). + WithExec([]string{"go", "mod", "download"}). + WithExec([]string{"go", "build", "-o", "cli", "./cmd/openfeature"}) + + // Generate Java client + generated := cli.WithExec([]string{ + "./cli", "generate", "java", + "--manifest=/src/sample/sample_manifest.json", + "--output=/tmp/generated", + "--package-name=dev.openfeature.generated", + }) + + // Get generated files + generatedFiles := generated.Directory("/tmp/generated") + + // Test Java compilation with the generated files + javaContainer := client.Container(). + From("maven:3.9-eclipse-temurin-21-alpine"). + WithWorkdir("/app"). + WithDirectory("/app", testFiles). + WithDirectory("/app/src/main/java/dev/openfeature/generated", generatedFiles). + WithExec([]string{"mvn", "clean", "compile", "-B", "-q"}). + WithExec([]string{"mvn", "exec:java", "-Dexec.mainClass=dev.openfeature.Main", "-q"}) + + return javaContainer, nil +} + +// Name returns the name of the integration test +func (t *Test) Name() string { + return "java" +} + +func main() { + ctx := context.Background() + + // Get project root + projectDir, err := os.Getwd() + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to get project dir: %v\n", err) + os.Exit(1) + } + + // Get test directory + testDir := filepath.Join(projectDir, "test/java-integration") + + // Create and run the Java integration test + test := New(projectDir, testDir) + + if err := integration.RunTest(ctx, test); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } +} diff --git a/test/integration/cmd/run.go b/test/integration/cmd/run.go index 5923c26..e75acaf 100644 --- a/test/integration/cmd/run.go +++ b/test/integration/cmd/run.go @@ -45,6 +45,15 @@ func main() { os.Exit(1) } + // Run the Java integration test + javaCmd := exec.Command("go", "run", "github.com/open-feature/cli/test/integration/cmd/java") + javaCmd.Stdout = os.Stdout + javaCmd.Stderr = os.Stderr + if err := javaCmd.Run(); err != nil { + fmt.Fprintf(os.Stderr, "Error running Java integration test: %v\n", err) + os.Exit(1) + } + // Add more tests here as they are available fmt.Println("=== All integration tests passed successfully ===") diff --git a/test/java-integration/pom.xml b/test/java-integration/pom.xml new file mode 100644 index 0000000..d1bc7c7 --- /dev/null +++ b/test/java-integration/pom.xml @@ -0,0 +1,48 @@ + + + 4.0.0 + + dev.openfeature + cli-java-integration-test + 1.0-SNAPSHOT + + + 21 + 21 + UTF-8 + 1.14.0 + 3.11.0 + 3.1.0 + + + + + + dev.openfeature + sdk + ${openfeature.sdk.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + + + + org.codehaus.mojo + exec-maven-plugin + ${exec-maven-plugin.version} + + dev.openfeature.Main + + + + + diff --git a/test/java-integration/src/main/java/dev/openfeature/Main.java b/test/java-integration/src/main/java/dev/openfeature/Main.java new file mode 100644 index 0000000..5d6a2d1 --- /dev/null +++ b/test/java-integration/src/main/java/dev/openfeature/Main.java @@ -0,0 +1,103 @@ +package dev.openfeature; + +import dev.openfeature.generated.*; +import dev.openfeature.sdk.*; +import dev.openfeature.sdk.providers.memory.Flag; +import dev.openfeature.sdk.providers.memory.InMemoryProvider; + +import java.util.Map; + +public class Main { + public static void main(String[] args) { + try { + run(); + System.out.println("Generated Java code compiles successfully!"); + } catch (Exception e) { + System.err.println("Error: " + e.getMessage()); + e.printStackTrace(); + System.exit(1); + } + } + + private static void run() throws Exception { + // Set up the in-memory provider with test flags + Map themeConfig = Map.of( + "primaryColor", "#007bff", + "secondaryColor", "#6c757d" + ); + + Map> flags = Map.of( + "discountPercentage", Flag.builder() + .variant("default", 0.15) + .defaultVariant("default") + .build(), + "enableFeatureA", Flag.builder() + .variant("default", false) + .defaultVariant("default") + .build(), + "greetingMessage", Flag.builder() + .variant("default", "Hello there!") + .defaultVariant("default") + .build(), + "usernameMaxLength", Flag.builder() + .variant("default", 50) + .defaultVariant("default") + .build(), + "themeCustomization", Flag.builder() + .variant("default", new Value(themeConfig)) + .defaultVariant("default") + .build() + ); + + InMemoryProvider provider = new InMemoryProvider(flags); + + // Set the provider + OpenFeatureAPI.getInstance().setProviderAndWait(provider); + + Client client = OpenFeatureAPI.getInstance().getClient(); + MutableContext evalContext = new MutableContext(); + + // Use the generated code for all flag evaluations + Boolean enableFeatureA = EnableFeatureA.value(client, evalContext); + System.out.println("enableFeatureA: " + enableFeatureA); + FlagEvaluationDetails enableFeatureADetails = EnableFeatureA.valueWithDetails(client, evalContext); + if (enableFeatureADetails.getErrorCode() != null) { + throw new Exception("Error evaluating boolean flag: " + enableFeatureADetails.getFlagKey()); + } + + Double discount = DiscountPercentage.value(client, evalContext); + System.out.printf("Discount Percentage: %.2f%n", discount); + FlagEvaluationDetails discountDetails = DiscountPercentage.valueWithDetails(client, evalContext); + if (discountDetails.getErrorCode() != null) { + throw new Exception("Failed to get discount for flag: " + discountDetails.getFlagKey()); + } + + String greetingMessage = GreetingMessage.value(client, evalContext); + System.out.println("greetingMessage: " + greetingMessage); + FlagEvaluationDetails greetingDetails = GreetingMessage.valueWithDetails(client, evalContext); + if (greetingDetails.getErrorCode() != null) { + throw new Exception("Error evaluating string flag: " + greetingDetails.getFlagKey()); + } + + Integer usernameMaxLength = UsernameMaxLength.value(client, evalContext); + System.out.println("usernameMaxLength: " + usernameMaxLength); + FlagEvaluationDetails usernameDetails = UsernameMaxLength.valueWithDetails(client, evalContext); + if (usernameDetails.getErrorCode() != null) { + throw new Exception("Error evaluating int flag: " + usernameDetails.getFlagKey()); + } + + Value themeCustomization = ThemeCustomization.value(client, evalContext); + FlagEvaluationDetails themeDetails = ThemeCustomization.valueWithDetails(client, evalContext); + if (themeDetails.getErrorCode() != null) { + throw new Exception("Error evaluating object flag: " + themeDetails.getFlagKey()); + } + System.out.println("themeCustomization: " + themeCustomization); + + // Test the getKey() method functionality for all flags + System.out.println("enableFeatureA flag key: " + EnableFeatureA.getKey()); + System.out.println("discountPercentage flag key: " + DiscountPercentage.getKey()); + System.out.println("greetingMessage flag key: " + GreetingMessage.getKey()); + System.out.println("usernameMaxLength flag key: " + UsernameMaxLength.getKey()); + System.out.println("themeCustomization flag key: " + ThemeCustomization.getKey()); + } +}