@replanejs/test-suite 1.0.2 → 1.0.4
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/index.cjs +368 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +368 -2
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -203,6 +203,58 @@ function literal(value) {
|
|
|
203
203
|
value
|
|
204
204
|
};
|
|
205
205
|
}
|
|
206
|
+
/**
|
|
207
|
+
* Parse a JSON path string into a path array.
|
|
208
|
+
* Examples:
|
|
209
|
+
* - "" -> []
|
|
210
|
+
* - "foo.bar" -> ["foo", "bar"]
|
|
211
|
+
* - "foo[0]" -> ["foo", 0]
|
|
212
|
+
* - "foo.bar[1].baz" -> ["foo", "bar", 1, "baz"]
|
|
213
|
+
*/
|
|
214
|
+
function parseJsonPath(pathString) {
|
|
215
|
+
if (!pathString) return [];
|
|
216
|
+
const parts = [];
|
|
217
|
+
let current = "";
|
|
218
|
+
let inBracket = false;
|
|
219
|
+
for (let i = 0; i < pathString.length; i++) {
|
|
220
|
+
const char = pathString[i];
|
|
221
|
+
if (char === "[") {
|
|
222
|
+
if (current) {
|
|
223
|
+
parts.push(current);
|
|
224
|
+
current = "";
|
|
225
|
+
}
|
|
226
|
+
inBracket = true;
|
|
227
|
+
} else if (char === "]") {
|
|
228
|
+
if (inBracket && current) {
|
|
229
|
+
const num = parseInt(current, 10);
|
|
230
|
+
parts.push(isNaN(num) ? current : num);
|
|
231
|
+
current = "";
|
|
232
|
+
}
|
|
233
|
+
inBracket = false;
|
|
234
|
+
} else if (char === "." && !inBracket) {
|
|
235
|
+
if (current) {
|
|
236
|
+
parts.push(current);
|
|
237
|
+
current = "";
|
|
238
|
+
}
|
|
239
|
+
} else current += char;
|
|
240
|
+
}
|
|
241
|
+
if (current) parts.push(current);
|
|
242
|
+
return parts;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Helper to create a reference value for conditions.
|
|
246
|
+
* @param projectId - The project ID where the referenced config lives
|
|
247
|
+
* @param configName - The name of the config to reference
|
|
248
|
+
* @param path - JSON path string to extract from the config value (empty string for entire value)
|
|
249
|
+
*/
|
|
250
|
+
function reference(projectId, configName, path = "") {
|
|
251
|
+
return {
|
|
252
|
+
type: "reference",
|
|
253
|
+
projectId,
|
|
254
|
+
configName,
|
|
255
|
+
path: parseJsonPath(path)
|
|
256
|
+
};
|
|
257
|
+
}
|
|
206
258
|
const silentLogger = {
|
|
207
259
|
debug: () => {},
|
|
208
260
|
info: () => {},
|
|
@@ -300,7 +352,7 @@ function createTestContext(admin, workspaceId, projectId, environmentId, sdkKey,
|
|
|
300
352
|
function testSuite(options) {
|
|
301
353
|
const { superadminKey, adminApiBaseUrl, edgeApiBaseUrl } = options;
|
|
302
354
|
const defaultTimeout = options.defaultTimeout ?? 1e4;
|
|
303
|
-
(0, vitest.describe)("Replane E2E Test Suite", () => {
|
|
355
|
+
(0, vitest.describe)("Replane E2E Test Suite", { repeats: 8 }, () => {
|
|
304
356
|
let admin;
|
|
305
357
|
let workspaceId;
|
|
306
358
|
let projectId;
|
|
@@ -722,6 +774,320 @@ function testSuite(options) {
|
|
|
722
774
|
client.disconnect();
|
|
723
775
|
});
|
|
724
776
|
});
|
|
777
|
+
(0, vitest.describe)("Reference Evaluation", () => {
|
|
778
|
+
(0, vitest.it)("should evaluate reference with empty path (entire config value)", async () => {
|
|
779
|
+
await ctx.createConfig("ref-source-simple", "allowed-region");
|
|
780
|
+
await ctx.createConfig("ref-target-simple", "default", { overrides: [{
|
|
781
|
+
name: "ref-override",
|
|
782
|
+
conditions: [{
|
|
783
|
+
operator: "equals",
|
|
784
|
+
property: "region",
|
|
785
|
+
value: reference(projectId, "ref-source-simple", "")
|
|
786
|
+
}],
|
|
787
|
+
value: "matched-by-reference"
|
|
788
|
+
}] });
|
|
789
|
+
const client1 = trackClient(await ctx.createClient({ context: { region: "allowed-region" } }));
|
|
790
|
+
(0, vitest.expect)(client1.get("ref-target-simple")).toBe("matched-by-reference");
|
|
791
|
+
client1.disconnect();
|
|
792
|
+
const client2 = trackClient(await ctx.createClient({ context: { region: "other-region" } }));
|
|
793
|
+
(0, vitest.expect)(client2.get("ref-target-simple")).toBe("default");
|
|
794
|
+
client2.disconnect();
|
|
795
|
+
});
|
|
796
|
+
(0, vitest.it)("should evaluate reference with JSON path to nested object property", async () => {
|
|
797
|
+
await ctx.createConfig("ref-source-object", { settings: {
|
|
798
|
+
threshold: 100,
|
|
799
|
+
enabled: true
|
|
800
|
+
} });
|
|
801
|
+
await ctx.createConfig("ref-target-object", "below-threshold", { overrides: [{
|
|
802
|
+
name: "threshold-override",
|
|
803
|
+
conditions: [{
|
|
804
|
+
operator: "greater_than_or_equal",
|
|
805
|
+
property: "score",
|
|
806
|
+
value: reference(projectId, "ref-source-object", "settings.threshold")
|
|
807
|
+
}],
|
|
808
|
+
value: "above-threshold"
|
|
809
|
+
}] });
|
|
810
|
+
const client1 = trackClient(await ctx.createClient({ context: { score: 150 } }));
|
|
811
|
+
(0, vitest.expect)(client1.get("ref-target-object")).toBe("above-threshold");
|
|
812
|
+
client1.disconnect();
|
|
813
|
+
const client2 = trackClient(await ctx.createClient({ context: { score: 100 } }));
|
|
814
|
+
(0, vitest.expect)(client2.get("ref-target-object")).toBe("above-threshold");
|
|
815
|
+
client2.disconnect();
|
|
816
|
+
const client3 = trackClient(await ctx.createClient({ context: { score: 50 } }));
|
|
817
|
+
(0, vitest.expect)(client3.get("ref-target-object")).toBe("below-threshold");
|
|
818
|
+
client3.disconnect();
|
|
819
|
+
});
|
|
820
|
+
(0, vitest.it)("should evaluate reference with JSON path to array element", async () => {
|
|
821
|
+
await ctx.createConfig("ref-source-array", { tiers: [
|
|
822
|
+
"free",
|
|
823
|
+
"basic",
|
|
824
|
+
"premium",
|
|
825
|
+
"enterprise"
|
|
826
|
+
] });
|
|
827
|
+
await ctx.createConfig("ref-target-array", "no-match", { overrides: [{
|
|
828
|
+
name: "premium-override",
|
|
829
|
+
conditions: [{
|
|
830
|
+
operator: "equals",
|
|
831
|
+
property: "tier",
|
|
832
|
+
value: reference(projectId, "ref-source-array", "tiers[2]")
|
|
833
|
+
}],
|
|
834
|
+
value: "premium-match"
|
|
835
|
+
}] });
|
|
836
|
+
const client1 = trackClient(await ctx.createClient({ context: { tier: "premium" } }));
|
|
837
|
+
(0, vitest.expect)(client1.get("ref-target-array")).toBe("premium-match");
|
|
838
|
+
client1.disconnect();
|
|
839
|
+
const client2 = trackClient(await ctx.createClient({ context: { tier: "basic" } }));
|
|
840
|
+
(0, vitest.expect)(client2.get("ref-target-array")).toBe("no-match");
|
|
841
|
+
client2.disconnect();
|
|
842
|
+
});
|
|
843
|
+
(0, vitest.it)("should evaluate reference with JSON path to entire array for 'in' condition", async () => {
|
|
844
|
+
await ctx.createConfig("ref-source-regions", { allowedRegions: [
|
|
845
|
+
"us-east",
|
|
846
|
+
"us-west",
|
|
847
|
+
"eu-west"
|
|
848
|
+
] });
|
|
849
|
+
await ctx.createConfig("ref-target-regions", "blocked", { overrides: [{
|
|
850
|
+
name: "allowed-regions-override",
|
|
851
|
+
conditions: [{
|
|
852
|
+
operator: "in",
|
|
853
|
+
property: "region",
|
|
854
|
+
value: reference(projectId, "ref-source-regions", "allowedRegions")
|
|
855
|
+
}],
|
|
856
|
+
value: "allowed"
|
|
857
|
+
}] });
|
|
858
|
+
const client1 = trackClient(await ctx.createClient({ context: { region: "us-east" } }));
|
|
859
|
+
(0, vitest.expect)(client1.get("ref-target-regions")).toBe("allowed");
|
|
860
|
+
client1.disconnect();
|
|
861
|
+
const client2 = trackClient(await ctx.createClient({ context: { region: "eu-west" } }));
|
|
862
|
+
(0, vitest.expect)(client2.get("ref-target-regions")).toBe("allowed");
|
|
863
|
+
client2.disconnect();
|
|
864
|
+
const client3 = trackClient(await ctx.createClient({ context: { region: "ap-south" } }));
|
|
865
|
+
(0, vitest.expect)(client3.get("ref-target-regions")).toBe("blocked");
|
|
866
|
+
client3.disconnect();
|
|
867
|
+
});
|
|
868
|
+
(0, vitest.it)("should handle reference to non-existent config (condition fails)", async () => {
|
|
869
|
+
await ctx.createConfig("ref-target-nonexistent", "default-fallback", { overrides: [{
|
|
870
|
+
name: "nonexistent-ref-override",
|
|
871
|
+
conditions: [{
|
|
872
|
+
operator: "equals",
|
|
873
|
+
property: "value",
|
|
874
|
+
value: reference(projectId, "nonexistent-config-xyz", "")
|
|
875
|
+
}],
|
|
876
|
+
value: "should-not-match"
|
|
877
|
+
}] });
|
|
878
|
+
const client = trackClient(await ctx.createClient({ context: { value: "anything" } }));
|
|
879
|
+
(0, vitest.expect)(client.get("ref-target-nonexistent")).toBe("default-fallback");
|
|
880
|
+
client.disconnect();
|
|
881
|
+
});
|
|
882
|
+
(0, vitest.it)("should handle reference with invalid JSON path (condition fails)", async () => {
|
|
883
|
+
await ctx.createConfig("ref-source-invalid-path", { data: { value: 42 } });
|
|
884
|
+
await ctx.createConfig("ref-target-invalid-path", "default-value", { overrides: [{
|
|
885
|
+
name: "invalid-path-override",
|
|
886
|
+
conditions: [{
|
|
887
|
+
operator: "equals",
|
|
888
|
+
property: "test",
|
|
889
|
+
value: reference(projectId, "ref-source-invalid-path", "data.nonexistent.deep")
|
|
890
|
+
}],
|
|
891
|
+
value: "should-not-match"
|
|
892
|
+
}] });
|
|
893
|
+
const client = trackClient(await ctx.createClient({ context: { test: "anything" } }));
|
|
894
|
+
(0, vitest.expect)(client.get("ref-target-invalid-path")).toBe("default-value");
|
|
895
|
+
client.disconnect();
|
|
896
|
+
});
|
|
897
|
+
(0, vitest.it)("should handle reference with out-of-bounds array index", async () => {
|
|
898
|
+
await ctx.createConfig("ref-source-oob", { items: [
|
|
899
|
+
"a",
|
|
900
|
+
"b",
|
|
901
|
+
"c"
|
|
902
|
+
] });
|
|
903
|
+
await ctx.createConfig("ref-target-oob", "default-value", { overrides: [{
|
|
904
|
+
name: "oob-override",
|
|
905
|
+
conditions: [{
|
|
906
|
+
operator: "equals",
|
|
907
|
+
property: "item",
|
|
908
|
+
value: reference(projectId, "ref-source-oob", "items[99]")
|
|
909
|
+
}],
|
|
910
|
+
value: "matched"
|
|
911
|
+
}] });
|
|
912
|
+
const client = trackClient(await ctx.createClient({ context: { item: "anything" } }));
|
|
913
|
+
(0, vitest.expect)(client.get("ref-target-oob")).toBe("default-value");
|
|
914
|
+
client.disconnect();
|
|
915
|
+
});
|
|
916
|
+
(0, vitest.it)("should evaluate reference with deeply nested path", async () => {
|
|
917
|
+
await ctx.createConfig("ref-source-deep", { level1: { level2: { level3: { targetValue: "deep-secret" } } } });
|
|
918
|
+
await ctx.createConfig("ref-target-deep", "no-match", { overrides: [{
|
|
919
|
+
name: "deep-override",
|
|
920
|
+
conditions: [{
|
|
921
|
+
operator: "equals",
|
|
922
|
+
property: "secret",
|
|
923
|
+
value: reference(projectId, "ref-source-deep", "level1.level2.level3.targetValue")
|
|
924
|
+
}],
|
|
925
|
+
value: "deep-match"
|
|
926
|
+
}] });
|
|
927
|
+
const client1 = trackClient(await ctx.createClient({ context: { secret: "deep-secret" } }));
|
|
928
|
+
(0, vitest.expect)(client1.get("ref-target-deep")).toBe("deep-match");
|
|
929
|
+
client1.disconnect();
|
|
930
|
+
const client2 = trackClient(await ctx.createClient({ context: { secret: "wrong" } }));
|
|
931
|
+
(0, vitest.expect)(client2.get("ref-target-deep")).toBe("no-match");
|
|
932
|
+
client2.disconnect();
|
|
933
|
+
});
|
|
934
|
+
(0, vitest.it)("should evaluate reference with array of objects and path", async () => {
|
|
935
|
+
await ctx.createConfig("ref-source-array-obj", { users: [
|
|
936
|
+
{
|
|
937
|
+
id: 1,
|
|
938
|
+
name: "Alice"
|
|
939
|
+
},
|
|
940
|
+
{
|
|
941
|
+
id: 2,
|
|
942
|
+
name: "Bob"
|
|
943
|
+
},
|
|
944
|
+
{
|
|
945
|
+
id: 3,
|
|
946
|
+
name: "Charlie"
|
|
947
|
+
}
|
|
948
|
+
] });
|
|
949
|
+
await ctx.createConfig("ref-target-array-obj", "unknown-user", { overrides: [{
|
|
950
|
+
name: "bob-override",
|
|
951
|
+
conditions: [{
|
|
952
|
+
operator: "equals",
|
|
953
|
+
property: "username",
|
|
954
|
+
value: reference(projectId, "ref-source-array-obj", "users[1].name")
|
|
955
|
+
}],
|
|
956
|
+
value: "its-bob"
|
|
957
|
+
}] });
|
|
958
|
+
const client1 = trackClient(await ctx.createClient({ context: { username: "Bob" } }));
|
|
959
|
+
(0, vitest.expect)(client1.get("ref-target-array-obj")).toBe("its-bob");
|
|
960
|
+
client1.disconnect();
|
|
961
|
+
const client2 = trackClient(await ctx.createClient({ context: { username: "Alice" } }));
|
|
962
|
+
(0, vitest.expect)(client2.get("ref-target-array-obj")).toBe("unknown-user");
|
|
963
|
+
client2.disconnect();
|
|
964
|
+
});
|
|
965
|
+
(0, vitest.it)("should evaluate reference to boolean value", async () => {
|
|
966
|
+
await ctx.createConfig("ref-source-bool", { featureEnabled: true });
|
|
967
|
+
await ctx.createConfig("ref-target-bool", "feature-off", { overrides: [{
|
|
968
|
+
name: "feature-override",
|
|
969
|
+
conditions: [{
|
|
970
|
+
operator: "equals",
|
|
971
|
+
property: "enabled",
|
|
972
|
+
value: reference(projectId, "ref-source-bool", "featureEnabled")
|
|
973
|
+
}],
|
|
974
|
+
value: "feature-on"
|
|
975
|
+
}] });
|
|
976
|
+
const client1 = trackClient(await ctx.createClient({ context: { enabled: true } }));
|
|
977
|
+
(0, vitest.expect)(client1.get("ref-target-bool")).toBe("feature-on");
|
|
978
|
+
client1.disconnect();
|
|
979
|
+
const client2 = trackClient(await ctx.createClient({ context: { enabled: false } }));
|
|
980
|
+
(0, vitest.expect)(client2.get("ref-target-bool")).toBe("feature-off");
|
|
981
|
+
client2.disconnect();
|
|
982
|
+
});
|
|
983
|
+
(0, vitest.it)("should handle multiple overrides with different references", async () => {
|
|
984
|
+
await ctx.createConfig("ref-source-premium", { tier: "premium" });
|
|
985
|
+
await ctx.createConfig("ref-source-enterprise", { tier: "enterprise" });
|
|
986
|
+
await ctx.createConfig("ref-target-multi", "free-tier", { overrides: [{
|
|
987
|
+
name: "enterprise-override",
|
|
988
|
+
conditions: [{
|
|
989
|
+
operator: "equals",
|
|
990
|
+
property: "plan",
|
|
991
|
+
value: reference(projectId, "ref-source-enterprise", "tier")
|
|
992
|
+
}],
|
|
993
|
+
value: "enterprise-tier"
|
|
994
|
+
}, {
|
|
995
|
+
name: "premium-override",
|
|
996
|
+
conditions: [{
|
|
997
|
+
operator: "equals",
|
|
998
|
+
property: "plan",
|
|
999
|
+
value: reference(projectId, "ref-source-premium", "tier")
|
|
1000
|
+
}],
|
|
1001
|
+
value: "premium-tier"
|
|
1002
|
+
}] });
|
|
1003
|
+
const client1 = trackClient(await ctx.createClient({ context: { plan: "enterprise" } }));
|
|
1004
|
+
(0, vitest.expect)(client1.get("ref-target-multi")).toBe("enterprise-tier");
|
|
1005
|
+
client1.disconnect();
|
|
1006
|
+
const client2 = trackClient(await ctx.createClient({ context: { plan: "premium" } }));
|
|
1007
|
+
(0, vitest.expect)(client2.get("ref-target-multi")).toBe("premium-tier");
|
|
1008
|
+
client2.disconnect();
|
|
1009
|
+
const client3 = trackClient(await ctx.createClient({ context: { plan: "basic" } }));
|
|
1010
|
+
(0, vitest.expect)(client3.get("ref-target-multi")).toBe("free-tier");
|
|
1011
|
+
client3.disconnect();
|
|
1012
|
+
});
|
|
1013
|
+
(0, vitest.it)("should evaluate reference combined with literal conditions using AND", async () => {
|
|
1014
|
+
await ctx.createConfig("ref-source-combined", { requiredLevel: 10 });
|
|
1015
|
+
await ctx.createConfig("ref-target-combined", "access-denied", { overrides: [{
|
|
1016
|
+
name: "combined-override",
|
|
1017
|
+
conditions: [{
|
|
1018
|
+
operator: "and",
|
|
1019
|
+
conditions: [{
|
|
1020
|
+
operator: "equals",
|
|
1021
|
+
property: "role",
|
|
1022
|
+
value: literal("admin")
|
|
1023
|
+
}, {
|
|
1024
|
+
operator: "greater_than_or_equal",
|
|
1025
|
+
property: "level",
|
|
1026
|
+
value: reference(projectId, "ref-source-combined", "requiredLevel")
|
|
1027
|
+
}]
|
|
1028
|
+
}],
|
|
1029
|
+
value: "access-granted"
|
|
1030
|
+
}] });
|
|
1031
|
+
const client1 = trackClient(await ctx.createClient({ context: {
|
|
1032
|
+
role: "admin",
|
|
1033
|
+
level: 15
|
|
1034
|
+
} }));
|
|
1035
|
+
(0, vitest.expect)(client1.get("ref-target-combined")).toBe("access-granted");
|
|
1036
|
+
client1.disconnect();
|
|
1037
|
+
const client2 = trackClient(await ctx.createClient({ context: {
|
|
1038
|
+
role: "admin",
|
|
1039
|
+
level: 5
|
|
1040
|
+
} }));
|
|
1041
|
+
(0, vitest.expect)(client2.get("ref-target-combined")).toBe("access-denied");
|
|
1042
|
+
client2.disconnect();
|
|
1043
|
+
const client3 = trackClient(await ctx.createClient({ context: {
|
|
1044
|
+
role: "user",
|
|
1045
|
+
level: 15
|
|
1046
|
+
} }));
|
|
1047
|
+
(0, vitest.expect)(client3.get("ref-target-combined")).toBe("access-denied");
|
|
1048
|
+
client3.disconnect();
|
|
1049
|
+
});
|
|
1050
|
+
(0, vitest.it)("should handle reference to null value", async () => {
|
|
1051
|
+
await ctx.createConfig("ref-source-null", null);
|
|
1052
|
+
await ctx.createConfig("ref-target-null", "has-value", { overrides: [{
|
|
1053
|
+
name: "null-override",
|
|
1054
|
+
conditions: [{
|
|
1055
|
+
operator: "equals",
|
|
1056
|
+
property: "data",
|
|
1057
|
+
value: reference(projectId, "ref-source-null", "")
|
|
1058
|
+
}],
|
|
1059
|
+
value: "is-null"
|
|
1060
|
+
}] });
|
|
1061
|
+
const client1 = trackClient(await ctx.createClient({ context: { data: null } }));
|
|
1062
|
+
(0, vitest.expect)(client1.get("ref-target-null")).toBe("is-null");
|
|
1063
|
+
client1.disconnect();
|
|
1064
|
+
const client2 = trackClient(await ctx.createClient({ context: { data: "something" } }));
|
|
1065
|
+
(0, vitest.expect)(client2.get("ref-target-null")).toBe("has-value");
|
|
1066
|
+
client2.disconnect();
|
|
1067
|
+
});
|
|
1068
|
+
(0, vitest.it)("should evaluate reference with not_in condition", async () => {
|
|
1069
|
+
await ctx.createConfig("ref-source-blocked", { blockedCountries: [
|
|
1070
|
+
"XX",
|
|
1071
|
+
"YY",
|
|
1072
|
+
"ZZ"
|
|
1073
|
+
] });
|
|
1074
|
+
await ctx.createConfig("ref-target-blocked", "blocked", { overrides: [{
|
|
1075
|
+
name: "allowed-override",
|
|
1076
|
+
conditions: [{
|
|
1077
|
+
operator: "not_in",
|
|
1078
|
+
property: "country",
|
|
1079
|
+
value: reference(projectId, "ref-source-blocked", "blockedCountries")
|
|
1080
|
+
}],
|
|
1081
|
+
value: "allowed"
|
|
1082
|
+
}] });
|
|
1083
|
+
const client1 = trackClient(await ctx.createClient({ context: { country: "US" } }));
|
|
1084
|
+
(0, vitest.expect)(client1.get("ref-target-blocked")).toBe("allowed");
|
|
1085
|
+
client1.disconnect();
|
|
1086
|
+
const client2 = trackClient(await ctx.createClient({ context: { country: "XX" } }));
|
|
1087
|
+
(0, vitest.expect)(client2.get("ref-target-blocked")).toBe("blocked");
|
|
1088
|
+
client2.disconnect();
|
|
1089
|
+
});
|
|
1090
|
+
});
|
|
725
1091
|
(0, vitest.describe)("Snapshot", () => {
|
|
726
1092
|
(0, vitest.it)("should create snapshot with current configs", async () => {
|
|
727
1093
|
await ctx.createConfig("snap-config-1", "value-1");
|
|
@@ -835,7 +1201,7 @@ function testSuite(options) {
|
|
|
835
1201
|
client2.disconnect();
|
|
836
1202
|
});
|
|
837
1203
|
});
|
|
838
|
-
}
|
|
1204
|
+
});
|
|
839
1205
|
}
|
|
840
1206
|
|
|
841
1207
|
//#endregion
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/test-suite.ts","../src/utils.ts"],"sourcesContent":[],"mappings":";;;;;;;AAMA;AAgBiB,UAhBA,gBAAA,CAgBW;EAAA;EAAA,aAEnB,EAAA,MAAA;EAAY;EAmBJ,eAKiB,EAAA,MAAA;EAAM;EACpB,cACG,EAAA,MAAA;EAAC;EAAF,cACA,CAAA,EAAA,MAAA;EAAC;EAAF,KAAf,CAAA,EAAA,OAAA;;;;;AAsBY,UAnDD,WAAA,CAmDC;EAAQ;EAEd,KAKkB,EAxDrB,YAwDqB;EAAO;;;;
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/test-suite.ts","../src/utils.ts"],"sourcesContent":[],"mappings":"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"}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/test-suite.ts","../src/utils.ts"],"sourcesContent":[],"mappings":";;;;;;;AAMA;AAgBiB,UAhBA,gBAAA,CAgBW;EAAA;EAAA,aAEnB,EAAA,MAAA;EAAY;EAmBJ,eAKiB,EAAA,MAAA;EAAM;EACpB,cACG,EAAA,MAAA;EAAC;EAAF,cACA,CAAA,EAAA,MAAA;EAAC;EAAF,KAAf,CAAA,EAAA,OAAA;;;;;AAsBY,UAnDD,WAAA,CAmDC;EAAQ;EAEd,KAKkB,EAxDrB,YAwDqB;EAAO;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/test-suite.ts","../src/utils.ts"],"sourcesContent":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -180,6 +180,58 @@ function literal(value) {
|
|
|
180
180
|
value
|
|
181
181
|
};
|
|
182
182
|
}
|
|
183
|
+
/**
|
|
184
|
+
* Parse a JSON path string into a path array.
|
|
185
|
+
* Examples:
|
|
186
|
+
* - "" -> []
|
|
187
|
+
* - "foo.bar" -> ["foo", "bar"]
|
|
188
|
+
* - "foo[0]" -> ["foo", 0]
|
|
189
|
+
* - "foo.bar[1].baz" -> ["foo", "bar", 1, "baz"]
|
|
190
|
+
*/
|
|
191
|
+
function parseJsonPath(pathString) {
|
|
192
|
+
if (!pathString) return [];
|
|
193
|
+
const parts = [];
|
|
194
|
+
let current = "";
|
|
195
|
+
let inBracket = false;
|
|
196
|
+
for (let i = 0; i < pathString.length; i++) {
|
|
197
|
+
const char = pathString[i];
|
|
198
|
+
if (char === "[") {
|
|
199
|
+
if (current) {
|
|
200
|
+
parts.push(current);
|
|
201
|
+
current = "";
|
|
202
|
+
}
|
|
203
|
+
inBracket = true;
|
|
204
|
+
} else if (char === "]") {
|
|
205
|
+
if (inBracket && current) {
|
|
206
|
+
const num = parseInt(current, 10);
|
|
207
|
+
parts.push(isNaN(num) ? current : num);
|
|
208
|
+
current = "";
|
|
209
|
+
}
|
|
210
|
+
inBracket = false;
|
|
211
|
+
} else if (char === "." && !inBracket) {
|
|
212
|
+
if (current) {
|
|
213
|
+
parts.push(current);
|
|
214
|
+
current = "";
|
|
215
|
+
}
|
|
216
|
+
} else current += char;
|
|
217
|
+
}
|
|
218
|
+
if (current) parts.push(current);
|
|
219
|
+
return parts;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Helper to create a reference value for conditions.
|
|
223
|
+
* @param projectId - The project ID where the referenced config lives
|
|
224
|
+
* @param configName - The name of the config to reference
|
|
225
|
+
* @param path - JSON path string to extract from the config value (empty string for entire value)
|
|
226
|
+
*/
|
|
227
|
+
function reference(projectId, configName, path = "") {
|
|
228
|
+
return {
|
|
229
|
+
type: "reference",
|
|
230
|
+
projectId,
|
|
231
|
+
configName,
|
|
232
|
+
path: parseJsonPath(path)
|
|
233
|
+
};
|
|
234
|
+
}
|
|
183
235
|
const silentLogger = {
|
|
184
236
|
debug: () => {},
|
|
185
237
|
info: () => {},
|
|
@@ -277,7 +329,7 @@ function createTestContext(admin, workspaceId, projectId, environmentId, sdkKey,
|
|
|
277
329
|
function testSuite(options) {
|
|
278
330
|
const { superadminKey, adminApiBaseUrl, edgeApiBaseUrl } = options;
|
|
279
331
|
const defaultTimeout = options.defaultTimeout ?? 1e4;
|
|
280
|
-
describe("Replane E2E Test Suite", () => {
|
|
332
|
+
describe("Replane E2E Test Suite", { repeats: 8 }, () => {
|
|
281
333
|
let admin;
|
|
282
334
|
let workspaceId;
|
|
283
335
|
let projectId;
|
|
@@ -699,6 +751,320 @@ function testSuite(options) {
|
|
|
699
751
|
client.disconnect();
|
|
700
752
|
});
|
|
701
753
|
});
|
|
754
|
+
describe("Reference Evaluation", () => {
|
|
755
|
+
it("should evaluate reference with empty path (entire config value)", async () => {
|
|
756
|
+
await ctx.createConfig("ref-source-simple", "allowed-region");
|
|
757
|
+
await ctx.createConfig("ref-target-simple", "default", { overrides: [{
|
|
758
|
+
name: "ref-override",
|
|
759
|
+
conditions: [{
|
|
760
|
+
operator: "equals",
|
|
761
|
+
property: "region",
|
|
762
|
+
value: reference(projectId, "ref-source-simple", "")
|
|
763
|
+
}],
|
|
764
|
+
value: "matched-by-reference"
|
|
765
|
+
}] });
|
|
766
|
+
const client1 = trackClient(await ctx.createClient({ context: { region: "allowed-region" } }));
|
|
767
|
+
expect(client1.get("ref-target-simple")).toBe("matched-by-reference");
|
|
768
|
+
client1.disconnect();
|
|
769
|
+
const client2 = trackClient(await ctx.createClient({ context: { region: "other-region" } }));
|
|
770
|
+
expect(client2.get("ref-target-simple")).toBe("default");
|
|
771
|
+
client2.disconnect();
|
|
772
|
+
});
|
|
773
|
+
it("should evaluate reference with JSON path to nested object property", async () => {
|
|
774
|
+
await ctx.createConfig("ref-source-object", { settings: {
|
|
775
|
+
threshold: 100,
|
|
776
|
+
enabled: true
|
|
777
|
+
} });
|
|
778
|
+
await ctx.createConfig("ref-target-object", "below-threshold", { overrides: [{
|
|
779
|
+
name: "threshold-override",
|
|
780
|
+
conditions: [{
|
|
781
|
+
operator: "greater_than_or_equal",
|
|
782
|
+
property: "score",
|
|
783
|
+
value: reference(projectId, "ref-source-object", "settings.threshold")
|
|
784
|
+
}],
|
|
785
|
+
value: "above-threshold"
|
|
786
|
+
}] });
|
|
787
|
+
const client1 = trackClient(await ctx.createClient({ context: { score: 150 } }));
|
|
788
|
+
expect(client1.get("ref-target-object")).toBe("above-threshold");
|
|
789
|
+
client1.disconnect();
|
|
790
|
+
const client2 = trackClient(await ctx.createClient({ context: { score: 100 } }));
|
|
791
|
+
expect(client2.get("ref-target-object")).toBe("above-threshold");
|
|
792
|
+
client2.disconnect();
|
|
793
|
+
const client3 = trackClient(await ctx.createClient({ context: { score: 50 } }));
|
|
794
|
+
expect(client3.get("ref-target-object")).toBe("below-threshold");
|
|
795
|
+
client3.disconnect();
|
|
796
|
+
});
|
|
797
|
+
it("should evaluate reference with JSON path to array element", async () => {
|
|
798
|
+
await ctx.createConfig("ref-source-array", { tiers: [
|
|
799
|
+
"free",
|
|
800
|
+
"basic",
|
|
801
|
+
"premium",
|
|
802
|
+
"enterprise"
|
|
803
|
+
] });
|
|
804
|
+
await ctx.createConfig("ref-target-array", "no-match", { overrides: [{
|
|
805
|
+
name: "premium-override",
|
|
806
|
+
conditions: [{
|
|
807
|
+
operator: "equals",
|
|
808
|
+
property: "tier",
|
|
809
|
+
value: reference(projectId, "ref-source-array", "tiers[2]")
|
|
810
|
+
}],
|
|
811
|
+
value: "premium-match"
|
|
812
|
+
}] });
|
|
813
|
+
const client1 = trackClient(await ctx.createClient({ context: { tier: "premium" } }));
|
|
814
|
+
expect(client1.get("ref-target-array")).toBe("premium-match");
|
|
815
|
+
client1.disconnect();
|
|
816
|
+
const client2 = trackClient(await ctx.createClient({ context: { tier: "basic" } }));
|
|
817
|
+
expect(client2.get("ref-target-array")).toBe("no-match");
|
|
818
|
+
client2.disconnect();
|
|
819
|
+
});
|
|
820
|
+
it("should evaluate reference with JSON path to entire array for 'in' condition", async () => {
|
|
821
|
+
await ctx.createConfig("ref-source-regions", { allowedRegions: [
|
|
822
|
+
"us-east",
|
|
823
|
+
"us-west",
|
|
824
|
+
"eu-west"
|
|
825
|
+
] });
|
|
826
|
+
await ctx.createConfig("ref-target-regions", "blocked", { overrides: [{
|
|
827
|
+
name: "allowed-regions-override",
|
|
828
|
+
conditions: [{
|
|
829
|
+
operator: "in",
|
|
830
|
+
property: "region",
|
|
831
|
+
value: reference(projectId, "ref-source-regions", "allowedRegions")
|
|
832
|
+
}],
|
|
833
|
+
value: "allowed"
|
|
834
|
+
}] });
|
|
835
|
+
const client1 = trackClient(await ctx.createClient({ context: { region: "us-east" } }));
|
|
836
|
+
expect(client1.get("ref-target-regions")).toBe("allowed");
|
|
837
|
+
client1.disconnect();
|
|
838
|
+
const client2 = trackClient(await ctx.createClient({ context: { region: "eu-west" } }));
|
|
839
|
+
expect(client2.get("ref-target-regions")).toBe("allowed");
|
|
840
|
+
client2.disconnect();
|
|
841
|
+
const client3 = trackClient(await ctx.createClient({ context: { region: "ap-south" } }));
|
|
842
|
+
expect(client3.get("ref-target-regions")).toBe("blocked");
|
|
843
|
+
client3.disconnect();
|
|
844
|
+
});
|
|
845
|
+
it("should handle reference to non-existent config (condition fails)", async () => {
|
|
846
|
+
await ctx.createConfig("ref-target-nonexistent", "default-fallback", { overrides: [{
|
|
847
|
+
name: "nonexistent-ref-override",
|
|
848
|
+
conditions: [{
|
|
849
|
+
operator: "equals",
|
|
850
|
+
property: "value",
|
|
851
|
+
value: reference(projectId, "nonexistent-config-xyz", "")
|
|
852
|
+
}],
|
|
853
|
+
value: "should-not-match"
|
|
854
|
+
}] });
|
|
855
|
+
const client = trackClient(await ctx.createClient({ context: { value: "anything" } }));
|
|
856
|
+
expect(client.get("ref-target-nonexistent")).toBe("default-fallback");
|
|
857
|
+
client.disconnect();
|
|
858
|
+
});
|
|
859
|
+
it("should handle reference with invalid JSON path (condition fails)", async () => {
|
|
860
|
+
await ctx.createConfig("ref-source-invalid-path", { data: { value: 42 } });
|
|
861
|
+
await ctx.createConfig("ref-target-invalid-path", "default-value", { overrides: [{
|
|
862
|
+
name: "invalid-path-override",
|
|
863
|
+
conditions: [{
|
|
864
|
+
operator: "equals",
|
|
865
|
+
property: "test",
|
|
866
|
+
value: reference(projectId, "ref-source-invalid-path", "data.nonexistent.deep")
|
|
867
|
+
}],
|
|
868
|
+
value: "should-not-match"
|
|
869
|
+
}] });
|
|
870
|
+
const client = trackClient(await ctx.createClient({ context: { test: "anything" } }));
|
|
871
|
+
expect(client.get("ref-target-invalid-path")).toBe("default-value");
|
|
872
|
+
client.disconnect();
|
|
873
|
+
});
|
|
874
|
+
it("should handle reference with out-of-bounds array index", async () => {
|
|
875
|
+
await ctx.createConfig("ref-source-oob", { items: [
|
|
876
|
+
"a",
|
|
877
|
+
"b",
|
|
878
|
+
"c"
|
|
879
|
+
] });
|
|
880
|
+
await ctx.createConfig("ref-target-oob", "default-value", { overrides: [{
|
|
881
|
+
name: "oob-override",
|
|
882
|
+
conditions: [{
|
|
883
|
+
operator: "equals",
|
|
884
|
+
property: "item",
|
|
885
|
+
value: reference(projectId, "ref-source-oob", "items[99]")
|
|
886
|
+
}],
|
|
887
|
+
value: "matched"
|
|
888
|
+
}] });
|
|
889
|
+
const client = trackClient(await ctx.createClient({ context: { item: "anything" } }));
|
|
890
|
+
expect(client.get("ref-target-oob")).toBe("default-value");
|
|
891
|
+
client.disconnect();
|
|
892
|
+
});
|
|
893
|
+
it("should evaluate reference with deeply nested path", async () => {
|
|
894
|
+
await ctx.createConfig("ref-source-deep", { level1: { level2: { level3: { targetValue: "deep-secret" } } } });
|
|
895
|
+
await ctx.createConfig("ref-target-deep", "no-match", { overrides: [{
|
|
896
|
+
name: "deep-override",
|
|
897
|
+
conditions: [{
|
|
898
|
+
operator: "equals",
|
|
899
|
+
property: "secret",
|
|
900
|
+
value: reference(projectId, "ref-source-deep", "level1.level2.level3.targetValue")
|
|
901
|
+
}],
|
|
902
|
+
value: "deep-match"
|
|
903
|
+
}] });
|
|
904
|
+
const client1 = trackClient(await ctx.createClient({ context: { secret: "deep-secret" } }));
|
|
905
|
+
expect(client1.get("ref-target-deep")).toBe("deep-match");
|
|
906
|
+
client1.disconnect();
|
|
907
|
+
const client2 = trackClient(await ctx.createClient({ context: { secret: "wrong" } }));
|
|
908
|
+
expect(client2.get("ref-target-deep")).toBe("no-match");
|
|
909
|
+
client2.disconnect();
|
|
910
|
+
});
|
|
911
|
+
it("should evaluate reference with array of objects and path", async () => {
|
|
912
|
+
await ctx.createConfig("ref-source-array-obj", { users: [
|
|
913
|
+
{
|
|
914
|
+
id: 1,
|
|
915
|
+
name: "Alice"
|
|
916
|
+
},
|
|
917
|
+
{
|
|
918
|
+
id: 2,
|
|
919
|
+
name: "Bob"
|
|
920
|
+
},
|
|
921
|
+
{
|
|
922
|
+
id: 3,
|
|
923
|
+
name: "Charlie"
|
|
924
|
+
}
|
|
925
|
+
] });
|
|
926
|
+
await ctx.createConfig("ref-target-array-obj", "unknown-user", { overrides: [{
|
|
927
|
+
name: "bob-override",
|
|
928
|
+
conditions: [{
|
|
929
|
+
operator: "equals",
|
|
930
|
+
property: "username",
|
|
931
|
+
value: reference(projectId, "ref-source-array-obj", "users[1].name")
|
|
932
|
+
}],
|
|
933
|
+
value: "its-bob"
|
|
934
|
+
}] });
|
|
935
|
+
const client1 = trackClient(await ctx.createClient({ context: { username: "Bob" } }));
|
|
936
|
+
expect(client1.get("ref-target-array-obj")).toBe("its-bob");
|
|
937
|
+
client1.disconnect();
|
|
938
|
+
const client2 = trackClient(await ctx.createClient({ context: { username: "Alice" } }));
|
|
939
|
+
expect(client2.get("ref-target-array-obj")).toBe("unknown-user");
|
|
940
|
+
client2.disconnect();
|
|
941
|
+
});
|
|
942
|
+
it("should evaluate reference to boolean value", async () => {
|
|
943
|
+
await ctx.createConfig("ref-source-bool", { featureEnabled: true });
|
|
944
|
+
await ctx.createConfig("ref-target-bool", "feature-off", { overrides: [{
|
|
945
|
+
name: "feature-override",
|
|
946
|
+
conditions: [{
|
|
947
|
+
operator: "equals",
|
|
948
|
+
property: "enabled",
|
|
949
|
+
value: reference(projectId, "ref-source-bool", "featureEnabled")
|
|
950
|
+
}],
|
|
951
|
+
value: "feature-on"
|
|
952
|
+
}] });
|
|
953
|
+
const client1 = trackClient(await ctx.createClient({ context: { enabled: true } }));
|
|
954
|
+
expect(client1.get("ref-target-bool")).toBe("feature-on");
|
|
955
|
+
client1.disconnect();
|
|
956
|
+
const client2 = trackClient(await ctx.createClient({ context: { enabled: false } }));
|
|
957
|
+
expect(client2.get("ref-target-bool")).toBe("feature-off");
|
|
958
|
+
client2.disconnect();
|
|
959
|
+
});
|
|
960
|
+
it("should handle multiple overrides with different references", async () => {
|
|
961
|
+
await ctx.createConfig("ref-source-premium", { tier: "premium" });
|
|
962
|
+
await ctx.createConfig("ref-source-enterprise", { tier: "enterprise" });
|
|
963
|
+
await ctx.createConfig("ref-target-multi", "free-tier", { overrides: [{
|
|
964
|
+
name: "enterprise-override",
|
|
965
|
+
conditions: [{
|
|
966
|
+
operator: "equals",
|
|
967
|
+
property: "plan",
|
|
968
|
+
value: reference(projectId, "ref-source-enterprise", "tier")
|
|
969
|
+
}],
|
|
970
|
+
value: "enterprise-tier"
|
|
971
|
+
}, {
|
|
972
|
+
name: "premium-override",
|
|
973
|
+
conditions: [{
|
|
974
|
+
operator: "equals",
|
|
975
|
+
property: "plan",
|
|
976
|
+
value: reference(projectId, "ref-source-premium", "tier")
|
|
977
|
+
}],
|
|
978
|
+
value: "premium-tier"
|
|
979
|
+
}] });
|
|
980
|
+
const client1 = trackClient(await ctx.createClient({ context: { plan: "enterprise" } }));
|
|
981
|
+
expect(client1.get("ref-target-multi")).toBe("enterprise-tier");
|
|
982
|
+
client1.disconnect();
|
|
983
|
+
const client2 = trackClient(await ctx.createClient({ context: { plan: "premium" } }));
|
|
984
|
+
expect(client2.get("ref-target-multi")).toBe("premium-tier");
|
|
985
|
+
client2.disconnect();
|
|
986
|
+
const client3 = trackClient(await ctx.createClient({ context: { plan: "basic" } }));
|
|
987
|
+
expect(client3.get("ref-target-multi")).toBe("free-tier");
|
|
988
|
+
client3.disconnect();
|
|
989
|
+
});
|
|
990
|
+
it("should evaluate reference combined with literal conditions using AND", async () => {
|
|
991
|
+
await ctx.createConfig("ref-source-combined", { requiredLevel: 10 });
|
|
992
|
+
await ctx.createConfig("ref-target-combined", "access-denied", { overrides: [{
|
|
993
|
+
name: "combined-override",
|
|
994
|
+
conditions: [{
|
|
995
|
+
operator: "and",
|
|
996
|
+
conditions: [{
|
|
997
|
+
operator: "equals",
|
|
998
|
+
property: "role",
|
|
999
|
+
value: literal("admin")
|
|
1000
|
+
}, {
|
|
1001
|
+
operator: "greater_than_or_equal",
|
|
1002
|
+
property: "level",
|
|
1003
|
+
value: reference(projectId, "ref-source-combined", "requiredLevel")
|
|
1004
|
+
}]
|
|
1005
|
+
}],
|
|
1006
|
+
value: "access-granted"
|
|
1007
|
+
}] });
|
|
1008
|
+
const client1 = trackClient(await ctx.createClient({ context: {
|
|
1009
|
+
role: "admin",
|
|
1010
|
+
level: 15
|
|
1011
|
+
} }));
|
|
1012
|
+
expect(client1.get("ref-target-combined")).toBe("access-granted");
|
|
1013
|
+
client1.disconnect();
|
|
1014
|
+
const client2 = trackClient(await ctx.createClient({ context: {
|
|
1015
|
+
role: "admin",
|
|
1016
|
+
level: 5
|
|
1017
|
+
} }));
|
|
1018
|
+
expect(client2.get("ref-target-combined")).toBe("access-denied");
|
|
1019
|
+
client2.disconnect();
|
|
1020
|
+
const client3 = trackClient(await ctx.createClient({ context: {
|
|
1021
|
+
role: "user",
|
|
1022
|
+
level: 15
|
|
1023
|
+
} }));
|
|
1024
|
+
expect(client3.get("ref-target-combined")).toBe("access-denied");
|
|
1025
|
+
client3.disconnect();
|
|
1026
|
+
});
|
|
1027
|
+
it("should handle reference to null value", async () => {
|
|
1028
|
+
await ctx.createConfig("ref-source-null", null);
|
|
1029
|
+
await ctx.createConfig("ref-target-null", "has-value", { overrides: [{
|
|
1030
|
+
name: "null-override",
|
|
1031
|
+
conditions: [{
|
|
1032
|
+
operator: "equals",
|
|
1033
|
+
property: "data",
|
|
1034
|
+
value: reference(projectId, "ref-source-null", "")
|
|
1035
|
+
}],
|
|
1036
|
+
value: "is-null"
|
|
1037
|
+
}] });
|
|
1038
|
+
const client1 = trackClient(await ctx.createClient({ context: { data: null } }));
|
|
1039
|
+
expect(client1.get("ref-target-null")).toBe("is-null");
|
|
1040
|
+
client1.disconnect();
|
|
1041
|
+
const client2 = trackClient(await ctx.createClient({ context: { data: "something" } }));
|
|
1042
|
+
expect(client2.get("ref-target-null")).toBe("has-value");
|
|
1043
|
+
client2.disconnect();
|
|
1044
|
+
});
|
|
1045
|
+
it("should evaluate reference with not_in condition", async () => {
|
|
1046
|
+
await ctx.createConfig("ref-source-blocked", { blockedCountries: [
|
|
1047
|
+
"XX",
|
|
1048
|
+
"YY",
|
|
1049
|
+
"ZZ"
|
|
1050
|
+
] });
|
|
1051
|
+
await ctx.createConfig("ref-target-blocked", "blocked", { overrides: [{
|
|
1052
|
+
name: "allowed-override",
|
|
1053
|
+
conditions: [{
|
|
1054
|
+
operator: "not_in",
|
|
1055
|
+
property: "country",
|
|
1056
|
+
value: reference(projectId, "ref-source-blocked", "blockedCountries")
|
|
1057
|
+
}],
|
|
1058
|
+
value: "allowed"
|
|
1059
|
+
}] });
|
|
1060
|
+
const client1 = trackClient(await ctx.createClient({ context: { country: "US" } }));
|
|
1061
|
+
expect(client1.get("ref-target-blocked")).toBe("allowed");
|
|
1062
|
+
client1.disconnect();
|
|
1063
|
+
const client2 = trackClient(await ctx.createClient({ context: { country: "XX" } }));
|
|
1064
|
+
expect(client2.get("ref-target-blocked")).toBe("blocked");
|
|
1065
|
+
client2.disconnect();
|
|
1066
|
+
});
|
|
1067
|
+
});
|
|
702
1068
|
describe("Snapshot", () => {
|
|
703
1069
|
it("should create snapshot with current configs", async () => {
|
|
704
1070
|
await ctx.createConfig("snap-config-1", "value-1");
|
|
@@ -812,7 +1178,7 @@ function testSuite(options) {
|
|
|
812
1178
|
client2.disconnect();
|
|
813
1179
|
});
|
|
814
1180
|
});
|
|
815
|
-
}
|
|
1181
|
+
});
|
|
816
1182
|
}
|
|
817
1183
|
|
|
818
1184
|
//#endregion
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["resolve!: (value: T) => void","reject!: (reason?: unknown) => void","condition: () => boolean | Promise<boolean>","options: WaitForOptions","value: T | undefined","v: T","values: T[]","listeners: Array<() => void>","value: T","count: number","predicate: (value: T) => boolean","ms: number","request: {\n edgeApiBaseUrl: string;\n sdkKey: string;\n}","value: T","admin: ReplaneAdmin","workspaceId: string","projectId: string","environmentId: string","sdkKey: string","options: TestSuiteOptions","clientOptions?: {\n context?: ReplaneContext;\n defaults?: Partial<T>;\n }","name: string","value: ConfigValue","configOptions?: {\n description?: string;\n overrides?: Override[];\n }","ctx: TestContext","activeClients: Replane<Record<string, unknown>>[]","client: Replane<T>"],"sources":["../src/utils.ts","../src/test-suite.ts"],"sourcesContent":["/**\n * Test utilities for waiting with early resolution\n */\n\nexport interface Deferred<T> {\n promise: Promise<T>;\n resolve: (value: T) => void;\n reject: (reason?: unknown) => void;\n}\n\n/**\n * Creates a deferred promise that can be resolved/rejected externally\n */\nexport function createDeferred<T>(): Deferred<T> {\n let resolve!: (value: T) => void;\n let reject!: (reason?: unknown) => void;\n\n const promise = new Promise<T>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n return { promise, resolve, reject };\n}\n\n/**\n * Options for waitFor utility\n */\nexport interface WaitForOptions {\n /** Timeout in milliseconds (default: 5000) */\n timeout?: number;\n /** Custom error message on timeout */\n timeoutMessage?: string;\n}\n\n/**\n * Waits for a condition to be met or times out.\n * Returns immediately when condition is satisfied.\n *\n * @example\n * ```ts\n * let value: string | null = null;\n * client.subscribe(\"config\", (v) => { value = v; });\n *\n * await waitFor(() => value !== null, { timeout: 2000 });\n * expect(value).toBe(\"expected\");\n * ```\n */\nexport async function waitFor(\n condition: () => boolean | Promise<boolean>,\n options: WaitForOptions = {}\n): Promise<void> {\n const { timeout = 5000, timeoutMessage = \"waitFor timed out\" } = options;\n\n const startTime = Date.now();\n\n while (true) {\n const result = await condition();\n if (result) return;\n\n if (Date.now() - startTime >= timeout) {\n throw new Error(timeoutMessage);\n }\n\n // Yield to event loop, check again soon\n await new Promise((resolve) => setTimeout(resolve, 10));\n }\n}\n\n/**\n * A signal that can be awaited and triggered.\n * Useful for waiting for async events in tests.\n *\n * @example\n * ```ts\n * const signal = createSignal<string>();\n *\n * client.subscribe(\"config\", (value) => {\n * signal.trigger(value);\n * });\n *\n * const value = await signal.wait({ timeout: 2000 });\n * expect(value).toBe(\"expected\");\n * ```\n */\nexport interface Signal<T> {\n /** Wait for the signal to be triggered */\n wait(options?: WaitForOptions): Promise<T>;\n /** Trigger the signal with a value */\n trigger(value: T): void;\n /** Check if signal has been triggered */\n isTriggered(): boolean;\n /** Reset the signal to untriggered state */\n reset(): void;\n /** Get the triggered value (undefined if not triggered) */\n getValue(): T | undefined;\n}\n\nexport function createSignal<T = void>(): Signal<T> {\n let deferred = createDeferred<T>();\n let triggered = false;\n let value: T | undefined;\n\n return {\n wait(options: WaitForOptions = {}) {\n const { timeout = 5000, timeoutMessage = \"Signal wait timed out\" } = options;\n\n return Promise.race([\n deferred.promise,\n new Promise<T>((_, reject) => setTimeout(() => reject(new Error(timeoutMessage)), timeout)),\n ]);\n },\n\n trigger(v: T) {\n if (!triggered) {\n triggered = true;\n value = v;\n deferred.resolve(v);\n }\n },\n\n isTriggered() {\n return triggered;\n },\n\n reset() {\n triggered = false;\n value = undefined;\n deferred = createDeferred<T>();\n },\n\n getValue() {\n return value;\n },\n };\n}\n\n/**\n * A collector that accumulates values and can wait for a specific count.\n * Useful for collecting multiple updates in tests.\n *\n * @example\n * ```ts\n * const collector = createCollector<string>();\n *\n * client.subscribe(\"config\", (value) => {\n * collector.push(value);\n * });\n *\n * // Wait for 3 updates\n * const values = await collector.waitForCount(3, { timeout: 5000 });\n * expect(values).toEqual([\"v1\", \"v2\", \"v3\"]);\n * ```\n */\nexport interface Collector<T> {\n /** Push a value to the collector */\n push(value: T): void;\n /** Get all collected values */\n getValues(): T[];\n /** Get the count of collected values */\n count(): number;\n /** Wait for at least N values to be collected */\n waitForCount(count: number, options?: WaitForOptions): Promise<T[]>;\n /** Wait for a value matching the predicate */\n waitFor(predicate: (value: T) => boolean, options?: WaitForOptions): Promise<T>;\n /** Clear all collected values */\n clear(): void;\n}\n\nexport function createCollector<T>(): Collector<T> {\n const values: T[] = [];\n const listeners: Array<() => void> = [];\n\n const notify = () => {\n for (const listener of listeners) {\n listener();\n }\n };\n\n return {\n push(value: T) {\n values.push(value);\n notify();\n },\n\n getValues() {\n return [...values];\n },\n\n count() {\n return values.length;\n },\n\n async waitForCount(count: number, options: WaitForOptions = {}) {\n const { timeout = 5000, timeoutMessage = `Collector timed out waiting for ${count} values` } =\n options;\n\n if (values.length >= count) {\n return [...values];\n }\n\n return new Promise<T[]>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n const idx = listeners.indexOf(listener);\n if (idx !== -1) listeners.splice(idx, 1);\n reject(new Error(timeoutMessage));\n }, timeout);\n\n const listener = () => {\n if (values.length >= count) {\n clearTimeout(timeoutId);\n const idx = listeners.indexOf(listener);\n if (idx !== -1) listeners.splice(idx, 1);\n resolve([...values]);\n }\n };\n\n listeners.push(listener);\n });\n },\n\n async waitFor(predicate: (value: T) => boolean, options: WaitForOptions = {}) {\n const { timeout = 5000, timeoutMessage = \"Collector timed out waiting for matching value\" } =\n options;\n\n // Check existing values first\n for (const v of values) {\n if (predicate(v)) return v;\n }\n\n return new Promise<T>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n const idx = listeners.indexOf(listener);\n if (idx !== -1) listeners.splice(idx, 1);\n reject(new Error(timeoutMessage));\n }, timeout);\n\n const listener = () => {\n // Check the latest value\n const latest = values[values.length - 1];\n if (latest !== undefined && predicate(latest)) {\n clearTimeout(timeoutId);\n const idx = listeners.indexOf(listener);\n if (idx !== -1) listeners.splice(idx, 1);\n resolve(latest);\n }\n };\n\n listeners.push(listener);\n });\n },\n\n clear() {\n values.length = 0;\n },\n };\n}\n\n/**\n * Delay for a specified time (use sparingly in tests)\n */\nexport function delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Generate a unique test identifier\n */\nexport function uniqueId(prefix = \"test\"): string {\n return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n}\n\n/**\n * Sync the edge replica with the database.\n * Requires TESTING_MODE=true on the server.\n *\n * This is useful in tests to ensure the edge replica has\n * received all config changes before making assertions.\n *\n * @param request - The request object containing the edge API base URL and admin API key\n * @param request.edgeApiBaseUrl - The base URL of the edge API (e.g., \"http://localhost:8080\")\n * @param request.adminApiKey - The admin API key\n */\nexport async function syncReplica(request: {\n edgeApiBaseUrl: string;\n sdkKey: string;\n}): Promise<void> {\n const { edgeApiBaseUrl, sdkKey } = request;\n\n const response = await fetch(`${edgeApiBaseUrl}/api/sdk/v1/testing/sync`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${sdkKey}`,\n },\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to sync replica: ${response.status} ${body}`);\n }\n}\n","/**\n * Replane E2E Test Suite\n *\n * Comprehensive test suite for testing @replanejs/sdk with real @replanejs/admin API\n *\n * NOTE: This test suite is designed for multi-server setups where admin API and edge API\n * may be on different servers. Config changes are not immediate and require waiting for\n * replication to complete.\n */\n\nimport { describe, it, beforeAll, afterAll, afterEach, expect } from \"vitest\";\nimport { ReplaneAdmin, type ConfigValue, type Override } from \"@replanejs/admin\";\nimport { Replane, ReplaneError, ReplaneErrorCode } from \"@replanejs/sdk\";\nimport type { ReplaneContext } from \"@replanejs/sdk\";\nimport type { TestSuiteOptions, TestContext } from \"./types\";\nimport { createSignal, createCollector, delay, uniqueId, syncReplica } from \"./utils\";\n\n/**\n * Helper to create a literal value for conditions.\n */\nfunction literal<T>(value: T) {\n return { type: \"literal\" as const, value };\n}\n\nconst silentLogger = {\n debug: () => {},\n info: () => {},\n warn: () => {},\n error: () => {},\n};\n\n/**\n * Creates a test context with helper methods\n */\nfunction createTestContext(\n admin: ReplaneAdmin,\n workspaceId: string,\n projectId: string,\n environmentId: string,\n sdkKey: string,\n options: TestSuiteOptions\n): TestContext {\n const defaultTimeout = options.defaultTimeout ?? 10000;\n\n function sync(): Promise<void> {\n return syncReplica({ edgeApiBaseUrl: options.edgeApiBaseUrl, sdkKey });\n }\n\n return {\n admin,\n workspaceId,\n projectId,\n environmentId,\n sdkKey,\n edgeApiBaseUrl: options.edgeApiBaseUrl,\n adminApiBaseUrl: options.adminApiBaseUrl,\n defaultTimeout,\n\n sync,\n\n async createClient<T extends object = Record<string, unknown>>(clientOptions?: {\n context?: ReplaneContext;\n defaults?: Partial<T>;\n }): Promise<Replane<T>> {\n await sync();\n\n const client = new Replane<T>({\n logger: options.debug ? console : silentLogger,\n context: clientOptions?.context,\n defaults: clientOptions?.defaults as T | undefined,\n });\n\n await client.connect({\n sdkKey,\n baseUrl: options.edgeApiBaseUrl,\n connectTimeoutMs: defaultTimeout,\n });\n\n return client;\n },\n\n async createConfig(\n name: string,\n value: ConfigValue,\n configOptions?: {\n description?: string;\n overrides?: Override[];\n }\n ): Promise<void> {\n await admin.configs.create({\n projectId,\n name,\n description: configOptions?.description ?? \"\",\n editors: [],\n base: {\n value,\n schema: null,\n overrides: configOptions?.overrides ?? [],\n },\n variants: [],\n });\n await sync();\n },\n\n async updateConfig(\n name: string,\n value: ConfigValue,\n configOptions?: {\n description?: string;\n overrides?: Override[];\n }\n ): Promise<void> {\n await admin.configs.update({\n projectId,\n configName: name,\n description: configOptions?.description ?? \"\",\n editors: [],\n base: {\n value,\n schema: null,\n overrides: configOptions?.overrides ?? [],\n },\n variants: [],\n });\n await sync();\n },\n\n async deleteConfig(name: string): Promise<void> {\n await admin.configs.delete({ projectId, configName: name });\n await sync();\n },\n };\n}\n\n/**\n * Main test suite function\n *\n * @example\n * ```ts\n * import { testSuite } from \"@replanejs/test-suite\";\n *\n * testSuite({\n * superadminKey: process.env.SUPERADMIN_KEY!,\n * adminApiBaseUrl: \"http://localhost:8080\",\n * edgeApiBaseUrl: \"http://localhost:8080\",\n * });\n * ```\n */\nexport function testSuite(options: TestSuiteOptions): void {\n const { superadminKey, adminApiBaseUrl, edgeApiBaseUrl } = options;\n const defaultTimeout = options.defaultTimeout ?? 10000;\n\n describe(\n \"Replane E2E Test Suite\",\n () => {\n let admin: ReplaneAdmin;\n let workspaceId: string;\n let projectId: string;\n let environmentId: string;\n let sdkKey: string;\n let ctx: TestContext;\n\n // Track clients for cleanup\n const activeClients: Replane<Record<string, unknown>>[] = [];\n\n beforeAll(async () => {\n // Create admin client with superadmin key\n admin = new ReplaneAdmin({\n apiKey: superadminKey,\n baseUrl: adminApiBaseUrl,\n });\n\n // Create a unique workspace for this test run\n const workspaceName = uniqueId(\"e2e-workspace\");\n const workspaceRes = await admin.workspaces.create({ name: workspaceName });\n workspaceId = workspaceRes.id;\n\n // Create a project in the workspace\n const projectName = uniqueId(\"e2e-project\");\n const projectRes = await admin.projects.create({\n workspaceId,\n name: projectName,\n description: \"E2E test project\",\n });\n projectId = projectRes.id;\n\n // Get environments (use production)\n const envRes = await admin.environments.list({ projectId });\n const prodEnv =\n envRes.environments.find((e) => e.name === \"Production\") ?? envRes.environments[0];\n if (!prodEnv) {\n throw new Error(\"No environments found\");\n }\n environmentId = prodEnv.id;\n\n // Create SDK key\n const sdkKeyRes = await admin.sdkKeys.create({\n projectId,\n name: uniqueId(\"e2e-sdk-key\"),\n environmentId,\n });\n sdkKey = sdkKeyRes.key;\n\n await syncReplica({ edgeApiBaseUrl, sdkKey });\n\n // Create test context\n ctx = createTestContext(admin, workspaceId, projectId, environmentId, sdkKey, options);\n });\n\n afterAll(async () => {\n // Disconnect all active clients\n for (const client of activeClients) {\n try {\n client.disconnect();\n } catch {\n // Ignore errors during cleanup\n }\n }\n activeClients.length = 0;\n\n // Clean up: delete workspace (cascades to project, configs, keys)\n if (workspaceId) {\n await admin.workspaces.delete({ workspaceId });\n }\n });\n\n // Helper to track clients for cleanup\n const trackClient = <T extends object>(client: Replane<T>): Replane<T> => {\n activeClients.push(client as Replane<Record<string, unknown>>);\n return client;\n };\n\n afterEach(async () => {\n const configs = await admin.configs.list({ projectId });\n for (const config of configs.configs) {\n await admin.configs.delete({ projectId, configName: config.name });\n }\n await syncReplica({ edgeApiBaseUrl, sdkKey });\n });\n\n // ==================== CONNECTION TESTS ====================\n\n describe(\"SDK Connection\", () => {\n it(\"should connect and receive initial configs\", async () => {\n // Create config via admin API\n await ctx.createConfig(\"test-config\", \"initial-value\");\n\n // Create client - sync ensures configs are available\n const client = trackClient(await ctx.createClient<{ \"test-config\": string }>({}));\n\n const value = client.get(\"test-config\");\n expect(value).toBe(\"initial-value\");\n\n client.disconnect();\n });\n\n it(\"should handle empty project (no configs)\", async () => {\n // Create a new project with no configs\n const emptyProjectRes = await admin.projects.create({\n workspaceId,\n name: uniqueId(\"empty-project\"),\n description: \"Empty project for testing\",\n });\n const emptyEnvRes = await admin.environments.list({ projectId: emptyProjectRes.id });\n const emptyEnv = emptyEnvRes.environments[0];\n const emptySdkKeyRes = await admin.sdkKeys.create({\n projectId: emptyProjectRes.id,\n name: uniqueId(\"empty-sdk-key\"),\n environmentId: emptyEnv.id,\n });\n\n // Wait for SDK key to sync\n await syncReplica({ edgeApiBaseUrl, sdkKey });\n\n const emptyClient = new Replane({\n logger: silentLogger,\n });\n await emptyClient.connect({\n sdkKey: emptySdkKeyRes.key,\n baseUrl: edgeApiBaseUrl,\n connectTimeoutMs: defaultTimeout,\n });\n const client = trackClient(emptyClient);\n\n // Should not throw, but get() with no default should throw\n expect(() => client.get(\"nonexistent\")).toThrow();\n\n client.disconnect();\n\n // Cleanup\n await admin.projects.delete({ projectId: emptyProjectRes.id });\n });\n\n it(\"should use default values when config not found\", async () => {\n const client = trackClient(\n await ctx.createClient({\n defaults: { \"missing-config\": \"default-value\" },\n })\n );\n\n const value = client.get(\"missing-config\");\n expect(value).toBe(\"default-value\");\n\n client.disconnect();\n });\n });\n\n // ==================== GET CONFIG TESTS ====================\n\n describe(\"Get Config\", () => {\n it(\"should get string config\", async () => {\n await ctx.createConfig(\"string-config\", \"hello\");\n\n const client = trackClient(await ctx.createClient<{ \"string-config\": string }>({}));\n\n expect(client.get(\"string-config\")).toBe(\"hello\");\n client.disconnect();\n });\n\n it(\"should get number config\", async () => {\n await ctx.createConfig(\"number-config\", 42);\n\n const client = trackClient(await ctx.createClient<{ \"number-config\": number }>({}));\n\n expect(client.get(\"number-config\")).toBe(42);\n client.disconnect();\n });\n\n it(\"should get boolean config\", async () => {\n await ctx.createConfig(\"boolean-config\", true);\n\n const client = trackClient(await ctx.createClient<{ \"boolean-config\": boolean }>({}));\n\n expect(client.get(\"boolean-config\")).toBe(true);\n client.disconnect();\n });\n\n it(\"should get object config\", async () => {\n const objValue = { nested: { value: \"deep\" } };\n await ctx.createConfig(\"object-config\", objValue);\n\n const client = trackClient(\n await ctx.createClient<{ \"object-config\": typeof objValue }>({})\n );\n\n expect(client.get(\"object-config\")).toEqual(objValue);\n client.disconnect();\n });\n\n it(\"should get array config\", async () => {\n const arrValue = [1, 2, 3];\n await ctx.createConfig(\"array-config\", arrValue);\n\n const client = trackClient(await ctx.createClient<{ \"array-config\": number[] }>({}));\n\n expect(client.get(\"array-config\")).toEqual(arrValue);\n client.disconnect();\n });\n\n it(\"should get null config\", async () => {\n await ctx.createConfig(\"null-config\", null);\n\n const client = trackClient(await ctx.createClient<{ \"null-config\": null }>({}));\n\n expect(client.get(\"null-config\")).toBe(null);\n client.disconnect();\n });\n\n it(\"should return default value when config not found\", async () => {\n const client = trackClient(await ctx.createClient());\n const value = client.get(\"nonexistent\", { default: \"fallback\" });\n expect(value).toBe(\"fallback\");\n client.disconnect();\n });\n\n it(\"should throw ReplaneError when config not found and no default\", async () => {\n const client = trackClient(await ctx.createClient());\n try {\n client.get(\"nonexistent\");\n expect.fail(\"Should have thrown\");\n } catch (error) {\n expect(error).toBeInstanceOf(ReplaneError);\n expect((error as ReplaneError).code).toBe(ReplaneErrorCode.NotFound);\n }\n client.disconnect();\n });\n });\n\n // ==================== REAL-TIME UPDATES TESTS ====================\n\n describe(\"Real-time Updates\", () => {\n it(\"should receive config updates via subscription\", async () => {\n await ctx.createConfig(\"live-config\", \"initial\");\n\n const client = trackClient(await ctx.createClient<{ \"live-config\": string }>({}));\n\n expect(client.get(\"live-config\")).toBe(\"initial\");\n\n // Set up signal for next update\n const updateSignal = createSignal<string>();\n client.subscribe(\"live-config\", (config) => {\n if (config.value !== \"initial\") {\n updateSignal.trigger(config.value as string);\n }\n });\n\n // Update config\n await ctx.updateConfig(\"live-config\", \"updated\");\n\n // Wait for update (with timeout)\n const newValue = await updateSignal.wait({ timeout: defaultTimeout });\n expect(newValue).toBe(\"updated\");\n\n // Verify get() returns new value\n expect(client.get(\"live-config\")).toBe(\"updated\");\n\n client.disconnect();\n });\n\n it(\"should receive multiple updates in order\", async () => {\n await ctx.createConfig(\"multi-update-config\", 0);\n\n const client = trackClient(await ctx.createClient<{ \"multi-update-config\": number }>({}));\n\n expect(client.get(\"multi-update-config\")).toBe(0);\n\n const collector = createCollector<number>();\n client.subscribe(\"multi-update-config\", (config) => {\n const val = config.value as number;\n if (val > 0) {\n collector.push(val);\n }\n });\n\n // Send multiple updates with small delays to ensure ordering\n await ctx.updateConfig(\"multi-update-config\", 1);\n await delay(100);\n await ctx.updateConfig(\"multi-update-config\", 2);\n await delay(100);\n await ctx.updateConfig(\"multi-update-config\", 3);\n\n // Wait for all updates\n const values = await collector.waitForCount(3, { timeout: defaultTimeout });\n expect(values).toEqual([1, 2, 3]);\n\n client.disconnect();\n });\n\n it(\"should handle rapid updates\", async () => {\n await ctx.createConfig(\"rapid-config\", 0);\n\n const client = trackClient(await ctx.createClient<{ \"rapid-config\": number }>({}));\n\n expect(client.get(\"rapid-config\")).toBe(0);\n\n const collector = createCollector<number>();\n client.subscribe(\"rapid-config\", (config) => {\n collector.push(config.value as number);\n });\n\n // Send rapid updates\n const updateCount = 10;\n for (let i = 1; i <= updateCount; i++) {\n await ctx.updateConfig(\"rapid-config\", i);\n }\n\n // Wait for final value (may not get all intermediate values due to batching)\n await collector.waitFor((v) => v === updateCount, { timeout: defaultTimeout });\n\n // Final value should be correct\n expect(client.get(\"rapid-config\")).toBe(updateCount);\n\n client.disconnect();\n });\n\n it(\"should allow unsubscribing\", async () => {\n await ctx.createConfig(\"unsub-config\", \"initial\");\n\n const client = trackClient(await ctx.createClient<{ \"unsub-config\": string }>({}));\n\n expect(client.get(\"unsub-config\")).toBe(\"initial\");\n\n const collector = createCollector<string>();\n const unsubscribe = client.subscribe(\"unsub-config\", (config) => {\n if (config.value !== \"initial\") {\n collector.push(config.value as string);\n }\n });\n\n // First update should be received\n await ctx.updateConfig(\"unsub-config\", \"update-1\");\n await collector.waitForCount(1, { timeout: defaultTimeout });\n\n // Unsubscribe\n unsubscribe();\n\n // Second update should NOT be received\n await ctx.updateConfig(\"unsub-config\", \"update-2\");\n await delay(2000); // Give time for potential update to propagate\n\n // Should only have 1 update\n expect(collector.count()).toBe(1);\n\n client.disconnect();\n });\n });\n\n // ==================== OVERRIDE TESTS ====================\n\n describe(\"Override Evaluation\", () => {\n it(\"should evaluate equals condition\", async () => {\n await ctx.createConfig(\"env-config\", \"default\", {\n overrides: [\n {\n name: \"prod-override\",\n conditions: [{ operator: \"equals\", property: \"env\", value: literal(\"production\") }],\n value: \"production-value\",\n },\n ],\n });\n\n // Without context - should get default\n const client1 = trackClient(await ctx.createClient<{ \"env-config\": string }>({}));\n expect(client1.get(\"env-config\")).toBe(\"default\");\n client1.disconnect();\n\n // With matching context\n const client2 = trackClient(\n await ctx.createClient<{ \"env-config\": string }>({\n context: { env: \"production\" },\n })\n );\n expect(client2.get(\"env-config\")).toBe(\"production-value\");\n client2.disconnect();\n\n // With non-matching context\n const client3 = trackClient(\n await ctx.createClient<{ \"env-config\": string }>({\n context: { env: \"staging\" },\n })\n );\n expect(client3.get(\"env-config\")).toBe(\"default\");\n client3.disconnect();\n });\n\n it(\"should evaluate in condition\", async () => {\n await ctx.createConfig(\"region-config\", \"default\", {\n overrides: [\n {\n name: \"western-override\",\n conditions: [{ operator: \"in\", property: \"region\", value: literal([\"us\", \"eu\"]) }],\n value: \"western\",\n },\n ],\n });\n\n const client1 = trackClient(\n await ctx.createClient<{ \"region-config\": string }>({\n context: { region: \"us\" },\n })\n );\n expect(client1.get(\"region-config\")).toBe(\"western\");\n client1.disconnect();\n\n const client2 = trackClient(\n await ctx.createClient<{ \"region-config\": string }>({\n context: { region: \"eu\" },\n })\n );\n expect(client2.get(\"region-config\")).toBe(\"western\");\n client2.disconnect();\n\n const client3 = trackClient(\n await ctx.createClient<{ \"region-config\": string }>({\n context: { region: \"asia\" },\n })\n );\n expect(client3.get(\"region-config\")).toBe(\"default\");\n client3.disconnect();\n });\n\n it(\"should evaluate not_in condition\", async () => {\n await ctx.createConfig(\"allow-config\", \"allowed\", {\n overrides: [\n {\n name: \"not-blocked-override\",\n conditions: [\n {\n operator: \"not_in\",\n property: \"country\",\n value: literal([\"blocked1\", \"blocked2\"]),\n },\n ],\n value: \"not-blocked\",\n },\n ],\n });\n\n const client1 = trackClient(\n await ctx.createClient<{ \"allow-config\": string }>({\n context: { country: \"blocked1\" },\n })\n );\n expect(client1.get(\"allow-config\")).toBe(\"allowed\");\n client1.disconnect();\n\n const client2 = trackClient(\n await ctx.createClient<{ \"allow-config\": string }>({\n context: { country: \"normal\" },\n })\n );\n expect(client2.get(\"allow-config\")).toBe(\"not-blocked\");\n client2.disconnect();\n });\n\n it(\"should evaluate numeric comparison conditions\", async () => {\n await ctx.createConfig(\"tier-config\", \"free\", {\n overrides: [\n {\n name: \"premium-override\",\n conditions: [\n { operator: \"greater_than_or_equal\", property: \"level\", value: literal(10) },\n ],\n value: \"premium\",\n },\n ],\n });\n\n const client1 = trackClient(\n await ctx.createClient<{ \"tier-config\": string }>({\n context: { level: 5 },\n })\n );\n expect(client1.get(\"tier-config\")).toBe(\"free\");\n client1.disconnect();\n\n const client2 = trackClient(\n await ctx.createClient<{ \"tier-config\": string }>({\n context: { level: 10 },\n })\n );\n expect(client2.get(\"tier-config\")).toBe(\"premium\");\n client2.disconnect();\n\n const client3 = trackClient(\n await ctx.createClient<{ \"tier-config\": string }>({\n context: { level: 15 },\n })\n );\n expect(client3.get(\"tier-config\")).toBe(\"premium\");\n client3.disconnect();\n });\n\n it(\"should evaluate and condition\", async () => {\n await ctx.createConfig(\"combo-config\", \"default\", {\n overrides: [\n {\n name: \"combo-override\",\n conditions: [\n {\n operator: \"and\",\n conditions: [\n { operator: \"equals\", property: \"plan\", value: literal(\"enterprise\") },\n { operator: \"equals\", property: \"verified\", value: literal(true) },\n ],\n },\n ],\n value: \"enterprise-verified\",\n },\n ],\n });\n\n // Both conditions must match\n const client1 = trackClient(\n await ctx.createClient<{ \"combo-config\": string }>({\n context: { plan: \"enterprise\", verified: true },\n })\n );\n expect(client1.get(\"combo-config\")).toBe(\"enterprise-verified\");\n client1.disconnect();\n\n // Only one matches\n const client2 = trackClient(\n await ctx.createClient<{ \"combo-config\": string }>({\n context: { plan: \"enterprise\", verified: false },\n })\n );\n expect(client2.get(\"combo-config\")).toBe(\"default\");\n client2.disconnect();\n });\n\n it(\"should evaluate or condition\", async () => {\n await ctx.createConfig(\"either-config\", \"default\", {\n overrides: [\n {\n name: \"privileged-override\",\n conditions: [\n {\n operator: \"or\",\n conditions: [\n { operator: \"equals\", property: \"role\", value: literal(\"admin\") },\n { operator: \"equals\", property: \"role\", value: literal(\"superadmin\") },\n ],\n },\n ],\n value: \"privileged\",\n },\n ],\n });\n\n const client1 = trackClient(\n await ctx.createClient<{ \"either-config\": string }>({\n context: { role: \"admin\" },\n })\n );\n expect(client1.get(\"either-config\")).toBe(\"privileged\");\n client1.disconnect();\n\n const client2 = trackClient(\n await ctx.createClient<{ \"either-config\": string }>({\n context: { role: \"superadmin\" },\n })\n );\n expect(client2.get(\"either-config\")).toBe(\"privileged\");\n client2.disconnect();\n\n const client3 = trackClient(\n await ctx.createClient<{ \"either-config\": string }>({\n context: { role: \"user\" },\n })\n );\n expect(client3.get(\"either-config\")).toBe(\"default\");\n client3.disconnect();\n });\n\n it(\"should allow per-request context override\", async () => {\n await ctx.createConfig(\"dynamic-config\", \"default\", {\n overrides: [\n {\n name: \"feature-override\",\n conditions: [\n { operator: \"equals\", property: \"feature\", value: literal(\"enabled\") },\n ],\n value: \"feature-on\",\n },\n ],\n });\n\n const client = trackClient(await ctx.createClient<{ \"dynamic-config\": string }>({}));\n\n // Without context\n expect(client.get(\"dynamic-config\")).toBe(\"default\");\n\n // With per-request context\n expect(client.get(\"dynamic-config\", { context: { feature: \"enabled\" } })).toBe(\n \"feature-on\"\n );\n\n // Original still returns default\n expect(client.get(\"dynamic-config\")).toBe(\"default\");\n\n client.disconnect();\n });\n\n it(\"should apply first matching override\", async () => {\n await ctx.createConfig(\"priority-config\", \"default\", {\n overrides: [\n {\n name: \"gold-override\",\n conditions: [{ operator: \"equals\", property: \"tier\", value: literal(\"gold\") }],\n value: \"gold-value\",\n },\n {\n name: \"silver-override\",\n conditions: [{ operator: \"equals\", property: \"tier\", value: literal(\"silver\") }],\n value: \"silver-value\",\n },\n {\n name: \"score-override\",\n conditions: [{ operator: \"greater_than\", property: \"score\", value: literal(0) }],\n value: \"has-score\",\n },\n ],\n });\n\n // First override matches\n const client = trackClient(\n await ctx.createClient<{ \"priority-config\": string }>({\n context: { tier: \"gold\", score: 100 },\n })\n );\n expect(client.get(\"priority-config\")).toBe(\"gold-value\");\n client.disconnect();\n });\n });\n\n // ==================== SNAPSHOT TESTS ====================\n\n describe(\"Snapshot\", () => {\n it(\"should create snapshot with current configs\", async () => {\n await ctx.createConfig(\"snap-config-1\", \"value-1\");\n await ctx.createConfig(\"snap-config-2\", \"value-2\");\n\n const client = trackClient(\n await ctx.createClient<{ \"snap-config-1\": string; \"snap-config-2\": string }>({})\n );\n\n const snapshot = client.getSnapshot();\n\n expect(snapshot.configs).toMatchInlineSnapshot(`\n [\n {\n \"name\": \"snap-config-1\",\n \"overrides\": [],\n \"value\": \"value-1\",\n },\n {\n \"name\": \"snap-config-2\",\n \"overrides\": [],\n \"value\": \"value-2\",\n },\n ]\n `);\n expect(snapshot.configs.map((c) => c.name).sort()).toEqual([\n \"snap-config-1\",\n \"snap-config-2\",\n ]);\n\n client.disconnect();\n });\n });\n\n // ==================== ERROR HANDLING TESTS ====================\n\n describe(\"Error Handling\", () => {\n it(\"should throw on invalid SDK key\", async () => {\n const invalidClient = new Replane({\n logger: silentLogger,\n });\n await expect(\n invalidClient.connect({\n sdkKey: \"invalid-key\",\n baseUrl: edgeApiBaseUrl,\n connectTimeoutMs: 2000,\n })\n ).rejects.toThrow();\n });\n\n it(\"should handle disconnected client gracefully\", async () => {\n await ctx.createConfig(\"close-test\", \"value\");\n\n const client = trackClient(\n await ctx.createClient<{ \"close-test\": string }>({\n defaults: { \"close-test\": \"default\" },\n })\n );\n\n expect(client.get(\"close-test\")).toBe(\"value\");\n\n client.disconnect();\n\n // After disconnect, client should still return cached value (doesn't throw)\n // This verifies the client handles disconnect gracefully\n const cachedValue = client.get(\"close-test\");\n expect(cachedValue).toBe(\"value\");\n });\n\n it(\"should timeout on unreachable server\", async () => {\n const unreachableClient = new Replane({\n logger: silentLogger,\n });\n await expect(\n unreachableClient.connect({\n sdkKey: \"rp_test\",\n baseUrl: \"http://localhost:59999\", // Non-existent port\n connectTimeoutMs: 1000,\n })\n ).rejects.toThrow();\n });\n });\n\n // ==================== CONFIG LIFECYCLE TESTS ====================\n\n describe(\"Config Lifecycle\", () => {\n it(\"should handle config creation after client connects\", async () => {\n // Create client first with no configs\n const client = trackClient(\n await ctx.createClient({ defaults: { \"late-config\": \"waiting\" } })\n );\n\n // Set up signal to wait for the config\n const configSignal = createSignal<string>();\n client.subscribe(\"late-config\", (config) => {\n configSignal.trigger(config.value as string);\n });\n\n // Create config after connection\n await ctx.createConfig(\"late-config\", \"late-value\");\n\n // Wait for the config to appear via subscription\n const value = await configSignal.wait({ timeout: defaultTimeout });\n expect(value).toBe(\"late-value\");\n\n expect(client.get(\"late-config\")).toBe(\"late-value\");\n\n client.disconnect();\n });\n\n it(\"should ignore config deletion on client\", async () => {\n await ctx.createConfig(\"delete-me\", \"exists\");\n\n const client = trackClient(await ctx.createClient<{ \"delete-me\": string }>({}));\n\n expect(client.get(\"delete-me\")).toBe(\"exists\");\n\n // Delete config\n await ctx.deleteConfig(\"delete-me\");\n\n await delay(1000);\n\n expect(client.get(\"delete-me\")).toBe(\"exists\");\n\n client.disconnect();\n });\n });\n\n // ==================== CONCURRENT CLIENTS TESTS ====================\n\n describe(\"Concurrent Clients\", () => {\n it(\"should handle multiple clients with same SDK key\", async () => {\n await ctx.createConfig(\"shared-config\", \"initial\");\n\n const client1 = trackClient(await ctx.createClient<{ \"shared-config\": string }>({}));\n const client2 = trackClient(await ctx.createClient<{ \"shared-config\": string }>({}));\n\n expect(client1.get(\"shared-config\")).toBe(\"initial\");\n expect(client2.get(\"shared-config\")).toBe(\"initial\");\n\n // Both should receive updates\n const signal1 = createSignal<string>();\n const signal2 = createSignal<string>();\n\n client1.subscribe(\"shared-config\", (c) => {\n if (c.value === \"updated\") signal1.trigger(c.value as string);\n });\n client2.subscribe(\"shared-config\", (c) => {\n if (c.value === \"updated\") signal2.trigger(c.value as string);\n });\n\n await ctx.updateConfig(\"shared-config\", \"updated\");\n\n const [v1, v2] = await Promise.all([\n signal1.wait({ timeout: defaultTimeout }),\n signal2.wait({ timeout: defaultTimeout }),\n ]);\n\n expect(v1).toBe(\"updated\");\n expect(v2).toBe(\"updated\");\n\n client1.disconnect();\n client2.disconnect();\n });\n\n it(\"should isolate context between clients\", async () => {\n await ctx.createConfig(\"context-config\", \"default\", {\n overrides: [\n {\n name: \"prod-override\",\n conditions: [{ operator: \"equals\", property: \"env\", value: literal(\"prod\") }],\n value: \"prod-value\",\n },\n ],\n });\n\n const client1 = trackClient(\n await ctx.createClient<{ \"context-config\": string }>({\n context: { env: \"prod\" },\n })\n );\n const client2 = trackClient(\n await ctx.createClient<{ \"context-config\": string }>({\n context: { env: \"dev\" },\n })\n );\n\n expect(client1.get(\"context-config\")).toBe(\"prod-value\");\n expect(client2.get(\"context-config\")).toBe(\"default\");\n\n client1.disconnect();\n client2.disconnect();\n });\n });\n },\n { repeats: 8 } // several repeats to ensure that the replica implementation is stable\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["resolve!: (value: T) => void","reject!: (reason?: unknown) => void","condition: () => boolean | Promise<boolean>","options: WaitForOptions","value: T | undefined","v: T","values: T[]","listeners: Array<() => void>","value: T","count: number","predicate: (value: T) => boolean","ms: number","request: {\n edgeApiBaseUrl: string;\n sdkKey: string;\n}","value: T","pathString: string","parts: (string | number)[]","projectId: string","configName: string","path: string","admin: ReplaneAdmin","workspaceId: string","environmentId: string","sdkKey: string","options: TestSuiteOptions","clientOptions?: {\n context?: ReplaneContext;\n defaults?: Partial<T>;\n }","name: string","value: ConfigValue","configOptions?: {\n description?: string;\n overrides?: Override[];\n }","ctx: TestContext","activeClients: Replane<Record<string, unknown>>[]","client: Replane<T>"],"sources":["../src/utils.ts","../src/test-suite.ts"],"sourcesContent":["/**\n * Test utilities for waiting with early resolution\n */\n\nexport interface Deferred<T> {\n promise: Promise<T>;\n resolve: (value: T) => void;\n reject: (reason?: unknown) => void;\n}\n\n/**\n * Creates a deferred promise that can be resolved/rejected externally\n */\nexport function createDeferred<T>(): Deferred<T> {\n let resolve!: (value: T) => void;\n let reject!: (reason?: unknown) => void;\n\n const promise = new Promise<T>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n return { promise, resolve, reject };\n}\n\n/**\n * Options for waitFor utility\n */\nexport interface WaitForOptions {\n /** Timeout in milliseconds (default: 5000) */\n timeout?: number;\n /** Custom error message on timeout */\n timeoutMessage?: string;\n}\n\n/**\n * Waits for a condition to be met or times out.\n * Returns immediately when condition is satisfied.\n *\n * @example\n * ```ts\n * let value: string | null = null;\n * client.subscribe(\"config\", (v) => { value = v; });\n *\n * await waitFor(() => value !== null, { timeout: 2000 });\n * expect(value).toBe(\"expected\");\n * ```\n */\nexport async function waitFor(\n condition: () => boolean | Promise<boolean>,\n options: WaitForOptions = {}\n): Promise<void> {\n const { timeout = 5000, timeoutMessage = \"waitFor timed out\" } = options;\n\n const startTime = Date.now();\n\n while (true) {\n const result = await condition();\n if (result) return;\n\n if (Date.now() - startTime >= timeout) {\n throw new Error(timeoutMessage);\n }\n\n // Yield to event loop, check again soon\n await new Promise((resolve) => setTimeout(resolve, 10));\n }\n}\n\n/**\n * A signal that can be awaited and triggered.\n * Useful for waiting for async events in tests.\n *\n * @example\n * ```ts\n * const signal = createSignal<string>();\n *\n * client.subscribe(\"config\", (value) => {\n * signal.trigger(value);\n * });\n *\n * const value = await signal.wait({ timeout: 2000 });\n * expect(value).toBe(\"expected\");\n * ```\n */\nexport interface Signal<T> {\n /** Wait for the signal to be triggered */\n wait(options?: WaitForOptions): Promise<T>;\n /** Trigger the signal with a value */\n trigger(value: T): void;\n /** Check if signal has been triggered */\n isTriggered(): boolean;\n /** Reset the signal to untriggered state */\n reset(): void;\n /** Get the triggered value (undefined if not triggered) */\n getValue(): T | undefined;\n}\n\nexport function createSignal<T = void>(): Signal<T> {\n let deferred = createDeferred<T>();\n let triggered = false;\n let value: T | undefined;\n\n return {\n wait(options: WaitForOptions = {}) {\n const { timeout = 5000, timeoutMessage = \"Signal wait timed out\" } = options;\n\n return Promise.race([\n deferred.promise,\n new Promise<T>((_, reject) => setTimeout(() => reject(new Error(timeoutMessage)), timeout)),\n ]);\n },\n\n trigger(v: T) {\n if (!triggered) {\n triggered = true;\n value = v;\n deferred.resolve(v);\n }\n },\n\n isTriggered() {\n return triggered;\n },\n\n reset() {\n triggered = false;\n value = undefined;\n deferred = createDeferred<T>();\n },\n\n getValue() {\n return value;\n },\n };\n}\n\n/**\n * A collector that accumulates values and can wait for a specific count.\n * Useful for collecting multiple updates in tests.\n *\n * @example\n * ```ts\n * const collector = createCollector<string>();\n *\n * client.subscribe(\"config\", (value) => {\n * collector.push(value);\n * });\n *\n * // Wait for 3 updates\n * const values = await collector.waitForCount(3, { timeout: 5000 });\n * expect(values).toEqual([\"v1\", \"v2\", \"v3\"]);\n * ```\n */\nexport interface Collector<T> {\n /** Push a value to the collector */\n push(value: T): void;\n /** Get all collected values */\n getValues(): T[];\n /** Get the count of collected values */\n count(): number;\n /** Wait for at least N values to be collected */\n waitForCount(count: number, options?: WaitForOptions): Promise<T[]>;\n /** Wait for a value matching the predicate */\n waitFor(predicate: (value: T) => boolean, options?: WaitForOptions): Promise<T>;\n /** Clear all collected values */\n clear(): void;\n}\n\nexport function createCollector<T>(): Collector<T> {\n const values: T[] = [];\n const listeners: Array<() => void> = [];\n\n const notify = () => {\n for (const listener of listeners) {\n listener();\n }\n };\n\n return {\n push(value: T) {\n values.push(value);\n notify();\n },\n\n getValues() {\n return [...values];\n },\n\n count() {\n return values.length;\n },\n\n async waitForCount(count: number, options: WaitForOptions = {}) {\n const { timeout = 5000, timeoutMessage = `Collector timed out waiting for ${count} values` } =\n options;\n\n if (values.length >= count) {\n return [...values];\n }\n\n return new Promise<T[]>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n const idx = listeners.indexOf(listener);\n if (idx !== -1) listeners.splice(idx, 1);\n reject(new Error(timeoutMessage));\n }, timeout);\n\n const listener = () => {\n if (values.length >= count) {\n clearTimeout(timeoutId);\n const idx = listeners.indexOf(listener);\n if (idx !== -1) listeners.splice(idx, 1);\n resolve([...values]);\n }\n };\n\n listeners.push(listener);\n });\n },\n\n async waitFor(predicate: (value: T) => boolean, options: WaitForOptions = {}) {\n const { timeout = 5000, timeoutMessage = \"Collector timed out waiting for matching value\" } =\n options;\n\n // Check existing values first\n for (const v of values) {\n if (predicate(v)) return v;\n }\n\n return new Promise<T>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n const idx = listeners.indexOf(listener);\n if (idx !== -1) listeners.splice(idx, 1);\n reject(new Error(timeoutMessage));\n }, timeout);\n\n const listener = () => {\n // Check the latest value\n const latest = values[values.length - 1];\n if (latest !== undefined && predicate(latest)) {\n clearTimeout(timeoutId);\n const idx = listeners.indexOf(listener);\n if (idx !== -1) listeners.splice(idx, 1);\n resolve(latest);\n }\n };\n\n listeners.push(listener);\n });\n },\n\n clear() {\n values.length = 0;\n },\n };\n}\n\n/**\n * Delay for a specified time (use sparingly in tests)\n */\nexport function delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Generate a unique test identifier\n */\nexport function uniqueId(prefix = \"test\"): string {\n return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n}\n\n/**\n * Sync the edge replica with the database.\n * Requires TESTING_MODE=true on the server.\n *\n * This is useful in tests to ensure the edge replica has\n * received all config changes before making assertions.\n *\n * @param request - The request object containing the edge API base URL and admin API key\n * @param request.edgeApiBaseUrl - The base URL of the edge API (e.g., \"http://localhost:8080\")\n * @param request.adminApiKey - The admin API key\n */\nexport async function syncReplica(request: {\n edgeApiBaseUrl: string;\n sdkKey: string;\n}): Promise<void> {\n const { edgeApiBaseUrl, sdkKey } = request;\n\n const response = await fetch(`${edgeApiBaseUrl}/api/sdk/v1/testing/sync`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${sdkKey}`,\n },\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to sync replica: ${response.status} ${body}`);\n }\n}\n","/**\n * Replane E2E Test Suite\n *\n * Comprehensive test suite for testing @replanejs/sdk with real @replanejs/admin API\n *\n * NOTE: This test suite is designed for multi-server setups where admin API and edge API\n * may be on different servers. Config changes are not immediate and require waiting for\n * replication to complete.\n */\n\nimport { describe, it, beforeAll, afterAll, afterEach, expect } from \"vitest\";\nimport { ReplaneAdmin, type ConfigValue, type Override } from \"@replanejs/admin\";\nimport { Replane, ReplaneError, ReplaneErrorCode } from \"@replanejs/sdk\";\nimport type { ReplaneContext } from \"@replanejs/sdk\";\nimport type { TestSuiteOptions, TestContext } from \"./types\";\nimport { createSignal, createCollector, delay, uniqueId, syncReplica } from \"./utils\";\n\n/**\n * Helper to create a literal value for conditions.\n */\nfunction literal<T>(value: T) {\n return { type: \"literal\" as const, value };\n}\n\n/**\n * Parse a JSON path string into a path array.\n * Examples:\n * - \"\" -> []\n * - \"foo.bar\" -> [\"foo\", \"bar\"]\n * - \"foo[0]\" -> [\"foo\", 0]\n * - \"foo.bar[1].baz\" -> [\"foo\", \"bar\", 1, \"baz\"]\n */\nfunction parseJsonPath(pathString: string): (string | number)[] {\n if (!pathString) return [];\n\n const parts: (string | number)[] = [];\n let current = \"\";\n let inBracket = false;\n\n for (let i = 0; i < pathString.length; i++) {\n const char = pathString[i];\n\n if (char === \"[\") {\n if (current) {\n parts.push(current);\n current = \"\";\n }\n inBracket = true;\n } else if (char === \"]\") {\n if (inBracket && current) {\n const num = parseInt(current, 10);\n parts.push(isNaN(num) ? current : num);\n current = \"\";\n }\n inBracket = false;\n } else if (char === \".\" && !inBracket) {\n if (current) {\n parts.push(current);\n current = \"\";\n }\n } else {\n current += char;\n }\n }\n\n if (current) {\n parts.push(current);\n }\n\n return parts;\n}\n\n/**\n * Helper to create a reference value for conditions.\n * @param projectId - The project ID where the referenced config lives\n * @param configName - The name of the config to reference\n * @param path - JSON path string to extract from the config value (empty string for entire value)\n */\nfunction reference(projectId: string, configName: string, path: string = \"\") {\n return { type: \"reference\" as const, projectId, configName, path: parseJsonPath(path) };\n}\n\nconst silentLogger = {\n debug: () => {},\n info: () => {},\n warn: () => {},\n error: () => {},\n};\n\n/**\n * Creates a test context with helper methods\n */\nfunction createTestContext(\n admin: ReplaneAdmin,\n workspaceId: string,\n projectId: string,\n environmentId: string,\n sdkKey: string,\n options: TestSuiteOptions\n): TestContext {\n const defaultTimeout = options.defaultTimeout ?? 10000;\n\n function sync(): Promise<void> {\n return syncReplica({ edgeApiBaseUrl: options.edgeApiBaseUrl, sdkKey });\n }\n\n return {\n admin,\n workspaceId,\n projectId,\n environmentId,\n sdkKey,\n edgeApiBaseUrl: options.edgeApiBaseUrl,\n adminApiBaseUrl: options.adminApiBaseUrl,\n defaultTimeout,\n\n sync,\n\n async createClient<T extends object = Record<string, unknown>>(clientOptions?: {\n context?: ReplaneContext;\n defaults?: Partial<T>;\n }): Promise<Replane<T>> {\n await sync();\n\n const client = new Replane<T>({\n logger: options.debug ? console : silentLogger,\n context: clientOptions?.context,\n defaults: clientOptions?.defaults as T | undefined,\n });\n\n await client.connect({\n sdkKey,\n baseUrl: options.edgeApiBaseUrl,\n connectTimeoutMs: defaultTimeout,\n });\n\n return client;\n },\n\n async createConfig(\n name: string,\n value: ConfigValue,\n configOptions?: {\n description?: string;\n overrides?: Override[];\n }\n ): Promise<void> {\n await admin.configs.create({\n projectId,\n name,\n description: configOptions?.description ?? \"\",\n editors: [],\n base: {\n value,\n schema: null,\n overrides: configOptions?.overrides ?? [],\n },\n variants: [],\n });\n await sync();\n },\n\n async updateConfig(\n name: string,\n value: ConfigValue,\n configOptions?: {\n description?: string;\n overrides?: Override[];\n }\n ): Promise<void> {\n await admin.configs.update({\n projectId,\n configName: name,\n description: configOptions?.description ?? \"\",\n editors: [],\n base: {\n value,\n schema: null,\n overrides: configOptions?.overrides ?? [],\n },\n variants: [],\n });\n await sync();\n },\n\n async deleteConfig(name: string): Promise<void> {\n await admin.configs.delete({ projectId, configName: name });\n await sync();\n },\n };\n}\n\n/**\n * Main test suite function\n *\n * @example\n * ```ts\n * import { testSuite } from \"@replanejs/test-suite\";\n *\n * testSuite({\n * superadminKey: process.env.SUPERADMIN_KEY!,\n * adminApiBaseUrl: \"http://localhost:8080\",\n * edgeApiBaseUrl: \"http://localhost:8080\",\n * });\n * ```\n */\nexport function testSuite(options: TestSuiteOptions): void {\n const { superadminKey, adminApiBaseUrl, edgeApiBaseUrl } = options;\n const defaultTimeout = options.defaultTimeout ?? 10000;\n\n describe(\n \"Replane E2E Test Suite\",\n { repeats: 8 }, // several repeats to ensure that the replica implementation is stable\n () => {\n let admin: ReplaneAdmin;\n let workspaceId: string;\n let projectId: string;\n let environmentId: string;\n let sdkKey: string;\n let ctx: TestContext;\n\n // Track clients for cleanup\n const activeClients: Replane<Record<string, unknown>>[] = [];\n\n beforeAll(async () => {\n // Create admin client with superadmin key\n admin = new ReplaneAdmin({\n apiKey: superadminKey,\n baseUrl: adminApiBaseUrl,\n });\n\n // Create a unique workspace for this test run\n const workspaceName = uniqueId(\"e2e-workspace\");\n const workspaceRes = await admin.workspaces.create({ name: workspaceName });\n workspaceId = workspaceRes.id;\n\n // Create a project in the workspace\n const projectName = uniqueId(\"e2e-project\");\n const projectRes = await admin.projects.create({\n workspaceId,\n name: projectName,\n description: \"E2E test project\",\n });\n projectId = projectRes.id;\n\n // Get environments (use production)\n const envRes = await admin.environments.list({ projectId });\n const prodEnv =\n envRes.environments.find((e) => e.name === \"Production\") ?? envRes.environments[0];\n if (!prodEnv) {\n throw new Error(\"No environments found\");\n }\n environmentId = prodEnv.id;\n\n // Create SDK key\n const sdkKeyRes = await admin.sdkKeys.create({\n projectId,\n name: uniqueId(\"e2e-sdk-key\"),\n environmentId,\n });\n sdkKey = sdkKeyRes.key;\n\n await syncReplica({ edgeApiBaseUrl, sdkKey });\n\n // Create test context\n ctx = createTestContext(admin, workspaceId, projectId, environmentId, sdkKey, options);\n });\n\n afterAll(async () => {\n // Disconnect all active clients\n for (const client of activeClients) {\n try {\n client.disconnect();\n } catch {\n // Ignore errors during cleanup\n }\n }\n activeClients.length = 0;\n\n // Clean up: delete workspace (cascades to project, configs, keys)\n if (workspaceId) {\n await admin.workspaces.delete({ workspaceId });\n }\n });\n\n // Helper to track clients for cleanup\n const trackClient = <T extends object>(client: Replane<T>): Replane<T> => {\n activeClients.push(client as Replane<Record<string, unknown>>);\n return client;\n };\n\n afterEach(async () => {\n const configs = await admin.configs.list({ projectId });\n for (const config of configs.configs) {\n await admin.configs.delete({ projectId, configName: config.name });\n }\n await syncReplica({ edgeApiBaseUrl, sdkKey });\n });\n\n // ==================== CONNECTION TESTS ====================\n\n describe(\"SDK Connection\", () => {\n it(\"should connect and receive initial configs\", async () => {\n // Create config via admin API\n await ctx.createConfig(\"test-config\", \"initial-value\");\n\n // Create client - sync ensures configs are available\n const client = trackClient(await ctx.createClient<{ \"test-config\": string }>({}));\n\n const value = client.get(\"test-config\");\n expect(value).toBe(\"initial-value\");\n\n client.disconnect();\n });\n\n it(\"should handle empty project (no configs)\", async () => {\n // Create a new project with no configs\n const emptyProjectRes = await admin.projects.create({\n workspaceId,\n name: uniqueId(\"empty-project\"),\n description: \"Empty project for testing\",\n });\n const emptyEnvRes = await admin.environments.list({ projectId: emptyProjectRes.id });\n const emptyEnv = emptyEnvRes.environments[0];\n const emptySdkKeyRes = await admin.sdkKeys.create({\n projectId: emptyProjectRes.id,\n name: uniqueId(\"empty-sdk-key\"),\n environmentId: emptyEnv.id,\n });\n\n // Wait for SDK key to sync\n await syncReplica({ edgeApiBaseUrl, sdkKey });\n\n const emptyClient = new Replane({\n logger: silentLogger,\n });\n await emptyClient.connect({\n sdkKey: emptySdkKeyRes.key,\n baseUrl: edgeApiBaseUrl,\n connectTimeoutMs: defaultTimeout,\n });\n const client = trackClient(emptyClient);\n\n // Should not throw, but get() with no default should throw\n expect(() => client.get(\"nonexistent\")).toThrow();\n\n client.disconnect();\n\n // Cleanup\n await admin.projects.delete({ projectId: emptyProjectRes.id });\n });\n\n it(\"should use default values when config not found\", async () => {\n const client = trackClient(\n await ctx.createClient({\n defaults: { \"missing-config\": \"default-value\" },\n })\n );\n\n const value = client.get(\"missing-config\");\n expect(value).toBe(\"default-value\");\n\n client.disconnect();\n });\n });\n\n // ==================== GET CONFIG TESTS ====================\n\n describe(\"Get Config\", () => {\n it(\"should get string config\", async () => {\n await ctx.createConfig(\"string-config\", \"hello\");\n\n const client = trackClient(await ctx.createClient<{ \"string-config\": string }>({}));\n\n expect(client.get(\"string-config\")).toBe(\"hello\");\n client.disconnect();\n });\n\n it(\"should get number config\", async () => {\n await ctx.createConfig(\"number-config\", 42);\n\n const client = trackClient(await ctx.createClient<{ \"number-config\": number }>({}));\n\n expect(client.get(\"number-config\")).toBe(42);\n client.disconnect();\n });\n\n it(\"should get boolean config\", async () => {\n await ctx.createConfig(\"boolean-config\", true);\n\n const client = trackClient(await ctx.createClient<{ \"boolean-config\": boolean }>({}));\n\n expect(client.get(\"boolean-config\")).toBe(true);\n client.disconnect();\n });\n\n it(\"should get object config\", async () => {\n const objValue = { nested: { value: \"deep\" } };\n await ctx.createConfig(\"object-config\", objValue);\n\n const client = trackClient(\n await ctx.createClient<{ \"object-config\": typeof objValue }>({})\n );\n\n expect(client.get(\"object-config\")).toEqual(objValue);\n client.disconnect();\n });\n\n it(\"should get array config\", async () => {\n const arrValue = [1, 2, 3];\n await ctx.createConfig(\"array-config\", arrValue);\n\n const client = trackClient(await ctx.createClient<{ \"array-config\": number[] }>({}));\n\n expect(client.get(\"array-config\")).toEqual(arrValue);\n client.disconnect();\n });\n\n it(\"should get null config\", async () => {\n await ctx.createConfig(\"null-config\", null);\n\n const client = trackClient(await ctx.createClient<{ \"null-config\": null }>({}));\n\n expect(client.get(\"null-config\")).toBe(null);\n client.disconnect();\n });\n\n it(\"should return default value when config not found\", async () => {\n const client = trackClient(await ctx.createClient());\n const value = client.get(\"nonexistent\", { default: \"fallback\" });\n expect(value).toBe(\"fallback\");\n client.disconnect();\n });\n\n it(\"should throw ReplaneError when config not found and no default\", async () => {\n const client = trackClient(await ctx.createClient());\n try {\n client.get(\"nonexistent\");\n expect.fail(\"Should have thrown\");\n } catch (error) {\n expect(error).toBeInstanceOf(ReplaneError);\n expect((error as ReplaneError).code).toBe(ReplaneErrorCode.NotFound);\n }\n client.disconnect();\n });\n });\n\n // ==================== REAL-TIME UPDATES TESTS ====================\n\n describe(\"Real-time Updates\", () => {\n it(\"should receive config updates via subscription\", async () => {\n await ctx.createConfig(\"live-config\", \"initial\");\n\n const client = trackClient(await ctx.createClient<{ \"live-config\": string }>({}));\n\n expect(client.get(\"live-config\")).toBe(\"initial\");\n\n // Set up signal for next update\n const updateSignal = createSignal<string>();\n client.subscribe(\"live-config\", (config) => {\n if (config.value !== \"initial\") {\n updateSignal.trigger(config.value as string);\n }\n });\n\n // Update config\n await ctx.updateConfig(\"live-config\", \"updated\");\n\n // Wait for update (with timeout)\n const newValue = await updateSignal.wait({ timeout: defaultTimeout });\n expect(newValue).toBe(\"updated\");\n\n // Verify get() returns new value\n expect(client.get(\"live-config\")).toBe(\"updated\");\n\n client.disconnect();\n });\n\n it(\"should receive multiple updates in order\", async () => {\n await ctx.createConfig(\"multi-update-config\", 0);\n\n const client = trackClient(await ctx.createClient<{ \"multi-update-config\": number }>({}));\n\n expect(client.get(\"multi-update-config\")).toBe(0);\n\n const collector = createCollector<number>();\n client.subscribe(\"multi-update-config\", (config) => {\n const val = config.value as number;\n if (val > 0) {\n collector.push(val);\n }\n });\n\n // Send multiple updates with small delays to ensure ordering\n await ctx.updateConfig(\"multi-update-config\", 1);\n await delay(100);\n await ctx.updateConfig(\"multi-update-config\", 2);\n await delay(100);\n await ctx.updateConfig(\"multi-update-config\", 3);\n\n // Wait for all updates\n const values = await collector.waitForCount(3, { timeout: defaultTimeout });\n expect(values).toEqual([1, 2, 3]);\n\n client.disconnect();\n });\n\n it(\"should handle rapid updates\", async () => {\n await ctx.createConfig(\"rapid-config\", 0);\n\n const client = trackClient(await ctx.createClient<{ \"rapid-config\": number }>({}));\n\n expect(client.get(\"rapid-config\")).toBe(0);\n\n const collector = createCollector<number>();\n client.subscribe(\"rapid-config\", (config) => {\n collector.push(config.value as number);\n });\n\n // Send rapid updates\n const updateCount = 10;\n for (let i = 1; i <= updateCount; i++) {\n await ctx.updateConfig(\"rapid-config\", i);\n }\n\n // Wait for final value (may not get all intermediate values due to batching)\n await collector.waitFor((v) => v === updateCount, { timeout: defaultTimeout });\n\n // Final value should be correct\n expect(client.get(\"rapid-config\")).toBe(updateCount);\n\n client.disconnect();\n });\n\n it(\"should allow unsubscribing\", async () => {\n await ctx.createConfig(\"unsub-config\", \"initial\");\n\n const client = trackClient(await ctx.createClient<{ \"unsub-config\": string }>({}));\n\n expect(client.get(\"unsub-config\")).toBe(\"initial\");\n\n const collector = createCollector<string>();\n const unsubscribe = client.subscribe(\"unsub-config\", (config) => {\n if (config.value !== \"initial\") {\n collector.push(config.value as string);\n }\n });\n\n // First update should be received\n await ctx.updateConfig(\"unsub-config\", \"update-1\");\n await collector.waitForCount(1, { timeout: defaultTimeout });\n\n // Unsubscribe\n unsubscribe();\n\n // Second update should NOT be received\n await ctx.updateConfig(\"unsub-config\", \"update-2\");\n await delay(2000); // Give time for potential update to propagate\n\n // Should only have 1 update\n expect(collector.count()).toBe(1);\n\n client.disconnect();\n });\n });\n\n // ==================== OVERRIDE TESTS ====================\n\n describe(\"Override Evaluation\", () => {\n it(\"should evaluate equals condition\", async () => {\n await ctx.createConfig(\"env-config\", \"default\", {\n overrides: [\n {\n name: \"prod-override\",\n conditions: [{ operator: \"equals\", property: \"env\", value: literal(\"production\") }],\n value: \"production-value\",\n },\n ],\n });\n\n // Without context - should get default\n const client1 = trackClient(await ctx.createClient<{ \"env-config\": string }>({}));\n expect(client1.get(\"env-config\")).toBe(\"default\");\n client1.disconnect();\n\n // With matching context\n const client2 = trackClient(\n await ctx.createClient<{ \"env-config\": string }>({\n context: { env: \"production\" },\n })\n );\n expect(client2.get(\"env-config\")).toBe(\"production-value\");\n client2.disconnect();\n\n // With non-matching context\n const client3 = trackClient(\n await ctx.createClient<{ \"env-config\": string }>({\n context: { env: \"staging\" },\n })\n );\n expect(client3.get(\"env-config\")).toBe(\"default\");\n client3.disconnect();\n });\n\n it(\"should evaluate in condition\", async () => {\n await ctx.createConfig(\"region-config\", \"default\", {\n overrides: [\n {\n name: \"western-override\",\n conditions: [{ operator: \"in\", property: \"region\", value: literal([\"us\", \"eu\"]) }],\n value: \"western\",\n },\n ],\n });\n\n const client1 = trackClient(\n await ctx.createClient<{ \"region-config\": string }>({\n context: { region: \"us\" },\n })\n );\n expect(client1.get(\"region-config\")).toBe(\"western\");\n client1.disconnect();\n\n const client2 = trackClient(\n await ctx.createClient<{ \"region-config\": string }>({\n context: { region: \"eu\" },\n })\n );\n expect(client2.get(\"region-config\")).toBe(\"western\");\n client2.disconnect();\n\n const client3 = trackClient(\n await ctx.createClient<{ \"region-config\": string }>({\n context: { region: \"asia\" },\n })\n );\n expect(client3.get(\"region-config\")).toBe(\"default\");\n client3.disconnect();\n });\n\n it(\"should evaluate not_in condition\", async () => {\n await ctx.createConfig(\"allow-config\", \"allowed\", {\n overrides: [\n {\n name: \"not-blocked-override\",\n conditions: [\n {\n operator: \"not_in\",\n property: \"country\",\n value: literal([\"blocked1\", \"blocked2\"]),\n },\n ],\n value: \"not-blocked\",\n },\n ],\n });\n\n const client1 = trackClient(\n await ctx.createClient<{ \"allow-config\": string }>({\n context: { country: \"blocked1\" },\n })\n );\n expect(client1.get(\"allow-config\")).toBe(\"allowed\");\n client1.disconnect();\n\n const client2 = trackClient(\n await ctx.createClient<{ \"allow-config\": string }>({\n context: { country: \"normal\" },\n })\n );\n expect(client2.get(\"allow-config\")).toBe(\"not-blocked\");\n client2.disconnect();\n });\n\n it(\"should evaluate numeric comparison conditions\", async () => {\n await ctx.createConfig(\"tier-config\", \"free\", {\n overrides: [\n {\n name: \"premium-override\",\n conditions: [\n { operator: \"greater_than_or_equal\", property: \"level\", value: literal(10) },\n ],\n value: \"premium\",\n },\n ],\n });\n\n const client1 = trackClient(\n await ctx.createClient<{ \"tier-config\": string }>({\n context: { level: 5 },\n })\n );\n expect(client1.get(\"tier-config\")).toBe(\"free\");\n client1.disconnect();\n\n const client2 = trackClient(\n await ctx.createClient<{ \"tier-config\": string }>({\n context: { level: 10 },\n })\n );\n expect(client2.get(\"tier-config\")).toBe(\"premium\");\n client2.disconnect();\n\n const client3 = trackClient(\n await ctx.createClient<{ \"tier-config\": string }>({\n context: { level: 15 },\n })\n );\n expect(client3.get(\"tier-config\")).toBe(\"premium\");\n client3.disconnect();\n });\n\n it(\"should evaluate and condition\", async () => {\n await ctx.createConfig(\"combo-config\", \"default\", {\n overrides: [\n {\n name: \"combo-override\",\n conditions: [\n {\n operator: \"and\",\n conditions: [\n { operator: \"equals\", property: \"plan\", value: literal(\"enterprise\") },\n { operator: \"equals\", property: \"verified\", value: literal(true) },\n ],\n },\n ],\n value: \"enterprise-verified\",\n },\n ],\n });\n\n // Both conditions must match\n const client1 = trackClient(\n await ctx.createClient<{ \"combo-config\": string }>({\n context: { plan: \"enterprise\", verified: true },\n })\n );\n expect(client1.get(\"combo-config\")).toBe(\"enterprise-verified\");\n client1.disconnect();\n\n // Only one matches\n const client2 = trackClient(\n await ctx.createClient<{ \"combo-config\": string }>({\n context: { plan: \"enterprise\", verified: false },\n })\n );\n expect(client2.get(\"combo-config\")).toBe(\"default\");\n client2.disconnect();\n });\n\n it(\"should evaluate or condition\", async () => {\n await ctx.createConfig(\"either-config\", \"default\", {\n overrides: [\n {\n name: \"privileged-override\",\n conditions: [\n {\n operator: \"or\",\n conditions: [\n { operator: \"equals\", property: \"role\", value: literal(\"admin\") },\n { operator: \"equals\", property: \"role\", value: literal(\"superadmin\") },\n ],\n },\n ],\n value: \"privileged\",\n },\n ],\n });\n\n const client1 = trackClient(\n await ctx.createClient<{ \"either-config\": string }>({\n context: { role: \"admin\" },\n })\n );\n expect(client1.get(\"either-config\")).toBe(\"privileged\");\n client1.disconnect();\n\n const client2 = trackClient(\n await ctx.createClient<{ \"either-config\": string }>({\n context: { role: \"superadmin\" },\n })\n );\n expect(client2.get(\"either-config\")).toBe(\"privileged\");\n client2.disconnect();\n\n const client3 = trackClient(\n await ctx.createClient<{ \"either-config\": string }>({\n context: { role: \"user\" },\n })\n );\n expect(client3.get(\"either-config\")).toBe(\"default\");\n client3.disconnect();\n });\n\n it(\"should allow per-request context override\", async () => {\n await ctx.createConfig(\"dynamic-config\", \"default\", {\n overrides: [\n {\n name: \"feature-override\",\n conditions: [\n { operator: \"equals\", property: \"feature\", value: literal(\"enabled\") },\n ],\n value: \"feature-on\",\n },\n ],\n });\n\n const client = trackClient(await ctx.createClient<{ \"dynamic-config\": string }>({}));\n\n // Without context\n expect(client.get(\"dynamic-config\")).toBe(\"default\");\n\n // With per-request context\n expect(client.get(\"dynamic-config\", { context: { feature: \"enabled\" } })).toBe(\n \"feature-on\"\n );\n\n // Original still returns default\n expect(client.get(\"dynamic-config\")).toBe(\"default\");\n\n client.disconnect();\n });\n\n it(\"should apply first matching override\", async () => {\n await ctx.createConfig(\"priority-config\", \"default\", {\n overrides: [\n {\n name: \"gold-override\",\n conditions: [{ operator: \"equals\", property: \"tier\", value: literal(\"gold\") }],\n value: \"gold-value\",\n },\n {\n name: \"silver-override\",\n conditions: [{ operator: \"equals\", property: \"tier\", value: literal(\"silver\") }],\n value: \"silver-value\",\n },\n {\n name: \"score-override\",\n conditions: [{ operator: \"greater_than\", property: \"score\", value: literal(0) }],\n value: \"has-score\",\n },\n ],\n });\n\n // First override matches\n const client = trackClient(\n await ctx.createClient<{ \"priority-config\": string }>({\n context: { tier: \"gold\", score: 100 },\n })\n );\n expect(client.get(\"priority-config\")).toBe(\"gold-value\");\n client.disconnect();\n });\n });\n\n // ==================== REFERENCE TESTS ====================\n\n describe(\"Reference Evaluation\", () => {\n it(\"should evaluate reference with empty path (entire config value)\", async () => {\n // Create source config that will be referenced\n await ctx.createConfig(\"ref-source-simple\", \"allowed-region\");\n\n // Create config with override that references the source config\n await ctx.createConfig(\"ref-target-simple\", \"default\", {\n overrides: [\n {\n name: \"ref-override\",\n conditions: [\n {\n operator: \"equals\",\n property: \"region\",\n value: reference(projectId, \"ref-source-simple\", \"\"),\n },\n ],\n value: \"matched-by-reference\",\n },\n ],\n });\n\n // Context matches the referenced value\n const client1 = trackClient(\n await ctx.createClient<{ \"ref-target-simple\": string }>({\n context: { region: \"allowed-region\" },\n })\n );\n expect(client1.get(\"ref-target-simple\")).toBe(\"matched-by-reference\");\n client1.disconnect();\n\n // Context doesn't match\n const client2 = trackClient(\n await ctx.createClient<{ \"ref-target-simple\": string }>({\n context: { region: \"other-region\" },\n })\n );\n expect(client2.get(\"ref-target-simple\")).toBe(\"default\");\n client2.disconnect();\n });\n\n it(\"should evaluate reference with JSON path to nested object property\", async () => {\n // Create source config with nested object\n await ctx.createConfig(\"ref-source-object\", {\n settings: {\n threshold: 100,\n enabled: true,\n },\n });\n\n // Create config that references a nested property\n await ctx.createConfig(\"ref-target-object\", \"below-threshold\", {\n overrides: [\n {\n name: \"threshold-override\",\n conditions: [\n {\n operator: \"greater_than_or_equal\",\n property: \"score\",\n value: reference(projectId, \"ref-source-object\", \"settings.threshold\"),\n },\n ],\n value: \"above-threshold\",\n },\n ],\n });\n\n // Score meets threshold from reference\n const client1 = trackClient(\n await ctx.createClient<{ \"ref-target-object\": string }>({\n context: { score: 150 },\n })\n );\n expect(client1.get(\"ref-target-object\")).toBe(\"above-threshold\");\n client1.disconnect();\n\n // Score exactly at threshold\n const client2 = trackClient(\n await ctx.createClient<{ \"ref-target-object\": string }>({\n context: { score: 100 },\n })\n );\n expect(client2.get(\"ref-target-object\")).toBe(\"above-threshold\");\n client2.disconnect();\n\n // Score below threshold\n const client3 = trackClient(\n await ctx.createClient<{ \"ref-target-object\": string }>({\n context: { score: 50 },\n })\n );\n expect(client3.get(\"ref-target-object\")).toBe(\"below-threshold\");\n client3.disconnect();\n });\n\n it(\"should evaluate reference with JSON path to array element\", async () => {\n // Create source config with array\n await ctx.createConfig(\"ref-source-array\", {\n tiers: [\"free\", \"basic\", \"premium\", \"enterprise\"],\n });\n\n // Create config that references an array element\n await ctx.createConfig(\"ref-target-array\", \"no-match\", {\n overrides: [\n {\n name: \"premium-override\",\n conditions: [\n {\n operator: \"equals\",\n property: \"tier\",\n value: reference(projectId, \"ref-source-array\", \"tiers[2]\"),\n },\n ],\n value: \"premium-match\",\n },\n ],\n });\n\n // Context matches the third element (\"premium\")\n const client1 = trackClient(\n await ctx.createClient<{ \"ref-target-array\": string }>({\n context: { tier: \"premium\" },\n })\n );\n expect(client1.get(\"ref-target-array\")).toBe(\"premium-match\");\n client1.disconnect();\n\n // Context matches a different tier\n const client2 = trackClient(\n await ctx.createClient<{ \"ref-target-array\": string }>({\n context: { tier: \"basic\" },\n })\n );\n expect(client2.get(\"ref-target-array\")).toBe(\"no-match\");\n client2.disconnect();\n });\n\n it(\"should evaluate reference with JSON path to entire array for 'in' condition\", async () => {\n // Create source config with allowed regions array\n await ctx.createConfig(\"ref-source-regions\", {\n allowedRegions: [\"us-east\", \"us-west\", \"eu-west\"],\n });\n\n // Create config that uses the array in an 'in' condition\n await ctx.createConfig(\"ref-target-regions\", \"blocked\", {\n overrides: [\n {\n name: \"allowed-regions-override\",\n conditions: [\n {\n operator: \"in\",\n property: \"region\",\n value: reference(projectId, \"ref-source-regions\", \"allowedRegions\"),\n },\n ],\n value: \"allowed\",\n },\n ],\n });\n\n // Region is in allowed list\n const client1 = trackClient(\n await ctx.createClient<{ \"ref-target-regions\": string }>({\n context: { region: \"us-east\" },\n })\n );\n expect(client1.get(\"ref-target-regions\")).toBe(\"allowed\");\n client1.disconnect();\n\n const client2 = trackClient(\n await ctx.createClient<{ \"ref-target-regions\": string }>({\n context: { region: \"eu-west\" },\n })\n );\n expect(client2.get(\"ref-target-regions\")).toBe(\"allowed\");\n client2.disconnect();\n\n // Region is not in allowed list\n const client3 = trackClient(\n await ctx.createClient<{ \"ref-target-regions\": string }>({\n context: { region: \"ap-south\" },\n })\n );\n expect(client3.get(\"ref-target-regions\")).toBe(\"blocked\");\n client3.disconnect();\n });\n\n it(\"should handle reference to non-existent config (condition fails)\", async () => {\n // Create config that references a non-existent config\n await ctx.createConfig(\"ref-target-nonexistent\", \"default-fallback\", {\n overrides: [\n {\n name: \"nonexistent-ref-override\",\n conditions: [\n {\n operator: \"equals\",\n property: \"value\",\n value: reference(projectId, \"nonexistent-config-xyz\", \"\"),\n },\n ],\n value: \"should-not-match\",\n },\n ],\n });\n\n // The override should not match since reference is invalid\n const client = trackClient(\n await ctx.createClient<{ \"ref-target-nonexistent\": string }>({\n context: { value: \"anything\" },\n })\n );\n expect(client.get(\"ref-target-nonexistent\")).toBe(\"default-fallback\");\n client.disconnect();\n });\n\n it(\"should handle reference with invalid JSON path (condition fails)\", async () => {\n // Create source config\n await ctx.createConfig(\"ref-source-invalid-path\", { data: { value: 42 } });\n\n // Create config that references with invalid path\n await ctx.createConfig(\"ref-target-invalid-path\", \"default-value\", {\n overrides: [\n {\n name: \"invalid-path-override\",\n conditions: [\n {\n operator: \"equals\",\n property: \"test\",\n value: reference(projectId, \"ref-source-invalid-path\", \"data.nonexistent.deep\"),\n },\n ],\n value: \"should-not-match\",\n },\n ],\n });\n\n // The override should not match since path doesn't resolve\n const client = trackClient(\n await ctx.createClient<{ \"ref-target-invalid-path\": string }>({\n context: { test: \"anything\" },\n })\n );\n expect(client.get(\"ref-target-invalid-path\")).toBe(\"default-value\");\n client.disconnect();\n });\n\n it(\"should handle reference with out-of-bounds array index\", async () => {\n // Create source config with small array\n await ctx.createConfig(\"ref-source-oob\", { items: [\"a\", \"b\", \"c\"] });\n\n // Create config that references out-of-bounds index\n await ctx.createConfig(\"ref-target-oob\", \"default-value\", {\n overrides: [\n {\n name: \"oob-override\",\n conditions: [\n {\n operator: \"equals\",\n property: \"item\",\n value: reference(projectId, \"ref-source-oob\", \"items[99]\"),\n },\n ],\n value: \"matched\",\n },\n ],\n });\n\n // The override should not match\n const client = trackClient(\n await ctx.createClient<{ \"ref-target-oob\": string }>({\n context: { item: \"anything\" },\n })\n );\n expect(client.get(\"ref-target-oob\")).toBe(\"default-value\");\n client.disconnect();\n });\n\n it(\"should evaluate reference with deeply nested path\", async () => {\n // Create source config with deeply nested structure\n await ctx.createConfig(\"ref-source-deep\", {\n level1: {\n level2: {\n level3: {\n targetValue: \"deep-secret\",\n },\n },\n },\n });\n\n // Create config that references deep path\n await ctx.createConfig(\"ref-target-deep\", \"no-match\", {\n overrides: [\n {\n name: \"deep-override\",\n conditions: [\n {\n operator: \"equals\",\n property: \"secret\",\n value: reference(\n projectId,\n \"ref-source-deep\",\n \"level1.level2.level3.targetValue\"\n ),\n },\n ],\n value: \"deep-match\",\n },\n ],\n });\n\n // Context matches the deeply nested value\n const client1 = trackClient(\n await ctx.createClient<{ \"ref-target-deep\": string }>({\n context: { secret: \"deep-secret\" },\n })\n );\n expect(client1.get(\"ref-target-deep\")).toBe(\"deep-match\");\n client1.disconnect();\n\n // Context doesn't match\n const client2 = trackClient(\n await ctx.createClient<{ \"ref-target-deep\": string }>({\n context: { secret: \"wrong\" },\n })\n );\n expect(client2.get(\"ref-target-deep\")).toBe(\"no-match\");\n client2.disconnect();\n });\n\n it(\"should evaluate reference with array of objects and path\", async () => {\n // Create source config with array of objects\n await ctx.createConfig(\"ref-source-array-obj\", {\n users: [\n { id: 1, name: \"Alice\" },\n { id: 2, name: \"Bob\" },\n { id: 3, name: \"Charlie\" },\n ],\n });\n\n // Create config that references specific object property in array\n await ctx.createConfig(\"ref-target-array-obj\", \"unknown-user\", {\n overrides: [\n {\n name: \"bob-override\",\n conditions: [\n {\n operator: \"equals\",\n property: \"username\",\n value: reference(projectId, \"ref-source-array-obj\", \"users[1].name\"),\n },\n ],\n value: \"its-bob\",\n },\n ],\n });\n\n // Context matches Bob's name\n const client1 = trackClient(\n await ctx.createClient<{ \"ref-target-array-obj\": string }>({\n context: { username: \"Bob\" },\n })\n );\n expect(client1.get(\"ref-target-array-obj\")).toBe(\"its-bob\");\n client1.disconnect();\n\n // Context matches different user\n const client2 = trackClient(\n await ctx.createClient<{ \"ref-target-array-obj\": string }>({\n context: { username: \"Alice\" },\n })\n );\n expect(client2.get(\"ref-target-array-obj\")).toBe(\"unknown-user\");\n client2.disconnect();\n });\n\n it(\"should evaluate reference to boolean value\", async () => {\n // Create source config with boolean\n await ctx.createConfig(\"ref-source-bool\", { featureEnabled: true });\n\n // Create config that references boolean\n await ctx.createConfig(\"ref-target-bool\", \"feature-off\", {\n overrides: [\n {\n name: \"feature-override\",\n conditions: [\n {\n operator: \"equals\",\n property: \"enabled\",\n value: reference(projectId, \"ref-source-bool\", \"featureEnabled\"),\n },\n ],\n value: \"feature-on\",\n },\n ],\n });\n\n // Context matches boolean true\n const client1 = trackClient(\n await ctx.createClient<{ \"ref-target-bool\": string }>({\n context: { enabled: true },\n })\n );\n expect(client1.get(\"ref-target-bool\")).toBe(\"feature-on\");\n client1.disconnect();\n\n // Context doesn't match\n const client2 = trackClient(\n await ctx.createClient<{ \"ref-target-bool\": string }>({\n context: { enabled: false },\n })\n );\n expect(client2.get(\"ref-target-bool\")).toBe(\"feature-off\");\n client2.disconnect();\n });\n\n it(\"should handle multiple overrides with different references\", async () => {\n // Create multiple source configs\n await ctx.createConfig(\"ref-source-premium\", { tier: \"premium\" });\n await ctx.createConfig(\"ref-source-enterprise\", { tier: \"enterprise\" });\n\n // Create config with multiple reference overrides\n await ctx.createConfig(\"ref-target-multi\", \"free-tier\", {\n overrides: [\n {\n name: \"enterprise-override\",\n conditions: [\n {\n operator: \"equals\",\n property: \"plan\",\n value: reference(projectId, \"ref-source-enterprise\", \"tier\"),\n },\n ],\n value: \"enterprise-tier\",\n },\n {\n name: \"premium-override\",\n conditions: [\n {\n operator: \"equals\",\n property: \"plan\",\n value: reference(projectId, \"ref-source-premium\", \"tier\"),\n },\n ],\n value: \"premium-tier\",\n },\n ],\n });\n\n // Matches enterprise (first override)\n const client1 = trackClient(\n await ctx.createClient<{ \"ref-target-multi\": string }>({\n context: { plan: \"enterprise\" },\n })\n );\n expect(client1.get(\"ref-target-multi\")).toBe(\"enterprise-tier\");\n client1.disconnect();\n\n // Matches premium (second override)\n const client2 = trackClient(\n await ctx.createClient<{ \"ref-target-multi\": string }>({\n context: { plan: \"premium\" },\n })\n );\n expect(client2.get(\"ref-target-multi\")).toBe(\"premium-tier\");\n client2.disconnect();\n\n // No match\n const client3 = trackClient(\n await ctx.createClient<{ \"ref-target-multi\": string }>({\n context: { plan: \"basic\" },\n })\n );\n expect(client3.get(\"ref-target-multi\")).toBe(\"free-tier\");\n client3.disconnect();\n });\n\n it(\"should evaluate reference combined with literal conditions using AND\", async () => {\n // Create source config\n await ctx.createConfig(\"ref-source-combined\", { requiredLevel: 10 });\n\n // Create config with AND condition combining reference and literal\n await ctx.createConfig(\"ref-target-combined\", \"access-denied\", {\n overrides: [\n {\n name: \"combined-override\",\n conditions: [\n {\n operator: \"and\",\n conditions: [\n { operator: \"equals\", property: \"role\", value: literal(\"admin\") },\n {\n operator: \"greater_than_or_equal\",\n property: \"level\",\n value: reference(projectId, \"ref-source-combined\", \"requiredLevel\"),\n },\n ],\n },\n ],\n value: \"access-granted\",\n },\n ],\n });\n\n // Both conditions met\n const client1 = trackClient(\n await ctx.createClient<{ \"ref-target-combined\": string }>({\n context: { role: \"admin\", level: 15 },\n })\n );\n expect(client1.get(\"ref-target-combined\")).toBe(\"access-granted\");\n client1.disconnect();\n\n // Only role matches\n const client2 = trackClient(\n await ctx.createClient<{ \"ref-target-combined\": string }>({\n context: { role: \"admin\", level: 5 },\n })\n );\n expect(client2.get(\"ref-target-combined\")).toBe(\"access-denied\");\n client2.disconnect();\n\n // Only level matches\n const client3 = trackClient(\n await ctx.createClient<{ \"ref-target-combined\": string }>({\n context: { role: \"user\", level: 15 },\n })\n );\n expect(client3.get(\"ref-target-combined\")).toBe(\"access-denied\");\n client3.disconnect();\n });\n\n it(\"should handle reference to null value\", async () => {\n // Create source config with null\n await ctx.createConfig(\"ref-source-null\", null);\n\n // Create config that references null\n await ctx.createConfig(\"ref-target-null\", \"has-value\", {\n overrides: [\n {\n name: \"null-override\",\n conditions: [\n {\n operator: \"equals\",\n property: \"data\",\n value: reference(projectId, \"ref-source-null\", \"\"),\n },\n ],\n value: \"is-null\",\n },\n ],\n });\n\n // Context is null\n const client1 = trackClient(\n await ctx.createClient<{ \"ref-target-null\": string }>({\n context: { data: null },\n })\n );\n expect(client1.get(\"ref-target-null\")).toBe(\"is-null\");\n client1.disconnect();\n\n // Context is not null\n const client2 = trackClient(\n await ctx.createClient<{ \"ref-target-null\": string }>({\n context: { data: \"something\" },\n })\n );\n expect(client2.get(\"ref-target-null\")).toBe(\"has-value\");\n client2.disconnect();\n });\n\n it(\"should evaluate reference with not_in condition\", async () => {\n // Create source config with blocked countries\n await ctx.createConfig(\"ref-source-blocked\", {\n blockedCountries: [\"XX\", \"YY\", \"ZZ\"],\n });\n\n // Create config that uses not_in with reference\n await ctx.createConfig(\"ref-target-blocked\", \"blocked\", {\n overrides: [\n {\n name: \"allowed-override\",\n conditions: [\n {\n operator: \"not_in\",\n property: \"country\",\n value: reference(projectId, \"ref-source-blocked\", \"blockedCountries\"),\n },\n ],\n value: \"allowed\",\n },\n ],\n });\n\n // Country is not in blocked list\n const client1 = trackClient(\n await ctx.createClient<{ \"ref-target-blocked\": string }>({\n context: { country: \"US\" },\n })\n );\n expect(client1.get(\"ref-target-blocked\")).toBe(\"allowed\");\n client1.disconnect();\n\n // Country is in blocked list\n const client2 = trackClient(\n await ctx.createClient<{ \"ref-target-blocked\": string }>({\n context: { country: \"XX\" },\n })\n );\n expect(client2.get(\"ref-target-blocked\")).toBe(\"blocked\");\n client2.disconnect();\n });\n });\n\n // ==================== SNAPSHOT TESTS ====================\n\n describe(\"Snapshot\", () => {\n it(\"should create snapshot with current configs\", async () => {\n await ctx.createConfig(\"snap-config-1\", \"value-1\");\n await ctx.createConfig(\"snap-config-2\", \"value-2\");\n\n const client = trackClient(\n await ctx.createClient<{ \"snap-config-1\": string; \"snap-config-2\": string }>({})\n );\n\n const snapshot = client.getSnapshot();\n\n expect(snapshot.configs).toMatchInlineSnapshot(`\n [\n {\n \"name\": \"snap-config-1\",\n \"overrides\": [],\n \"value\": \"value-1\",\n },\n {\n \"name\": \"snap-config-2\",\n \"overrides\": [],\n \"value\": \"value-2\",\n },\n ]\n `);\n expect(snapshot.configs.map((c) => c.name).sort()).toEqual([\n \"snap-config-1\",\n \"snap-config-2\",\n ]);\n\n client.disconnect();\n });\n });\n\n // ==================== ERROR HANDLING TESTS ====================\n\n describe(\"Error Handling\", () => {\n it(\"should throw on invalid SDK key\", async () => {\n const invalidClient = new Replane({\n logger: silentLogger,\n });\n await expect(\n invalidClient.connect({\n sdkKey: \"invalid-key\",\n baseUrl: edgeApiBaseUrl,\n connectTimeoutMs: 2000,\n })\n ).rejects.toThrow();\n });\n\n it(\"should handle disconnected client gracefully\", async () => {\n await ctx.createConfig(\"close-test\", \"value\");\n\n const client = trackClient(\n await ctx.createClient<{ \"close-test\": string }>({\n defaults: { \"close-test\": \"default\" },\n })\n );\n\n expect(client.get(\"close-test\")).toBe(\"value\");\n\n client.disconnect();\n\n // After disconnect, client should still return cached value (doesn't throw)\n // This verifies the client handles disconnect gracefully\n const cachedValue = client.get(\"close-test\");\n expect(cachedValue).toBe(\"value\");\n });\n\n it(\"should timeout on unreachable server\", async () => {\n const unreachableClient = new Replane({\n logger: silentLogger,\n });\n await expect(\n unreachableClient.connect({\n sdkKey: \"rp_test\",\n baseUrl: \"http://localhost:59999\", // Non-existent port\n connectTimeoutMs: 1000,\n })\n ).rejects.toThrow();\n });\n });\n\n // ==================== CONFIG LIFECYCLE TESTS ====================\n\n describe(\"Config Lifecycle\", () => {\n it(\"should handle config creation after client connects\", async () => {\n // Create client first with no configs\n const client = trackClient(\n await ctx.createClient({ defaults: { \"late-config\": \"waiting\" } })\n );\n\n // Set up signal to wait for the config\n const configSignal = createSignal<string>();\n client.subscribe(\"late-config\", (config) => {\n configSignal.trigger(config.value as string);\n });\n\n // Create config after connection\n await ctx.createConfig(\"late-config\", \"late-value\");\n\n // Wait for the config to appear via subscription\n const value = await configSignal.wait({ timeout: defaultTimeout });\n expect(value).toBe(\"late-value\");\n\n expect(client.get(\"late-config\")).toBe(\"late-value\");\n\n client.disconnect();\n });\n\n it(\"should ignore config deletion on client\", async () => {\n await ctx.createConfig(\"delete-me\", \"exists\");\n\n const client = trackClient(await ctx.createClient<{ \"delete-me\": string }>({}));\n\n expect(client.get(\"delete-me\")).toBe(\"exists\");\n\n // Delete config\n await ctx.deleteConfig(\"delete-me\");\n\n await delay(1000);\n\n expect(client.get(\"delete-me\")).toBe(\"exists\");\n\n client.disconnect();\n });\n });\n\n // ==================== CONCURRENT CLIENTS TESTS ====================\n\n describe(\"Concurrent Clients\", () => {\n it(\"should handle multiple clients with same SDK key\", async () => {\n await ctx.createConfig(\"shared-config\", \"initial\");\n\n const client1 = trackClient(await ctx.createClient<{ \"shared-config\": string }>({}));\n const client2 = trackClient(await ctx.createClient<{ \"shared-config\": string }>({}));\n\n expect(client1.get(\"shared-config\")).toBe(\"initial\");\n expect(client2.get(\"shared-config\")).toBe(\"initial\");\n\n // Both should receive updates\n const signal1 = createSignal<string>();\n const signal2 = createSignal<string>();\n\n client1.subscribe(\"shared-config\", (c) => {\n if (c.value === \"updated\") signal1.trigger(c.value as string);\n });\n client2.subscribe(\"shared-config\", (c) => {\n if (c.value === \"updated\") signal2.trigger(c.value as string);\n });\n\n await ctx.updateConfig(\"shared-config\", \"updated\");\n\n const [v1, v2] = await Promise.all([\n signal1.wait({ timeout: defaultTimeout }),\n signal2.wait({ timeout: defaultTimeout }),\n ]);\n\n expect(v1).toBe(\"updated\");\n expect(v2).toBe(\"updated\");\n\n client1.disconnect();\n client2.disconnect();\n });\n\n it(\"should isolate context between clients\", async () => {\n await ctx.createConfig(\"context-config\", \"default\", {\n overrides: [\n {\n name: \"prod-override\",\n conditions: [{ operator: \"equals\", property: \"env\", value: literal(\"prod\") }],\n value: \"prod-value\",\n },\n ],\n });\n\n const client1 = trackClient(\n await ctx.createClient<{ \"context-config\": string }>({\n context: { env: \"prod\" },\n })\n );\n const client2 = trackClient(\n await ctx.createClient<{ \"context-config\": string }>({\n context: { env: \"dev\" },\n })\n );\n\n expect(client1.get(\"context-config\")).toBe(\"prod-value\");\n expect(client2.get(\"context-config\")).toBe(\"default\");\n\n client1.disconnect();\n client2.disconnect();\n });\n });\n }\n );\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@replanejs/test-suite",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "End-to-end test suite for Replane SDK and Admin API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
],
|
|
40
40
|
"sideEffects": false,
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@replanejs/sdk": "^1.0.
|
|
43
|
-
"@replanejs/admin": "^1.0.
|
|
42
|
+
"@replanejs/sdk": "^1.0.4",
|
|
43
|
+
"@replanejs/admin": "^1.0.4"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
46
|
"vitest": ">=2.0.0"
|