-
Notifications
You must be signed in to change notification settings - Fork 19
feat: add integration tests for NestJS generator #223
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,98 @@ | ||||||
| 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", "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 := os.Getwd() | ||||||
|
||||||
| projectDir, err := os.Getwd() | |
| projectDir, err := filepath.Abs(os.Getenv("PWD")) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,184 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { NestFactory } from '@nestjs/core'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { GeneratedOpenFeatureModule } from './generated/openfeature-module'; | |
| import { GeneratedOpenFeatureModule } from './generated/openfeature-decorators'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The bootstrap function is quite long and handles multiple testing concerns. To improve readability and maintainability, consider breaking it down into smaller, more focused functions. For example, you could have separate functions for testing decorators, direct flag evaluation, and getKey() methods.
async function testDecorators(testService: TestService) {
// ... logic for testing decorators
}
async function testDirectEvaluation(client: Client) {
// ... logic for testing direct evaluation
}
function testGetKeyMethods() {
// ... logic for testing getKey()
}
async function bootstrap() {
const app = await NestFactory.createApplicationContext(AppModule);
try {
const client = app.get<Client>(OPENFEATURE_CLIENT);
const testService = app.get(TestService);
testDecorators(testService);
await testDirectEvaluation(client);
testGetKeyMethods();
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);
} catch (error) {
console.error('Error:', error);
await app.close();
process.exit(1);
}
}This structure makes the test flow clearer and each part easier to understand and modify.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The bootstrap function contains duplicated code for closing the application context (await app.close()). This can be refactored using a try...catch...finally block to improve maintainability and ensure the cleanup logic is always executed in a single place, regardless of whether an error occurred.
| async function bootstrap() { | |
| const app = await NestFactory.createApplicationContext(AppModule); | |
| try { | |
| const client = app.get<Client>(OPENFEATURE_CLIENT); | |
| const testService = app.get(TestService); | |
| testNestJSDecorators(testService); | |
| await testDirectFlagEvaluation(client); | |
| testFlagKeys(); | |
| printSuccessMessages(); | |
| await app.close(); | |
| process.exit(0); | |
| } catch (error) { | |
| console.error('Error:', error); | |
| await app.close(); | |
| process.exit(1); | |
| } | |
| } | |
| async function bootstrap() { | |
| const app = await NestFactory.createApplicationContext(AppModule); | |
| let exitCode = 0; | |
| try { | |
| const client = app.get<Client>(OPENFEATURE_CLIENT); | |
| const testService = app.get(TestService); | |
| testNestJSDecorators(testService); | |
| await testDirectFlagEvaluation(client); | |
| testFlagKeys(); | |
| printSuccessMessages(); | |
| } catch (error) { | |
| console.error('Error:', error); | |
| exitCode = 1; | |
| } finally { | |
| await app.close(); | |
| process.exit(exitCode); | |
| } | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Node.js version used here (node:20-alpine) is inconsistent with other Node.js-based integration tests. Both the Angular and NodeJS integration tests use node:22-alpine. For consistency and to ensure tests run with the same Node.js version, this should be updated to use node:22-alpine instead.