From bb25649e206316df0c4460cfa30150a59c44d8e5 Mon Sep 17 00:00:00 2001 From: vikasrao23 Date: Fri, 20 Feb 2026 20:47:54 -0800 Subject: [PATCH 1/3] feat: add integration tests for NestJS generator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements integration test coverage for the NestJS generator following the standardized Dagger-based testing pattern. Changes: - test/integration/cmd/nestjs/run.go - Dagger test runner - test/nestjs-integration/package.json - npm configuration - test/nestjs-integration/tsconfig.json - TypeScript config - test/nestjs-integration/src/main.ts - NestJS test application - test/integration/cmd/run.go - Added NestJS test to main runner - Makefile - Added test-integration-nestjs target The test: 1. Builds the CLI from source 2. Generates NestJS/TypeScript code 3. Compiles with TypeScript and npm 4. Validates NestJS module integration with OpenFeature 5. Tests all flag types and methods Acceptance criteria met: ✅ NestJS generator covered by integration test ✅ Generated code compiles and passes execution ✅ Test integrated into CI suite ✅ Follows documented integration testing structure Closes #116 Signed-off-by: vikasrao23 --- Makefile | 6 ++ test/integration/cmd/nestjs/run.go | 99 +++++++++++++++++++++ test/integration/cmd/run.go | 9 +- test/nestjs-integration/package.json | 21 +++++ test/nestjs-integration/src/main.ts | 120 ++++++++++++++++++++++++++ test/nestjs-integration/tsconfig.json | 22 +++++ 6 files changed, 276 insertions(+), 1 deletion(-) create mode 100644 test/integration/cmd/nestjs/run.go create mode 100644 test/nestjs-integration/package.json create mode 100644 test/nestjs-integration/src/main.ts create mode 100644 test/nestjs-integration/tsconfig.json diff --git a/Makefile b/Makefile index b889d85..2b05c40 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-nestjs - Run NestJS 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-nestjs +test-integration-nestjs: + @echo "Running NestJS integration test with Dagger..." + @go run ./test/integration/cmd/nestjs/run.go + .PHONY: test-integration test-integration: @echo "Running all integration tests with Dagger..." diff --git a/test/integration/cmd/nestjs/run.go b/test/integration/cmd/nestjs/run.go new file mode 100644 index 0000000..29179d4 --- /dev/null +++ b/test/integration/cmd/nestjs/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 NestJS 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 NestJS 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{"package.json", "tsconfig.json", "src/**/*.ts"}, + }) + + // 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 NestJS client + generated := cli.WithExec([]string{ + "./cli", "generate", "nestjs", + "--manifest=/src/sample/sample_manifest.json", + "--output=/tmp/generated", + }) + + // Get generated files + generatedFiles := generated.Directory("/tmp/generated") + + // Test NestJS compilation with the generated files + nestjsContainer := client.Container(). + From("node:20-alpine"). + WithWorkdir("/app"). + WithDirectory("/app", testFiles). + WithDirectory("/app/src/generated", generatedFiles). + WithExec([]string{"npm", "install"}). + WithExec([]string{"npm", "run", "build"}). + WithExec([]string{"node", "dist/main.js"}) + + return nestjsContainer, nil +} + +// Name returns the name of the integration test +func (t *Test) Name() string { + return "nestjs" +} + +func main() { + ctx := context.Background() + + // Get project root + projectDir, err := filepath.Abs(os.Getenv("PWD")) + 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/nestjs-integration")) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to get test dir: %v\n", err) + os.Exit(1) + } + + // Create and run the NestJS 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..47ac429 100644 --- a/test/integration/cmd/run.go +++ b/test/integration/cmd/run.go @@ -45,7 +45,14 @@ func main() { os.Exit(1) } - // Add more tests here as they are available + // Run the NestJS integration test + nestjsCmd := exec.Command("go", "run", "github.com/open-feature/cli/test/integration/cmd/nestjs") + nestjsCmd.Stdout = os.Stdout + nestjsCmd.Stderr = os.Stderr + if err := nestjsCmd.Run(); err != nil { + fmt.Fprintf(os.Stderr, "Error running NestJS integration test: %v\n", err) + os.Exit(1) + } fmt.Println("=== All integration tests passed successfully ===") } diff --git a/test/nestjs-integration/package.json b/test/nestjs-integration/package.json new file mode 100644 index 0000000..528971e --- /dev/null +++ b/test/nestjs-integration/package.json @@ -0,0 +1,21 @@ +{ + "name": "cli-nestjs-integration-test", + "version": "1.0.0", + "description": "Integration test for OpenFeature CLI NestJS generator", + "scripts": { + "build": "tsc", + "start": "node dist/main.js" + }, + "dependencies": { + "@nestjs/common": "^10.0.0", + "@nestjs/core": "^10.0.0", + "@openfeature/nestjs-sdk": "^0.7.0", + "@openfeature/server-sdk": "^1.34.0", + "reflect-metadata": "^0.1.13", + "rxjs": "^7.8.1" + }, + "devDependencies": { + "@types/node": "^20.0.0", + "typescript": "^5.3.0" + } +} diff --git a/test/nestjs-integration/src/main.ts b/test/nestjs-integration/src/main.ts new file mode 100644 index 0000000..f4bf7aa --- /dev/null +++ b/test/nestjs-integration/src/main.ts @@ -0,0 +1,120 @@ +import { NestFactory } from '@nestjs/core'; +import { Module } from '@nestjs/common'; +import { OpenFeatureModule } from '@openfeature/nestjs-sdk'; +import { InMemoryProvider } from '@openfeature/server-sdk'; +import * as generated from './generated'; + +@Module({ + imports: [ + OpenFeatureModule.forRoot({ + provider: new InMemoryProvider({ + discountPercentage: { + disabled: false, + variants: { + default: 0.15, + }, + defaultVariant: 'default', + }, + enableFeatureA: { + disabled: false, + variants: { + default: false, + }, + defaultVariant: 'default', + }, + greetingMessage: { + disabled: false, + variants: { + default: 'Hello there!', + }, + defaultVariant: 'default', + }, + usernameMaxLength: { + disabled: false, + variants: { + default: 50, + }, + defaultVariant: 'default', + }, + themeCustomization: { + disabled: false, + variants: { + default: { + primaryColor: '#007bff', + secondaryColor: '#6c757d', + }, + }, + defaultVariant: 'default', + }, + }), + }), + ], +}) +class AppModule {} + +async function bootstrap() { + const app = await NestFactory.createApplicationContext(AppModule); + + try { + const client = app.get('OPENFEATURE_CLIENT'); + + // Use the generated code for all flag evaluations + const enableFeatureA = await generated.EnableFeatureA.value(client, {}); + console.log('enableFeatureA:', enableFeatureA); + + const enableFeatureADetails = await generated.EnableFeatureA.valueWithDetails(client, {}); + if (enableFeatureADetails.errorCode) { + throw new Error('Error evaluating boolean flag'); + } + + const discount = await generated.DiscountPercentage.value(client, {}); + console.log('Discount Percentage:', discount.toFixed(2)); + + const discountDetails = await generated.DiscountPercentage.valueWithDetails(client, {}); + if (discountDetails.errorCode) { + throw new Error('Failed to get discount'); + } + + const greetingMessage = await generated.GreetingMessage.value(client, {}); + console.log('greetingMessage:', greetingMessage); + + const greetingDetails = await generated.GreetingMessage.valueWithDetails(client, {}); + if (greetingDetails.errorCode) { + throw new Error('Error evaluating string flag'); + } + + const usernameMaxLength = await generated.UsernameMaxLength.value(client, {}); + console.log('usernameMaxLength:', usernameMaxLength); + + const usernameDetails = await generated.UsernameMaxLength.valueWithDetails(client, {}); + if (usernameDetails.errorCode) { + throw new Error('Error evaluating int flag'); + } + + const themeCustomization = await generated.ThemeCustomization.value(client, {}); + console.log('themeCustomization:', themeCustomization); + + const themeDetails = await generated.ThemeCustomization.valueWithDetails(client, {}); + if (themeDetails.errorCode) { + throw new Error('Error evaluating object flag'); + } + + // Test the getKey() method functionality for all flags + console.log('enableFeatureA flag key:', generated.EnableFeatureA.getKey()); + console.log('discountPercentage flag key:', generated.DiscountPercentage.getKey()); + console.log('greetingMessage flag key:', generated.GreetingMessage.getKey()); + console.log('usernameMaxLength flag key:', generated.UsernameMaxLength.getKey()); + console.log('themeCustomization flag key:', generated.ThemeCustomization.getKey()); + + console.log('Generated NestJS code compiles successfully!'); + + await app.close(); + process.exit(0); + } catch (error) { + console.error('Error:', error); + await app.close(); + process.exit(1); + } +} + +bootstrap(); diff --git a/test/nestjs-integration/tsconfig.json b/test/nestjs-integration/tsconfig.json new file mode 100644 index 0000000..5485d9d --- /dev/null +++ b/test/nestjs-integration/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "module": "commonjs", + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "allowSyntheticDefaultImports": true, + "target": "ES2021", + "sourceMap": true, + "outDir": "./dist", + "baseUrl": "./", + "incremental": true, + "skipLibCheck": true, + "strictNullChecks": false, + "noImplicitAny": false, + "strictBindCallApply": false, + "forceConsistentCasingInFileNames": false, + "noFallthroughCasesInSwitch": false, + "esModuleInterop": true + } +} From cd924dc7e01b216c184bc454b362a2d24cb60b6b Mon Sep 17 00:00:00 2001 From: Vikas Rao Date: Sun, 22 Feb 2026 08:49:15 -0800 Subject: [PATCH 2/3] Address Gemini code review feedback - Use os.Getwd() instead of os.Getenv("PWD") for better portability - Remove go mod tidy from integration test build (unnecessary) - Fix import path to './generated/openfeature' for clarity - Use OPENFEATURE_CLIENT constant instead of hardcoded string - Use GeneratedOpenFeatureModule to properly test the generator - Add TestService with NestJS decorators to validate primary generator output - Verify decorators inject flag values correctly --- test/integration/cmd/nestjs/run.go | 3 +- test/nestjs-integration/src/main.ts | 78 +++++++++++++++++++++-------- 2 files changed, 59 insertions(+), 22 deletions(-) diff --git a/test/integration/cmd/nestjs/run.go b/test/integration/cmd/nestjs/run.go index 29179d4..126c3bf 100644 --- a/test/integration/cmd/nestjs/run.go +++ b/test/integration/cmd/nestjs/run.go @@ -40,7 +40,6 @@ func (t *Test) Run(ctx context.Context, client *dagger.Client) (*dagger.Containe 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"}) @@ -76,7 +75,7 @@ func main() { ctx := context.Background() // Get project root - projectDir, err := filepath.Abs(os.Getenv("PWD")) + projectDir, err := os.Getwd() if err != nil { fmt.Fprintf(os.Stderr, "Failed to get project dir: %v\n", err) os.Exit(1) diff --git a/test/nestjs-integration/src/main.ts b/test/nestjs-integration/src/main.ts index f4bf7aa..d2dacb4 100644 --- a/test/nestjs-integration/src/main.ts +++ b/test/nestjs-integration/src/main.ts @@ -1,12 +1,35 @@ import { NestFactory } from '@nestjs/core'; -import { Module } from '@nestjs/common'; -import { OpenFeatureModule } from '@openfeature/nestjs-sdk'; -import { InMemoryProvider } from '@openfeature/server-sdk'; -import * as generated from './generated'; +import { Module, Injectable } from '@nestjs/common'; +import { OpenFeatureModule, OPENFEATURE_CLIENT } from '@openfeature/nestjs-sdk'; +import { InMemoryProvider, Client } from '@openfeature/server-sdk'; +import * as generated from './generated/openfeature'; +import { GeneratedOpenFeatureModule } from './generated/openfeature-module'; + +// Service that uses generated decorators to test NestJS-specific functionality +@Injectable() +class TestService { + constructor( + @generated.EnableFeatureA() private enableFeatureA: boolean, + @generated.DiscountPercentage() private discountPercentage: number, + @generated.GreetingMessage() private greetingMessage: string, + @generated.UsernameMaxLength() private usernameMaxLength: number, + @generated.ThemeCustomization() private themeCustomization: any, + ) {} + + getFlags() { + return { + enableFeatureA: this.enableFeatureA, + discountPercentage: this.discountPercentage, + greetingMessage: this.greetingMessage, + usernameMaxLength: this.usernameMaxLength, + themeCustomization: this.themeCustomization, + }; + } +} @Module({ imports: [ - OpenFeatureModule.forRoot({ + GeneratedOpenFeatureModule.forRoot({ provider: new InMemoryProvider({ discountPercentage: { disabled: false, @@ -49,6 +72,7 @@ import * as generated from './generated'; }), }), ], + providers: [TestService], }) class AppModule {} @@ -56,11 +80,22 @@ async function bootstrap() { const app = await NestFactory.createApplicationContext(AppModule); try { - const client = app.get('OPENFEATURE_CLIENT'); - - // Use the generated code for all flag evaluations + const client = app.get(OPENFEATURE_CLIENT); + const testService = app.get(TestService); + + // Test NestJS decorators by getting flags from the service + const flagsFromDecorators = testService.getFlags(); + console.log('Flags from NestJS decorators:'); + console.log(' enableFeatureA:', flagsFromDecorators.enableFeatureA); + console.log(' discountPercentage:', flagsFromDecorators.discountPercentage.toFixed(2)); + console.log(' greetingMessage:', flagsFromDecorators.greetingMessage); + console.log(' usernameMaxLength:', flagsFromDecorators.usernameMaxLength); + console.log(' themeCustomization:', flagsFromDecorators.themeCustomization); + + // Use the generated code for flag evaluations with client const enableFeatureA = await generated.EnableFeatureA.value(client, {}); - console.log('enableFeatureA:', enableFeatureA); + console.log('\nDirect flag evaluation:'); + console.log(' enableFeatureA:', enableFeatureA); const enableFeatureADetails = await generated.EnableFeatureA.valueWithDetails(client, {}); if (enableFeatureADetails.errorCode) { @@ -68,7 +103,7 @@ async function bootstrap() { } const discount = await generated.DiscountPercentage.value(client, {}); - console.log('Discount Percentage:', discount.toFixed(2)); + console.log(' Discount Percentage:', discount.toFixed(2)); const discountDetails = await generated.DiscountPercentage.valueWithDetails(client, {}); if (discountDetails.errorCode) { @@ -76,7 +111,7 @@ async function bootstrap() { } const greetingMessage = await generated.GreetingMessage.value(client, {}); - console.log('greetingMessage:', greetingMessage); + console.log(' greetingMessage:', greetingMessage); const greetingDetails = await generated.GreetingMessage.valueWithDetails(client, {}); if (greetingDetails.errorCode) { @@ -84,7 +119,7 @@ async function bootstrap() { } const usernameMaxLength = await generated.UsernameMaxLength.value(client, {}); - console.log('usernameMaxLength:', usernameMaxLength); + console.log(' usernameMaxLength:', usernameMaxLength); const usernameDetails = await generated.UsernameMaxLength.valueWithDetails(client, {}); if (usernameDetails.errorCode) { @@ -92,7 +127,7 @@ async function bootstrap() { } const themeCustomization = await generated.ThemeCustomization.value(client, {}); - console.log('themeCustomization:', themeCustomization); + console.log(' themeCustomization:', themeCustomization); const themeDetails = await generated.ThemeCustomization.valueWithDetails(client, {}); if (themeDetails.errorCode) { @@ -100,13 +135,16 @@ async function bootstrap() { } // Test the getKey() method functionality for all flags - console.log('enableFeatureA flag key:', generated.EnableFeatureA.getKey()); - console.log('discountPercentage flag key:', generated.DiscountPercentage.getKey()); - console.log('greetingMessage flag key:', generated.GreetingMessage.getKey()); - console.log('usernameMaxLength flag key:', generated.UsernameMaxLength.getKey()); - console.log('themeCustomization flag key:', generated.ThemeCustomization.getKey()); - - console.log('Generated NestJS code compiles successfully!'); + console.log('\nFlag keys:'); + console.log(' enableFeatureA flag key:', generated.EnableFeatureA.getKey()); + console.log(' discountPercentage flag key:', generated.DiscountPercentage.getKey()); + console.log(' greetingMessage flag key:', generated.GreetingMessage.getKey()); + console.log(' usernameMaxLength flag key:', generated.UsernameMaxLength.getKey()); + console.log(' themeCustomization flag key:', generated.ThemeCustomization.getKey()); + + console.log('\n✅ Generated NestJS code compiles successfully!'); + console.log('✅ NestJS decorators work correctly!'); + console.log('✅ GeneratedOpenFeatureModule integrates properly!'); await app.close(); process.exit(0); From f2dbfd91b9a71412619d02fdf8ca6c196a5f0e01 Mon Sep 17 00:00:00 2001 From: Vikas Rao Date: Sun, 22 Feb 2026 19:28:24 -0800 Subject: [PATCH 3/3] Address Gemini code review comments - Refactor duplicated test runner code in test/integration/cmd/run.go - Replace 'any' type with ThemeCustomization interface in main.ts - Break down long bootstrap function into focused helper functions - Enable strictNullChecks and noImplicitAny in tsconfig.json --- test/integration/cmd/run.go | 61 ++++------- test/nestjs-integration/src/main.ts | 152 +++++++++++++++----------- test/nestjs-integration/tsconfig.json | 4 +- 3 files changed, 110 insertions(+), 107 deletions(-) diff --git a/test/integration/cmd/run.go b/test/integration/cmd/run.go index 47ac429..eae4e7a 100644 --- a/test/integration/cmd/run.go +++ b/test/integration/cmd/run.go @@ -6,52 +6,29 @@ import ( "os/exec" ) -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) +// runIntegrationTest runs a single integration test for the specified language +func runIntegrationTest(language string) error { + cmd := exec.Command("go", "run", fmt.Sprintf("github.com/open-feature/cli/test/integration/cmd/%s", language)) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return fmt.Errorf("error running %s integration test: %w", language, err) } + return nil +} - // 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) - } - // 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) - } +func main() { + // List of all integration tests to run + tests := []string{"csharp", "go", "nodejs", "angular", "nestjs"} - // 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) - } + fmt.Println("=== Running all integration tests ===") - // Run the NestJS integration test - nestjsCmd := exec.Command("go", "run", "github.com/open-feature/cli/test/integration/cmd/nestjs") - nestjsCmd.Stdout = os.Stdout - nestjsCmd.Stderr = os.Stderr - if err := nestjsCmd.Run(); err != nil { - fmt.Fprintf(os.Stderr, "Error running NestJS integration test: %v\n", err) - os.Exit(1) + // Run each integration test + for _, test := range tests { + if err := runIntegrationTest(test); err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + os.Exit(1) + } } fmt.Println("=== All integration tests passed successfully ===") diff --git a/test/nestjs-integration/src/main.ts b/test/nestjs-integration/src/main.ts index d2dacb4..6cc7e58 100644 --- a/test/nestjs-integration/src/main.ts +++ b/test/nestjs-integration/src/main.ts @@ -5,6 +5,12 @@ import { InMemoryProvider, Client } from '@openfeature/server-sdk'; import * as generated from './generated/openfeature'; import { GeneratedOpenFeatureModule } from './generated/openfeature-module'; +// Type definition for theme customization object +interface ThemeCustomization { + primaryColor: string; + secondaryColor: string; +} + // Service that uses generated decorators to test NestJS-specific functionality @Injectable() class TestService { @@ -13,7 +19,7 @@ class TestService { @generated.DiscountPercentage() private discountPercentage: number, @generated.GreetingMessage() private greetingMessage: string, @generated.UsernameMaxLength() private usernameMaxLength: number, - @generated.ThemeCustomization() private themeCustomization: any, + @generated.ThemeCustomization() private themeCustomization: ThemeCustomization, ) {} getFlags() { @@ -76,6 +82,84 @@ class TestService { }) class AppModule {} +// Test NestJS decorators by getting flags from the service +function testNestJSDecorators(testService: TestService): void { + const flagsFromDecorators = testService.getFlags(); + console.log('Flags from NestJS decorators:'); + console.log(' enableFeatureA:', flagsFromDecorators.enableFeatureA); + console.log(' discountPercentage:', flagsFromDecorators.discountPercentage.toFixed(2)); + console.log(' greetingMessage:', flagsFromDecorators.greetingMessage); + console.log(' usernameMaxLength:', flagsFromDecorators.usernameMaxLength); + console.log(' themeCustomization:', flagsFromDecorators.themeCustomization); +} + +// Test direct flag evaluation using the generated client methods +async function testDirectFlagEvaluation(client: Client): Promise { + console.log('\nDirect flag evaluation:'); + + // Boolean flag + const enableFeatureA = await generated.EnableFeatureA.value(client, {}); + console.log(' enableFeatureA:', enableFeatureA); + + const enableFeatureADetails = await generated.EnableFeatureA.valueWithDetails(client, {}); + if (enableFeatureADetails.errorCode) { + throw new Error('Error evaluating boolean flag'); + } + + // Number flag + const discount = await generated.DiscountPercentage.value(client, {}); + console.log(' Discount Percentage:', discount.toFixed(2)); + + const discountDetails = await generated.DiscountPercentage.valueWithDetails(client, {}); + if (discountDetails.errorCode) { + throw new Error('Failed to get discount'); + } + + // String flag + const greetingMessage = await generated.GreetingMessage.value(client, {}); + console.log(' greetingMessage:', greetingMessage); + + const greetingDetails = await generated.GreetingMessage.valueWithDetails(client, {}); + if (greetingDetails.errorCode) { + throw new Error('Error evaluating string flag'); + } + + // Integer flag + const usernameMaxLength = await generated.UsernameMaxLength.value(client, {}); + console.log(' usernameMaxLength:', usernameMaxLength); + + const usernameDetails = await generated.UsernameMaxLength.valueWithDetails(client, {}); + if (usernameDetails.errorCode) { + throw new Error('Error evaluating int flag'); + } + + // Object flag + const themeCustomization = await generated.ThemeCustomization.value(client, {}); + console.log(' themeCustomization:', themeCustomization); + + const themeDetails = await generated.ThemeCustomization.valueWithDetails(client, {}); + if (themeDetails.errorCode) { + throw new Error('Error evaluating object flag'); + } +} + +// Test the getKey() method functionality for all flags +function testFlagKeys(): void { + console.log('\nFlag keys:'); + console.log(' enableFeatureA flag key:', generated.EnableFeatureA.getKey()); + console.log(' discountPercentage flag key:', generated.DiscountPercentage.getKey()); + console.log(' greetingMessage flag key:', generated.GreetingMessage.getKey()); + console.log(' usernameMaxLength flag key:', generated.UsernameMaxLength.getKey()); + console.log(' themeCustomization flag key:', generated.ThemeCustomization.getKey()); +} + +// Print success messages +function printSuccessMessages(): void { + console.log('\n✅ Generated NestJS code compiles successfully!'); + console.log('✅ NestJS decorators work correctly!'); + console.log('✅ GeneratedOpenFeatureModule integrates properly!'); +} + async function bootstrap() { const app = await NestFactory.createApplicationContext(AppModule); @@ -83,68 +167,10 @@ async function bootstrap() { const client = app.get(OPENFEATURE_CLIENT); const testService = app.get(TestService); - // Test NestJS decorators by getting flags from the service - const flagsFromDecorators = testService.getFlags(); - console.log('Flags from NestJS decorators:'); - console.log(' enableFeatureA:', flagsFromDecorators.enableFeatureA); - console.log(' discountPercentage:', flagsFromDecorators.discountPercentage.toFixed(2)); - console.log(' greetingMessage:', flagsFromDecorators.greetingMessage); - console.log(' usernameMaxLength:', flagsFromDecorators.usernameMaxLength); - console.log(' themeCustomization:', flagsFromDecorators.themeCustomization); - - // Use the generated code for flag evaluations with client - const enableFeatureA = await generated.EnableFeatureA.value(client, {}); - console.log('\nDirect flag evaluation:'); - console.log(' enableFeatureA:', enableFeatureA); - - const enableFeatureADetails = await generated.EnableFeatureA.valueWithDetails(client, {}); - if (enableFeatureADetails.errorCode) { - throw new Error('Error evaluating boolean flag'); - } - - const discount = await generated.DiscountPercentage.value(client, {}); - console.log(' Discount Percentage:', discount.toFixed(2)); - - const discountDetails = await generated.DiscountPercentage.valueWithDetails(client, {}); - if (discountDetails.errorCode) { - throw new Error('Failed to get discount'); - } - - const greetingMessage = await generated.GreetingMessage.value(client, {}); - console.log(' greetingMessage:', greetingMessage); - - const greetingDetails = await generated.GreetingMessage.valueWithDetails(client, {}); - if (greetingDetails.errorCode) { - throw new Error('Error evaluating string flag'); - } - - const usernameMaxLength = await generated.UsernameMaxLength.value(client, {}); - console.log(' usernameMaxLength:', usernameMaxLength); - - const usernameDetails = await generated.UsernameMaxLength.valueWithDetails(client, {}); - if (usernameDetails.errorCode) { - throw new Error('Error evaluating int flag'); - } - - const themeCustomization = await generated.ThemeCustomization.value(client, {}); - console.log(' themeCustomization:', themeCustomization); - - const themeDetails = await generated.ThemeCustomization.valueWithDetails(client, {}); - if (themeDetails.errorCode) { - throw new Error('Error evaluating object flag'); - } - - // Test the getKey() method functionality for all flags - console.log('\nFlag keys:'); - console.log(' enableFeatureA flag key:', generated.EnableFeatureA.getKey()); - console.log(' discountPercentage flag key:', generated.DiscountPercentage.getKey()); - console.log(' greetingMessage flag key:', generated.GreetingMessage.getKey()); - console.log(' usernameMaxLength flag key:', generated.UsernameMaxLength.getKey()); - console.log(' themeCustomization flag key:', generated.ThemeCustomization.getKey()); - - console.log('\n✅ Generated NestJS code compiles successfully!'); - console.log('✅ NestJS decorators work correctly!'); - console.log('✅ GeneratedOpenFeatureModule integrates properly!'); + testNestJSDecorators(testService); + await testDirectFlagEvaluation(client); + testFlagKeys(); + printSuccessMessages(); await app.close(); process.exit(0); diff --git a/test/nestjs-integration/tsconfig.json b/test/nestjs-integration/tsconfig.json index 5485d9d..8e53fa0 100644 --- a/test/nestjs-integration/tsconfig.json +++ b/test/nestjs-integration/tsconfig.json @@ -12,8 +12,8 @@ "baseUrl": "./", "incremental": true, "skipLibCheck": true, - "strictNullChecks": false, - "noImplicitAny": false, + "strictNullChecks": true, + "noImplicitAny": true, "strictBindCallApply": false, "forceConsistentCasingInFileNames": false, "noFallthroughCasesInSwitch": false,