repos / starfx

a micro-mvc framework for react apps
git clone https://github.com/neurosnap/starfx.git

starfx / src / test
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});