50 lines
1.9 KiB
TypeScript
50 lines
1.9 KiB
TypeScript
|
|
import { describe, it, expect, beforeAll } from 'vitest'
|
||
|
|
import * as db from '../src/database.js'
|
||
|
|
|
||
|
|
const pubkey = 'dup-test-' + Date.now()
|
||
|
|
const email = 'dup-test-' + Date.now() + '@example.com'
|
||
|
|
let token: string
|
||
|
|
|
||
|
|
describe('Duplicate subscription prevention — idempotent re-register after confirm', () => {
|
||
|
|
beforeAll(async () => {
|
||
|
|
await db.migrate()
|
||
|
|
})
|
||
|
|
|
||
|
|
it('registers a subscription (fresh)', async () => {
|
||
|
|
const sub = await db.insertSubscription(pubkey, email, 'daily')
|
||
|
|
expect(sub).toBeTruthy()
|
||
|
|
expect(sub!.confirmed_at).toBeFalsy()
|
||
|
|
expect(sub!.unsubscribed_at).toBeFalsy()
|
||
|
|
token = sub!.key
|
||
|
|
})
|
||
|
|
|
||
|
|
it('confirms the subscription', async () => {
|
||
|
|
const result = await db.confirmSubscription(token)
|
||
|
|
expect(result).toBeTruthy()
|
||
|
|
expect(result!.alreadyConfirmed).toBe(false)
|
||
|
|
expect(result!.sub.confirmed_at).toBeTruthy()
|
||
|
|
})
|
||
|
|
|
||
|
|
it('re-registers with the same email and frequency (idempotent PUT)', async () => {
|
||
|
|
// This simulates a second PUT from the client with identical params.
|
||
|
|
// The bug would create a second active row + send a new confirmation email.
|
||
|
|
const sub = await db.insertSubscription(pubkey, email, 'daily')
|
||
|
|
expect(sub).toBeTruthy()
|
||
|
|
// Must return the existing confirmed row, NOT a fresh unconfirmed row
|
||
|
|
expect(sub!.confirmed_at).toBeTruthy()
|
||
|
|
expect(sub!.unsubscribed_at).toBeFalsy()
|
||
|
|
|
||
|
|
// The key must remain unchanged — a new row would have a different key
|
||
|
|
expect(sub!.key).toBe(token)
|
||
|
|
})
|
||
|
|
|
||
|
|
it('has exactly one active row for this pubkey', async () => {
|
||
|
|
// getSubscriptionByPubkey filters by unsubscribed_at IS NULL and
|
||
|
|
// returns at most one row (enforced by the partial unique index).
|
||
|
|
// If a second active row exists, the index is absent or bypassed.
|
||
|
|
const active = await db.getSubscriptionByPubkey(pubkey)
|
||
|
|
expect(active).toBeTruthy()
|
||
|
|
expect(active!.confirmed_at).toBeTruthy()
|
||
|
|
expect(active!.key).toBe(token)
|
||
|
|
})
|
||
|
|
})
|