commands.test.mjs 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
  1. import test from 'node:test';
  2. import assert from 'node:assert/strict';
  3. import { prepareCommand, canonical, mayHaveCommitted } from '../src/utils/command-core.mjs';
  4. test('a timeout retry preserves the original payload and key', () => {
  5. const first = prepareCommand(null, { sku_id: 12, quantity: 8 }, () => 'request-1');
  6. const stored = JSON.parse(JSON.stringify(first));
  7. const retry = prepareCommand(stored, { quantity: 8, sku_id: 12 }, () => {
  8. throw new Error('must not generate another key');
  9. });
  10. assert.deepEqual(retry, first);
  11. });
  12. test('changing an unresolved command cannot silently create a second booking', () => {
  13. const first = prepareCommand(null, { quantity: 8 }, () => 'request-1');
  14. assert.throws(
  15. () => prepareCommand(first, { quantity: 9 }, () => 'request-2'),
  16. /上次提交结果尚未确认/,
  17. );
  18. });
  19. test('a completed command permits a new operation with a new key', () => {
  20. const first = prepareCommand(null, { quantity: 8 }, () => 'request-1');
  21. const second = prepareCommand(null, { quantity: 8 }, () => 'request-2');
  22. assert.notEqual(first.request_key, second.request_key);
  23. });
  24. test('checkout retries repair invalid stored keys before sending', () => {
  25. const payload = { items: [{ package_id: 1, quantity: 2 }], invoice: null };
  26. for (const key of [undefined, null, '', 123, 'short', 'x'.repeat(81), 'invalid key']) {
  27. const stored = { ...payload, request_key: key };
  28. const repaired = prepareCommand(stored, payload, () => 'checkout-repaired-1');
  29. assert.deepEqual(repaired, { ...payload, request_key: 'checkout-repaired-1' });
  30. assert.deepEqual(
  31. prepareCommand(repaired, payload, () => { throw new Error('must preserve repaired key'); }),
  32. repaired,
  33. );
  34. }
  35. });
  36. test('invalid newly generated keys never reach the transport', () => {
  37. assert.throws(() => prepareCommand(null, {}, () => ''), /请求标识/);
  38. });
  39. test('nested checkout data compares by content, while photo selection order is preserved', () => {
  40. assert.equal(
  41. canonical({ items: [{ package_id: 1, quantity: 2 }], invoice: null }),
  42. canonical({ invoice: null, items: [{ quantity: 2, package_id: 1 }] }),
  43. );
  44. assert.notEqual(canonical({ photo_ids: [1, 2] }), canonical({ photo_ids: [2, 1] }));
  45. });
  46. test('network and server failures retain the key; explicit business rejections do not', () => {
  47. for (const status of [0, 500, 502, 503, 504]) assert.equal(mayHaveCommitted(status), true);
  48. for (const status of [400, 401, 403, 409, 422, 429])
  49. assert.equal(mayHaveCommitted(status), false);
  50. });