import { test } from 'node:test'; import assert from 'node:assert/strict'; import { createClient } from '../dist/index.js'; const identity = { anonymous_id: 'anonymous', master_id: 'master', anonymous_token: 'signed-anonymous-token' }; const response = (body, status = 200) => new Response(JSON.stringify(body), { status, headers: { 'Content-Type': 'application/json' } }); test('concurrent tracks initialize once, reuse identity and replay the same event envelope', async () => { const calls = []; globalThis.fetch = async (url, options) => { calls.push({ url, options, body: JSON.parse(options.body) }); if (url.endsWith('/anonymous')) return response(identity); return response({ ...identity, event_id: JSON.parse(options.body).event_id, duplicate: false }); }; const values = new Map(); const client = createClient({ baseUrl: 'https://api.example/', appId: 'shop', collectionKey: 'collection-demo', storage: { getItem: (key) => values.get(key) ?? null, setItem: (key, value) => values.set(key, value) } }); const properties = { product_id: 'camera', quantity: 1 }; const prepared = client.prepareEvent('add_cart', properties); properties.quantity = 999; await Promise.all([client.sendEvent(prepared), client.track('page', { page_id: 'home' })]); await client.sendEvent(prepared); assert.equal(calls.filter((call) => call.url.endsWith('/anonymous')).length, 1); const events = calls.filter((call) => call.url.endsWith('/events')); assert.equal(events[0].body.properties.quantity, 1); assert.equal(events[0].body.event_id, events[2].body.event_id); assert.equal(events[0].body.occurred_at, events[2].body.occurred_at); assert.equal(events[0].body.anonymous_token, identity.anonymous_token); assert.equal(events[0].options.headers['X-Adcloud-Key'], 'collection-demo'); assert.equal(events[0].options.credentials, 'include'); assert.equal(values.size, 1); }); test('initialization failure can retry and persisted anonymous token is restored', async () => { let calls = 0; globalThis.fetch = async (_url, options) => { calls++; assert.equal(JSON.parse(options.body).anonymous_token, identity.anonymous_token); return calls === 1 ? response({ detail: 'Unavailable' }, 503) : response(identity); }; const client = createClient({ baseUrl: 'https://api.example', storage: { getItem: () => identity.anonymous_token, setItem: () => {} } }); await assert.rejects(client.initialize(), /HTTP 503/); assert.deepEqual(await client.initialize(), identity); }); test('tracking requires collection configuration and cannot send another app envelope', async () => { assert.throws(() => createClient({ baseUrl: 'https://api.example', storage: null }).prepareEvent('page', {}), /requires appId/); const client = createClient({ baseUrl: 'https://api.example', appId: 'shop', collectionKey: 'collection-demo', storage: null }); await assert.rejects(client.sendEvent({ ...client.prepareEvent('page', {}), app_id: 'other' }), /configured application/); }); test('login identity update and logout rotation replace the cached tracking identity', async () => { const requests = []; const next = { ...identity, anonymous_id: 'new-anonymous', anonymous_token: 'new-signed-token' }; globalThis.fetch = async (url, options) => { requests.push({ url, body: JSON.parse(options.body) }); if (url.endsWith('?rotate=true')) return response(next); return response(identity); }; const client = createClient({ baseUrl: 'https://api.example', appId: 'shop', collectionKey: 'collection-demo', storage: null }); client.updateIdentity(next); await client.track('page', { page_id: 'home' }); assert.equal(requests[0].body.anonymous_token, next.anonymous_token); await client.resetIdentity(); assert.equal(requests[1].url, 'https://api.example/v1/identity/anonymous?rotate=true'); assert.throws(() => client.updateIdentity({}), /Invalid anonymous identity/); });