← Public packages
@kentcdodds/google
Google helpers for Gmail, Calendar, Drive, People, YouTube (including upload), and Analytics.
src/idempotency.test.ts
179 lines · 4.4 KB · TypeScriptimport assert from 'node:assert/strict'
import { describe, it } from 'node:test'
import {
IDEMPOTENCY_INFLIGHT_MS,
IDEMPOTENCY_RETENTION_MS,
idempotencyStorageKey,
normalizeIdempotencyKey,
readFreshFingerprint,
withSendIdempotency,
type IdempotencyStorage,
} from './idempotency.ts'
function memoryStorage(initial: Record<string, unknown> = {}): IdempotencyStorage & { data: Record<string, unknown> } {
const data = { ...initial }
return {
data,
async get(key) {
return data[key]
},
async set(key, value) {
data[key] = value
},
async delete(key) {
delete data[key]
},
}
}
describe('normalizeIdempotencyKey', () => {
it('trims and accepts non-empty keys', () => {
assert.equal(normalizeIdempotencyKey(' batch:1 '), 'batch:1')
})
it('treats blank as absent', () => {
assert.equal(normalizeIdempotencyKey(''), null)
assert.equal(normalizeIdempotencyKey(' '), null)
assert.equal(normalizeIdempotencyKey(undefined), null)
})
it('rejects oversized keys', () => {
assert.throws(() => normalizeIdempotencyKey('x'.repeat(257)), /at most 256/)
})
})
describe('idempotencyStorageKey', () => {
it('scopes by action, account, and key', () => {
assert.equal(
idempotencyStorageKey('sendMessage', 'business', 'welcome:42'),
'idempotency:sendMessage:business:welcome:42',
)
})
})
describe('readFreshFingerprint', () => {
it('returns success within retention', () => {
const now = Date.parse('2026-09-11T20:00:00.000Z')
const fresh = readFreshFingerprint(
{
status: 'success',
result: { id: 'msg-1' },
completedAt: new Date(now - 60_000).toISOString(),
},
now,
)
assert.deepEqual(fresh, { kind: 'success', result: { id: 'msg-1' } })
})
it('expires success after retention', () => {
const now = Date.parse('2026-09-11T20:00:00.000Z')
const stale = readFreshFingerprint(
{
status: 'success',
result: { id: 'msg-1' },
completedAt: new Date(now - IDEMPOTENCY_RETENTION_MS - 1).toISOString(),
},
now,
)
assert.equal(stale, null)
})
it('detects fresh inflight', () => {
const now = Date.parse('2026-09-11T20:00:00.000Z')
const inflight = readFreshFingerprint(
{
status: 'inflight',
startedAt: new Date(now - IDEMPOTENCY_INFLIGHT_MS + 1_000).toISOString(),
},
now,
)
assert.deepEqual(inflight, { kind: 'inflight' })
})
})
describe('withSendIdempotency', () => {
it('passes through when key omitted', async () => {
const storage = memoryStorage()
const result = await withSendIdempotency({
action: 'sendMessage',
account: 'business',
storage,
run: async () => ({ id: 'fresh' }),
})
assert.deepEqual(result, { id: 'fresh' })
assert.deepEqual(storage.data, {})
})
it('stores and replays successful sends', async () => {
const storage = memoryStorage()
let calls = 0
const first = await withSendIdempotency({
action: 'sendMessage',
account: 'business',
idempotencyKey: 'welcome:42',
storage,
run: async () => {
calls += 1
return { id: 'msg-1' }
},
})
const second = await withSendIdempotency({
action: 'sendMessage',
account: 'business',
idempotencyKey: 'welcome:42',
storage,
run: async () => {
calls += 1
return { id: 'msg-2' }
},
})
assert.deepEqual(first, { id: 'msg-1' })
assert.deepEqual(second, { id: 'msg-1' })
assert.equal(calls, 1)
})
it('treats different keys as different sends', async () => {
const storage = memoryStorage()
const a = await withSendIdempotency({
action: 'sendMessage',
account: 'business',
idempotencyKey: 'a',
storage,
run: async () => ({ id: 'a' }),
})
const b = await withSendIdempotency({
action: 'sendMessage',
account: 'business',
idempotencyKey: 'b',
storage,
run: async () => ({ id: 'b' }),
})
assert.deepEqual(a, { id: 'a' })
assert.deepEqual(b, { id: 'b' })
})
it('clears inflight on failure so retries can send', async () => {
const storage = memoryStorage()
await assert.rejects(
withSendIdempotency({
action: 'sendMessage',
account: 'business',
idempotencyKey: 'fail-once',
storage,
run: async () => {
throw new Error('boom')
},
}),
/boom/,
)
assert.equal(Object.keys(storage.data).length, 0)
const recovered = await withSendIdempotency({
action: 'sendMessage',
account: 'business',
idempotencyKey: 'fail-once',
storage,
run: async () => ({ id: 'ok' }),
})
assert.deepEqual(recovered, { id: 'ok' })
})
})