mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: Add support for global segments (#1577)
feat: Add support for global segments
This commit is contained in:
		
							parent
							
								
									f122e884c7
								
							
						
					
					
						commit
						0c1213ff81
					
				| @ -78,16 +78,10 @@ export default class FeatureToggleClientStore | |||||||
|             'fs.strategy_name as strategy_name', |             'fs.strategy_name as strategy_name', | ||||||
|             'fs.parameters as parameters', |             'fs.parameters as parameters', | ||||||
|             'fs.constraints as constraints', |             'fs.constraints as constraints', | ||||||
|  |             'segments.id as segment_id', | ||||||
|  |             'segments.constraints as segment_constraints', | ||||||
|         ]; |         ]; | ||||||
| 
 | 
 | ||||||
|         if (inlineSegmentConstraints) { |  | ||||||
|             selectColumns = [ |  | ||||||
|                 ...selectColumns, |  | ||||||
|                 'segments.id as segment_id', |  | ||||||
|                 'segments.constraints as segment_constraints', |  | ||||||
|             ]; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         let query = this.db('features') |         let query = this.db('features') | ||||||
|             .select(selectColumns) |             .select(selectColumns) | ||||||
|             .fullOuterJoin( |             .fullOuterJoin( | ||||||
| @ -105,17 +99,13 @@ export default class FeatureToggleClientStore | |||||||
|                     .as('fe'), |                     .as('fe'), | ||||||
|                 'fe.feature_name', |                 'fe.feature_name', | ||||||
|                 'features.name', |                 'features.name', | ||||||
|             ); |             ) | ||||||
| 
 |             .fullOuterJoin( | ||||||
|         if (inlineSegmentConstraints) { |                 'feature_strategy_segment as fss', | ||||||
|             query = query |                 `fss.feature_strategy_id`, | ||||||
|                 .fullOuterJoin( |                 `fs.id`, | ||||||
|                     'feature_strategy_segment as fss', |             ) | ||||||
|                     `fss.feature_strategy_id`, |             .fullOuterJoin('segments', `segments.id`, `fss.segment_id`); | ||||||
|                     `fs.id`, |  | ||||||
|                 ) |  | ||||||
|                 .fullOuterJoin('segments', `segments.id`, `fss.segment_id`); |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         query = query.where({ |         query = query.where({ | ||||||
|             archived, |             archived, | ||||||
| @ -155,6 +145,8 @@ export default class FeatureToggleClientStore | |||||||
|             } |             } | ||||||
|             if (inlineSegmentConstraints && r.segment_id) { |             if (inlineSegmentConstraints && r.segment_id) { | ||||||
|                 this.addSegmentToStrategy(feature, r); |                 this.addSegmentToStrategy(feature, r); | ||||||
|  |             } else if (!inlineSegmentConstraints && r.segment_id) { | ||||||
|  |                 this.addSegmentIdsToStrategy(feature, r); | ||||||
|             } |             } | ||||||
|             feature.impressionData = r.impression_data; |             feature.impressionData = r.impression_data; | ||||||
|             feature.enabled = !!r.enabled; |             feature.enabled = !!r.enabled; | ||||||
| @ -220,6 +212,22 @@ export default class FeatureToggleClientStore | |||||||
|             ?.constraints.push(...row.segment_constraints); |             ?.constraints.push(...row.segment_constraints); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     private addSegmentIdsToStrategy( | ||||||
|  |         feature: PartialDeep<IFeatureToggleClient>, | ||||||
|  |         row: Record<string, any>, | ||||||
|  |     ) { | ||||||
|  |         const strategy = feature.strategies.find( | ||||||
|  |             (s) => s.id === row.strategy_id, | ||||||
|  |         ); | ||||||
|  |         if (!strategy) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         if (!strategy.segments) { | ||||||
|  |             strategy.segments = []; | ||||||
|  |         } | ||||||
|  |         strategy.segments.push(row.segment_id); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     async getClient( |     async getClient( | ||||||
|         featureQuery?: IFeatureToggleQuery, |         featureQuery?: IFeatureToggleQuery, | ||||||
|     ): Promise<IFeatureToggleClient[]> { |     ): Promise<IFeatureToggleClient[]> { | ||||||
|  | |||||||
| @ -60,13 +60,19 @@ test('should get empty getFeatures via client', () => { | |||||||
| 
 | 
 | ||||||
| test('if caching is enabled should memoize', async () => { | test('if caching is enabled should memoize', async () => { | ||||||
|     const getClientFeatures = jest.fn().mockReturnValue([]); |     const getClientFeatures = jest.fn().mockReturnValue([]); | ||||||
|  |     const getActive = jest.fn().mockReturnValue([]); | ||||||
| 
 | 
 | ||||||
|     const featureToggleServiceV2 = { |     const featureToggleServiceV2 = { | ||||||
|         getClientFeatures, |         getClientFeatures, | ||||||
|     }; |     }; | ||||||
|  | 
 | ||||||
|  |     const segmentService = { | ||||||
|  |         getActive, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|     const controller = new FeatureController( |     const controller = new FeatureController( | ||||||
|         // @ts-ignore
 |         // @ts-ignore
 | ||||||
|         { featureToggleServiceV2 }, |         { featureToggleServiceV2, segmentService }, | ||||||
|         { |         { | ||||||
|             getLogger, |             getLogger, | ||||||
|             experimental: { |             experimental: { | ||||||
| @ -87,12 +93,19 @@ test('if caching is enabled should memoize', async () => { | |||||||
| test('if caching is not enabled all calls goes to service', async () => { | test('if caching is not enabled all calls goes to service', async () => { | ||||||
|     const getClientFeatures = jest.fn().mockReturnValue([]); |     const getClientFeatures = jest.fn().mockReturnValue([]); | ||||||
| 
 | 
 | ||||||
|  |     const getActive = jest.fn().mockReturnValue([]); | ||||||
|  | 
 | ||||||
|     const featureToggleServiceV2 = { |     const featureToggleServiceV2 = { | ||||||
|         getClientFeatures, |         getClientFeatures, | ||||||
|     }; |     }; | ||||||
|  | 
 | ||||||
|  |     const segmentService = { | ||||||
|  |         getActive, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|     const controller = new FeatureController( |     const controller = new FeatureController( | ||||||
|         // @ts-ignore
 |         // @ts-ignore
 | ||||||
|         { featureToggleServiceV2 }, |         { featureToggleServiceV2, segmentService }, | ||||||
|         { |         { | ||||||
|             getLogger, |             getLogger, | ||||||
|             experimental: { |             experimental: { | ||||||
|  | |||||||
| @ -6,11 +6,13 @@ import { IUnleashConfig } from '../../types/option'; | |||||||
| import FeatureToggleService from '../../services/feature-toggle-service'; | import FeatureToggleService from '../../services/feature-toggle-service'; | ||||||
| import { Logger } from '../../logger'; | import { Logger } from '../../logger'; | ||||||
| import { querySchema } from '../../schema/feature-schema'; | import { querySchema } from '../../schema/feature-schema'; | ||||||
| import { IFeatureToggleQuery } from '../../types/model'; | import { IFeatureToggleQuery, ISegment } from '../../types/model'; | ||||||
| import NotFoundError from '../../error/notfound-error'; | import NotFoundError from '../../error/notfound-error'; | ||||||
| import { IAuthRequest } from '../unleash-types'; | import { IAuthRequest } from '../unleash-types'; | ||||||
| import ApiUser from '../../types/api-user'; | import ApiUser from '../../types/api-user'; | ||||||
| import { ALL, isAllProjects } from '../../types/models/api-token'; | import { ALL, isAllProjects } from '../../types/models/api-token'; | ||||||
|  | import { SegmentService } from '../../services/segment-service'; | ||||||
|  | import { FeatureConfigurationClient } from '../../types/stores/feature-strategies-store'; | ||||||
| 
 | 
 | ||||||
| const version = 2; | const version = 2; | ||||||
| 
 | 
 | ||||||
| @ -24,27 +26,36 @@ export default class FeatureController extends Controller { | |||||||
| 
 | 
 | ||||||
|     private featureToggleServiceV2: FeatureToggleService; |     private featureToggleServiceV2: FeatureToggleService; | ||||||
| 
 | 
 | ||||||
|  |     private segmentService: SegmentService; | ||||||
|  | 
 | ||||||
|     private readonly cache: boolean; |     private readonly cache: boolean; | ||||||
| 
 | 
 | ||||||
|     private cachedFeatures: any; |     private cachedFeatures: any; | ||||||
| 
 | 
 | ||||||
|  |     private useGlobalSegments: boolean; | ||||||
|  | 
 | ||||||
|     constructor( |     constructor( | ||||||
|         { |         { | ||||||
|             featureToggleServiceV2, |             featureToggleServiceV2, | ||||||
|         }: Pick<IUnleashServices, 'featureToggleServiceV2'>, |             segmentService, | ||||||
|  |         }: Pick<IUnleashServices, 'featureToggleServiceV2' | 'segmentService'>, | ||||||
|         config: IUnleashConfig, |         config: IUnleashConfig, | ||||||
|     ) { |     ) { | ||||||
|         super(config); |         super(config); | ||||||
|         const { experimental } = config; |         const { experimental } = config; | ||||||
|         this.featureToggleServiceV2 = featureToggleServiceV2; |         this.featureToggleServiceV2 = featureToggleServiceV2; | ||||||
|  |         this.segmentService = segmentService; | ||||||
|         this.logger = config.getLogger('client-api/feature.js'); |         this.logger = config.getLogger('client-api/feature.js'); | ||||||
|         this.get('/', this.getAll); |         this.get('/', this.getAll); | ||||||
|         this.get('/:featureName', this.getFeatureToggle); |         this.get('/:featureName', this.getFeatureToggle); | ||||||
|  |         this.useGlobalSegments = | ||||||
|  |             experimental && !experimental?.segments?.inlineSegmentConstraints; | ||||||
|  | 
 | ||||||
|         if (experimental && experimental.clientFeatureMemoize) { |         if (experimental && experimental.clientFeatureMemoize) { | ||||||
|             // @ts-ignore
 |             // @ts-ignore
 | ||||||
|             this.cache = experimental.clientFeatureMemoize.enabled; |             this.cache = experimental.clientFeatureMemoize.enabled; | ||||||
|             this.cachedFeatures = memoizee( |             this.cachedFeatures = memoizee( | ||||||
|                 (query) => this.featureToggleServiceV2.getClientFeatures(query), |                 (query) => this.resolveFeaturesAndSegments(query), | ||||||
|                 { |                 { | ||||||
|                     promise: true, |                     promise: true, | ||||||
|                     // @ts-ignore
 |                     // @ts-ignore
 | ||||||
| @ -58,6 +69,23 @@ export default class FeatureController extends Controller { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     private async resolveSegments() { | ||||||
|  |         if (this.useGlobalSegments) { | ||||||
|  |             return this.segmentService.getActive(); | ||||||
|  |         } | ||||||
|  |         return Promise.resolve([]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private async resolveFeaturesAndSegments( | ||||||
|  |         query?: IFeatureToggleQuery, | ||||||
|  |     ): Promise<[FeatureConfigurationClient[], ISegment[]]> { | ||||||
|  |         let segments = this.resolveSegments(); | ||||||
|  |         return Promise.all([ | ||||||
|  |             this.featureToggleServiceV2.getClientFeatures(query), | ||||||
|  |             segments, | ||||||
|  |         ]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private async resolveQuery( |     private async resolveQuery( | ||||||
|         req: IAuthRequest, |         req: IAuthRequest, | ||||||
|     ): Promise<IFeatureToggleQuery> { |     ): Promise<IFeatureToggleQuery> { | ||||||
| @ -110,15 +138,30 @@ export default class FeatureController extends Controller { | |||||||
| 
 | 
 | ||||||
|     async getAll(req: IAuthRequest, res: Response): Promise<void> { |     async getAll(req: IAuthRequest, res: Response): Promise<void> { | ||||||
|         const featureQuery = await this.resolveQuery(req); |         const featureQuery = await this.resolveQuery(req); | ||||||
|         let features; |         let features, segments; | ||||||
|         if (this.cache) { |         if (this.cache) { | ||||||
|             features = await this.cachedFeatures(featureQuery); |             [features, segments] = await this.cachedFeatures(featureQuery); | ||||||
|         } else { |         } else { | ||||||
|             features = await this.featureToggleServiceV2.getClientFeatures( |             features = await this.featureToggleServiceV2.getClientFeatures( | ||||||
|                 featureQuery, |                 featureQuery, | ||||||
|             ); |             ); | ||||||
|  |             segments = await this.resolveSegments(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         const response = { | ||||||
|  |             version, | ||||||
|  |             features, | ||||||
|  |             query: featureQuery, | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         if (this.useGlobalSegments) { | ||||||
|  |             res.json({ | ||||||
|  |                 ...response, | ||||||
|  |                 segments, | ||||||
|  |             }); | ||||||
|  |         } else { | ||||||
|  |             res.json(response); | ||||||
|         } |         } | ||||||
|         res.json({ version, features, query: featureQuery }); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async getFeatureToggle(req: IAuthRequest, res: Response): Promise<void> { |     async getFeatureToggle(req: IAuthRequest, res: Response): Promise<void> { | ||||||
|  | |||||||
| @ -23,6 +23,7 @@ export interface IStrategyConfig { | |||||||
|     id?: string; |     id?: string; | ||||||
|     name: string; |     name: string; | ||||||
|     constraints?: IConstraint[]; |     constraints?: IConstraint[]; | ||||||
|  |     segments?: number[]; | ||||||
|     parameters?: { [key: string]: string }; |     parameters?: { [key: string]: string }; | ||||||
|     sortOrder?: number; |     sortOrder?: number; | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										155
									
								
								src/test/e2e/api/client/global.segment.e2e.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								src/test/e2e/api/client/global.segment.e2e.test.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,155 @@ | |||||||
|  | import dbInit, { ITestDb } from '../../helpers/database-init'; | ||||||
|  | import getLogger from '../../../fixtures/no-logger'; | ||||||
|  | import { | ||||||
|  |     IUnleashTest, | ||||||
|  |     setupAppWithCustomConfig, | ||||||
|  | } from '../../helpers/test-helper'; | ||||||
|  | import { | ||||||
|  |     IConstraint, | ||||||
|  |     IFeatureToggleClient, | ||||||
|  |     ISegment, | ||||||
|  | } from '../../../../lib/types/model'; | ||||||
|  | import { randomId } from '../../../../lib/util/random-id'; | ||||||
|  | import User from '../../../../lib/types/user'; | ||||||
|  | 
 | ||||||
|  | let db: ITestDb; | ||||||
|  | let app: IUnleashTest; | ||||||
|  | 
 | ||||||
|  | const FEATURES_ADMIN_BASE_PATH = '/api/admin/features'; | ||||||
|  | const FEATURES_CLIENT_BASE_PATH = '/api/client/features'; | ||||||
|  | 
 | ||||||
|  | interface ApiResponse { | ||||||
|  |     features: IFeatureToggleClient[]; | ||||||
|  |     version: number; | ||||||
|  |     segments: ISegment[]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const fetchSegments = (): Promise<ISegment[]> => { | ||||||
|  |     return app.services.segmentService.getAll(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const fetchFeatures = (): Promise<IFeatureToggleClient[]> => { | ||||||
|  |     return app.request | ||||||
|  |         .get(FEATURES_ADMIN_BASE_PATH) | ||||||
|  |         .expect(200) | ||||||
|  |         .then((res) => res.body.features); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const fetchClientResponse = (): Promise<ApiResponse> => { | ||||||
|  |     return app.request | ||||||
|  |         .get(FEATURES_CLIENT_BASE_PATH) | ||||||
|  |         .expect(200) | ||||||
|  |         .then((res) => res.body); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const createSegment = (postData: object): Promise<unknown> => { | ||||||
|  |     const user = { email: 'test@example.com' } as User; | ||||||
|  |     return app.services.segmentService.create(postData, user); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const createFeatureToggle = ( | ||||||
|  |     postData: object, | ||||||
|  |     expectStatusCode = 201, | ||||||
|  | ): Promise<unknown> => { | ||||||
|  |     return app.request | ||||||
|  |         .post(FEATURES_ADMIN_BASE_PATH) | ||||||
|  |         .send(postData) | ||||||
|  |         .expect(expectStatusCode); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const addSegmentToStrategy = ( | ||||||
|  |     segmentId: number, | ||||||
|  |     strategyId: string, | ||||||
|  | ): Promise<unknown> => { | ||||||
|  |     return app.services.segmentService.addToStrategy(segmentId, strategyId); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const mockFeatureToggle = (): object => { | ||||||
|  |     return { | ||||||
|  |         name: randomId(), | ||||||
|  |         strategies: [{ name: randomId(), constraints: [], parameters: {} }], | ||||||
|  |     }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const mockConstraints = (): IConstraint[] => { | ||||||
|  |     return Array.from({ length: 5 }).map(() => ({ | ||||||
|  |         values: ['x', 'y', 'z'], | ||||||
|  |         operator: 'IN', | ||||||
|  |         contextName: 'a', | ||||||
|  |     })); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const createTestSegments = async (): Promise<void> => { | ||||||
|  |     const constraints = mockConstraints(); | ||||||
|  |     await createSegment({ name: 'S1', constraints }); | ||||||
|  |     await createSegment({ name: 'S2', constraints }); | ||||||
|  |     await createSegment({ name: 'S3', constraints }); | ||||||
|  |     await createFeatureToggle(mockFeatureToggle()); | ||||||
|  |     await createFeatureToggle(mockFeatureToggle()); | ||||||
|  |     await createFeatureToggle(mockFeatureToggle()); | ||||||
|  |     const [feature1, feature2] = await fetchFeatures(); | ||||||
|  |     const [segment1, segment2] = await fetchSegments(); | ||||||
|  | 
 | ||||||
|  |     await addSegmentToStrategy(segment1.id, feature1.strategies[0].id); | ||||||
|  |     await addSegmentToStrategy(segment2.id, feature1.strategies[0].id); | ||||||
|  |     await addSegmentToStrategy(segment2.id, feature2.strategies[0].id); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | beforeAll(async () => { | ||||||
|  |     const experimentalConfig = { | ||||||
|  |         segments: { | ||||||
|  |             enableSegmentsAdminApi: true, | ||||||
|  |             enableSegmentsClientApi: true, | ||||||
|  |             inlineSegmentConstraints: false, | ||||||
|  |         }, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     db = await dbInit('global_segments', getLogger, { | ||||||
|  |         experimental: experimentalConfig, | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     app = await setupAppWithCustomConfig(db.stores, { | ||||||
|  |         experimental: experimentalConfig, | ||||||
|  |     }); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | afterAll(async () => { | ||||||
|  |     await app.destroy(); | ||||||
|  |     await db.destroy(); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | afterEach(async () => { | ||||||
|  |     await db.stores.segmentStore.deleteAll(); | ||||||
|  |     await db.stores.featureToggleStore.deleteAll(); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | test('should return segments in base of toggle response if inline is disabled', async () => { | ||||||
|  |     await createTestSegments(); | ||||||
|  | 
 | ||||||
|  |     const clientFeatures = await fetchClientResponse(); | ||||||
|  |     expect(clientFeatures.segments.length).toBeDefined(); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | test('should only send segments that are in use', async () => { | ||||||
|  |     await createTestSegments(); | ||||||
|  | 
 | ||||||
|  |     const clientFeatures = await fetchClientResponse(); | ||||||
|  |     //3 segments were created in createTestSegments, only 2 are in use
 | ||||||
|  |     expect(clientFeatures.segments.length).toEqual(2); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | test('should send all segments that are in use by feature', async () => { | ||||||
|  |     await createTestSegments(); | ||||||
|  | 
 | ||||||
|  |     const clientFeatures = await fetchClientResponse(); | ||||||
|  |     const globalSegments = clientFeatures.segments; | ||||||
|  |     const globalSegmentIds = globalSegments.map((segment) => segment.id); | ||||||
|  |     const allSegmentIds = clientFeatures.features | ||||||
|  |         .map((feat) => feat.strategies.map((strategy) => strategy.segments)) | ||||||
|  |         .flat() | ||||||
|  |         .flat() | ||||||
|  |         .filter((x) => !!x); | ||||||
|  |     const toggleSegmentIds = [...new Set(allSegmentIds)]; | ||||||
|  | 
 | ||||||
|  |     expect(globalSegmentIds).toEqual(toggleSegmentIds); | ||||||
|  | }); | ||||||
| @ -38,6 +38,13 @@ const fetchClientFeatures = (): Promise<IFeatureToggleClient[]> => { | |||||||
|         .then((res) => res.body.features); |         .then((res) => res.body.features); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | const fetchGlobalSegments = (): Promise<ISegment[] | undefined> => { | ||||||
|  |     return app.request | ||||||
|  |         .get(FEATURES_CLIENT_BASE_PATH) | ||||||
|  |         .expect(200) | ||||||
|  |         .then((res) => res.body.segments); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| const fetchClientSegmentsActive = (): Promise<ISegment[]> => { | const fetchClientSegmentsActive = (): Promise<ISegment[]> => { | ||||||
|     return app.request |     return app.request | ||||||
|         .get('/api/client/segments/active') |         .get('/api/client/segments/active') | ||||||
| @ -216,3 +223,22 @@ test('should validate feature strategy segment limit', async () => { | |||||||
|         addSegmentToStrategy(segments[5].id, feature1.strategies[0].id), |         addSegmentToStrategy(segments[5].id, feature1.strategies[0].id), | ||||||
|     ).rejects.toThrow(`Strategies may not have more than ${limit} segments`); |     ).rejects.toThrow(`Strategies may not have more than ${limit} segments`); | ||||||
| }); | }); | ||||||
|  | 
 | ||||||
|  | test('should not return segments in base of toggle response if inline is enabled', async () => { | ||||||
|  |     const constraints = mockConstraints(); | ||||||
|  |     await createSegment({ name: 'S1', constraints }); | ||||||
|  |     await createSegment({ name: 'S2', constraints }); | ||||||
|  |     await createSegment({ name: 'S3', constraints }); | ||||||
|  |     await createFeatureToggle(mockFeatureToggle()); | ||||||
|  |     await createFeatureToggle(mockFeatureToggle()); | ||||||
|  |     await createFeatureToggle(mockFeatureToggle()); | ||||||
|  |     const [feature1, feature2] = await fetchFeatures(); | ||||||
|  |     const [segment1, segment2] = await fetchSegments(); | ||||||
|  | 
 | ||||||
|  |     await addSegmentToStrategy(segment1.id, feature1.strategies[0].id); | ||||||
|  |     await addSegmentToStrategy(segment2.id, feature1.strategies[0].id); | ||||||
|  |     await addSegmentToStrategy(segment2.id, feature2.strategies[0].id); | ||||||
|  | 
 | ||||||
|  |     const globalSegments = await fetchGlobalSegments(); | ||||||
|  |     expect(globalSegments).toBe(undefined); | ||||||
|  | }); | ||||||
|  | |||||||
| @ -10,6 +10,7 @@ import EnvironmentStore from '../../../lib/db/environment-store'; | |||||||
| import { IUnleashStores } from '../../../lib/types'; | import { IUnleashStores } from '../../../lib/types'; | ||||||
| import { IFeatureEnvironmentStore } from '../../../lib/types/stores/feature-environment-store'; | import { IFeatureEnvironmentStore } from '../../../lib/types/stores/feature-environment-store'; | ||||||
| import { DEFAULT_ENV } from '../../../lib/util/constants'; | import { DEFAULT_ENV } from '../../../lib/util/constants'; | ||||||
|  | import { IUnleashOptions } from 'lib/server-impl'; | ||||||
| 
 | 
 | ||||||
| // require('db-migrate-shared').log.silence(false);
 | // require('db-migrate-shared').log.silence(false);
 | ||||||
| 
 | 
 | ||||||
| @ -79,6 +80,7 @@ export interface ITestDb { | |||||||
| export default async function init( | export default async function init( | ||||||
|     databaseSchema: string = 'test', |     databaseSchema: string = 'test', | ||||||
|     getLogger: LogProvider = noLoggerProvider, |     getLogger: LogProvider = noLoggerProvider, | ||||||
|  |     configOverride: Partial<IUnleashOptions> = {}, | ||||||
| ): Promise<ITestDb> { | ): Promise<ITestDb> { | ||||||
|     const config = createTestConfig({ |     const config = createTestConfig({ | ||||||
|         db: { |         db: { | ||||||
| @ -87,6 +89,7 @@ export default async function init( | |||||||
|             schema: databaseSchema, |             schema: databaseSchema, | ||||||
|             ssl: false, |             ssl: false, | ||||||
|         }, |         }, | ||||||
|  |         ...configOverride, | ||||||
|         getLogger, |         getLogger, | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user