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.
Files changed (256) hide show
  1. package/dist/lib/db/event-store.d.ts +2 -3
  2. package/dist/lib/db/event-store.js +26 -36
  3. package/dist/lib/db/event-store.js.map +1 -1
  4. package/dist/lib/db/event-store.test.js +1 -1
  5. package/dist/lib/db/event-store.test.js.map +1 -1
  6. package/dist/lib/db/feature-strategy-store.js +3 -3
  7. package/dist/lib/db/feature-strategy-store.js.map +1 -1
  8. package/dist/lib/db/feature-toggle-client-store.d.ts +1 -1
  9. package/dist/lib/db/feature-toggle-client-store.js +6 -6
  10. package/dist/lib/db/feature-toggle-client-store.js.map +1 -1
  11. package/dist/lib/db/group-store.d.ts +0 -1
  12. package/dist/lib/db/group-store.js +1 -14
  13. package/dist/lib/db/group-store.js.map +1 -1
  14. package/dist/lib/db/user-store.js +8 -1
  15. package/dist/lib/db/user-store.js.map +1 -1
  16. package/dist/lib/openapi/index.d.ts +2388 -952
  17. package/dist/lib/openapi/index.js +8 -4
  18. package/dist/lib/openapi/index.js.map +1 -1
  19. package/dist/lib/openapi/spec/client-feature-schema.d.ts +72 -7
  20. package/dist/lib/openapi/spec/client-feature-schema.js +5 -3
  21. package/dist/lib/openapi/spec/client-feature-schema.js.map +1 -1
  22. package/dist/lib/openapi/spec/client-features-schema.d.ts +138 -16
  23. package/dist/lib/openapi/spec/client-features-schema.js +2 -2
  24. package/dist/lib/openapi/spec/client-features-schema.js.map +1 -1
  25. package/dist/lib/openapi/spec/client-features-schema.test.js +10 -0
  26. package/dist/lib/openapi/spec/client-features-schema.test.js.map +1 -1
  27. package/dist/lib/openapi/spec/constraint-schema.d.ts +51 -2
  28. package/dist/lib/openapi/spec/constraint-schema.js +17 -4
  29. package/dist/lib/openapi/spec/constraint-schema.js.map +1 -1
  30. package/dist/lib/openapi/spec/context-field-schema.test.js +5 -1
  31. package/dist/lib/openapi/spec/context-field-schema.test.js.map +1 -1
  32. package/dist/lib/openapi/spec/create-feature-strategy-schema.d.ts +12 -2
  33. package/dist/lib/openapi/spec/feature-environment-schema.d.ts +12 -2
  34. package/dist/lib/openapi/spec/feature-events-schema.d.ts +1 -1
  35. package/dist/lib/openapi/spec/feature-events-schema.js +1 -1
  36. package/dist/lib/openapi/spec/feature-events-schema.js.map +1 -1
  37. package/dist/lib/openapi/spec/feature-schema.d.ts +25 -5
  38. package/dist/lib/openapi/spec/feature-schema.test.js +13 -1
  39. package/dist/lib/openapi/spec/feature-schema.test.js.map +1 -1
  40. package/dist/lib/openapi/spec/feature-strategy-schema.d.ts +12 -2
  41. package/dist/lib/openapi/spec/feature-variants-schema.d.ts +1 -1
  42. package/dist/lib/openapi/spec/features-schema.d.ts +50 -10
  43. package/dist/lib/openapi/spec/group-schema.d.ts +1 -4
  44. package/dist/lib/openapi/spec/group-user-model-schema.d.ts +1 -4
  45. package/dist/lib/openapi/spec/group-user-model-schema.js +1 -4
  46. package/dist/lib/openapi/spec/group-user-model-schema.js.map +1 -1
  47. package/dist/lib/openapi/spec/groups-schema.d.ts +2 -8
  48. package/dist/lib/openapi/spec/groups-schema.test.js +0 -1
  49. package/dist/lib/openapi/spec/groups-schema.test.js.map +1 -1
  50. package/dist/lib/openapi/spec/health-overview-schema.d.ts +50 -10
  51. package/dist/lib/openapi/spec/health-report-schema.d.ts +50 -10
  52. package/dist/lib/openapi/spec/playground-constraint-schema.d.ts +47 -0
  53. package/dist/lib/openapi/spec/playground-constraint-schema.js +18 -0
  54. package/dist/lib/openapi/spec/playground-constraint-schema.js.map +1 -0
  55. package/dist/lib/openapi/spec/playground-feature-schema.d.ts +388 -2
  56. package/dist/lib/openapi/spec/playground-feature-schema.js +95 -9
  57. package/dist/lib/openapi/spec/playground-feature-schema.js.map +1 -1
  58. package/dist/lib/openapi/spec/playground-feature-schema.test.js +64 -3
  59. package/dist/lib/openapi/spec/playground-feature-schema.test.js.map +1 -1
  60. package/dist/lib/openapi/spec/playground-request-schema.d.ts +2 -0
  61. package/dist/lib/openapi/spec/playground-request-schema.js +6 -1
  62. package/dist/lib/openapi/spec/playground-request-schema.js.map +1 -1
  63. package/dist/lib/openapi/spec/playground-response-schema.d.ts +774 -3
  64. package/dist/lib/openapi/spec/playground-response-schema.js +15 -1
  65. package/dist/lib/openapi/spec/playground-response-schema.js.map +1 -1
  66. package/dist/lib/openapi/spec/playground-segment-schema.d.ts +79 -0
  67. package/dist/lib/openapi/spec/playground-segment-schema.js +36 -0
  68. package/dist/lib/openapi/spec/playground-segment-schema.js.map +1 -0
  69. package/dist/lib/openapi/spec/playground-strategy-schema.d.ts +257 -0
  70. package/dist/lib/openapi/spec/playground-strategy-schema.js +102 -0
  71. package/dist/lib/openapi/spec/playground-strategy-schema.js.map +1 -0
  72. package/dist/lib/openapi/spec/{bootstrap-ui-schema.test.d.ts → project-schema.test.d.ts} +0 -0
  73. package/dist/lib/openapi/spec/project-schema.test.js +17 -0
  74. package/dist/lib/openapi/spec/project-schema.test.js.map +1 -0
  75. package/dist/lib/openapi/spec/search-events-schema.d.ts +37 -0
  76. package/dist/lib/openapi/spec/search-events-schema.js +47 -0
  77. package/dist/lib/openapi/spec/search-events-schema.js.map +1 -0
  78. package/dist/lib/openapi/spec/segment-schema.d.ts +12 -2
  79. package/dist/lib/openapi/spec/state-schema.d.ts +61 -11
  80. package/dist/lib/{routes/admin-api/bootstrap-ui.test.d.ts → openapi/spec/tag-types-schema.test.d.ts} +0 -0
  81. package/dist/lib/openapi/spec/tag-types-schema.test.js +23 -0
  82. package/dist/lib/openapi/spec/tag-types-schema.test.js.map +1 -0
  83. package/dist/lib/openapi/spec/ui-config-schema.d.ts +3 -0
  84. package/dist/lib/openapi/spec/ui-config-schema.js +3 -0
  85. package/dist/lib/openapi/spec/ui-config-schema.js.map +1 -1
  86. package/dist/lib/openapi/spec/update-feature-schema.d.ts +12 -2
  87. package/dist/lib/openapi/spec/update-feature-strategy-schema.d.ts +12 -2
  88. package/dist/{test/e2e/api/admin/bootstrap.test.d.ts → lib/openapi/spec/user-schema.test.d.ts} +0 -0
  89. package/dist/lib/openapi/spec/user-schema.test.js +17 -0
  90. package/dist/lib/openapi/spec/user-schema.test.js.map +1 -0
  91. package/dist/lib/openapi/spec/users-groups-base-schema.d.ts +2 -8
  92. package/dist/lib/openapi/spec/variant-schema.d.ts +1 -1
  93. package/dist/lib/openapi/spec/variant-schema.js +1 -1
  94. package/dist/lib/openapi/spec/variant-schema.js.map +1 -1
  95. package/dist/lib/openapi/spec/variants-schema.d.ts +1 -1
  96. package/dist/lib/openapi/util/standard-responses.d.ts +6 -0
  97. package/dist/lib/openapi/util/standard-responses.js +10 -2
  98. package/dist/lib/openapi/util/standard-responses.js.map +1 -1
  99. package/dist/lib/routes/admin-api/config.d.ts +2 -1
  100. package/dist/lib/routes/admin-api/config.js +3 -1
  101. package/dist/lib/routes/admin-api/config.js.map +1 -1
  102. package/dist/lib/routes/admin-api/event.d.ts +3 -1
  103. package/dist/lib/routes/admin-api/event.js +30 -7
  104. package/dist/lib/routes/admin-api/event.js.map +1 -1
  105. package/dist/lib/routes/admin-api/index.js +0 -2
  106. package/dist/lib/routes/admin-api/index.js.map +1 -1
  107. package/dist/lib/routes/admin-api/playground.js.map +1 -1
  108. package/dist/lib/services/event-service.d.ts +2 -2
  109. package/dist/lib/services/event-service.js +2 -5
  110. package/dist/lib/services/event-service.js.map +1 -1
  111. package/dist/lib/services/feature-toggle-service.d.ts +1 -1
  112. package/dist/lib/services/feature-toggle-service.js +2 -2
  113. package/dist/lib/services/feature-toggle-service.js.map +1 -1
  114. package/dist/lib/services/group-service.d.ts +1 -1
  115. package/dist/lib/services/group-service.js +1 -5
  116. package/dist/lib/services/group-service.js.map +1 -1
  117. package/dist/lib/services/index.js +1 -0
  118. package/dist/lib/services/index.js.map +1 -1
  119. package/dist/lib/services/openapi-service.js +1 -1
  120. package/dist/lib/services/openapi-service.js.map +1 -1
  121. package/dist/lib/services/playground-service.d.ts +2 -1
  122. package/dist/lib/services/playground-service.js +30 -11
  123. package/dist/lib/services/playground-service.js.map +1 -1
  124. package/dist/lib/types/group.d.ts +1 -2
  125. package/dist/lib/types/group.js.map +1 -1
  126. package/dist/lib/types/stores/event-store.d.ts +2 -3
  127. package/dist/lib/types/stores/feature-toggle-client-store.d.ts +1 -1
  128. package/dist/lib/types/stores/group-store.d.ts +0 -1
  129. package/dist/lib/util/feature-evaluator/client.d.ts +25 -0
  130. package/dist/lib/util/feature-evaluator/client.js +135 -0
  131. package/dist/lib/util/feature-evaluator/client.js.map +1 -0
  132. package/dist/lib/util/feature-evaluator/constraint.d.ts +28 -0
  133. package/dist/lib/util/feature-evaluator/constraint.js +129 -0
  134. package/dist/lib/util/feature-evaluator/constraint.js.map +1 -0
  135. package/dist/lib/util/feature-evaluator/context.d.ts +13 -0
  136. package/dist/lib/util/feature-evaluator/context.js +3 -0
  137. package/dist/lib/util/feature-evaluator/context.js.map +1 -0
  138. package/dist/lib/util/feature-evaluator/feature-evaluator.d.ts +36 -0
  139. package/dist/lib/util/feature-evaluator/feature-evaluator.js +55 -0
  140. package/dist/lib/util/feature-evaluator/feature-evaluator.js.map +1 -0
  141. package/dist/lib/util/feature-evaluator/feature.d.ts +19 -0
  142. package/dist/lib/util/feature-evaluator/feature.js +3 -0
  143. package/dist/lib/util/feature-evaluator/feature.js.map +1 -0
  144. package/dist/lib/util/feature-evaluator/helpers.d.ts +6 -0
  145. package/dist/lib/util/feature-evaluator/helpers.js +32 -0
  146. package/dist/lib/util/feature-evaluator/helpers.js.map +1 -0
  147. package/dist/lib/util/feature-evaluator/index.d.ts +8 -0
  148. package/dist/lib/util/feature-evaluator/index.js +14 -0
  149. package/dist/lib/util/feature-evaluator/index.js.map +1 -0
  150. package/dist/lib/util/feature-evaluator/repository/bootstrap-provider.d.ts +16 -0
  151. package/dist/lib/util/feature-evaluator/repository/bootstrap-provider.js +25 -0
  152. package/dist/lib/util/feature-evaluator/repository/bootstrap-provider.js.map +1 -0
  153. package/dist/lib/util/feature-evaluator/repository/index.d.ts +35 -0
  154. package/dist/lib/util/feature-evaluator/repository/index.js +63 -0
  155. package/dist/lib/util/feature-evaluator/repository/index.js.map +1 -0
  156. package/dist/lib/util/feature-evaluator/repository/storage-provider-in-mem.d.ts +6 -0
  157. package/dist/lib/util/feature-evaluator/repository/storage-provider-in-mem.js +16 -0
  158. package/dist/lib/util/feature-evaluator/repository/storage-provider-in-mem.js.map +1 -0
  159. package/dist/lib/util/feature-evaluator/repository/storage-provider.d.ts +14 -0
  160. package/dist/lib/util/feature-evaluator/repository/storage-provider.js +50 -0
  161. package/dist/lib/util/feature-evaluator/repository/storage-provider.js.map +1 -0
  162. package/dist/lib/util/feature-evaluator/strategy/application-hostname-strategy.d.ts +8 -0
  163. package/dist/lib/util/feature-evaluator/strategy/application-hostname-strategy.js +23 -0
  164. package/dist/lib/util/feature-evaluator/strategy/application-hostname-strategy.js.map +1 -0
  165. package/dist/lib/util/feature-evaluator/strategy/default-strategy.d.ts +5 -0
  166. package/dist/lib/util/feature-evaluator/strategy/default-strategy.js +13 -0
  167. package/dist/lib/util/feature-evaluator/strategy/default-strategy.js.map +1 -0
  168. package/dist/lib/util/feature-evaluator/strategy/flexible-rollout-strategy.d.ts +12 -0
  169. package/dist/lib/util/feature-evaluator/strategy/flexible-rollout-strategy.js +49 -0
  170. package/dist/lib/util/feature-evaluator/strategy/flexible-rollout-strategy.js.map +1 -0
  171. package/dist/lib/util/feature-evaluator/strategy/gradual-rollout-random.d.ts +9 -0
  172. package/dist/lib/util/feature-evaluator/strategy/gradual-rollout-random.js +19 -0
  173. package/dist/lib/util/feature-evaluator/strategy/gradual-rollout-random.js.map +1 -0
  174. package/dist/lib/util/feature-evaluator/strategy/gradual-rollout-session-id.d.ts +9 -0
  175. package/dist/lib/util/feature-evaluator/strategy/gradual-rollout-session-id.js +24 -0
  176. package/dist/lib/util/feature-evaluator/strategy/gradual-rollout-session-id.js.map +1 -0
  177. package/dist/lib/util/feature-evaluator/strategy/gradual-rollout-user-id.d.ts +9 -0
  178. package/dist/lib/util/feature-evaluator/strategy/gradual-rollout-user-id.js +24 -0
  179. package/dist/lib/util/feature-evaluator/strategy/gradual-rollout-user-id.js.map +1 -0
  180. package/dist/lib/util/feature-evaluator/strategy/index.d.ts +4 -0
  181. package/dist/lib/util/feature-evaluator/strategy/index.js +29 -0
  182. package/dist/lib/util/feature-evaluator/strategy/index.js.map +1 -0
  183. package/dist/lib/util/feature-evaluator/strategy/remote-address-strategy.d.ts +8 -0
  184. package/dist/lib/util/feature-evaluator/strategy/remote-address-strategy.js +35 -0
  185. package/dist/lib/util/feature-evaluator/strategy/remote-address-strategy.js.map +1 -0
  186. package/dist/lib/util/feature-evaluator/strategy/strategy.d.ts +41 -0
  187. package/dist/lib/util/feature-evaluator/strategy/strategy.js +75 -0
  188. package/dist/lib/util/feature-evaluator/strategy/strategy.js.map +1 -0
  189. package/dist/lib/util/feature-evaluator/strategy/unknown-strategy.d.ts +9 -0
  190. package/dist/lib/util/feature-evaluator/strategy/unknown-strategy.js +29 -0
  191. package/dist/lib/util/feature-evaluator/strategy/unknown-strategy.js.map +1 -0
  192. package/dist/lib/util/feature-evaluator/strategy/user-with-id-strategy.d.ts +8 -0
  193. package/dist/lib/util/feature-evaluator/strategy/user-with-id-strategy.js +16 -0
  194. package/dist/lib/util/feature-evaluator/strategy/user-with-id-strategy.js.map +1 -0
  195. package/dist/lib/util/feature-evaluator/strategy/util.d.ts +1 -0
  196. package/dist/lib/util/feature-evaluator/strategy/util.js +31 -0
  197. package/dist/lib/util/feature-evaluator/strategy/util.js.map +1 -0
  198. package/dist/lib/util/feature-evaluator/variant.d.ts +28 -0
  199. package/dist/lib/util/feature-evaluator/variant.js +73 -0
  200. package/dist/lib/util/feature-evaluator/variant.js.map +1 -0
  201. package/dist/lib/util/offline-unleash-client.d.ts +13 -2
  202. package/dist/lib/util/offline-unleash-client.js +20 -20
  203. package/dist/lib/util/offline-unleash-client.js.map +1 -1
  204. package/dist/lib/util/offline-unleash-client.test.d.ts +3 -1
  205. package/dist/lib/util/offline-unleash-client.test.js +349 -139
  206. package/dist/lib/util/offline-unleash-client.test.js.map +1 -1
  207. package/dist/migrations/20220808084524-add-group-permissions.d.ts +2 -0
  208. package/dist/migrations/20220808084524-add-group-permissions.js +12 -0
  209. package/dist/migrations/20220808084524-add-group-permissions.js.map +1 -0
  210. package/dist/migrations/20220808110415-add-projects-foreign-key.d.ts +2 -0
  211. package/dist/migrations/20220808110415-add-projects-foreign-key.js +14 -0
  212. package/dist/migrations/20220808110415-add-projects-foreign-key.js.map +1 -0
  213. package/dist/test/arbitraries.test.d.ts +12 -2
  214. package/dist/test/arbitraries.test.js +67 -9
  215. package/dist/test/arbitraries.test.js.map +1 -1
  216. package/dist/test/e2e/api/admin/config.e2e.test.js +2 -1
  217. package/dist/test/e2e/api/admin/config.e2e.test.js.map +1 -1
  218. package/dist/test/e2e/api/admin/event.e2e.test.js +57 -0
  219. package/dist/test/e2e/api/admin/event.e2e.test.js.map +1 -1
  220. package/dist/test/e2e/api/admin/feature.e2e.test.js +8 -7
  221. package/dist/test/e2e/api/admin/feature.e2e.test.js.map +1 -1
  222. package/dist/test/e2e/api/admin/playground.e2e.test.js +22 -0
  223. package/dist/test/e2e/api/admin/playground.e2e.test.js.map +1 -1
  224. package/dist/test/e2e/services/access-service.e2e.test.js +38 -20
  225. package/dist/test/e2e/services/access-service.e2e.test.js.map +1 -1
  226. package/dist/test/e2e/services/playground-service.test.d.ts +5 -1
  227. package/dist/test/e2e/services/playground-service.test.js +725 -37
  228. package/dist/test/e2e/services/playground-service.test.js.map +1 -1
  229. package/dist/test/e2e/services/project-service.e2e.test.js +12 -4
  230. package/dist/test/e2e/services/project-service.e2e.test.js.map +1 -1
  231. package/dist/test/e2e/services/setting-service.test.js +9 -3
  232. package/dist/test/e2e/services/setting-service.test.js.map +1 -1
  233. package/dist/test/e2e/stores/event-store.e2e.test.js +6 -2
  234. package/dist/test/e2e/stores/event-store.e2e.test.js.map +1 -1
  235. package/dist/test/fixtures/fake-event-store.d.ts +1 -3
  236. package/dist/test/fixtures/fake-event-store.js +2 -8
  237. package/dist/test/fixtures/fake-event-store.js.map +1 -1
  238. package/dist/test/fixtures/fake-group-store.d.ts +0 -1
  239. package/dist/test/fixtures/fake-group-store.js +0 -3
  240. package/dist/test/fixtures/fake-group-store.js.map +1 -1
  241. package/package.json +15 -13
  242. package/dist/lib/openapi/spec/bootstrap-ui-schema.d.ts +0 -491
  243. package/dist/lib/openapi/spec/bootstrap-ui-schema.js +0 -94
  244. package/dist/lib/openapi/spec/bootstrap-ui-schema.js.map +0 -1
  245. package/dist/lib/openapi/spec/bootstrap-ui-schema.test.js +0 -145
  246. package/dist/lib/openapi/spec/bootstrap-ui-schema.test.js.map +0 -1
  247. package/dist/lib/openapi/spec/client-variant-schema.d.ts +0 -35
  248. package/dist/lib/openapi/spec/client-variant-schema.js +0 -37
  249. package/dist/lib/openapi/spec/client-variant-schema.js.map +0 -1
  250. package/dist/lib/routes/admin-api/bootstrap-ui.d.ts +0 -25
  251. package/dist/lib/routes/admin-api/bootstrap-ui.js +0 -82
  252. package/dist/lib/routes/admin-api/bootstrap-ui.js.map +0 -1
  253. package/dist/lib/routes/admin-api/bootstrap-ui.test.js +0 -60
  254. package/dist/lib/routes/admin-api/bootstrap-ui.test.js.map +0 -1
  255. package/dist/test/e2e/api/admin/bootstrap.test.js +0 -43
  256. 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 offline_unleash_client_1 = require("../../../lib/util/offline-unleash-client");
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
- featureToggleService = new feature_toggle_service_1.default(stores, config, new segment_service_1.SegmentService(stores, config));
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 toFeatureToggleDTO = (feature) => ({
38
- ...feature,
39
- // the arbitrary generator takes care of this
40
- variants: feature.variants,
41
- createdAt: undefined,
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.clientFeatures)({ minLength: 1 }), (0, sdk_context_schema_test_1.generate)(), async (toggles, context) => {
46
- await Promise.all(toggles.map((feature) => stores.featureToggleStore.create(feature.project, toFeatureToggleDTO(feature))));
47
- const projects = '*';
48
- const env = 'default';
49
- const serviceToggles = await service.evaluateQuery(projects, env, context);
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, offline_unleash_client_1.offlineUnleashClient)([head, ...rest], context, console.log);
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
- const enabledStateMatches = feature.isEnabled ===
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
- // if x.isEnabled then variant should === variant.name. Otherwise it should be null
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.isEnabled &&
76
- isDisabledVariant(feature.variant)) {
173
+ if (isDisabledVariant(feature.variant)) {
77
174
  return (enabledStateMatches &&
78
175
  isDisabledVariant(clientVariant));
79
176
  }
80
- return (enabledStateMatches &&
81
- clientVariant.name === feature.variant.name &&
82
- clientVariant.enabled ===
83
- feature.variant.enabled &&
84
- clientVariant.payload ===
85
- feature.variant.payload);
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(async () => {
89
- await stores.featureToggleStore.deleteAll();
90
- }), testParams);
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.clientFeatures)({ minLength: 1 }), (0, sdk_context_schema_test_1.generate)(), async (toggles, context) => {
95
- await Promise.all(toggles.map((feature) => stores.featureToggleStore.create(feature.project, toFeatureToggleDTO(feature))));
96
- const projects = '*';
97
- const env = 'default';
98
- const serviceToggles = await service.evaluateQuery(projects, env, context);
99
- const variantsMap = toggles.reduce((acc, feature) => ({
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
- serviceToggles.forEach((feature) => {
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(async () => {
114
- await stores.featureToggleStore.deleteAll();
115
- }), testParams);
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