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..c80eaf9 --- /dev/null +++ b/test/integration/cmd/java/run.go @@ -0,0 +1,99 @@ +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, err := filepath.Abs(filepath.Join(projectDir, "test/java-integration")) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to get test dir: %v\n", err) + os.Exit(1) + } + + // 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..3c8ac8e 100644 --- a/test/integration/cmd/run.go +++ b/test/integration/cmd/run.go @@ -7,45 +7,29 @@ import ( ) func main() { - // Run the language-specific tests fmt.Println("=== Running all integration tests ===") - // Run the C# integration test - csharpCmd := exec.Command("go", "run", "github.com/open-feature/cli/test/integration/cmd/csharp") - csharpCmd.Stdout = os.Stdout - csharpCmd.Stderr = os.Stderr - if err := csharpCmd.Run(); err != nil { - fmt.Fprintf(os.Stderr, "Error running C# integration test: %v\n", err) - os.Exit(1) + tests := []struct { + name string + path string + }{ + {"C#", "github.com/open-feature/cli/test/integration/cmd/csharp"}, + {"Go", "github.com/open-feature/cli/test/integration/cmd/go"}, + {"NodeJS", "github.com/open-feature/cli/test/integration/cmd/nodejs"}, + {"Angular", "github.com/open-feature/cli/test/integration/cmd/angular"}, + {"Java", "github.com/open-feature/cli/test/integration/cmd/java"}, } - // Run the Go integration test - goCmd := exec.Command("go", "run", "github.com/open-feature/cli/test/integration/cmd/go") - goCmd.Stdout = os.Stdout - goCmd.Stderr = os.Stderr - if err := goCmd.Run(); err != nil { - fmt.Fprintf(os.Stderr, "Error running Go integration test: %v\n", err) - os.Exit(1) + for _, test := range tests { + fmt.Printf("--- Running %s integration test ---\n", test.name) + cmd := exec.Command("go", "run", test.path) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + fmt.Fprintf(os.Stderr, "Error running %s integration test: %v\n", test.name, err) + os.Exit(1) + } } - // Run the nodejs test - nodeCmd := exec.Command("go", "run", "github.com/open-feature/cli/test/integration/cmd/nodejs") - nodeCmd.Stdout = os.Stdout - nodeCmd.Stderr = os.Stderr - if err := nodeCmd.Run(); err != nil { - fmt.Fprintf(os.Stderr, "Error running nodejs integration test: %v\n", err) - os.Exit(1) - } - - // Run the Angular integration test - angularCmd := exec.Command("go", "run", "github.com/open-feature/cli/test/integration/cmd/angular") - angularCmd.Stdout = os.Stdout - angularCmd.Stderr = os.Stderr - if err := angularCmd.Run(); err != nil { - fmt.Fprintf(os.Stderr, "Error running Angular 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..0bb7be4 --- /dev/null +++ b/test/java-integration/pom.xml @@ -0,0 +1,47 @@ + + + 4.0.0 + + dev.openfeature + cli-java-integration-test + 1.0-SNAPSHOT + + + 21 + 21 + UTF-8 + + + + + + dev.openfeature + sdk + 1.14.0 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + 21 + 21 + + + + org.codehaus.mojo + exec-maven-plugin + 3.1.0 + + 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..7fb10a2 --- /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"); + } + + 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"); + } + + 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"); + } + + 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"); + } + + Value themeCustomization = ThemeCustomization.value(client, evalContext); + FlagEvaluationDetails themeDetails = ThemeCustomization.valueWithDetails(client, evalContext); + if (themeDetails.getErrorCode() != null) { + throw new Exception("Error evaluating object flag"); + } + 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()); + } +}