@traffical/core 0.1.3 → 0.2.0

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.
@@ -1,241 +0,0 @@
1
- /**
2
- * Resolution Engine Tests
3
- *
4
- * Validates parameter resolution using test vectors.
5
- */
6
-
7
- import { describe, test, expect } from "bun:test";
8
- import { resolveParameters, decide } from "./engine.js";
9
- import type { ConfigBundle } from "../types/index.js";
10
-
11
- // Load test fixtures from @traffical/sdk-spec
12
- import { bundleBasic, bundleConditions } from "@traffical/sdk-spec";
13
-
14
- // Default values for basic bundle parameters
15
- const basicDefaults = {
16
- "ui.primaryColor": "#0000FF",
17
- "ui.buttonText": "Click Me",
18
- "pricing.discount": 0,
19
- };
20
-
21
- // Default values for conditions bundle parameters
22
- const conditionsDefaults = {
23
- "checkout.ctaText": "Complete Purchase",
24
- "checkout.showUrgency": false,
25
- };
26
-
27
- describe("resolveParameters", () => {
28
- const bundle = bundleBasic as unknown as ConfigBundle;
29
-
30
- test("resolves user-abc correctly", () => {
31
- // Bucket 551 for layer_ui (treatment: 500-999)
32
- // Bucket 913 for layer_pricing (no allocation: outside 0-599)
33
- const assignments = resolveParameters(bundle, { userId: "user-abc" }, basicDefaults);
34
-
35
- expect(assignments["ui.primaryColor"]).toBe("#FF0000"); // treatment (bucket 551 >= 500)
36
- expect(assignments["ui.buttonText"]).toBe("Click Me"); // default
37
- expect(assignments["pricing.discount"]).toBe(0); // no allocation (bucket 913 >= 600)
38
- });
39
-
40
- test("resolves user-xyz correctly", () => {
41
- // Bucket 214 for layer_ui (control: 0-499)
42
- // Bucket 42 for layer_pricing (discount_10: 0-299)
43
- const assignments = resolveParameters(bundle, { userId: "user-xyz" }, basicDefaults);
44
-
45
- expect(assignments["ui.primaryColor"]).toBe("#0000FF"); // control (bucket 214 < 500)
46
- expect(assignments["ui.buttonText"]).toBe("Click Me"); // default
47
- expect(assignments["pricing.discount"]).toBe(10); // discount_10 (bucket 42 in 0-299)
48
- });
49
-
50
- test("resolves user-123 correctly", () => {
51
- // Bucket 871 for layer_ui (treatment: 500-999)
52
- // Bucket 177 for layer_pricing (discount_10: 0-299)
53
- const assignments = resolveParameters(bundle, { userId: "user-123" }, basicDefaults);
54
-
55
- expect(assignments["ui.primaryColor"]).toBe("#FF0000"); // treatment (bucket 871 >= 500)
56
- expect(assignments["ui.buttonText"]).toBe("Click Me"); // default
57
- expect(assignments["pricing.discount"]).toBe(10); // discount_10 (bucket 177 in 0-299)
58
- });
59
-
60
- test("returns defaults when unit key is missing", () => {
61
- const assignments = resolveParameters(bundle, {}, basicDefaults);
62
-
63
- // Should return caller defaults when unit key is missing
64
- expect(assignments["ui.primaryColor"]).toBe("#0000FF");
65
- expect(assignments["ui.buttonText"]).toBe("Click Me");
66
- expect(assignments["pricing.discount"]).toBe(0);
67
- });
68
-
69
- test("returns defaults when bundle is null", () => {
70
- const assignments = resolveParameters(null, { userId: "user-abc" }, basicDefaults);
71
-
72
- expect(assignments["ui.primaryColor"]).toBe("#0000FF");
73
- expect(assignments["ui.buttonText"]).toBe("Click Me");
74
- expect(assignments["pricing.discount"]).toBe(0);
75
- });
76
-
77
- test("only resolves requested parameters from defaults", () => {
78
- const partialDefaults = {
79
- "ui.primaryColor": "#FFFFFF",
80
- };
81
- const assignments = resolveParameters(bundle, { userId: "user-abc" }, partialDefaults);
82
-
83
- expect(Object.keys(assignments)).toEqual(["ui.primaryColor"]);
84
- expect(assignments["ui.primaryColor"]).toBe("#FF0000");
85
- });
86
- });
87
-
88
- describe("resolveParameters with conditions", () => {
89
- const bundle = bundleConditions as unknown as ConfigBundle;
90
-
91
- test("high value cart triggers urgency", () => {
92
- const assignments = resolveParameters(
93
- bundle,
94
- {
95
- userId: "user-high-value",
96
- cartValue: 150,
97
- deviceType: "desktop",
98
- },
99
- conditionsDefaults
100
- );
101
-
102
- expect(assignments["checkout.ctaText"]).toBe("Buy Now - Limited Stock!");
103
- expect(assignments["checkout.showUrgency"]).toBe(true);
104
- });
105
-
106
- test("mobile user with low cart gets mobile CTA", () => {
107
- const assignments = resolveParameters(
108
- bundle,
109
- {
110
- userId: "user-mobile",
111
- cartValue: 50,
112
- deviceType: "mobile",
113
- },
114
- conditionsDefaults
115
- );
116
-
117
- expect(assignments["checkout.ctaText"]).toBe("Buy Now");
118
- expect(assignments["checkout.showUrgency"]).toBe(false);
119
- });
120
-
121
- test("desktop user with low cart gets defaults", () => {
122
- const assignments = resolveParameters(
123
- bundle,
124
- {
125
- userId: "user-desktop",
126
- cartValue: 50,
127
- deviceType: "desktop",
128
- },
129
- conditionsDefaults
130
- );
131
-
132
- expect(assignments["checkout.ctaText"]).toBe("Complete Purchase");
133
- expect(assignments["checkout.showUrgency"]).toBe(false);
134
- });
135
-
136
- test("high value cart takes precedence over mobile", () => {
137
- const assignments = resolveParameters(
138
- bundle,
139
- {
140
- userId: "user-mobile-high",
141
- cartValue: 200,
142
- deviceType: "mobile",
143
- },
144
- conditionsDefaults
145
- );
146
-
147
- expect(assignments["checkout.ctaText"]).toBe("Buy Now - Limited Stock!");
148
- expect(assignments["checkout.showUrgency"]).toBe(true);
149
- });
150
- });
151
-
152
- describe("resolveParameters graceful degradation", () => {
153
- const bundle = bundleBasic as unknown as ConfigBundle;
154
-
155
- test("returns defaults when bundle is null", () => {
156
- const defaults = {
157
- "ui.primaryColor": "#FFFFFF",
158
- "ui.fontSize": 16,
159
- };
160
-
161
- const result = resolveParameters(null, { userId: "user-abc" }, defaults);
162
-
163
- expect(result["ui.primaryColor"]).toBe("#FFFFFF");
164
- expect(result["ui.fontSize"]).toBe(16);
165
- });
166
-
167
- test("merges bundle values with caller defaults", () => {
168
- const defaults = {
169
- "ui.primaryColor": "#FFFFFF",
170
- "ui.fontSize": 16,
171
- };
172
-
173
- const result = resolveParameters(bundle, { userId: "user-abc" }, defaults);
174
-
175
- // From bundle (overridden by policy) - bucket 551 = treatment
176
- expect(result["ui.primaryColor"]).toBe("#FF0000");
177
- // From caller defaults (not in bundle)
178
- expect(result["ui.fontSize"]).toBe(16);
179
- });
180
-
181
- test("returns caller defaults when unit key is missing", () => {
182
- const defaults = {
183
- "ui.primaryColor": "#FFFFFF",
184
- };
185
-
186
- const result = resolveParameters(bundle, {}, defaults);
187
-
188
- expect(result["ui.primaryColor"]).toBe("#FFFFFF");
189
- });
190
- });
191
-
192
- describe("decide", () => {
193
- const bundle = bundleBasic as unknown as ConfigBundle;
194
-
195
- test("returns decision with metadata", () => {
196
- const decision = decide(bundle, { userId: "user-abc" }, basicDefaults);
197
-
198
- expect(decision.decisionId).toMatch(/^dec_/);
199
- expect(decision.assignments["ui.primaryColor"]).toBe("#FF0000");
200
- expect(decision.metadata.unitKeyValue).toBe("user-abc");
201
- expect(decision.metadata.layers).toHaveLength(2);
202
- });
203
-
204
- test("includes correct layer resolution info", () => {
205
- const decision = decide(bundle, { userId: "user-abc" }, basicDefaults);
206
-
207
- const uiLayer = decision.metadata.layers.find(
208
- (l) => l.layerId === "layer_ui"
209
- );
210
- expect(uiLayer).toBeDefined();
211
- expect(uiLayer!.bucket).toBe(551);
212
- expect(uiLayer!.policyId).toBe("policy_color_test");
213
- expect(uiLayer!.allocationName).toBe("treatment");
214
-
215
- const pricingLayer = decision.metadata.layers.find(
216
- (l) => l.layerId === "layer_pricing"
217
- );
218
- expect(pricingLayer).toBeDefined();
219
- expect(pricingLayer!.bucket).toBe(913);
220
- // No allocation matched (bucket 913 is outside all ranges 0-599)
221
- expect(pricingLayer!.policyId).toBeUndefined();
222
- });
223
-
224
- test("returns defaults with empty metadata when bundle is null", () => {
225
- const decision = decide(null, { userId: "user-abc" }, basicDefaults);
226
-
227
- expect(decision.decisionId).toMatch(/^dec_/);
228
- expect(decision.assignments["ui.primaryColor"]).toBe("#0000FF");
229
- expect(decision.metadata.unitKeyValue).toBe("");
230
- expect(decision.metadata.layers).toHaveLength(0);
231
- });
232
-
233
- test("returns defaults with empty metadata when unit key is missing", () => {
234
- const decision = decide(bundle, {}, basicDefaults);
235
-
236
- expect(decision.decisionId).toMatch(/^dec_/);
237
- expect(decision.assignments["ui.primaryColor"]).toBe("#0000FF");
238
- expect(decision.metadata.unitKeyValue).toBe("");
239
- expect(decision.metadata.layers).toHaveLength(0);
240
- });
241
- });