mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			256 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			256 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import { setupAppWithCustomConfig } from '../../helpers/test-helper';
 | |
| import dbInit from '../../helpers/database-init';
 | |
| import getLogger from '../../../fixtures/no-logger';
 | |
| import { DEFAULT_PROJECT } from '../../../../lib/types';
 | |
| 
 | |
| let app;
 | |
| let db;
 | |
| 
 | |
| beforeAll(async () => {
 | |
|     db = await dbInit('archive_serial', getLogger);
 | |
|     app = await setupAppWithCustomConfig(db.stores, {
 | |
|         experimental: {
 | |
|             flags: {
 | |
|                 strictSchemaValidation: true,
 | |
|                 bulkOperations: true,
 | |
|             },
 | |
|         },
 | |
|     });
 | |
|     await app.services.featureToggleServiceV2.createFeatureToggle(
 | |
|         'default',
 | |
|         {
 | |
|             name: 'featureX',
 | |
|             description: 'the #1 feature',
 | |
|         },
 | |
|         'test',
 | |
|     );
 | |
|     await app.services.featureToggleServiceV2.createFeatureToggle(
 | |
|         'default',
 | |
|         {
 | |
|             name: 'featureY',
 | |
|             description: 'soon to be the #1 feature',
 | |
|         },
 | |
|         'test',
 | |
|     );
 | |
|     await app.services.featureToggleServiceV2.createFeatureToggle(
 | |
|         'default',
 | |
|         {
 | |
|             name: 'featureZ',
 | |
|             description: 'terrible feature',
 | |
|         },
 | |
|         'test',
 | |
|     );
 | |
|     await app.services.featureToggleServiceV2.createFeatureToggle(
 | |
|         'default',
 | |
|         {
 | |
|             name: 'featureArchivedX',
 | |
|             description: 'the #1 feature',
 | |
|         },
 | |
|         'test',
 | |
|     );
 | |
|     await app.services.featureToggleServiceV2.archiveToggle(
 | |
|         'featureArchivedX',
 | |
|         'test',
 | |
|     );
 | |
|     await app.services.featureToggleServiceV2.createFeatureToggle(
 | |
|         'default',
 | |
|         {
 | |
|             name: 'featureArchivedY',
 | |
|             description: 'soon to be the #1 feature',
 | |
|         },
 | |
|         'test',
 | |
|     );
 | |
|     await app.services.featureToggleServiceV2.archiveToggle(
 | |
|         'featureArchivedY',
 | |
|         'test',
 | |
|     );
 | |
|     await app.services.featureToggleServiceV2.createFeatureToggle(
 | |
|         'default',
 | |
|         {
 | |
|             name: 'featureArchivedZ',
 | |
|             description: 'terrible feature',
 | |
|         },
 | |
|         'test',
 | |
|     );
 | |
|     await app.services.featureToggleServiceV2.archiveToggle(
 | |
|         'featureArchivedZ',
 | |
|         'test',
 | |
|     );
 | |
|     await app.services.featureToggleServiceV2.createFeatureToggle(
 | |
|         'default',
 | |
|         {
 | |
|             name: 'feature.with.variants',
 | |
|             description: 'A feature toggle with variants',
 | |
|             variants: [
 | |
|                 { name: 'control', weight: 50 },
 | |
|                 { name: 'new', weight: 50 },
 | |
|             ],
 | |
|         },
 | |
|         'test',
 | |
|     );
 | |
| });
 | |
| 
 | |
| afterAll(async () => {
 | |
|     await app.destroy();
 | |
|     await db.destroy();
 | |
| });
 | |
| 
 | |
| test('returns three archived toggles', async () => {
 | |
|     expect.assertions(1);
 | |
|     return app.request
 | |
|         .get('/api/admin/archive/features')
 | |
|         .expect('Content-Type', /json/)
 | |
|         .expect(200)
 | |
|         .expect((res) => {
 | |
|             expect(res.body.features.length === 3).toBe(true);
 | |
|         });
 | |
| });
 | |
| 
 | |
| test('returns three archived toggles with archivedAt', async () => {
 | |
|     expect.assertions(3);
 | |
|     return app.request
 | |
|         .get('/api/admin/archive/features')
 | |
|         .expect('Content-Type', /json/)
 | |
|         .expect(200)
 | |
|         .expect((res) => {
 | |
|             expect(res.body.features.length).toEqual(3);
 | |
|             expect(res.body.features.every((f) => f.archived)).toEqual(true);
 | |
|             expect(res.body.features.every((f) => f.archivedAt)).toEqual(true);
 | |
|         });
 | |
| });
 | |
| 
 | |
| test('revives a feature by name', async () => {
 | |
|     return app.request
 | |
|         .post('/api/admin/archive/revive/featureArchivedX')
 | |
|         .set('Content-Type', 'application/json')
 | |
|         .expect(200);
 | |
| });
 | |
| 
 | |
