@@ -2,7 +2,12 @@ import type { TestingModule } from '@nestjs/testing';
2
2
import { Test } from '@nestjs/testing' ;
3
3
import type { INestApplication } from '@nestjs/common' ;
4
4
import supertest from 'supertest' ;
5
- import { OpenFeatureController , OpenFeatureControllerContextScopedController , OpenFeatureTestService } from './test-app' ;
5
+ import {
6
+ OpenFeatureController ,
7
+ OpenFeatureContextScopedController ,
8
+ OpenFeatureRequireFlagsEnabledController ,
9
+ OpenFeatureTestService ,
10
+ } from './test-app' ;
6
11
import { exampleContextFactory , getOpenFeatureDefaultTestModule } from './fixtures' ;
7
12
import { OpenFeatureModule } from '../src' ;
8
13
import { defaultProvider , providers } from './fixtures' ;
@@ -14,11 +19,9 @@ describe('OpenFeature SDK', () => {
14
19
15
20
beforeAll ( async ( ) => {
16
21
moduleRef = await Test . createTestingModule ( {
17
- imports : [
18
- getOpenFeatureDefaultTestModule ( )
19
- ] ,
22
+ imports : [ getOpenFeatureDefaultTestModule ( ) ] ,
20
23
providers : [ OpenFeatureTestService ] ,
21
- controllers : [ OpenFeatureController ] ,
24
+ controllers : [ OpenFeatureController , OpenFeatureRequireFlagsEnabledController ] ,
22
25
} ) . compile ( ) ;
23
26
app = moduleRef . createNestApplication ( ) ;
24
27
app = await app . init ( ) ;
@@ -112,7 +115,7 @@ describe('OpenFeature SDK', () => {
112
115
} ) ;
113
116
114
117
describe ( 'evaluation context service should' , ( ) => {
115
- it ( 'inject the evaluation context from contex factory' , async function ( ) {
118
+ it ( 'inject the evaluation context from contex factory' , async function ( ) {
116
119
const evaluationSpy = jest . spyOn ( defaultProvider , 'resolveBooleanEvaluation' ) ;
117
120
await supertest ( app . getHttpServer ( ) )
118
121
. get ( '/dynamic-context-in-service' )
@@ -122,26 +125,77 @@ describe('OpenFeature SDK', () => {
122
125
expect ( evaluationSpy ) . toHaveBeenCalledWith ( 'testBooleanFlag' , false , { targetingKey : 'dynamic-user' } , { } ) ;
123
126
} ) ;
124
127
} ) ;
128
+
129
+ describe ( 'require flags enabled decorator' , ( ) => {
130
+ describe ( 'OpenFeatureController' , ( ) => {
131
+ it ( 'should sucessfully return the response if the flag is enabled' , async ( ) => {
132
+ await supertest ( app . getHttpServer ( ) ) . get ( '/flags-enabled' ) . expect ( 200 ) . expect ( 'Get Boolean Flag Success!' ) ;
133
+ } ) ;
134
+
135
+ it ( 'should throw an exception if the flag is disabled' , async ( ) => {
136
+ jest . spyOn ( defaultProvider , 'resolveBooleanEvaluation' ) . mockResolvedValueOnce ( {
137
+ value : false ,
138
+ reason : 'DISABLED' ,
139
+ } ) ;
140
+ await supertest ( app . getHttpServer ( ) ) . get ( '/flags-enabled' ) . expect ( 404 ) ;
141
+ } ) ;
142
+
143
+ it ( 'should throw a custom exception if the flag is disabled' , async ( ) => {
144
+ jest . spyOn ( defaultProvider , 'resolveBooleanEvaluation' ) . mockResolvedValueOnce ( {
145
+ value : false ,
146
+ reason : 'DISABLED' ,
147
+ } ) ;
148
+ await supertest ( app . getHttpServer ( ) ) . get ( '/flags-enabled-custom-exception' ) . expect ( 403 ) ;
149
+ } ) ;
150
+
151
+ it ( 'should throw a custom exception if the flag is disabled with context' , async ( ) => {
152
+ await supertest ( app . getHttpServer ( ) )
153
+ . get ( '/flags-enabled-custom-exception-with-context' )
154
+ . set ( 'x-user-id' , '123' )
155
+ . expect ( 403 ) ;
156
+ } ) ;
157
+ } ) ;
158
+
159
+ describe ( 'OpenFeatureControllerRequireFlagsEnabled' , ( ) => {
160
+ it ( 'should allow access to the RequireFlagsEnabled controller with global context interceptor' , async ( ) => {
161
+ await supertest ( app . getHttpServer ( ) )
162
+ . get ( '/require-flags-enabled' )
163
+ . set ( 'x-user-id' , '123' )
164
+ . expect ( 200 )
165
+ . expect ( 'Hello, world!' ) ;
166
+ } ) ;
167
+
168
+ it ( 'should throw a 403 - Forbidden exception if user does not match targeting requirements' , async ( ) => {
169
+ await supertest ( app . getHttpServer ( ) ) . get ( '/require-flags-enabled' ) . set ( 'x-user-id' , 'not-123' ) . expect ( 403 ) ;
170
+ } ) ;
171
+
172
+ it ( 'should throw a 403 - Forbidden exception if one of the flags is disabled' , async ( ) => {
173
+ jest . spyOn ( defaultProvider , 'resolveBooleanEvaluation' ) . mockResolvedValueOnce ( {
174
+ value : false ,
175
+ reason : 'DISABLED' ,
176
+ } ) ;
177
+ await supertest ( app . getHttpServer ( ) ) . get ( '/require-flags-enabled' ) . set ( 'x-user-id' , '123' ) . expect ( 403 ) ;
178
+ } ) ;
179
+ } ) ;
180
+ } ) ;
125
181
} ) ;
126
182
127
183
describe ( 'Without global context interceptor' , ( ) => {
128
-
129
184
let moduleRef : TestingModule ;
130
185
let app : INestApplication ;
131
186
132
187
beforeAll ( async ( ) => {
133
-
134
188
moduleRef = await Test . createTestingModule ( {
135
189
imports : [
136
190
OpenFeatureModule . forRoot ( {
137
191
contextFactory : exampleContextFactory ,
138
192
defaultProvider,
139
193
providers,
140
- useGlobalInterceptor : false
194
+ useGlobalInterceptor : false ,
141
195
} ) ,
142
196
] ,
143
197
providers : [ OpenFeatureTestService ] ,
144
- controllers : [ OpenFeatureController , OpenFeatureControllerContextScopedController ] ,
198
+ controllers : [ OpenFeatureController , OpenFeatureContextScopedController ] ,
145
199
} ) . compile ( ) ;
146
200
app = moduleRef . createNestApplication ( ) ;
147
201
app = await app . init ( ) ;
@@ -158,7 +212,7 @@ describe('OpenFeature SDK', () => {
158
212
} ) ;
159
213
160
214
describe ( 'evaluation context service should' , ( ) => {
161
- it ( 'inject empty context if no context interceptor is configured' , async function ( ) {
215
+ it ( 'inject empty context if no context interceptor is configured' , async function ( ) {
162
216
const evaluationSpy = jest . spyOn ( defaultProvider , 'resolveBooleanEvaluation' ) ;
163
217
await supertest ( app . getHttpServer ( ) )
164
218
. get ( '/dynamic-context-in-service' )
@@ -172,9 +226,26 @@ describe('OpenFeature SDK', () => {
172
226
describe ( 'With Controller bound Context interceptor' , ( ) => {
173
227
it ( 'should not use context if global context interceptor is not configured' , async ( ) => {
174
228
const evaluationSpy = jest . spyOn ( defaultProvider , 'resolveBooleanEvaluation' ) ;
175
- await supertest ( app . getHttpServer ( ) ) . get ( '/controller-context' ) . set ( 'x-user-id' , '123' ) . expect ( 200 ) . expect ( 'true' ) ;
229
+ await supertest ( app . getHttpServer ( ) )
230
+ . get ( '/controller-context' )
231
+ . set ( 'x-user-id' , '123' )
232
+ . expect ( 200 )
233
+ . expect ( 'true' ) ;
176
234
expect ( evaluationSpy ) . toHaveBeenCalledWith ( 'testBooleanFlag' , false , { targetingKey : '123' } , { } ) ;
177
235
} ) ;
178
236
} ) ;
237
+
238
+ describe ( 'require flags enabled decorator' , ( ) => {
239
+ it ( 'should return a 404 - Not Found exception if the flag is disabled' , async ( ) => {
240
+ jest . spyOn ( providers . domainScopedClient , 'resolveBooleanEvaluation' ) . mockResolvedValueOnce ( {
241
+ value : false ,
242
+ reason : 'DISABLED' ,
243
+ } ) ;
244
+ await supertest ( app . getHttpServer ( ) )
245
+ . get ( '/controller-context/flags-enabled' )
246
+ . set ( 'x-user-id' , '123' )
247
+ . expect ( 404 ) ;
248
+ } ) ;
249
+ } ) ;
179
250
} ) ;
180
251
} ) ;
0 commit comments