zappier-edge/tests/db-customer.test.ts

116 lines
4.7 KiB
TypeScript

import Database from 'better-sqlite3';
import { Customer } from '../src/auth';
import { SqliteCustomerRepo } from '../src/db/customer-repo';
const customer = (apiKey: string): Customer => ({
id: 'cust_1',
name: 'Ada',
tierId: 'pro',
apiKey,
stripeCustomerId: 'cus_123',
});
describe('SqliteCustomerRepo', () => {
it('finds a customer by API key', () => {
const repo = new SqliteCustomerRepo(new Database(':memory:'), [customer('key-ada')]);
expect(repo.findByApiKey('key-ada')?.tierId).toBe('pro');
});
it('returns undefined for an unknown key', () => {
const repo = new SqliteCustomerRepo(new Database(':memory:'), [customer('key-ada')]);
expect(repo.findByApiKey('wrong')).toBeUndefined();
});
it('lists all customers with their Stripe ids', () => {
const repo = new SqliteCustomerRepo(new Database(':memory:'), [customer('key-ada')]);
const all = repo.list();
expect(all).toHaveLength(1);
expect(all[0].stripeCustomerId).toBe('cus_123');
});
it('seeds only when the table is empty', () => {
const db = new Database(':memory:');
new SqliteCustomerRepo(db, [customer('key-ada')]);
const again = new SqliteCustomerRepo(db, [customer('key-other')]);
expect(again.list().map((c) => c.apiKey)).toEqual(['key-ada']);
});
it('save() upserts including the multiplier override', () => {
const repo = new SqliteCustomerRepo(new Database(':memory:'), [customer('key-ada')]);
repo.save({ ...customer('key-ada'), tierId: 'business', multiplierOverride: 0.4 });
const updated = repo.findByApiKey('key-ada');
expect(updated?.tierId).toBe('business');
expect(updated?.multiplierOverride).toBe(0.4);
expect(repo.list()).toHaveLength(1);
});
it('round-trips billingType and email', () => {
const repo = new SqliteCustomerRepo(new Database(':memory:'), [customer('key-ada')]);
repo.save({ ...customer('key-ada'), billingType: 'purchase_order', email: 'ada@example.com' });
const updated = repo.findByApiKey('key-ada');
expect(updated?.billingType).toBe('purchase_order');
expect(updated?.email).toBe('ada@example.com');
});
it('migrates a legacy table, defaulting billingType to stripe', () => {
const db = new Database(':memory:');
db.exec(`CREATE TABLE customers (
id TEXT PRIMARY KEY, name TEXT NOT NULL, tier_id TEXT NOT NULL,
api_key TEXT NOT NULL UNIQUE, stripe_customer_id TEXT, multiplier_override REAL
)`);
db.prepare(
`INSERT INTO customers (id, name, tier_id, api_key) VALUES ('cust_1', 'Ada', 'pro', 'key-ada')`,
).run();
const repo = new SqliteCustomerRepo(db);
const migrated = repo.findByApiKey('key-ada');
expect(migrated?.billingType).toBe('stripe');
expect(migrated?.email).toBeUndefined();
// and new columns are writable after migration
repo.save({ ...migrated!, billingType: 'purchase_order' });
expect(repo.findByApiKey('key-ada')?.billingType).toBe('purchase_order');
});
it('finds a customer by email (case-insensitive)', () => {
const repo = new SqliteCustomerRepo(new Database(':memory:'), [
{ ...customer('key-ada'), email: 'Ada@Example.com' },
]);
expect(repo.findByEmail('ada@example.com')?.id).toBe('cust_1');
expect(repo.findByEmail('nobody@example.com')).toBeUndefined();
});
it('round-trips portal identity fields', () => {
const repo = new SqliteCustomerRepo(new Database(':memory:'), [customer('key-ada')]);
repo.save({
...customer('key-ada'),
passwordHash: 'scrypt:16384:8:1:c2FsdA==:aGFzaA==',
totpSecret: 'ABC234',
totpEnabled: true,
balanceCents: 2500,
emailInvoicing: true,
});
const updated = repo.findByApiKey('key-ada');
expect(updated?.passwordHash).toBe('scrypt:16384:8:1:c2FsdA==:aGFzaA==');
expect(updated?.totpSecret).toBe('ABC234');
expect(updated?.totpEnabled).toBe(true);
expect(updated?.balanceCents).toBe(2500);
expect(updated?.emailInvoicing).toBe(true);
});
it('defaults portal identity fields for legacy rows', () => {
const db = new Database(':memory:');
db.exec(`CREATE TABLE customers (
id TEXT PRIMARY KEY, name TEXT NOT NULL, tier_id TEXT NOT NULL,
api_key TEXT NOT NULL UNIQUE, stripe_customer_id TEXT, multiplier_override REAL
)`);
db.prepare(
`INSERT INTO customers (id, name, tier_id, api_key) VALUES ('cust_1', 'Ada', 'pro', 'key-ada')`,
).run();
const repo = new SqliteCustomerRepo(db);
const migrated = repo.findByApiKey('key-ada');
expect(migrated?.passwordHash).toBeUndefined();
expect(migrated?.totpSecret).toBeUndefined();
expect(migrated?.totpEnabled).toBe(false);
expect(migrated?.balanceCents).toBe(0);
expect(migrated?.emailInvoicing).toBe(false);
});
});