import assert from 'node:assert'; import fs from 'node:fs'; import { test } from 'node:test'; // Sandbox BEFORE importing store.js — never bind the test to production data. const dataDir = fs.mkdtempSync(`${process.env.TMPDIR || '/tmp'}/dashcaddy-license-order-test-`); process.env.DATA_DIR = dataDir; process.env.DASHCADDY_LICENSE_SECRET = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; const store = await import('../src/store.js'); // Production webhook order: Stripe delivers customer.subscription.created BEFORE // the first invoice.paid (verified live 2026-09-22: sub created 23:06:08, invoice // events 23:06:12). The license row therefore exists when invoice.paid arrives. // The welcome-vs-renewal classification MUST survive both arrival orders. test('welcome classification is webhook-order independent', () => { const uniq = `ordertest_${Date.now()}`; const customerId = `cus_${uniq}`; const subscriptionId = `sub_${uniq}`; const subPatch = { subscriptionId, customerId, customerEmail: `${uniq}@example.com`, planCode: 'premium_30d', durationDays: 30, status: 'active', premiumFeatures: { sso: true, recipes: true, swarm: true }, deactivatedAt: null }; // ORDER A (production): subscription.created pre-creates the license... const preCreated = store.createOrUpdateLicenseBySubscription(subscriptionId, { ...subPatch, expiresAt: new Date(Date.now() + 30 * 86400000).toISOString(), lastStripeEventCreated: 1, lastStripeEventId: `evt_${uniq}_sub_created` }); assert.equal(preCreated.isNewLicense, true); // ...then invoice.paid arrives. extended must come from emailDeliveredAt // (no email ever delivered -> extended=false -> WELCOME email). let license = store.findLicenseByCustomerId(customerId); assert.equal(Boolean(license.emailDeliveredAt), false, 'order A: pre-created license with no delivered email must classify as welcome'); // simulate the email delivery that invoice.paid triggers: store.updateLicense(license.id, { emailDeliveryStatus: 'delivered', emailDeliveredAt: new Date().toISOString(), lastEmailDeliveryId: `in_${uniq}_1` }); // RENEWAL (next month's invoice.paid): an email WAS delivered -> extended=true. license = store.findLicenseByCustomerId(customerId); assert.equal(Boolean(license.emailDeliveredAt), true, 'after a delivered welcome email, the next invoice must classify as renewal/extended'); }); // The server must call the shared classifier, not inline its own logic. // (Behavioral coverage of the classifier itself lives in license-classify.test.js.) test('server.js delegates classification to classifySubscriptionEmail', () => { const src = fs.readFileSync(new URL('../src/server.js', import.meta.url), 'utf8'); assert.ok(src.includes('classifySubscriptionEmail(license).extended'), 'handler must use the shared classifier'); assert.ok(!src.includes('const extended = !license.isNewLicense'), 'old row-existence classification must be gone'); assert.ok(!src.includes('const extended = Boolean(license.emailDeliveredAt)'), 'no inline duplicate of the logic'); });