storage-admin.integration.test.ts84 lines · main
1/**
2 * Storage admin against REAL DoltGres — sprint plan Sprint 4.
3 *
4 * Phase 1: getProjectRowCount counts user tables + total rows correctly on
5 * DoltGres and excludes _briven_* platform tables. (Bytes stay unmeasurable on
6 * DoltGres, so rows + tables are what we report.)
7 *
8 * Skips when BRIVEN_DATA_PLANE_URL is unset.
9 */
10import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
11import pg from 'pg';
12
13import {
14 closeProjectDbPools,
15 dropProjectDatabase,
16 provisionProjectDatabase,
17 runInProjectDatabase,
18} from '../db/data-plane.js';
19import { getProjectRowCount } from './storage-admin.js';
20
21const URL = process.env.BRIVEN_DATA_PLANE_URL;
22const HAS_DB = Boolean(URL);
23const PROJECT_ID = `p_stor${Date.now().toString(36)}`;
24
25describe.skipIf(!HAS_DB)('storage-admin getProjectRowCount on real DoltGres (Sprint 4)', () => {
26 beforeAll(async () => {
27 await provisionProjectDatabase(PROJECT_ID);
28 await runInProjectDatabase(PROJECT_ID, async (tx) => {
29 await tx.unsafe('SET dolt_transaction_commit = 1');
30 await tx.unsafe(`CREATE TABLE "widgets" (id integer PRIMARY KEY, label text)`);
31 await tx.unsafe(`CREATE TABLE "gadgets" (id integer PRIMARY KEY)`);
32 await tx.unsafe(`INSERT INTO "widgets" (id, label) VALUES (1,'a'),(2,'b'),(3,'c')`);
33 await tx.unsafe(`INSERT INTO "gadgets" (id) VALUES (10),(20)`);
34 });
35 // NOTE: provisionProjectDatabase already creates the platform _briven_*
36 // tables (_briven_meta, _briven_migrations). Those exercise the exclusion
37 // filter for free — getProjectRowCount must NOT count them.
38 });
39
40 afterAll(async () => {
41 await dropProjectDatabase(PROJECT_ID).catch(() => {});
42 await closeProjectDbPools().catch(() => {});
43 });
44
45 test('counts user tables + total rows, excluding _briven_*', async () => {
46 const { rowCount, tableCount } = await getProjectRowCount(PROJECT_ID);
47 expect(tableCount).toBe(2); // widgets + gadgets; _briven_meta excluded
48 expect(rowCount).toBe(5); // 3 widgets + 2 gadgets; _briven_meta row excluded
49 });
50
51 test('returns zeros (no throw) for an unprovisioned project', async () => {
52 const res = await getProjectRowCount('p_does_not_exist_zzz');
53 expect(res).toEqual({ rowCount: 0, tableCount: 0 });
54 });
55
56 // Migration 0033 runs against the CONTROL-plane DB, which is DoltGres too.
57 // Prove its DDL is DoltGres-compatible: the tier_storage_caps table, the
58 // idempotent ON CONFLICT seed, and ADD COLUMN bigint (against a stand-in for
59 // `projects`, which doesn't exist in the bare data-plane container).
60 test('migration 0033 DDL is DoltGres-safe', async () => {
61 const c = new pg.Client({ connectionString: URL });
62 await c.connect();
63 try {
64 await c.query(`DROP TABLE IF EXISTS "tier_storage_caps"`);
65 await c.query(
66 `CREATE TABLE IF NOT EXISTS "tier_storage_caps" ("tier" text PRIMARY KEY NOT NULL, "max_rows" bigint NOT NULL, "max_tables" bigint NOT NULL, "updated_at" timestamp with time zone DEFAULT now() NOT NULL, "updated_by" text)`,
67 );
68 const seed = `INSERT INTO "tier_storage_caps" ("tier","max_rows","max_tables") VALUES ('free',100000,50),('pro',5000000,500),('team',50000000,5000) ON CONFLICT ("tier") DO NOTHING`;
69 await c.query(seed);
70 await c.query(seed); // idempotent — DoltGres supports ON CONFLICT DO NOTHING
71 const r = await c.query(`SELECT tier FROM "tier_storage_caps" ORDER BY tier`);
72 expect(r.rows.map((x) => x.tier)).toEqual(['free', 'pro', 'team']);
73
74 await c.query(`DROP TABLE IF EXISTS "_proj_addcol_test"`);
75 await c.query(`CREATE TABLE "_proj_addcol_test" (id text PRIMARY KEY)`);
76 await c.query(`ALTER TABLE "_proj_addcol_test" ADD COLUMN "storage_max_rows" bigint`);
77 await c.query(`ALTER TABLE "_proj_addcol_test" ADD COLUMN "storage_max_tables" bigint`);
78 } finally {
79 await c.query(`DROP TABLE IF EXISTS "_proj_addcol_test"`).catch(() => {});
80 await c.query(`DROP TABLE IF EXISTS "tier_storage_caps"`).catch(() => {});
81 await c.end();
82 }
83 });
84});