const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const os = require('node:os'); const path = require('node:path'); const dbPath = path.join(os.tmpdir(), `bill-tracker-subscription-service-test-${process.pid}.sqlite`); process.env.DB_PATH = dbPath; const { getDb, closeDb } = require('../db/database'); const { getSubscriptionRecommendations, searchSubscriptionTransactions, } = require('../services/subscriptionService'); function createUser(db, suffix) { return db.prepare(` INSERT INTO users (username, password_hash, role, active, email, created_at, updated_at) VALUES (?, 'x', 'user', 1, ?, datetime('now'), datetime('now')) `).run(`subscription-user-${suffix}`, `subscription-user-${suffix}@local`).lastInsertRowid; } function createTransaction(db, userId, overrides = {}) { return db.prepare(` INSERT INTO transactions (user_id, source_type, posted_date, amount, currency, description, payee, match_status, ignored) VALUES (?, 'manual', ?, ?, 'USD', ?, ?, 'unmatched', 0) `).run( userId, overrides.posted_date || new Date().toISOString().slice(0, 10), overrides.amount ?? -1599, overrides.description || 'NETFLIX.COM', overrides.payee || 'NETFLIX.COM', ).lastInsertRowid; } test.after(() => { closeDb(); for (const suffix of ['', '-wal', '-shm']) { fs.rmSync(`${dbPath}${suffix}`, { force: true }); } }); test('known catalog services appear as high-confidence subscription recommendations', () => { const db = getDb(); const userId = createUser(db, 'recommendation'); createTransaction(db, userId); const recommendations = getSubscriptionRecommendations(db, userId); const netflix = recommendations.find(item => item.catalog_match?.name === 'Netflix'); assert.ok(netflix, 'Netflix catalog match should be recommended from one known charge'); assert.equal(netflix.subscription_type, 'streaming'); assert.equal(netflix.confidence >= 90, true); assert.match(netflix.reasons.join(' '), /Matches known service: Netflix/); }); test('subscription transaction search annotates known catalog matches', () => { const db = getDb(); const userId = createUser(db, 'search'); const transactionId = createTransaction(db, userId, { description: 'NETFLIX.COM 866-579-7172', payee: 'NETFLIX.COM', }); const matches = searchSubscriptionTransactions(db, userId, { q: 'netflix', limit: 10 }); const match = matches.find(item => item.id === transactionId); assert.ok(match, 'transaction should be returned by subscription search'); assert.equal(match.is_known_subscription, true); assert.equal(match.catalog_match.name, 'Netflix'); assert.equal(match.catalog_match.subscription_type, 'streaming'); }); test('Claude.ai catalog seed matches Anthropic transaction descriptors', () => { const db = getDb(); const userId = createUser(db, 'claude'); const transactionId = createTransaction(db, userId, { description: 'ANTHROPIC CLAUDE PRO', payee: 'ANTHROPIC', amount: -2000, }); const matches = searchSubscriptionTransactions(db, userId, { q: 'anthropic', limit: 10 }); const match = matches.find(item => item.id === transactionId); assert.ok(match, 'Anthropic transaction should be returned by subscription search'); assert.equal(match.is_known_subscription, true); assert.equal(match.catalog_match.name, 'Claude.ai'); assert.equal(match.catalog_match.subscription_type, 'software'); });