unleash-server 4.14.2 → 4.14.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/db/event-store.d.ts +2 -3
- package/dist/lib/db/event-store.js +26 -36
- package/dist/lib/db/event-store.js.map +1 -1
- package/dist/lib/db/event-store.test.js +1 -1
- package/dist/lib/db/event-store.test.js.map +1 -1
- package/dist/lib/db/feature-strategy-store.js +3 -3
- package/dist/lib/db/feature-strategy-store.js.map +1 -1
- package/dist/lib/db/feature-toggle-client-store.d.ts +1 -1
- package/dist/lib/db/feature-toggle-client-store.js +6 -6
- package/dist/lib/db/feature-toggle-client-store.js.map +1 -1
- package/dist/lib/db/group-store.d.ts +0 -1
- package/dist/lib/db/group-store.js +1 -14
- package/dist/lib/db/group-store.js.map +1 -1
- package/dist/lib/db/user-store.js +8 -1
- package/dist/lib/db/user-store.js.map +1 -1
- package/dist/lib/openapi/index.d.ts +2388 -952
- package/dist/lib/openapi/index.js +8 -4
- package/dist/lib/openapi/index.js.map +1 -1
- package/dist/lib/openapi/spec/client-feature-schema.d.ts +72 -7
- package/dist/lib/openapi/spec/client-feature-schema.js +5 -3
- package/dist/lib/openapi/spec/client-feature-schema.js.map +1 -1
- package/dist/lib/openapi/spec/client-features-schema.d.ts +138 -16
- package/dist/lib/openapi/spec/client-features-schema.js +2 -2
- package/dist/lib/openapi/spec/client-features-schema.js.map +1 -1
- package/dist/lib/openapi/spec/client-features-schema.test.js +10 -0
- package/dist/lib/openapi/spec/client-features-schema.test.js.map +1 -1
- package/dist/lib/openapi/spec/constraint-schema.d.ts +51 -2
- package/dist/lib/openapi/spec/constraint-schema.js +17 -4
- package/dist/lib/openapi/spec/constraint-schema.js.map +1 -1
- package/dist/lib/openapi/spec/context-field-schema.test.js +5 -1
- package/dist/lib/openapi/spec/context-field-schema.test.js.map +1 -1
- package/dist/lib/openapi/spec/create-feature-strategy-schema.d.ts +12 -2
- package/dist/lib/openapi/spec/feature-environment-schema.d.ts +12 -2
- package/dist/lib/openapi/spec/feature-events-schema.d.ts +1 -1
- package/dist/lib/openapi/spec/feature-events-schema.js +1 -1
- package/dist/lib/openapi/spec/feature-events-schema.js.map +1 -1
- package/dist/lib/openapi/spec/feature-schema.d.ts +25 -5
- package/dist/lib/openapi/spec/feature-schema.test.js +13 -1
- package/dist/lib/openapi/spec/feature-schema.test.js.map +1 -1
- package/dist/lib/openapi/spec/feature-strategy-schema.d.ts +12 -2
- package/dist/lib/openapi/spec/feature-variants-schema.d.ts +1 -1
- package/dist/lib/openapi/spec/features-schema.d.ts +50 -10
- package/dist/lib/openapi/spec/group-schema.d.ts +1 -4
- package/dist/lib/openapi/spec/group-user-model-schema.d.ts +1 -4
- package/dist/lib/openapi/spec/group-user-model-schema.js +1 -4
- package/dist/lib/openapi/spec/group-user-model-schema.js.map +1 -1
- package/dist/lib/openapi/spec/groups-schema.d.ts +2 -8
- package/dist/lib/openapi/spec/groups-schema.test.js +0 -1
- package/dist/lib/openapi/spec/groups-schema.test.js.map +1 -1
- package/dist/lib/openapi/spec/health-overview-schema.d.ts +50 -10
- package/dist/lib/openapi/spec/health-report-schema.d.ts +50 -10
- package/dist/lib/openapi/spec/playground-constraint-schema.d.ts +47 -0
- package/dist/lib/openapi/spec/playground-constraint-schema.js +18 -0
- package/dist/lib/openapi/spec/playground-constraint-schema.js.map +1 -0
- package/dist/lib/openapi/spec/playground-feature-schema.d.ts +388 -2
- package/dist/lib/openapi/spec/playground-feature-schema.js +95 -9
- package/dist/lib/openapi/spec/playground-feature-schema.js.map +1 -1
- package/dist/lib/openapi/spec/playground-feature-schema.test.js +64 -3
- package/dist/lib/openapi/spec/playground-feature-schema.test.js.map +1 -1
- package/dist/lib/openapi/spec/playground-request-schema.d.ts +2 -0
- package/dist/lib/openapi/spec/playground-request-schema.js +6 -1
- package/dist/lib/openapi/spec/playground-request-schema.js.map +1 -1
- package/dist/lib/openapi/spec/playground-response-schema.d.ts +774 -3
- package/dist/lib/openapi/spec/playground-response-schema.js +15 -1
- package/dist/lib/openapi/spec/playground-response-schema.js.map +1 -1
- package/dist/lib/openapi/spec/playground-segment-schema.d.ts +79 -0
- package/dist/lib/openapi/spec/playground-segment-schema.js +36 -0
- package/dist/lib/openapi/spec/playground-segment-schema.js.map +1 -0
- package/dist/lib/openapi/spec/playground-strategy-schema.d.ts +257 -0
- package/dist/lib/openapi/spec/playground-strategy-schema.js +102 -0
- package/dist/lib/openapi/spec/playground-strategy-schema.js.map +1 -0
- package/dist/lib/openapi/spec/{bootstrap-ui-schema.test.d.ts → project-schema.test.d.ts} +0 -0
- package/dist/lib/openapi/spec/project-schema.test.js +17 -0
- package/dist/lib/openapi/spec/project-schema.test.js.map +1 -0
- package/dist/lib/openapi/spec/search-events-schema.d.ts +37 -0
- package/dist/lib/openapi/spec/search-events-schema.js +47 -0
- package/dist/lib/openapi/spec/search-events-schema.js.map +1 -0
- package/dist/lib/openapi/spec/segment-schema.d.ts +12 -2
- package/dist/lib/openapi/spec/state-schema.d.ts +61 -11
- package/dist/lib/{routes/admin-api/bootstrap-ui.test.d.ts → openapi/spec/tag-types-schema.test.d.ts} +0 -0
- package/dist/lib/openapi/spec/tag-types-schema.test.js +23 -0
- package/dist/lib/openapi/spec/tag-types-schema.test.js.map +1 -0
- package/dist/lib/openapi/spec/ui-config-schema.d.ts +3 -0
- package/dist/lib/openapi/spec/ui-config-schema.js +3 -0
- package/dist/lib/openapi/spec/ui-config-schema.js.map +1 -1
- package/dist/lib/openapi/spec/update-feature-schema.d.ts +12 -2
- package/dist/lib/openapi/spec/update-feature-strategy-schema.d.ts +12 -2
- package/dist/{test/e2e/api/admin/bootstrap.test.d.ts → lib/openapi/spec/user-schema.test.d.ts} +0 -0
- package/dist/lib/openapi/spec/user-schema.test.js +17 -0
- package/dist/lib/openapi/spec/user-schema.test.js.map +1 -0
- package/dist/lib/openapi/spec/users-groups-base-schema.d.ts +2 -8
- package/dist/lib/openapi/spec/variant-schema.d.ts +1 -1
- package/dist/lib/openapi/spec/variant-schema.js +1 -1
- package/dist/lib/openapi/spec/variant-schema.js.map +1 -1
- package/dist/lib/openapi/spec/variants-schema.d.ts +1 -1
- package/dist/lib/openapi/util/standard-responses.d.ts +6 -0
- package/dist/lib/openapi/util/standard-responses.js +10 -2
- package/dist/lib/openapi/util/standard-responses.js.map +1 -1
- package/dist/lib/routes/admin-api/config.d.ts +2 -1
- package/dist/lib/routes/admin-api/config.js +3 -1
- package/dist/lib/routes/admin-api/config.js.map +1 -1
- package/dist/lib/routes/admin-api/event.d.ts +3 -1
- package/dist/lib/routes/admin-api/event.js +30 -7
- package/dist/lib/routes/admin-api/event.js.map +1 -1
- package/dist/lib/routes/admin-api/index.js +0 -2
- package/dist/lib/routes/admin-api/index.js.map +1 -1
- package/dist/lib/routes/admin-api/playground.js.map +1 -1
- package/dist/lib/services/event-service.d.ts +2 -2
- package/dist/lib/services/event-service.js +2 -5
- package/dist/lib/services/event-service.js.map +1 -1
- package/dist/lib/services/feature-toggle-service.d.ts +1 -1
- package/dist/lib/services/feature-toggle-service.js +2 -2
- package/dist/lib/services/feature-toggle-service.js.map +1 -1
- package/dist/lib/services/group-service.d.ts +1 -1
- package/dist/lib/services/group-service.js +1 -5
- package/dist/lib/services/group-service.js.map +1 -1
- package/dist/lib/services/index.js +1 -0
- package/dist/lib/services/index.js.map +1 -1
- package/dist/lib/services/openapi-service.js +1 -1
- package/dist/lib/services/openapi-service.js.map +1 -1
- package/dist/lib/services/playground-service.d.ts +2 -1
- package/dist/lib/services/playground-service.js +30 -11
- package/dist/lib/services/playground-service.js.map +1 -1
- package/dist/lib/types/group.d.ts +1 -2
- package/dist/lib/types/group.js.map +1 -1
- package/dist/lib/types/stores/event-store.d.ts +2 -3
- package/dist/lib/types/stores/feature-toggle-client-store.d.ts +1 -1
- package/dist/lib/types/stores/group-store.d.ts +0 -1
- package/dist/lib/util/feature-evaluator/client.d.ts +25 -0
- package/dist/lib/util/feature-evaluator/client.js +135 -0
- package/dist/lib/util/feature-evaluator/client.js.map +1 -0
- package/dist/lib/util/feature-evaluator/constraint.d.ts +28 -0
- package/dist/lib/util/feature-evaluator/constraint.js +129 -0
- package/dist/lib/util/feature-evaluator/constraint.js.map +1 -0
- package/dist/lib/util/feature-evaluator/context.d.ts +13 -0
- package/dist/lib/util/feature-evaluator/context.js +3 -0
- package/dist/lib/util/feature-evaluator/context.js.map +1 -0
- package/dist/lib/util/feature-evaluator/feature-evaluator.d.ts +36 -0
- package/dist/lib/util/feature-evaluator/feature-evaluator.js +55 -0
- package/dist/lib/util/feature-evaluator/feature-evaluator.js.map +1 -0
- package/dist/lib/util/feature-evaluator/feature.d.ts +19 -0
- package/dist/lib/util/feature-evaluator/feature.js +3 -0
- package/dist/lib/util/feature-evaluator/feature.js.map +1 -0
- package/dist/lib/util/feature-evaluator/helpers.d.ts +6 -0
- package/dist/lib/util/feature-evaluator/helpers.js +32 -0
- package/dist/lib/util/feature-evaluator/helpers.js.map +1 -0
- package/dist/lib/util/feature-evaluator/index.d.ts +8 -0
- package/dist/lib/util/feature-evaluator/index.js +14 -0
- package/dist/lib/util/feature-evaluator/index.js.map +1 -0
- package/dist/lib/util/feature-evaluator/repository/bootstrap-provider.d.ts +16 -0
- package/dist/lib/util/feature-evaluator/repository/bootstrap-provider.js +25 -0
- package/dist/lib/util/feature-evaluator/repository/bootstrap-provider.js.map +1 -0
- package/dist/lib/util/feature-evaluator/repository/index.d.ts +35 -0
- package/dist/lib/util/feature-evaluator/repository/index.js +63 -0
- package/dist/lib/util/feature-evaluator/repository/index.js.map +1 -0
- package/dist/lib/util/feature-evaluator/repository/storage-provider-in-mem.d.ts +6 -0
- package/dist/lib/util/feature-evaluator/repository/storage-provider-in-mem.js +16 -0
- package/dist/lib/util/feature-evaluator/repository/storage-provider-in-mem.js.map +1 -0
- package/dist/lib/util/feature-evaluator/repository/storage-provider.d.ts +14 -0
- package/dist/lib/util/feature-evaluator/repository/storage-provider.js +50 -0
- package/dist/lib/util/feature-evaluator/repository/storage-provider.js.map +1 -0
- package/dist/lib/util/feature-evaluator/strategy/application-hostname-strategy.d.ts +8 -0
- package/dist/lib/util/feature-evaluator/strategy/application-hostname-strategy.js +23 -0
- package/dist/lib/util/feature-evaluator/strategy/application-hostname-strategy.js.map +1 -0
- package/dist/lib/util/feature-evaluator/strategy/default-strategy.d.ts +5 -0
- package/dist/lib/util/feature-evaluator/strategy/default-strategy.js +13 -0
- package/dist/lib/util/feature-evaluator/strategy/default-strategy.js.map +1 -0
- package/dist/lib/util/feature-evaluator/strategy/flexible-rollout-strategy.d.ts +12 -0
- package/dist/lib/util/feature-evaluator/strategy/flexible-rollout-strategy.js +49 -0
- package/dist/lib/util/feature-evaluator/strategy/flexible-rollout-strategy.js.map +1 -0
- package/dist/lib/util/feature-evaluator/strategy/gradual-rollout-random.d.ts +9 -0
- package/dist/lib/util/feature-evaluator/strategy/gradual-rollout-random.js +19 -0
- package/dist/lib/util/feature-evaluator/strategy/gradual-rollout-random.js.map +1 -0
- package/dist/lib/util/feature-evaluator/strategy/gradual-rollout-session-id.d.ts +9 -0
- package/dist/lib/util/feature-evaluator/strategy/gradual-rollout-session-id.js +24 -0
- package/dist/lib/util/feature-evaluator/strategy/gradual-rollout-session-id.js.map +1 -0
- package/dist/lib/util/feature-evaluator/strategy/gradual-rollout-user-id.d.ts +9 -0
- package/dist/lib/util/feature-evaluator/strategy/gradual-rollout-user-id.js +24 -0
- package/dist/lib/util/feature-evaluator/strategy/gradual-rollout-user-id.js.map +1 -0
- package/dist/lib/util/feature-evaluator/strategy/index.d.ts +4 -0
- package/dist/lib/util/feature-evaluator/strategy/index.js +29 -0
- package/dist/lib/util/feature-evaluator/strategy/index.js.map +1 -0
- package/dist/lib/util/feature-evaluator/strategy/remote-address-strategy.d.ts +8 -0
- package/dist/lib/util/feature-evaluator/strategy/remote-address-strategy.js +35 -0
- package/dist/lib/util/feature-evaluator/strategy/remote-address-strategy.js.map +1 -0
- package/dist/lib/util/feature-evaluator/strategy/strategy.d.ts +41 -0
- package/dist/lib/util/feature-evaluator/strategy/strategy.js +75 -0
- package/dist/lib/util/feature-evaluator/strategy/strategy.js.map +1 -0
- package/dist/lib/util/feature-evaluator/strategy/unknown-strategy.d.ts +9 -0
- package/dist/lib/util/feature-evaluator/strategy/unknown-strategy.js +29 -0
- package/dist/lib/util/feature-evaluator/strategy/unknown-strategy.js.map +1 -0
- package/dist/lib/util/feature-evaluator/strategy/user-with-id-strategy.d.ts +8 -0
- package/dist/lib/util/feature-evaluator/strategy/user-with-id-strategy.js +16 -0
- package/dist/lib/util/feature-evaluator/strategy/user-with-id-strategy.js.map +1 -0
- package/dist/lib/util/feature-evaluator/strategy/util.d.ts +1 -0
- package/dist/lib/util/feature-evaluator/strategy/util.js +31 -0
- package/dist/lib/util/feature-evaluator/strategy/util.js.map +1 -0
- package/dist/lib/util/feature-evaluator/variant.d.ts +28 -0
- package/dist/lib/util/feature-evaluator/variant.js +73 -0
- package/dist/lib/util/feature-evaluator/variant.js.map +1 -0
- package/dist/lib/util/offline-unleash-client.d.ts +13 -2
- package/dist/lib/util/offline-unleash-client.js +20 -20
- package/dist/lib/util/offline-unleash-client.js.map +1 -1
- package/dist/lib/util/offline-unleash-client.test.d.ts +3 -1
- package/dist/lib/util/offline-unleash-client.test.js +349 -139
- package/dist/lib/util/offline-unleash-client.test.js.map +1 -1
- package/dist/migrations/20220808084524-add-group-permissions.d.ts +2 -0
- package/dist/migrations/20220808084524-add-group-permissions.js +12 -0
- package/dist/migrations/20220808084524-add-group-permissions.js.map +1 -0
- package/dist/migrations/20220808110415-add-projects-foreign-key.d.ts +2 -0
- package/dist/migrations/20220808110415-add-projects-foreign-key.js +14 -0
- package/dist/migrations/20220808110415-add-projects-foreign-key.js.map +1 -0
- package/dist/test/arbitraries.test.d.ts +12 -2
- package/dist/test/arbitraries.test.js +67 -9
- package/dist/test/arbitraries.test.js.map +1 -1
- package/dist/test/e2e/api/admin/config.e2e.test.js +2 -1
- package/dist/test/e2e/api/admin/config.e2e.test.js.map +1 -1
- package/dist/test/e2e/api/admin/event.e2e.test.js +57 -0
- package/dist/test/e2e/api/admin/event.e2e.test.js.map +1 -1
- package/dist/test/e2e/api/admin/feature.e2e.test.js +8 -7
- package/dist/test/e2e/api/admin/feature.e2e.test.js.map +1 -1
- package/dist/test/e2e/api/admin/playground.e2e.test.js +22 -0
- package/dist/test/e2e/api/admin/playground.e2e.test.js.map +1 -1
- package/dist/test/e2e/services/access-service.e2e.test.js +38 -20
- package/dist/test/e2e/services/access-service.e2e.test.js.map +1 -1
- package/dist/test/e2e/services/playground-service.test.d.ts +5 -1
- package/dist/test/e2e/services/playground-service.test.js +725 -37
- package/dist/test/e2e/services/playground-service.test.js.map +1 -1
- package/dist/test/e2e/services/project-service.e2e.test.js +12 -4
- package/dist/test/e2e/services/project-service.e2e.test.js.map +1 -1
- package/dist/test/e2e/services/setting-service.test.js +9 -3
- package/dist/test/e2e/services/setting-service.test.js.map +1 -1
- package/dist/test/e2e/stores/event-store.e2e.test.js +6 -2
- package/dist/test/e2e/stores/event-store.e2e.test.js.map +1 -1
- package/dist/test/fixtures/fake-event-store.d.ts +1 -3
- package/dist/test/fixtures/fake-event-store.js +2 -8
- package/dist/test/fixtures/fake-event-store.js.map +1 -1
- package/dist/test/fixtures/fake-group-store.d.ts +0 -1
- package/dist/test/fixtures/fake-group-store.js +0 -3
- package/dist/test/fixtures/fake-group-store.js.map +1 -1
- package/package.json +15 -13
- package/dist/lib/openapi/spec/bootstrap-ui-schema.d.ts +0 -491
- package/dist/lib/openapi/spec/bootstrap-ui-schema.js +0 -94
- package/dist/lib/openapi/spec/bootstrap-ui-schema.js.map +0 -1
- package/dist/lib/openapi/spec/bootstrap-ui-schema.test.js +0 -145
- package/dist/lib/openapi/spec/bootstrap-ui-schema.test.js.map +0 -1
- package/dist/lib/openapi/spec/client-variant-schema.d.ts +0 -35
- package/dist/lib/openapi/spec/client-variant-schema.js +0 -37
- package/dist/lib/openapi/spec/client-variant-schema.js.map +0 -1
- package/dist/lib/routes/admin-api/bootstrap-ui.d.ts +0 -25
- package/dist/lib/routes/admin-api/bootstrap-ui.js +0 -82
- package/dist/lib/routes/admin-api/bootstrap-ui.js.map +0 -1
- package/dist/lib/routes/admin-api/bootstrap-ui.test.js +0 -60
- package/dist/lib/routes/admin-api/bootstrap-ui.test.js.map +0 -1
- package/dist/test/e2e/api/admin/bootstrap.test.js +0 -43
- package/dist/test/e2e/api/admin/bootstrap.test.js.map +0 -1
|
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.seedDatabaseForPlaygroundTest = void 0;
|
|
6
7
|
const playground_service_1 = require("../../../lib/services/playground-service");
|
|
7
8
|
const arbitraries_test_1 = require("../../arbitraries.test");
|
|
8
9
|
const sdk_context_schema_test_1 = require("../../../lib/openapi/spec/sdk-context-schema.test");
|
|
@@ -11,47 +12,130 @@ const test_config_1 = require("../../config/test-config");
|
|
|
11
12
|
const database_init_1 = __importDefault(require("../helpers/database-init"));
|
|
12
13
|
const feature_toggle_service_1 = __importDefault(require("../../../lib/services/feature-toggle-service"));
|
|
13
14
|
const segment_service_1 = require("../../../lib/services/segment-service");
|
|
14
|
-
const
|
|
15
|
+
const model_1 = require("../../../lib/types/model");
|
|
16
|
+
const offline_unleash_client_test_1 = require("../../../lib/util/offline-unleash-client.test");
|
|
17
|
+
const playground_strategy_schema_1 = require("../../../lib/openapi/spec/playground-strategy-schema");
|
|
15
18
|
let stores;
|
|
16
19
|
let db;
|
|
17
20
|
let service;
|
|
18
21
|
let featureToggleService;
|
|
22
|
+
let segmentService;
|
|
19
23
|
beforeAll(async () => {
|
|
20
24
|
const config = (0, test_config_1.createTestConfig)();
|
|
21
25
|
db = await (0, database_init_1.default)('playground_service_serial', config.getLogger);
|
|
22
26
|
stores = db.stores;
|
|
23
|
-
|
|
27
|
+
segmentService = new segment_service_1.SegmentService(stores, config);
|
|
28
|
+
featureToggleService = new feature_toggle_service_1.default(stores, config, segmentService);
|
|
24
29
|
service = new playground_service_1.PlaygroundService(config, {
|
|
25
30
|
featureToggleServiceV2: featureToggleService,
|
|
31
|
+
segmentService,
|
|
26
32
|
});
|
|
27
33
|
});
|
|
28
34
|
afterAll(async () => {
|
|
29
35
|
await db.destroy();
|
|
30
36
|
});
|
|
37
|
+
const cleanup = async () => {
|
|
38
|
+
await stores.segmentStore.deleteAll();
|
|
39
|
+
await stores.featureToggleStore.deleteAll();
|
|
40
|
+
await stores.eventStore.deleteAll();
|
|
41
|
+
await stores.featureStrategiesStore.deleteAll();
|
|
42
|
+
await stores.segmentStore.deleteAll();
|
|
43
|
+
};
|
|
44
|
+
afterEach(cleanup);
|
|
31
45
|
const testParams = {
|
|
32
46
|
interruptAfterTimeLimit: 4000,
|
|
33
47
|
markInterruptAsFailure: false, // When set to false, timeout during initial cases will not be considered as a failure
|
|
34
48
|
};
|
|
49
|
+
const mapSegmentSchemaToISegment = (segment, index) => ({
|
|
50
|
+
...segment,
|
|
51
|
+
name: segment.name || `test-segment ${index ?? 'unnumbered'}`,
|
|
52
|
+
createdAt: new Date(),
|
|
53
|
+
});
|
|
54
|
+
const seedDatabaseForPlaygroundTest = async (database, features, environment, segments) => {
|
|
55
|
+
if (segments) {
|
|
56
|
+
await Promise.all(segments.map(async (segment, index) => database.stores.segmentStore.create(mapSegmentSchemaToISegment(segment, index), { username: 'test' })));
|
|
57
|
+
}
|
|
58
|
+
return Promise.all(features.map(async (feature) => {
|
|
59
|
+
// create feature
|
|
60
|
+
const toggle = await database.stores.featureToggleStore.create(feature.project, {
|
|
61
|
+
...feature,
|
|
62
|
+
createdAt: undefined,
|
|
63
|
+
variants: [
|
|
64
|
+
...(feature.variants ?? []).map((variant) => ({
|
|
65
|
+
...variant,
|
|
66
|
+
weightType: model_1.WeightType.VARIABLE,
|
|
67
|
+
stickiness: 'default',
|
|
68
|
+
})),
|
|
69
|
+
],
|
|
70
|
+
});
|
|
71
|
+
// create environment if necessary
|
|
72
|
+
await database.stores.environmentStore
|
|
73
|
+
.create({
|
|
74
|
+
name: environment,
|
|
75
|
+
type: 'development',
|
|
76
|
+
enabled: true,
|
|
77
|
+
})
|
|
78
|
+
.catch(() => {
|
|
79
|
+
// purposefully left empty: env creation may fail if the
|
|
80
|
+
// env already exists, and because of the async nature
|
|
81
|
+
// of things, this is the easiest way to make it work.
|
|
82
|
+
});
|
|
83
|
+
// assign strategies
|
|
84
|
+
await Promise.all((feature.strategies || []).map(async ({ segments: strategySegments, ...strategy }) => {
|
|
85
|
+
await database.stores.featureStrategiesStore.createStrategyFeatureEnv({
|
|
86
|
+
parameters: {},
|
|
87
|
+
constraints: [],
|
|
88
|
+
...strategy,
|
|
89
|
+
featureName: feature.name,
|
|
90
|
+
environment,
|
|
91
|
+
strategyName: strategy.name,
|
|
92
|
+
projectId: feature.project,
|
|
93
|
+
});
|
|
94
|
+
if (strategySegments) {
|
|
95
|
+
await Promise.all(strategySegments.map((segmentId) => database.stores.segmentStore.addToStrategy(segmentId, strategy.id)));
|
|
96
|
+
}
|
|
97
|
+
}));
|
|
98
|
+
// enable/disable the feature in environment
|
|
99
|
+
await database.stores.featureEnvironmentStore.addEnvironmentToFeature(feature.name, environment, feature.enabled);
|
|
100
|
+
return toggle;
|
|
101
|
+
}));
|
|
102
|
+
};
|
|
103
|
+
exports.seedDatabaseForPlaygroundTest = seedDatabaseForPlaygroundTest;
|
|
35
104
|
describe('the playground service (e2e)', () => {
|
|
36
105
|
const isDisabledVariant = ({ name, enabled, }) => name === 'disabled' && !enabled;
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
106
|
+
const insertAndEvaluateFeatures = async ({ features, context, env = 'default', segments, }) => {
|
|
107
|
+
await (0, exports.seedDatabaseForPlaygroundTest)(db, features, env, segments);
|
|
108
|
+
// const activeSegments = await db.stores.segmentStore.getAllFeatureStrategySegments()
|
|
109
|
+
// console.log("active segments db seeding", activeSegments)
|
|
110
|
+
const projects = '*';
|
|
111
|
+
const serviceFeatures = await service.evaluateQuery(projects, env, context);
|
|
112
|
+
return serviceFeatures;
|
|
113
|
+
};
|
|
43
114
|
test('should return the same enabled toggles as the raw SDK correctly mapped', async () => {
|
|
44
115
|
await fast_check_1.default.assert(fast_check_1.default
|
|
45
|
-
.asyncProperty((0, arbitraries_test_1.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
116
|
+
.asyncProperty((0, arbitraries_test_1.clientFeaturesAndSegments)({ minLength: 1 }), fast_check_1.default
|
|
117
|
+
.tuple((0, sdk_context_schema_test_1.generate)(), (0, arbitraries_test_1.commonISOTimestamp)())
|
|
118
|
+
.map(([context, currentTime]) => ({
|
|
119
|
+
...context,
|
|
120
|
+
userId: 'constant',
|
|
121
|
+
sessionId: 'constant2',
|
|
122
|
+
currentTime,
|
|
123
|
+
})), fast_check_1.default.context(), async ({ segments, features }, context, ctx) => {
|
|
124
|
+
const serviceToggles = await insertAndEvaluateFeatures({
|
|
125
|
+
features: features,
|
|
126
|
+
context,
|
|
127
|
+
segments,
|
|
128
|
+
});
|
|
50
129
|
const [head, ...rest] = await featureToggleService.getClientFeatures();
|
|
51
130
|
if (!head) {
|
|
52
131
|
return serviceToggles.length === 0;
|
|
53
132
|
}
|
|
54
|
-
const client = await (0,
|
|
133
|
+
const client = await (0, offline_unleash_client_test_1.offlineUnleashClientNode)({
|
|
134
|
+
features: [head, ...rest],
|
|
135
|
+
context,
|
|
136
|
+
logError: console.log,
|
|
137
|
+
segments: segments.map(mapSegmentSchemaToISegment),
|
|
138
|
+
});
|
|
55
139
|
const clientContext = {
|
|
56
140
|
...context,
|
|
57
141
|
currentTime: context.currentTime
|
|
@@ -59,48 +143,585 @@ describe('the playground service (e2e)', () => {
|
|
|
59
143
|
: undefined,
|
|
60
144
|
};
|
|
61
145
|
return serviceToggles.every((feature) => {
|
|
62
|
-
|
|
146
|
+
ctx.log(`Examining feature ${feature.name}: ${JSON.stringify(feature)}`);
|
|
147
|
+
// the playground differs from a normal SDK in that
|
|
148
|
+
// it _must_ evaluate all srategies and features
|
|
149
|
+
// regardless of whether they're supposed to be
|
|
150
|
+
// enabled in the current environment or not.
|
|
151
|
+
const expectedSDKState = feature.isEnabled;
|
|
152
|
+
const enabledStateMatches = expectedSDKState ===
|
|
63
153
|
client.isEnabled(feature.name, clientContext);
|
|
64
|
-
|
|
154
|
+
expect(enabledStateMatches).toBe(true);
|
|
155
|
+
ctx.log(`feature.isEnabled, feature.isEnabledInCurrentEnvironment, presumedSDKState: ${feature.isEnabled}, ${feature.isEnabledInCurrentEnvironment}, ${expectedSDKState}`);
|
|
156
|
+
ctx.log(`client.isEnabled: ${client.isEnabled(feature.name, clientContext)}`);
|
|
65
157
|
// if x is disabled, then the variant will be the
|
|
66
158
|
// disabled variant.
|
|
67
159
|
if (!feature.isEnabled) {
|
|
160
|
+
ctx.log(`${feature.name} is not enabled`);
|
|
161
|
+
ctx.log(JSON.stringify(feature.variant));
|
|
162
|
+
ctx.log(JSON.stringify(enabledStateMatches));
|
|
163
|
+
ctx.log(JSON.stringify(feature.variant.name === 'disabled'));
|
|
164
|
+
ctx.log(JSON.stringify(feature.variant.enabled === false));
|
|
68
165
|
return (enabledStateMatches &&
|
|
69
166
|
isDisabledVariant(feature.variant));
|
|
70
167
|
}
|
|
168
|
+
ctx.log('feature is enabled');
|
|
71
169
|
const clientVariant = client.getVariant(feature.name, clientContext);
|
|
72
170
|
// if x is enabled, but its variant is the disabled
|
|
73
171
|
// variant, then the source does not have any
|
|
74
172
|
// variants
|
|
75
|
-
if (feature.
|
|
76
|
-
isDisabledVariant(feature.variant)) {
|
|
173
|
+
if (isDisabledVariant(feature.variant)) {
|
|
77
174
|
return (enabledStateMatches &&
|
|
78
175
|
isDisabledVariant(clientVariant));
|
|
79
176
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
177
|
+
ctx.log(`feature "${feature.name}" has a variant`);
|
|
178
|
+
ctx.log(`Feature variant: ${JSON.stringify(feature.variant)}`);
|
|
179
|
+
ctx.log(`Client variant: ${JSON.stringify(clientVariant)}`);
|
|
180
|
+
ctx.log(`enabledStateMatches: ${enabledStateMatches}`);
|
|
181
|
+
// variants should be the same if the
|
|
182
|
+
// toggle is enabled in both versions. If
|
|
183
|
+
// they're not and one of them has a
|
|
184
|
+
// variant, then they should be different.
|
|
185
|
+
if (expectedSDKState === true) {
|
|
186
|
+
expect(feature.variant).toEqual(clientVariant);
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
expect(feature.variant).not.toEqual(clientVariant);
|
|
190
|
+
}
|
|
191
|
+
return enabledStateMatches;
|
|
86
192
|
});
|
|
87
193
|
})
|
|
88
|
-
.afterEach(
|
|
89
|
-
|
|
90
|
-
|
|
194
|
+
.afterEach(cleanup), { ...testParams, examples: [] });
|
|
195
|
+
});
|
|
196
|
+
// counterexamples found by fastcheck
|
|
197
|
+
const counterexamples = [
|
|
198
|
+
[
|
|
199
|
+
[
|
|
200
|
+
{
|
|
201
|
+
name: '-',
|
|
202
|
+
type: 'release',
|
|
203
|
+
project: 'A',
|
|
204
|
+
enabled: true,
|
|
205
|
+
lastSeenAt: '1970-01-01T00:00:00.000Z',
|
|
206
|
+
impressionData: null,
|
|
207
|
+
strategies: [],
|
|
208
|
+
variants: [
|
|
209
|
+
{
|
|
210
|
+
name: '-',
|
|
211
|
+
weight: 147,
|
|
212
|
+
weightType: 'variable',
|
|
213
|
+
stickiness: 'default',
|
|
214
|
+
payload: { type: 'string', value: '' },
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
name: '~3dignissim~gravidaod',
|
|
218
|
+
weight: 301,
|
|
219
|
+
weightType: 'variable',
|
|
220
|
+
stickiness: 'default',
|
|
221
|
+
payload: {
|
|
222
|
+
type: 'json',
|
|
223
|
+
value: '{"Sv7gRNNl=":[true,"Mfs >mp.D","O-jtK","y%i\\"Ub~",null,"J",false,"(\'R"],"F0g+>1X":3.892913121148499e-188,"Fi~k(":-4.882970135331098e+146,"":null,"nPT]":true}',
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
],
|
|
227
|
+
},
|
|
228
|
+
],
|
|
229
|
+
{
|
|
230
|
+
appName: '"$#',
|
|
231
|
+
currentTime: '9999-12-31T23:59:59.956Z',
|
|
232
|
+
environment: 'r',
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
logs: [
|
|
236
|
+
'feature is enabled',
|
|
237
|
+
'feature has a variant',
|
|
238
|
+
'{"name":"-","payload":{"type":"string","value":""},"enabled":true}',
|
|
239
|
+
'{"name":"~3dignissim~gravidaod","payload":{"type":"json","value":"{\\"Sv7gRNNl=\\":[true,\\"Mfs >mp.D\\",\\"O-jtK\\",\\"y%i\\\\\\"Ub~\\",null,\\"J\\",false,\\"(\'R\\"],\\"F0g+>1X\\":3.892913121148499e-188,\\"Fi~k(\\":-4.882970135331098e+146,\\"\\":null,\\"nPT]\\":true}"},"enabled":true}',
|
|
240
|
+
'true',
|
|
241
|
+
'false',
|
|
242
|
+
],
|
|
243
|
+
},
|
|
244
|
+
],
|
|
245
|
+
[
|
|
246
|
+
[
|
|
247
|
+
{
|
|
248
|
+
name: '-',
|
|
249
|
+
project: '0',
|
|
250
|
+
enabled: true,
|
|
251
|
+
strategies: [
|
|
252
|
+
{
|
|
253
|
+
name: 'default',
|
|
254
|
+
constraints: [
|
|
255
|
+
{
|
|
256
|
+
contextName: 'A',
|
|
257
|
+
operator: 'NOT_IN',
|
|
258
|
+
caseInsensitive: false,
|
|
259
|
+
inverted: false,
|
|
260
|
+
values: [],
|
|
261
|
+
value: '',
|
|
262
|
+
},
|
|
263
|
+
],
|
|
264
|
+
},
|
|
265
|
+
],
|
|
266
|
+
},
|
|
267
|
+
],
|
|
268
|
+
{ appName: ' ', userId: 'constant', sessionId: 'constant2' },
|
|
269
|
+
{ logs: [] },
|
|
270
|
+
],
|
|
271
|
+
[
|
|
272
|
+
[
|
|
273
|
+
{
|
|
274
|
+
name: 'a',
|
|
275
|
+
project: 'a',
|
|
276
|
+
enabled: true,
|
|
277
|
+
strategies: [
|
|
278
|
+
{
|
|
279
|
+
name: 'default',
|
|
280
|
+
constraints: [
|
|
281
|
+
{
|
|
282
|
+
contextName: '0',
|
|
283
|
+
operator: 'NOT_IN',
|
|
284
|
+
caseInsensitive: false,
|
|
285
|
+
inverted: false,
|
|
286
|
+
values: [],
|
|
287
|
+
value: '',
|
|
288
|
+
},
|
|
289
|
+
],
|
|
290
|
+
},
|
|
291
|
+
],
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
name: '-',
|
|
295
|
+
project: 'elementum',
|
|
296
|
+
enabled: false,
|
|
297
|
+
strategies: [],
|
|
298
|
+
},
|
|
299
|
+
],
|
|
300
|
+
{ appName: ' ', userId: 'constant', sessionId: 'constant2' },
|
|
301
|
+
{
|
|
302
|
+
logs: [
|
|
303
|
+
'feature is not enabled',
|
|
304
|
+
'{"name":"disabled","enabled":false}',
|
|
305
|
+
],
|
|
306
|
+
},
|
|
307
|
+
],
|
|
308
|
+
[
|
|
309
|
+
[
|
|
310
|
+
{
|
|
311
|
+
name: '0',
|
|
312
|
+
project: '-',
|
|
313
|
+
enabled: true,
|
|
314
|
+
strategies: [
|
|
315
|
+
{
|
|
316
|
+
name: 'default',
|
|
317
|
+
constraints: [
|
|
318
|
+
{
|
|
319
|
+
contextName: 'sed',
|
|
320
|
+
operator: 'NOT_IN',
|
|
321
|
+
caseInsensitive: false,
|
|
322
|
+
inverted: false,
|
|
323
|
+
values: [],
|
|
324
|
+
value: '',
|
|
325
|
+
},
|
|
326
|
+
],
|
|
327
|
+
},
|
|
328
|
+
],
|
|
329
|
+
},
|
|
330
|
+
],
|
|
331
|
+
{ appName: ' ', userId: 'constant', sessionId: 'constant2' },
|
|
332
|
+
{
|
|
333
|
+
logs: [
|
|
334
|
+
'0 is not enabled',
|
|
335
|
+
'{"name":"disabled","enabled":false}',
|
|
336
|
+
'true',
|
|
337
|
+
'true',
|
|
338
|
+
],
|
|
339
|
+
},
|
|
340
|
+
],
|
|
341
|
+
[
|
|
342
|
+
[
|
|
343
|
+
{
|
|
344
|
+
name: '0',
|
|
345
|
+
project: 'ac',
|
|
346
|
+
enabled: true,
|
|
347
|
+
strategies: [
|
|
348
|
+
{
|
|
349
|
+
name: 'default',
|
|
350
|
+
constraints: [
|
|
351
|
+
{
|
|
352
|
+
contextName: '0',
|
|
353
|
+
operator: 'NOT_IN',
|
|
354
|
+
caseInsensitive: false,
|
|
355
|
+
inverted: false,
|
|
356
|
+
values: [],
|
|
357
|
+
value: '',
|
|
358
|
+
},
|
|
359
|
+
],
|
|
360
|
+
},
|
|
361
|
+
],
|
|
362
|
+
},
|
|
363
|
+
],
|
|
364
|
+
{ appName: ' ', userId: 'constant', sessionId: 'constant2' },
|
|
365
|
+
{
|
|
366
|
+
logs: [
|
|
367
|
+
'feature.isEnabled: false',
|
|
368
|
+
'client.isEnabled: true',
|
|
369
|
+
'0 is not enabled',
|
|
370
|
+
'{"name":"disabled","enabled":false}',
|
|
371
|
+
'false',
|
|
372
|
+
'true',
|
|
373
|
+
'true',
|
|
374
|
+
],
|
|
375
|
+
},
|
|
376
|
+
],
|
|
377
|
+
[
|
|
378
|
+
[
|
|
379
|
+
{
|
|
380
|
+
name: '0',
|
|
381
|
+
project: 'aliquam',
|
|
382
|
+
enabled: true,
|
|
383
|
+
strategies: [
|
|
384
|
+
{
|
|
385
|
+
name: 'default',
|
|
386
|
+
constraints: [
|
|
387
|
+
{
|
|
388
|
+
contextName: '-',
|
|
389
|
+
operator: 'NOT_IN',
|
|
390
|
+
caseInsensitive: false,
|
|
391
|
+
inverted: false,
|
|
392
|
+
values: [],
|
|
393
|
+
value: '',
|
|
394
|
+
},
|
|
395
|
+
],
|
|
396
|
+
},
|
|
397
|
+
],
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
name: '-',
|
|
401
|
+
project: '-',
|
|
402
|
+
enabled: false,
|
|
403
|
+
strategies: [],
|
|
404
|
+
},
|
|
405
|
+
],
|
|
406
|
+
{
|
|
407
|
+
appName: ' ',
|
|
408
|
+
userId: 'constant',
|
|
409
|
+
sessionId: 'constant2',
|
|
410
|
+
currentTime: '1970-01-01T00:00:00.000Z',
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
logs: [
|
|
414
|
+
'feature.isEnabled: false',
|
|
415
|
+
'client.isEnabled: true',
|
|
416
|
+
'0 is not enabled',
|
|
417
|
+
'{"name":"disabled","enabled":false}',
|
|
418
|
+
'false',
|
|
419
|
+
'true',
|
|
420
|
+
'true',
|
|
421
|
+
],
|
|
422
|
+
},
|
|
423
|
+
],
|
|
424
|
+
];
|
|
425
|
+
// these tests test counterexamples found by fast check. The may seem redundant, but are concrete cases that might break.
|
|
426
|
+
counterexamples.map(async ([features, context], i) => {
|
|
427
|
+
it(`should do the same as the raw SDK: counterexample ${i}`, async () => {
|
|
428
|
+
const serviceFeatures = await insertAndEvaluateFeatures({
|
|
429
|
+
// @ts-expect-error
|
|
430
|
+
features,
|
|
431
|
+
// @ts-expect-error
|
|
432
|
+
context,
|
|
433
|
+
});
|
|
434
|
+
const [head, ...rest] = await featureToggleService.getClientFeatures();
|
|
435
|
+
if (!head) {
|
|
436
|
+
return serviceFeatures.length === 0;
|
|
437
|
+
}
|
|
438
|
+
const client = await (0, offline_unleash_client_test_1.offlineUnleashClientNode)({
|
|
439
|
+
features: [head, ...rest],
|
|
440
|
+
// @ts-expect-error
|
|
441
|
+
context,
|
|
442
|
+
logError: console.log,
|
|
443
|
+
});
|
|
444
|
+
const clientContext = {
|
|
445
|
+
...context,
|
|
446
|
+
// @ts-expect-error
|
|
447
|
+
currentTime: context.currentTime
|
|
448
|
+
? // @ts-expect-error
|
|
449
|
+
new Date(context.currentTime)
|
|
450
|
+
: undefined,
|
|
451
|
+
};
|
|
452
|
+
serviceFeatures.forEach((feature) => {
|
|
453
|
+
expect(feature.isEnabled).toEqual(
|
|
454
|
+
//@ts-expect-error
|
|
455
|
+
client.isEnabled(feature.name, clientContext));
|
|
456
|
+
});
|
|
457
|
+
});
|
|
458
|
+
});
|
|
459
|
+
test("should return all of a feature's strategies", async () => {
|
|
460
|
+
await fast_check_1.default.assert(fast_check_1.default
|
|
461
|
+
.asyncProperty((0, arbitraries_test_1.clientFeaturesAndSegments)({ minLength: 1 }), (0, sdk_context_schema_test_1.generate)(), fast_check_1.default.context(), async (data, context, ctx) => {
|
|
462
|
+
const log = (x) => ctx.log(JSON.stringify(x));
|
|
463
|
+
const serviceFeatures = await insertAndEvaluateFeatures({
|
|
464
|
+
...data,
|
|
465
|
+
context,
|
|
466
|
+
});
|
|
467
|
+
const serviceFeaturesDict = serviceFeatures.reduce((acc, feature) => ({
|
|
468
|
+
...acc,
|
|
469
|
+
[feature.name]: feature,
|
|
470
|
+
}), {});
|
|
471
|
+
// for each feature, find the corresponding evaluated feature
|
|
472
|
+
// and make sure that the evaluated
|
|
473
|
+
// return genFeat.length === servFeat.length && zip(gen, serv).
|
|
474
|
+
data.features.forEach((feature) => {
|
|
475
|
+
const mappedFeature = serviceFeaturesDict[feature.name];
|
|
476
|
+
// log(feature);
|
|
477
|
+
log(mappedFeature);
|
|
478
|
+
const featureStrategies = feature.strategies ?? [];
|
|
479
|
+
expect(mappedFeature.strategies.data.length).toEqual(featureStrategies.length);
|
|
480
|
+
// we can't guarantee that the order we inserted
|
|
481
|
+
// strategies into the database is the same as it
|
|
482
|
+
// was returned by the service , so we'll need to
|
|
483
|
+
// scan through the list of strats.
|
|
484
|
+
// extract the `result` property, because it
|
|
485
|
+
// doesn't exist in the input
|
|
486
|
+
const removeResult = ({ result, ...rest }) => rest;
|
|
487
|
+
const cleanedReceivedStrategies = mappedFeature.strategies.data.map((strategy) => {
|
|
488
|
+
const { segments: mappedSegments, ...mappedStrategy } = removeResult(strategy);
|
|
489
|
+
return {
|
|
490
|
+
...mappedStrategy,
|
|
491
|
+
constraints: mappedStrategy.constraints?.map(removeResult),
|
|
492
|
+
};
|
|
493
|
+
});
|
|
494
|
+
feature.strategies.forEach(({ segments, ...strategy }) => {
|
|
495
|
+
expect(cleanedReceivedStrategies).toEqual(expect.arrayContaining([
|
|
496
|
+
{
|
|
497
|
+
...strategy,
|
|
498
|
+
constraints: strategy.constraints ?? [],
|
|
499
|
+
parameters: strategy.parameters ?? {},
|
|
500
|
+
},
|
|
501
|
+
]));
|
|
502
|
+
});
|
|
503
|
+
});
|
|
504
|
+
})
|
|
505
|
+
.afterEach(cleanup), testParams);
|
|
506
|
+
});
|
|
507
|
+
test('should return feature strategies with all their segments', async () => {
|
|
508
|
+
await fast_check_1.default.assert(fast_check_1.default
|
|
509
|
+
.asyncProperty((0, arbitraries_test_1.clientFeaturesAndSegments)({ minLength: 1 }), (0, sdk_context_schema_test_1.generate)(), async ({ segments, features: generatedFeatures }, context) => {
|
|
510
|
+
const serviceFeatures = await insertAndEvaluateFeatures({
|
|
511
|
+
features: generatedFeatures,
|
|
512
|
+
context,
|
|
513
|
+
segments,
|
|
514
|
+
});
|
|
515
|
+
const serviceFeaturesDict = serviceFeatures.reduce((acc, feature) => ({
|
|
516
|
+
...acc,
|
|
517
|
+
[feature.name]: feature,
|
|
518
|
+
}), {});
|
|
519
|
+
// ensure that segments are mapped on to features
|
|
520
|
+
// correctly. We do not need to check whether the
|
|
521
|
+
// evaluation is correct; that is taken care of by other
|
|
522
|
+
// tests.
|
|
523
|
+
// For each feature strategy, find its list of segments and
|
|
524
|
+
// compare it to the input.
|
|
525
|
+
//
|
|
526
|
+
// We can assert three things:
|
|
527
|
+
//
|
|
528
|
+
// 1. The segments lists have the same length
|
|
529
|
+
//
|
|
530
|
+
// 2. All segment ids listed in an input id list are
|
|
531
|
+
// also in the original segments list
|
|
532
|
+
//
|
|
533
|
+
// 3. If a feature is considered enabled, _all_ segments
|
|
534
|
+
// must be true. If a feature is _disabled_, _at least_
|
|
535
|
+
// one segment is not true.
|
|
536
|
+
generatedFeatures.forEach((unmappedFeature) => {
|
|
537
|
+
const strategies = serviceFeaturesDict[unmappedFeature.name].strategies.data.reduce((acc, strategy) => ({
|
|
538
|
+
...acc,
|
|
539
|
+
[strategy.id]: strategy,
|
|
540
|
+
}), {});
|
|
541
|
+
unmappedFeature.strategies?.forEach((unmappedStrategy) => {
|
|
542
|
+
const mappedStrategySegments = strategies[unmappedStrategy.id]
|
|
543
|
+
.segments;
|
|
544
|
+
const unmappedSegments = unmappedStrategy.segments ?? [];
|
|
545
|
+
// 1. The segments lists have the same length
|
|
546
|
+
// 2. All segment ids listed in the input exist:
|
|
547
|
+
expect([
|
|
548
|
+
...mappedStrategySegments?.map((segment) => segment.id),
|
|
549
|
+
].sort()).toEqual([...unmappedSegments].sort());
|
|
550
|
+
switch (strategies[unmappedStrategy.id].result) {
|
|
551
|
+
case true:
|
|
552
|
+
// If a strategy is considered true, _all_ segments
|
|
553
|
+
// must be true.
|
|
554
|
+
expect(mappedStrategySegments.every((segment) => segment.result === true)).toBeTruthy();
|
|
555
|
+
case false:
|
|
556
|
+
// empty -- all segments can be true and
|
|
557
|
+
// the toggle still not enabled. We
|
|
558
|
+
// can't check for anything here.
|
|
559
|
+
case 'not found':
|
|
560
|
+
// empty -- we can't evaluate this
|
|
561
|
+
}
|
|
562
|
+
});
|
|
563
|
+
});
|
|
564
|
+
})
|
|
565
|
+
.afterEach(cleanup), testParams);
|
|
566
|
+
});
|
|
567
|
+
test("should evaluate a strategy to be unknown if it doesn't recognize the strategy and all constraints pass", async () => {
|
|
568
|
+
await fast_check_1.default.assert(fast_check_1.default
|
|
569
|
+
.asyncProperty((0, arbitraries_test_1.clientFeaturesAndSegments)({ minLength: 1 }).map(({ features, ...rest }) => ({
|
|
570
|
+
...rest,
|
|
571
|
+
features: features.map((feature) => ({
|
|
572
|
+
...feature,
|
|
573
|
+
// remove any constraints and use a name that doesn't exist
|
|
574
|
+
strategies: feature.strategies.map((strategy) => ({
|
|
575
|
+
...strategy,
|
|
576
|
+
name: 'bogus-strategy',
|
|
577
|
+
constraints: [],
|
|
578
|
+
segments: [],
|
|
579
|
+
})),
|
|
580
|
+
})),
|
|
581
|
+
})), (0, sdk_context_schema_test_1.generate)(), fast_check_1.default.context(), async (featsAndSegments, context, ctx) => {
|
|
582
|
+
const serviceFeatures = await insertAndEvaluateFeatures({
|
|
583
|
+
...featsAndSegments,
|
|
584
|
+
context,
|
|
585
|
+
});
|
|
586
|
+
serviceFeatures.forEach((feature) => feature.strategies.data.forEach((strategy) => {
|
|
587
|
+
expect(strategy.result.evaluationStatus).toBe(playground_strategy_schema_1.playgroundStrategyEvaluation.evaluationIncomplete);
|
|
588
|
+
expect(strategy.result.enabled).toBe(playground_strategy_schema_1.playgroundStrategyEvaluation.unknownResult);
|
|
589
|
+
}));
|
|
590
|
+
ctx.log(JSON.stringify(serviceFeatures));
|
|
591
|
+
serviceFeatures.forEach((feature) => {
|
|
592
|
+
// if there are strategies and they're all
|
|
593
|
+
// incomplete and unknown, then the feature can't be
|
|
594
|
+
// evaluated fully
|
|
595
|
+
if (feature.strategies.data.length) {
|
|
596
|
+
expect(feature.isEnabled).toBe(false);
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
})
|
|
600
|
+
.afterEach(cleanup), testParams);
|
|
601
|
+
});
|
|
602
|
+
test("should evaluate a strategy as false if it doesn't recognize the strategy and constraint checks fail", async () => {
|
|
603
|
+
await fast_check_1.default.assert(fast_check_1.default
|
|
604
|
+
.asyncProperty(fast_check_1.default
|
|
605
|
+
.tuple(fast_check_1.default.uuid(), (0, arbitraries_test_1.clientFeaturesAndSegments)({ minLength: 1 }))
|
|
606
|
+
.map(([uuid, { features, ...rest }]) => ({
|
|
607
|
+
...rest,
|
|
608
|
+
features: features.map((feature) => ({
|
|
609
|
+
...feature,
|
|
610
|
+
// use a constraint that will never be true
|
|
611
|
+
strategies: feature.strategies.map((strategy) => ({
|
|
612
|
+
...strategy,
|
|
613
|
+
name: 'bogusStrategy',
|
|
614
|
+
constraints: [
|
|
615
|
+
{
|
|
616
|
+
contextName: 'appName',
|
|
617
|
+
operator: 'IN',
|
|
618
|
+
values: [uuid],
|
|
619
|
+
},
|
|
620
|
+
],
|
|
621
|
+
})),
|
|
622
|
+
})),
|
|
623
|
+
})), (0, sdk_context_schema_test_1.generate)(), fast_check_1.default.context(), async (featsAndSegments, context, ctx) => {
|
|
624
|
+
const serviceFeatures = await insertAndEvaluateFeatures({
|
|
625
|
+
...featsAndSegments,
|
|
626
|
+
context,
|
|
627
|
+
});
|
|
628
|
+
serviceFeatures.forEach((feature) => feature.strategies.data.forEach((strategy) => {
|
|
629
|
+
expect(strategy.result.evaluationStatus).toBe(playground_strategy_schema_1.playgroundStrategyEvaluation.evaluationIncomplete);
|
|
630
|
+
expect(strategy.result.enabled).toBe(false);
|
|
631
|
+
}));
|
|
632
|
+
ctx.log(JSON.stringify(serviceFeatures));
|
|
633
|
+
serviceFeatures.forEach((feature) => {
|
|
634
|
+
if (feature.strategies.data.length) {
|
|
635
|
+
// if there are strategies and they're all
|
|
636
|
+
// incomplete and false, then the feature
|
|
637
|
+
// is also false
|
|
638
|
+
expect(feature.isEnabled).toEqual(false);
|
|
639
|
+
}
|
|
640
|
+
});
|
|
641
|
+
})
|
|
642
|
+
.afterEach(cleanup), testParams);
|
|
643
|
+
});
|
|
644
|
+
test('should evaluate a feature as unknown if there is at least one incomplete strategy among all failed strategies', async () => {
|
|
645
|
+
await fast_check_1.default.assert(fast_check_1.default
|
|
646
|
+
.asyncProperty(fast_check_1.default
|
|
647
|
+
.tuple(fast_check_1.default.uuid(), (0, arbitraries_test_1.clientFeaturesAndSegments)({ minLength: 1 }))
|
|
648
|
+
.map(([uuid, { features, ...rest }]) => ({
|
|
649
|
+
...rest,
|
|
650
|
+
features: features.map((feature) => ({
|
|
651
|
+
...feature,
|
|
652
|
+
// use a constraint that will never be true
|
|
653
|
+
strategies: [
|
|
654
|
+
...feature.strategies.map((strategy) => ({
|
|
655
|
+
...strategy,
|
|
656
|
+
constraints: [
|
|
657
|
+
{
|
|
658
|
+
contextName: 'appName',
|
|
659
|
+
operator: 'IN',
|
|
660
|
+
values: [uuid],
|
|
661
|
+
},
|
|
662
|
+
],
|
|
663
|
+
})),
|
|
664
|
+
{ name: 'my-custom-strategy' },
|
|
665
|
+
],
|
|
666
|
+
})),
|
|
667
|
+
})), (0, sdk_context_schema_test_1.generate)(), async (featsAndSegments, context) => {
|
|
668
|
+
const serviceFeatures = await insertAndEvaluateFeatures({
|
|
669
|
+
...featsAndSegments,
|
|
670
|
+
context,
|
|
671
|
+
});
|
|
672
|
+
serviceFeatures.forEach((feature) => {
|
|
673
|
+
if (feature.strategies.data.length) {
|
|
674
|
+
// if there are strategies and they're
|
|
675
|
+
// all incomplete and unknown, then
|
|
676
|
+
// the feature is also unknown and
|
|
677
|
+
// thus 'false' (from an SDK point of
|
|
678
|
+
// view)
|
|
679
|
+
expect(feature.isEnabled).toEqual(false);
|
|
680
|
+
}
|
|
681
|
+
});
|
|
682
|
+
})
|
|
683
|
+
.afterEach(cleanup), testParams);
|
|
684
|
+
});
|
|
685
|
+
test("features can't be evaluated to true if they're not enabled in the current environment", async () => {
|
|
686
|
+
await fast_check_1.default.assert(fast_check_1.default
|
|
687
|
+
.asyncProperty((0, arbitraries_test_1.clientFeaturesAndSegments)({ minLength: 1 }).map(({ features, ...rest }) => ({
|
|
688
|
+
...rest,
|
|
689
|
+
features: features.map((feature) => ({
|
|
690
|
+
...feature,
|
|
691
|
+
enabled: false,
|
|
692
|
+
// remove any constraints and use a name that doesn't exist
|
|
693
|
+
strategies: [{ name: 'default' }],
|
|
694
|
+
})),
|
|
695
|
+
})), (0, sdk_context_schema_test_1.generate)(), fast_check_1.default.context(), async (featsAndSegments, context, ctx) => {
|
|
696
|
+
const serviceFeatures = await insertAndEvaluateFeatures({
|
|
697
|
+
...featsAndSegments,
|
|
698
|
+
context,
|
|
699
|
+
});
|
|
700
|
+
serviceFeatures.forEach((feature) => feature.strategies.data.forEach((strategy) => {
|
|
701
|
+
expect(strategy.result.evaluationStatus).toBe(playground_strategy_schema_1.playgroundStrategyEvaluation.evaluationComplete);
|
|
702
|
+
expect(strategy.result.enabled).toBe(true);
|
|
703
|
+
}));
|
|
704
|
+
ctx.log(JSON.stringify(serviceFeatures));
|
|
705
|
+
serviceFeatures.forEach((feature) => {
|
|
706
|
+
expect(feature.isEnabled).toBe(false);
|
|
707
|
+
expect(feature.isEnabledInCurrentEnvironment).toBe(false);
|
|
708
|
+
});
|
|
709
|
+
})
|
|
710
|
+
.afterEach(cleanup), testParams);
|
|
91
711
|
});
|
|
92
712
|
test('output toggles should have the same variants as input toggles', async () => {
|
|
93
713
|
await fast_check_1.default.assert(fast_check_1.default
|
|
94
|
-
.asyncProperty((0, arbitraries_test_1.
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
714
|
+
.asyncProperty((0, arbitraries_test_1.clientFeaturesAndSegments)({ minLength: 1 }), (0, sdk_context_schema_test_1.generate)(), async ({ features, segments }, context) => {
|
|
715
|
+
const serviceFeatures = await insertAndEvaluateFeatures({
|
|
716
|
+
features,
|
|
717
|
+
segments,
|
|
718
|
+
context,
|
|
719
|
+
});
|
|
720
|
+
const variantsMap = features.reduce((acc, feature) => ({
|
|
100
721
|
...acc,
|
|
101
722
|
[feature.name]: feature.variants,
|
|
102
723
|
}), {});
|
|
103
|
-
|
|
724
|
+
serviceFeatures.forEach((feature) => {
|
|
104
725
|
if (variantsMap[feature.name]) {
|
|
105
726
|
expect(feature.variants).toEqual(expect.arrayContaining(variantsMap[feature.name]));
|
|
106
727
|
expect(variantsMap[feature.name]).toEqual(expect.arrayContaining(feature.variants));
|
|
@@ -110,9 +731,76 @@ describe('the playground service (e2e)', () => {
|
|
|
110
731
|
}
|
|
111
732
|
});
|
|
112
733
|
})
|
|
113
|
-
.afterEach(
|
|
114
|
-
|
|
115
|
-
|
|
734
|
+
.afterEach(cleanup), testParams);
|
|
735
|
+
});
|
|
736
|
+
test('isEnabled matches strategies.results', async () => {
|
|
737
|
+
await fast_check_1.default.assert(fast_check_1.default
|
|
738
|
+
.asyncProperty((0, arbitraries_test_1.clientFeaturesAndSegments)({ minLength: 1 }), (0, sdk_context_schema_test_1.generate)(), async ({ features, segments }, context) => {
|
|
739
|
+
const serviceFeatures = await insertAndEvaluateFeatures({
|
|
740
|
+
features,
|
|
741
|
+
segments,
|
|
742
|
+
context,
|
|
743
|
+
});
|
|
744
|
+
serviceFeatures.forEach((feature) => {
|
|
745
|
+
if (feature.isEnabled) {
|
|
746
|
+
expect(feature.isEnabledInCurrentEnvironment).toBe(true);
|
|
747
|
+
expect(feature.strategies.result).toBe(true);
|
|
748
|
+
}
|
|
749
|
+
else {
|
|
750
|
+
expect(!feature.isEnabledInCurrentEnvironment ||
|
|
751
|
+
feature.strategies.result !== true).toBe(true);
|
|
752
|
+
}
|
|
753
|
+
});
|
|
754
|
+
})
|
|
755
|
+
.afterEach(cleanup), testParams);
|
|
756
|
+
});
|
|
757
|
+
test('strategies.results matches the individual strategy results', async () => {
|
|
758
|
+
await fast_check_1.default.assert(fast_check_1.default
|
|
759
|
+
.asyncProperty((0, arbitraries_test_1.clientFeaturesAndSegments)({ minLength: 1 }), (0, sdk_context_schema_test_1.generate)(), async ({ features, segments }, context) => {
|
|
760
|
+
const serviceFeatures = await insertAndEvaluateFeatures({
|
|
761
|
+
features,
|
|
762
|
+
segments,
|
|
763
|
+
context,
|
|
764
|
+
});
|
|
765
|
+
serviceFeatures.forEach(({ strategies }) => {
|
|
766
|
+
if (strategies.result === false) {
|
|
767
|
+
expect(strategies.data.every((strategy) => strategy.result.enabled === false)).toBe(true);
|
|
768
|
+
}
|
|
769
|
+
else if (strategies.result ===
|
|
770
|
+
playground_strategy_schema_1.playgroundStrategyEvaluation.unknownResult) {
|
|
771
|
+
expect(strategies.data.some((strategy) => strategy.result.enabled ===
|
|
772
|
+
playground_strategy_schema_1.playgroundStrategyEvaluation.unknownResult)).toBe(true);
|
|
773
|
+
expect(strategies.data.every((strategy) => strategy.result.enabled !== true)).toBe(true);
|
|
774
|
+
}
|
|
775
|
+
else {
|
|
776
|
+
if (strategies.data.length > 0) {
|
|
777
|
+
expect(strategies.data.some((strategy) => strategy.result.enabled ===
|
|
778
|
+
true)).toBe(true);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
});
|
|
782
|
+
})
|
|
783
|
+
.afterEach(cleanup), testParams);
|
|
784
|
+
});
|
|
785
|
+
test('unevaluated features should not have variants', async () => {
|
|
786
|
+
await fast_check_1.default.assert(fast_check_1.default
|
|
787
|
+
.asyncProperty((0, arbitraries_test_1.clientFeaturesAndSegments)({ minLength: 1 }), (0, sdk_context_schema_test_1.generate)(), async ({ features, segments }, context) => {
|
|
788
|
+
const serviceFeatures = await insertAndEvaluateFeatures({
|
|
789
|
+
features,
|
|
790
|
+
segments,
|
|
791
|
+
context,
|
|
792
|
+
});
|
|
793
|
+
serviceFeatures.forEach((feature) => {
|
|
794
|
+
if (feature.strategies.result ===
|
|
795
|
+
playground_strategy_schema_1.playgroundStrategyEvaluation.unknownResult) {
|
|
796
|
+
expect(feature.variant).toEqual({
|
|
797
|
+
name: 'disabled',
|
|
798
|
+
enabled: false,
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
});
|
|
802
|
+
})
|
|
803
|
+
.afterEach(cleanup), testParams);
|
|
116
804
|
});
|
|
117
805
|
});
|
|
118
806
|
//# sourceMappingURL=playground-service.test.js.map
|