Skip to content

Commit aa5d2c9

Browse files
committed
test cases
1 parent bc1a371 commit aa5d2c9

2 files changed

Lines changed: 370 additions & 0 deletions

File tree

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
import {
2+
ClassSerializerInterceptor,
3+
INestApplication,
4+
ValidationPipe,
5+
} from '@nestjs/common';
6+
import { Test, TestingModule } from '@nestjs/testing';
7+
import * as request from 'supertest';
8+
import { Reflector } from '@nestjs/core';
9+
import * as GRPC from '@grpc/grpc-js';
10+
import * as ProtoLoader from '@grpc/proto-loader';
11+
import { ResetProtoFile } from 'juno-proto';
12+
import { AppModule } from '../src/app.module';
13+
import { RpcExceptionFilter } from 'src/rpc_exception_filter';
14+
15+
let app: INestApplication;
16+
let apiKey: string;
17+
18+
const ADMIN_EMAIL = 'test-superadmin@test.com';
19+
const ADMIN_PASSWORD = 'test-password';
20+
21+
jest.setTimeout(15000);
22+
23+
async function createApiKey(): Promise<string> {
24+
const response = await request(app.getHttpServer())
25+
.post('/auth/key')
26+
.set('X-User-Email', ADMIN_EMAIL)
27+
.set('X-User-Password', ADMIN_PASSWORD)
28+
.send({
29+
environment: 'prod',
30+
project: { name: 'test-seed-project' },
31+
});
32+
33+
return response.body.apiKey;
34+
}
35+
36+
beforeAll(async () => {
37+
const moduleFixture: TestingModule = await Test.createTestingModule({
38+
imports: [AppModule],
39+
}).compile();
40+
41+
app = moduleFixture.createNestApplication();
42+
app.useGlobalPipes(new ValidationPipe({ transform: true }));
43+
app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector)));
44+
app.useGlobalFilters(new RpcExceptionFilter());
45+
46+
await app.init();
47+
48+
const proto = ProtoLoader.loadSync([ResetProtoFile]) as any;
49+
const protoGrpc = GRPC.loadPackageDefinition(proto) as any;
50+
const resetClient = new protoGrpc.juno.reset_db.DatabaseReset(
51+
process.env.DB_SERVICE_ADDR,
52+
GRPC.credentials.createInsecure(),
53+
);
54+
55+
await new Promise<void>((resolve, reject) => {
56+
resetClient.resetDb({}, (error: Error | null) => {
57+
if (error) reject(error);
58+
else resolve();
59+
});
60+
});
61+
});
62+
63+
beforeEach(async () => {
64+
if (!apiKey) {
65+
apiKey = await createApiKey();
66+
}
67+
});
68+
69+
afterAll(async () => {
70+
await app.close();
71+
});
72+
73+
describe('Feature Flag Routes (e2e)', () => {
74+
it('creates and retrieves a feature flag', async () => {
75+
await request(app.getHttpServer())
76+
.post('/feature-flag/create')
77+
.set('Authorization', `Bearer ${apiKey}`)
78+
.send({
79+
id: 'new-flag',
80+
enabled: false,
81+
description: 'temp',
82+
})
83+
.expect(201)
84+
.expect((response) => {
85+
expect(response.body).toEqual({
86+
id: 'new-flag',
87+
enabled: false,
88+
description: 'temp',
89+
});
90+
});
91+
92+
await request(app.getHttpServer())
93+
.get('/feature-flag/new-flag')
94+
.set('Authorization', `Bearer ${apiKey}`)
95+
.expect(200)
96+
.expect((response) => {
97+
expect(response.body).toEqual({
98+
id: 'new-flag',
99+
enabled: false,
100+
description: 'temp',
101+
});
102+
});
103+
});
104+
105+
it('rejects an invalid create request', async () => {
106+
await request(app.getHttpServer())
107+
.post('/feature-flag/create')
108+
.set('Authorization', `Bearer ${apiKey}`)
109+
.send({ enabled: true })
110+
.expect(400);
111+
});
112+
113+
it('updates a feature flag', async () => {
114+
await request(app.getHttpServer())
115+
.post('/feature-flag/create')
116+
.set('Authorization', `Bearer ${apiKey}`)
117+
.send({
118+
id: 'updated-flag',
119+
enabled: false,
120+
description: 'original',
121+
})
122+
.expect(201);
123+
124+
await request(app.getHttpServer())
125+
.patch('/feature-flag/updated-flag')
126+
.set('Authorization', `Bearer ${apiKey}`)
127+
.send({ enabled: true, description: 'original' })
128+
.expect(200)
129+
.expect((response) => {
130+
expect(response.body).toEqual({
131+
id: 'updated-flag',
132+
enabled: true,
133+
description: 'original',
134+
});
135+
});
136+
});
137+
138+
it('deletes a feature flag', async () => {
139+
await request(app.getHttpServer())
140+
.post('/feature-flag/create')
141+
.set('Authorization', `Bearer ${apiKey}`)
142+
.send({
143+
id: 'deleted-flag',
144+
enabled: true,
145+
description: 'temp',
146+
})
147+
.expect(201);
148+
149+
await request(app.getHttpServer())
150+
.delete('/feature-flag/deleted-flag')
151+
.set('Authorization', `Bearer ${apiKey}`)
152+
.expect(200)
153+
.expect((response) => {
154+
expect(response.body).toEqual({ success: true });
155+
});
156+
});
157+
});
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
import { INestMicroservice } from '@nestjs/common';
2+
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
3+
import { Test, TestingModule } from '@nestjs/testing';
4+
import * as ProtoLoader from '@grpc/proto-loader';
5+
import * as GRPC from '@grpc/grpc-js';
6+
7+
import {
8+
FeatureFlagProtoFile,
9+
ResetProto,
10+
ResetProtoFile,
11+
FeatureFlagProto,
12+
} from 'juno-proto';
13+
import { AppModule } from 'src/app.module';
14+
import { FeatureFlag } from '@prisma/client';
15+
16+
const { JUNO_FEATURE_FLAG_PACKAGE_NAME } = FeatureFlagProto;
17+
18+
let app: INestMicroservice;
19+
20+
jest.setTimeout(10000);
21+
22+
async function initApp() {
23+
const moduleFixture: TestingModule = await Test.createTestingModule({
24+
imports: [AppModule],
25+
}).compile();
26+
27+
const app = moduleFixture.createNestMicroservice<MicroserviceOptions>({
28+
transport: Transport.GRPC,
29+
options: {
30+
package: [
31+
JUNO_FEATURE_FLAG_PACKAGE_NAME,
32+
ResetProto.JUNO_RESET_DB_PACKAGE_NAME,
33+
],
34+
protoPath: [FeatureFlagProtoFile, ResetProtoFile],
35+
url: process.env.DB_SERVICE_ADDR,
36+
},
37+
});
38+
39+
await app.init();
40+
await app.listen();
41+
return app;
42+
}
43+
44+
beforeAll(async () => {
45+
const app = await initApp();
46+
const proto = ProtoLoader.loadSync([ResetProtoFile]) as any;
47+
const protoGRPC = GRPC.loadPackageDefinition(proto) as any;
48+
49+
const resetClient = new protoGRPC.juno.reset_db.DatabaseReset(
50+
process.env.DB_SERVICE_ADDR,
51+
GRPC.credentials.createInsecure(),
52+
);
53+
54+
await new Promise((resolve) => {
55+
resetClient.resetDb({}, () => {
56+
resolve(0);
57+
});
58+
});
59+
60+
app.close();
61+
});
62+
63+
beforeEach(async () => {
64+
app = await initApp();
65+
});
66+
67+
afterEach(async () => {
68+
app.close();
69+
});
70+
71+
describe('DB Service Feature Flag Tests', () => {
72+
let featureFlagClient: any;
73+
74+
beforeEach(() => {
75+
const featureFlagProto = ProtoLoader.loadSync([
76+
FeatureFlagProtoFile,
77+
]) as any;
78+
79+
const featureFlagProtoGRPC = GRPC.loadPackageDefinition(
80+
featureFlagProto,
81+
) as any;
82+
83+
featureFlagClient =
84+
new featureFlagProtoGRPC.juno.feature_flag.FeatureFlagDbService(
85+
process.env.DB_SERVICE_ADDR,
86+
GRPC.credentials.createInsecure(),
87+
);
88+
});
89+
90+
it('creates a feature flag record correctly with all fields', async () => {
91+
const promise = new Promise((resolve) => {
92+
featureFlagClient.createFlag(
93+
{
94+
id: 'test-flag-full',
95+
enabled: true,
96+
description: 'A full test feature flag',
97+
},
98+
(err) => {
99+
expect(err).toBeNull();
100+
resolve({});
101+
},
102+
);
103+
});
104+
105+
await promise;
106+
});
107+
108+
it('can retrieve an existing feature flag record', async () => {
109+
const flagId = 'retrieve-test-flag';
110+
111+
const createPromise = new Promise((resolve) => {
112+
featureFlagClient.createFlag(
113+
{
114+
id: flagId,
115+
enabled: true,
116+
description: 'Ready for retrieval',
117+
},
118+
(err, resp: FeatureFlag) => {
119+
expect(err).toBeNull();
120+
resolve(resp);
121+
},
122+
);
123+
});
124+
125+
await createPromise;
126+
127+
const retrievePromise = new Promise((resolve) => {
128+
featureFlagClient.getFlag(
129+
{
130+
id: flagId,
131+
},
132+
(err, resp: FeatureFlag) => {
133+
expect(err).toBeNull();
134+
expect(resp).not.toBeNull();
135+
expect(resp.id).toEqual(flagId);
136+
expect(resp.enabled).toEqual(true);
137+
resolve({});
138+
},
139+
);
140+
});
141+
142+
await retrievePromise;
143+
});
144+
145+
it("can set a feature flag's state for an existing record", async () => {
146+
const flagId = 'set-test-flag';
147+
148+
const createPromise = new Promise((resolve) => {
149+
featureFlagClient.createFlag(
150+
{
151+
id: flagId,
152+
enabled: false,
153+
description: 'original',
154+
},
155+
(err, resp: FeatureFlag) => {
156+
expect(err).toBeNull();
157+
resolve(resp);
158+
},
159+
);
160+
});
161+
162+
await createPromise;
163+
164+
const updatePromise = new Promise((resolve) => {
165+
featureFlagClient.setFlag(
166+
{
167+
id: flagId,
168+
enabled: true,
169+
description: 'State has been updated',
170+
},
171+
(err, resp: FeatureFlag) => {
172+
expect(err).toBeNull();
173+
resolve({});
174+
},
175+
);
176+
});
177+
178+
await updatePromise;
179+
});
180+
181+
it('can delete an existing feature flag record', async () => {
182+
const flagId = 'delete-test-flag';
183+
184+
const createPromise = new Promise((resolve) => {
185+
featureFlagClient.createFlag(
186+
{
187+
id: flagId,
188+
enabled: false,
189+
},
190+
(err, resp) => {
191+
expect(err).toBeNull();
192+
resolve(resp);
193+
},
194+
);
195+
});
196+
197+
await createPromise;
198+
199+
const deletionPromise = new Promise((resolve) => {
200+
featureFlagClient.deleteFlag(
201+
{
202+
id: flagId,
203+
},
204+
(err, resp) => {
205+
expect(err).toBeNull();
206+
resolve({});
207+
},
208+
);
209+
});
210+
211+
await deletionPromise;
212+
});
213+
});

0 commit comments

Comments
 (0)