BillTracker/tests/spendingService.test.js

52 lines
2.3 KiB
JavaScript

'use strict';
// Phase 1 — spendingService budgets were untested. Pins the dollars↔cents
// round-trip and the set/update/clear semantics of setSpendingBudget /
// getSpendingBudgets (getSpendingSummary's gating is separately reconciled
// against the Tracker in tests/reconciliation.test.js).
const test = require('node:test');
const assert = require('node:assert/strict');
const os = require('node:os');
const path = require('node:path');
const fs = require('node:fs');
const dbPath = path.join(os.tmpdir(), `bill-tracker-spendsvc-${process.pid}.sqlite`);
process.env.DB_PATH = dbPath;
const { getDb, closeDb } = require('../db/database');
const { setSpendingBudget, getSpendingBudgets } = require('../services/spendingService');
let db, userId, catId;
const rawCents = () => db.prepare('SELECT amount FROM spending_budgets WHERE user_id=? AND category_id=? AND year=2026 AND month=7').get(userId, catId)?.amount;
test.before(() => {
db = getDb();
userId = db.prepare("INSERT INTO users (username, password_hash, role, active) VALUES ('spend-user','x','user',1)").run().lastInsertRowid;
catId = db.prepare("INSERT INTO categories (user_id, name, spending_enabled) VALUES (?, 'Groceries', 1)").run(userId).lastInsertRowid;
});
test.after(() => {
closeDb();
for (const s of ['', '-wal', '-shm']) { try { fs.rmSync(dbPath + s); } catch {} }
});
test('setSpendingBudget stores cents and getSpendingBudgets returns dollars', () => {
setSpendingBudget(db, userId, catId, 2026, 7, 200);
assert.equal(rawCents(), 20000, 'stored as integer cents');
const budgets = getSpendingBudgets(db, userId, 2026, 7);
assert.deepEqual(budgets, [{ category_id: catId, amount: 200, category_name: 'Groceries' }], 'returned in dollars');
});
test('setting the same category again updates in place (ON CONFLICT), no duplicate row', () => {
setSpendingBudget(db, userId, catId, 2026, 7, 350.50);
assert.equal(rawCents(), 35050);
const budgets = getSpendingBudgets(db, userId, 2026, 7);
assert.equal(budgets.length, 1, 'still one budget row for the category/month');
assert.equal(budgets[0].amount, 350.5);
});
test('setting null clears the budget', () => {
setSpendingBudget(db, userId, catId, 2026, 7, null);
assert.equal(rawCents(), undefined, 'row deleted');
assert.deepEqual(getSpendingBudgets(db, userId, 2026, 7), []);
});