Implement local SQLite backend and reactive UI

This commit is contained in:
2026-05-14 21:28:32 -04:00
parent 4aa3f7b362
commit f95b0ae912
35 changed files with 5444 additions and 2009 deletions

View File

@@ -1,8 +1,6 @@
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { activeCompanyId } from "../demoData.js";
import { closeDatabase, type Db, initDatabase } from "../db/database.js";
import { createRpcHandler } from "../db/rpcHandler.js";
import { seedDatabase } from "../db/seed.js";
describe("model RPC", () => {
let db: Db;
@@ -10,7 +8,7 @@ describe("model RPC", () => {
beforeEach(() => {
db = initDatabase({ inMemory: true });
seedDatabase(db);
db.prepare("INSERT INTO companies (id, ticker, name, sector, price, change_pct, thesis) VALUES ('aapl', 'AAPL', 'Apple Inc.', 'Technology', 0, 0, '')").run();
rpc = createRpcHandler(db);
});
@@ -20,7 +18,7 @@ describe("model RPC", () => {
it("rejects out-of-range model cell updates without throwing", async () => {
const result = await rpc("model.updateCell", {
companyId: activeCompanyId,
companyId: "aapl",
tab: "income",
row: 0,
col: 999,
@@ -29,4 +27,19 @@ describe("model RPC", () => {
expect(result).toEqual({ ok: true, data: { ok: false, affectedCells: [] } });
});
it("returns an empty model when no rows exist yet", async () => {
const result = await rpc("model.get", { companyId: "aapl", tab: "operating" });
expect(result).toEqual({ ok: true, data: { headers: [], rows: [] } });
});
it("returns a typed failure when model storage is unavailable", async () => {
db.prepare("DROP TABLE models").run();
const result = await rpc("model.get", { companyId: "aapl", tab: "operating" });
expect(result.ok).toBe(false);
if (result.ok) return;
expect(result.error.code).toBe("INTERNAL_ERROR");
expect(result.error.message).toBe("Could not load model for company.");
});
});