Eric Bower
·
2025-06-06
create-key.test.ts
1import { type ActionWithPayload, createApi } from "../index.js";
2import { afterAll, beforeAll, expect, test } from "../test.js";
3
4const getKeyOf = (action: ActionWithPayload<{ key: string }>): string =>
5 action.payload.key;
6
7const err = console.error;
8beforeAll(() => {
9 console.error = () => {};
10});
11
12afterAll(() => {
13 console.error = err;
14});
15
16test("options object keys order for action key identity - 0: empty options", () => {
17 console.warn = () => {};
18 const api = createApi();
19 api.use(api.routes());
20 // no param
21 const action0 = api.get("/users", function* (ctx, next) {
22 ctx.request = {
23 method: "GET",
24 };
25 yield* next();
26 });
27 const sendNop0 = action0();
28 const sendNop1 = action0();
29 expect(getKeyOf(sendNop0)).toEqual(getKeyOf(sendNop1));
30});
31
32test("options object keys order for action key identity - 1: simple object", () => {
33 const api = createApi();
34 api.use(api.routes());
35 // no param
36 const action0 = api.get<{
37 [key: string]: string | boolean | number | null | undefined;
38 }>("/users", function* (ctx, next) {
39 ctx.request = {
40 method: "GET",
41 };
42 yield* next();
43 });
44 const sendPojo0 = action0({
45 a: "a",
46 b: "b",
47 c: 1,
48 d: 2,
49 e: true,
50 f: false,
51 "100": 100,
52 101: "101",
53 });
54 const sendPojo1 = action0({
55 a: "a",
56 b: "b",
57 c: 1,
58 d: 2,
59 e: true,
60 f: false,
61 100: 100,
62 101: "101",
63 });
64 const sendPojo2 = action0({
65 e: true,
66 f: false,
67 "100": 100,
68 "101": "101",
69 a: "a",
70 b: "b",
71 c: 1,
72 d: 2,
73 });
74 const sendPojo3 = action0({
75 e: true,
76 f: false,
77 "100": 100,
78 "101": "101",
79 a: "a",
80 b: "b",
81 c: 1,
82 d: 2000000,
83 });
84 const sendPojo4 = action0({
85 e: null,
86 f: false,
87 "100": undefined,
88 "101": "101",
89 a: "a",
90 b: "b",
91 c: 1,
92 d: `Thomas O'Malley`,
93 });
94 const sendPojo5 = action0({
95 d: `Thomas O'Malley`,
96 e: null,
97 f: false,
98 "100": undefined,
99 "101": "101",
100 a: "a",
101 b: "b",
102 c: 1,
103 });
104 expect(getKeyOf(sendPojo0)).toEqual(getKeyOf(sendPojo1));
105 expect(getKeyOf(sendPojo0)).toEqual(getKeyOf(sendPojo2));
106 expect(getKeyOf(sendPojo0)).not.toEqual(getKeyOf(sendPojo3));
107 expect(getKeyOf(sendPojo4)).toEqual(getKeyOf(sendPojo5));
108});
109
110test("options object keys order for action key identity - 2: object (with array values)", () => {
111 interface Ip0 {
112 param1: string;
113 param2: string[];
114 }
115 const api = createApi();
116 api.use(api.routes());
117 const action = api.get<Ip0>("/users/:param1/:param2", function* (ctx, next) {
118 ctx.request = {
119 method: "GET",
120 };
121 yield* next();
122 });
123 const sendFirst = action({ param1: "1", param2: ["2", "e", "f"] });
124 const sendSecond = action({ param2: ["2", "f", "e"], param1: "1" });
125 const sendThird = action({ param2: ["2", "e", "f"], param1: "1" });
126 expect(getKeyOf(sendFirst)).not.toEqual(getKeyOf(sendSecond));
127 expect(getKeyOf(sendFirst)).toEqual(getKeyOf(sendThird));
128});
129
130test("options object keys order for action key identity - 3: nested object", () => {
131 interface Ip0 {
132 param1: string;
133 param2: string[];
134 }
135 interface Ip1 {
136 param1: string;
137 param2: string;
138 param3: number;
139 param4: Ip0;
140 param5: boolean;
141 }
142 const o1: Ip1 = {
143 param1: "1",
144 param2: "2",
145 param3: 3,
146 param4: {
147 param1: "4",
148 param2: ["5", "6"],
149 },
150 param5: true,
151 };
152 const o2: Ip1 = {
153 param4: {
154 param1: "4",
155 param2: ["5", "6"],
156 },
157 param5: true,
158 param2: "2",
159 param1: "1",
160 param3: 3,
161 };
162 const api = createApi();
163 api.use(api.routes());
164 //nested with array
165 const action2 = api.get<Ip1>(
166 "/users/:param1/:param2/:param3/:param4/:param5",
167 function* (ctx, next) {
168 ctx.request = {
169 method: "GET",
170 };
171 yield* next();
172 },
173 );
174 const sendO1 = action2(o1);
175 const sendO2 = action2(o2);
176 const sendO3 = action2({
177 ...o1,
178 param4: { ...o1.param4, param2: ["5", "6", "7"] },
179 });
180 expect(getKeyOf(sendO1)).toEqual(getKeyOf(sendO2));
181 expect(getKeyOf(sendO1)).not.toEqual(getKeyOf(sendO3));
182});
183
184test("options object keys order for action key identity - 4: deepNested object", () => {
185 interface Ip0 {
186 param1: string;
187 param2: string[];
188 }
189 interface Ip1 {
190 param1: string;
191 param2: string;
192 param3: number;
193 param4: Ip0;
194 param5: boolean;
195 }
196 interface Ip3 {
197 param1: string;
198 param2: {
199 param3: Ip1;
200 param4: Ip0;
201 };
202 }
203 const o1: Ip1 = {
204 param1: "1",
205 param2: "2",
206 param3: 3,
207 param4: {
208 param1: "4",
209 param2: ["5", "6"],
210 },
211 param5: true,
212 };
213 const oo1: Ip3 = {
214 param1: "1",
215 param2: {
216 param3: o1,
217 param4: {
218 param1: "4",
219 param2: ["5", "6"],
220 },
221 },
222 };
223 const oo2: Ip3 = {
224 param1: "1",
225 param2: {
226 param4: {
227 param1: "4",
228 param2: ["5", "6"],
229 },
230 param3: o1,
231 },
232 };
233 const api = createApi();
234 api.use(api.routes());
235 // deepNested
236 const action4 = api.get<Ip3>(
237 "/users/:param1/:param2/:param3/:param4/:param5",
238 function* (ctx, next) {
239 ctx.request = {
240 method: "GET",
241 };
242 yield* next();
243 },
244 );
245 const send_oo1 = action4(oo1);
246 const send_oo1_shuff = action4({ param2: oo1.param2, param1: oo1.param1 });
247 const send_oo1_value_changed = action4({ ...oo1, param1: "x" });
248 const send_oo2 = action4(oo2);
249 expect(send_oo1.payload.options).toEqual(send_oo2.payload.options);
250 expect(getKeyOf(send_oo1)).toEqual(getKeyOf(send_oo1_shuff));
251 expect(getKeyOf(send_oo1)).not.toEqual(getKeyOf(send_oo1_value_changed));
252 expect(getKeyOf(send_oo1)).toEqual(getKeyOf(send_oo2));
253});
254
255test("options object keys order for action key identity - 5: other", () => {
256 const api = createApi();
257 api.use(api.routes());
258 //array options
259 const action5 = api.post<
260 | number
261 | boolean
262 | string
263 | undefined
264 | null
265 | { param1: string; param2: (string | number)[] }[]
266 | string[]
267 >("/users/:allRecords", function* (ctx, next) {
268 ctx.request = {
269 method: "POST",
270 body: JSON.stringify(ctx.action.payload),
271 };
272 yield* next();
273 });
274 const falsy0 = action5(0);
275 const falsy1 = action5(false);
276 const falsy2 = action5("");
277 const falsy3 = action5(undefined);
278 const falsy4 = action5(null);
279 const primNo0 = action5(Number.NaN);
280 const primNo1 = action5(1);
281 const primNo1bis = action5(1);
282 const primNo2 = action5(2);
283 const str1 = action5("1234");
284 const str1bis = action5("1234");
285 const str2 = action5("2345");
286 const aStrings1 = action5(["1", "2", "3"]);
287 const aStrings2 = action5(["1", "2", "3"]);
288 const aStrings3 = action5(["1", "2", "1"]);
289 const aObjects1 = action5([
290 { param1: "1", param2: ["2", "3"] },
291 { param1: "2", param2: ["2", "3"] },
292 ]);
293 const aObjects2 = action5([
294 { param1: "1", param2: ["2", "3"] },
295 { param1: "2", param2: ["2", "3"] },
296 ]);
297 // the objects are not identical.
298 const aObjects3 = action5([
299 { param1: "1", param2: ["2", "3"] },
300 { param1: "2", param2: ["2", 3] },
301 ]);
302 //object inside the array is shuffled
303 const aObjects4 = action5([
304 { param2: ["2", "3"], param1: "1" },
305 { param2: ["2", "3"], param1: "2" },
306 ]);
307 // cont the order of array elements is changed will get different keys.
308 const aObjects5 = action5([
309 { param1: "2", param2: ["2", "3"] },
310 { param1: "1", param2: ["2", "3"] },
311 ]);
312 expect(getKeyOf(falsy0)).not.toEqual(getKeyOf(falsy1));
313 expect(getKeyOf(falsy1)).not.toEqual(getKeyOf(falsy2));
314 expect(getKeyOf(falsy1)).not.toEqual(getKeyOf(falsy3));
315 expect(getKeyOf(falsy3)).not.toEqual(getKeyOf(falsy4));
316 expect(getKeyOf(primNo0)).not.toEqual(getKeyOf(falsy0));
317 expect(getKeyOf(primNo0)).not.toEqual(getKeyOf(primNo1));
318 expect(getKeyOf(primNo1)).not.toEqual(getKeyOf(primNo2));
319 expect(getKeyOf(primNo1)).toEqual(getKeyOf(primNo1bis));
320 expect(getKeyOf(str1)).not.toEqual(getKeyOf(str2));
321 expect(getKeyOf(str1)).toEqual(getKeyOf(str1bis));
322 expect(getKeyOf(aStrings1)).toEqual(getKeyOf(aStrings2));
323 expect(getKeyOf(aStrings1)).not.toEqual(getKeyOf(aStrings3));
324 expect(getKeyOf(aObjects1)).toEqual(getKeyOf(aObjects2));
325 expect(getKeyOf(aObjects1)).not.toEqual(getKeyOf(aObjects3));
326 expect(getKeyOf(aObjects1)).toEqual(getKeyOf(aObjects4));
327 expect(getKeyOf(aObjects1)).not.toEqual(getKeyOf(aObjects5));
328});