| test('archived feature is not accessible via /features/:featureName', async () => {
 | |
|     expect.assertions(0);
 | |
| 
 | |
|     return app.request
 | |
|         .get('/api/admin/features/featureArchivedZ')
 | |
|         .set('Content-Type', 'application/json')
 | |
|         .expect(404);
 | |
| });
 | |
| 
 | |
| test('must set name when reviving toggle', async () => {
 | |
|     expect.assertions(0);
 | |
|     return app.request.post('/api/admin/archive/revive/').expect(404);
 | |
| });
 | |
| 
 | |
| test('should be allowed to reuse deleted toggle name', async () => {
 | |
|     expect.assertions(2);
 | |
|     await app.request
 | |
|         .post('/api/admin/features')
 | |
|         .send({
 | |
|             name: 'really.delete.feature',
 | |
|             enabled: false,
 | |
|             strategies: [{ name: 'default' }],
 | |
|         })
 | |
|         .set('Content-Type', 'application/json')
 | |
|         .expect(201)
 | |
|         .expect((res) => {
 | |
|             expect(res.body.name).toBe('really.delete.feature');
 | |
|             expect(res.body.createdAt).toBeTruthy();
 | |
|         });
 | |
|     await app.request
 | |
|         .delete('/api/admin/features/really.delete.feature')
 | |
|         .expect(200);
 | |
|     await app.request
 | |
|         .delete('/api/admin/archive/really.delete.feature')
 | |
|         .expect(200);
 | |
|     return app.request
 | |
|         .post('/api/admin/features/validate')
 | |
|         .send({ name: 'really.delete.feature' })
 | |
|         .set('Content-Type', 'application/json')
 | |
|         .expect(200);
 | |
| });
 | |
| test('Deleting an unarchived toggle should not take effect', async () => {
 | |
|     expect.assertions(2);
 | |
|     await app.request
 | |
|         .post('/api/admin/features')
 | |
|         .send({
 | |
|             name: 'really.delete.feature',
 | |
|             enabled: false,
 | |
|             strategies: [{ name: 'default' }],
 | |
|         })
 | |
|         .set('Content-Type', 'application/json')
 | |
|         .expect(201)
 | |
|         .expect((res) => {
 | |
|             expect(res.body.name).toBe('really.delete.feature');
 | |
|             expect(res.body.createdAt).toBeTruthy();
 | |
|         });
 | |
|     await app.request
 | |
|         .delete('/api/admin/archive/really.delete.feature')
 | |
|         .expect(200);
 | |
|     return app.request
 | |
|         .post('/api/admin/features/validate')
 | |
|         .send({ name: 'really.delete.feature' })
 | |
|         .set('Content-Type', 'application/json')
 | |
|         .expect(409); // because it still exists
 | |
| });
 | |
| 
 | |
| test('can bulk delete features and recreate after', async () => {
 | |
|     const features = ['first.bulk.issue', 'second.bulk.issue'];
 | |
|     for (const feature of features) {
 | |
|         await app.request
 | |
|             .post('/api/admin/features')
 | |
|             .send({
 | |
|                 name: feature,
 | |
|                 enabled: false,
 | |
|                 strategies: [{ name: 'default' }],
 | |
|             })
 | |
|             .set('Content-Type', 'application/json')
 | |
|             .expect(201);
 | |
|     }
 | |
|     await app.request
 | |
|         .post(`/api/admin/projects/${DEFAULT_PROJECT}/archive`)
 | |
|         .send({
 | |
|             features,
 | |
|         })
 | |
|         .expect(202);
 | |
|     await app.request
 | |
|         .post('/api/admin/projects/default/archive/delete')
 | |
|         .send({ features })
 | |
|         .expect(200);
 | |
|     for (const feature of features) {
 | |
|         await app.request
 | |
|             .post('/api/admin/features/validate')
 | |
|             .send({ name: feature })
 | |
|             .set('Content-Type', 'application/json')
 | |
|             .expect(200);
 | |
|     }
 | |
| });
 | |
| 
 | |
| test('can bulk revive features', async () => {
 | |
|     const features = ['first.revive.issue', 'second.revive.issue'];
 | |
|     for (const feature of features) {
 | |
|         await app.request
 | |
|             .post('/api/admin/features')
 | |
|             .send({
 | |
|                 name: feature,
 | |
|                 enabled: false,
 | |
|                 strategies: [{ name: 'default' }],
 | |
|             })
 | |
|             .set('Content-Type', 'application/json')
 | |
|             .expect(201);
 | |
|     }
 | |
|     await app.request
 | |
|         .post(`/api/admin/projects/${DEFAULT_PROJECT}/archive`)
 | |
|         .send({
 | |
|             features,
 | |
|         })
 | |
|         .expect(202);
 | |
|     await app.request
 | |
|         .post('/api/admin/projects/default/archive/revive')
 | |
|         .send({ features })
 | |
|         .expect(200);
 | |
|     for (const feature of features) {
 | |
|         await app.request
 | |
|             .get(`/api/admin/projects/default/features/${feature}`)
 | |
|             .expect(200);
 | |
|     }
 | |
| });
 |