Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions client/src/lib/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,22 @@ const BARREL_SRC = readFileSync(join(HERE, 'index.js'), 'utf8');
const README_SRC = readFileSync(join(HERE, 'README.md'), 'utf8');

const sourceFiles = readdirSync(HERE).filter(
(f) => f.endsWith('.js') && !f.endsWith('.test.js') && f !== 'index.js',
(f) => (f.endsWith('.js') || f.endsWith('.jsx'))
&& !f.endsWith('.test.js') && !f.endsWith('.test.jsx')
&& f !== 'index.js',
);

describe('client/src/lib/ barrel', () => {
it('re-exports every non-test .js file from index.js', () => {
it('re-exports every non-test source file from index.js', () => {
expect(Object.keys(barrel).length).toBeGreaterThan(0);
for (const f of sourceFiles) {
expect(BARREL_SRC, `missing barrel re-export for ${f}`).toContain(`'./${f}'`);
}
});

it('every non-test .js file has a README row', () => {
it('every non-test source file has a README row', () => {
for (const f of sourceFiles) {
const base = f.replace(/\.js$/, '');
const base = f.replace(/\.jsx?$/, '');
expect(README_SRC, `missing README entry for ${f}`).toContain(base);
}
});
Expand Down
3 changes: 2 additions & 1 deletion server/lib/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ grep -i "what you want to do" server/lib/README.md
```

The barrel `server/lib/index.js` is a machine-checkable enumeration of every public surface;
`server/lib/index.test.js` verifies that every non-test `.js` file is re-exported AND appears in this README, AND that no two flat-exported modules share an identifier name.
`server/lib/index.test.js` verifies that every non-test `.js`/`.jsx` file is re-exported AND appears in this README, AND that no two flat-exported modules share an identifier name.

**Namespace exports.** The validation modules (`brainValidation`, `digitalTwinValidation`, etc.), `runners`, and `storyBible` are surfaced through the barrel as namespace exports — `barrel.brainValidation.settingsUpdateInputSchema`, not bare `settingsUpdateInputSchema` — because their generic names collide with peers. Direct deep imports (`import { settingsUpdateInputSchema } from './brainValidation.js'`) are unaffected.

Expand Down Expand Up @@ -414,6 +414,7 @@ The barrel `server/lib/index.js` is a machine-checkable enumeration of every pub
| `cudaCapability.js` | NVIDIA CUDA host probe — the one place that shells to `nvidia-smi`. `detectCudaGpus()` / cached `getCudaCapability()` return `{ available, status, gpus, maxVramGb }` where `status` is three-way: `'available'`, `'absent'` (no driver, or driver present with zero GPUs), or `'unknown'` (nvidia-smi exists but wouldn't answer — `available` is `null`, never `false`, so callers can say "couldn't detect" instead of lying about the hardware). `unknown` is not memoized so a transient driver hiccup retries. `parseNvidiaSmiGpus(stdout)` is the pure CSV parser (a `[N/A]` VRAM column yields `vramGb: null`, not a dropped GPU). Gates the image-to-3D `local-cuda` lane and the Windows FLUX.2 CUDA-torch wheel choice (`pythonSetup.js#hasNvidiaGpu`). `detectCudaComputeCapability()` / cached `getCudaComputeCapability()` are a SEPARATE `nvidia-smi` query (`--query-gpu=name,compute_cap,memory.total`, parsed by `parseNvidiaSmiComputeCaps`) returning `primaryComputeCap` — the arch of the LARGEST card, since a render uses one GPU — for build flags like NATTEN's `NATTEN_CUDA_ARCH` and SGLang provider filtering; kept separate because an older driver rejects `compute_cap` and would fail the whole VRAM query with it. `detectCudaUtilization()` / `getCudaUtilization()` add live per-GPU utilization on a short TTL. |
| `systemCapabilities.js` | Machine-local capability snapshot plus the shared three-state hardware requirement evaluator. Captures coarse platform, architecture, Apple Silicon, memory, CPU, and cached CUDA facts; derives requirements for media models, local-LLM catalog entries, and provider runtimes; hides only known-incompatible choices while preserving `unknown` probe results. |
| `db.js` | PostgreSQL connection pool. |
| `db/` | Boot DDL for the PostgreSQL schema, split per domain (#2832). `db/schema/index.js` re-exports each module's statement array and composes the two ordered lists `ensureSchemaImpl()` runs on every boot (`buildUpgradeDdl()` then `buildCatalogDdl()`). **Statement order is load-bearing** — append inside the domain module and leave the composer order alone. See `db/schema/README.md` for the module catalog. |
| `pgTimestamp.js` | `mirrorTimestamp(value, fallback)` — coerce a hand-editable timestamp into a value Postgres TIMESTAMPTZ always accepts (or fall back), guarding boot-time binds against `Date.parse` rollover + out-of-range years. |
| `pgTools.js` | `pg_dump` binary resolution shared by the backup snapshot path and the native↔Docker export path: `resolvePgDumpBinary(serverMajor)` (PORTOS_PGDUMP override → version-aware auto-select → bare `pg_dump`), plus the lower-level `pickPgDump` / `discoverPgDumpCandidates` / `resolvePgDump`. Picks the closest installed `pg_dump` whose major is ≥ the running server's. |
| `ports.js` | Canonical PORTS object (re-exported from `ecosystem.config.cjs`). |
Expand Down
101 changes: 101 additions & 0 deletions server/lib/db/schema/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/**
* Directory-driven guard for the schema composer (#5682).
*
* schema.test.js pins the composer against a HAND-WRITTEN list of module
* names, so it can only check the modules someone remembered to add to it.
* This file derives its expectations from `readdirSync` instead, which is the
* half that catches the real failure mode: adding
* `server/lib/db/schema/foo.js` and wiring only the `import` + `export {}`
* block ships a module whose `CREATE TABLE` never runs, so a fresh install
* (and every peer install upgrading) is missing the table and the feature
* fails at first query with a Postgres `relation does not exist`.
*
* The directory-level barrel/README guard in server/lib/index.test.js does not
* reach here — its `readdirSync` is non-recursive — hence this local copy of
* the same contract, modeled on server/lib/editorial/checkInfraBarrel.test.js.
*
* Out of scope: DDL statement text and ordering (schema.test.js and
* db.catalogDdlParity.test.js own those).
*/

import { describe, it, expect } from 'vitest';
import { readdirSync, readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';

import { buildUpgradeDdl, buildCatalogDdl } from './index.js';

const HERE = dirname(fileURLToPath(import.meta.url));
const BARREL_SRC = readFileSync(join(HERE, 'index.js'), 'utf8');
const README_SRC = readFileSync(join(HERE, 'README.md'), 'utf8');

const MODULE_FILES = readdirSync(HERE)
.filter((f) => f.endsWith('.js') && !f.endsWith('.test.js') && f !== 'index.js')
.sort();

// Membership is checked statement-by-statement rather than by array identity,
// because a module legitimately contributes more than one separately-positioned
// array (catalog.js → `catalogDdl` + `catalogUserTypesDdl`) and audit.js
// contributes `auditDdl` plus the generated `buildAuditTriggers()`. Checking
// EVERY statement — not just the array's first — also catches a composer that
// spreads only part of an array. Membership encodes no ordering: statement
// order stays schema.test.js's job.
const COMPOSED_STATEMENTS = new Set([...buildUpgradeDdl(), ...buildCatalogDdl()]);

const isBuilderName = (name) => /^build.*(Ddl|Triggers)$/.test(name);

describe('db/schema composer covers every module in the directory (#5682)', () => {
it('found the domain modules', () => {
expect(MODULE_FILES.length).toBeGreaterThan(0);
});

it('index.js imports every non-test module', () => {
for (const f of MODULE_FILES) {
expect(BARREL_SRC, `db/schema/${f} is never imported by index.js`).toContain(`'./${f}'`);
}
});

it.each(MODULE_FILES)('%s contributes DDL and every statement of it is composed', async (f) => {
const mod = await import(/* @vite-ignore */ `./${f}`);

// Two shapes contribute DDL here, and both must be checked: a plain
// statement array (`<domain>Ddl`) and a zero-arg generator that derives
// statements from a table list (audit.js's `buildAuditTriggers()`).
// Checking only the arrays would let a module's generated statements be
// dropped from the composer unnoticed.
const contributions = Object.entries(mod).flatMap(([name, value]) => {
if (name.endsWith('Ddl') && !isBuilderName(name)) return [[name, value]];
if (isBuilderName(name) && typeof value === 'function' && value.length === 0) {
return [[`${name}()`, value()]];
}
return [];
});

// Naming convention is the hook this guard hangs on: a module whose DDL
// export matches neither shape would silently opt out of the check below.
expect(
contributions.length,
`${f} exports no *Ddl array and no build*Ddl/build*Triggers() generator (see README.md)`,
).toBeGreaterThan(0);

for (const [name, statements] of contributions) {
expect(Array.isArray(statements), `${f} export '${name}' is not a statement array`).toBe(true);
expect(statements.length, `${f} export '${name}' is empty`).toBeGreaterThan(0);
const uncomposed = statements.filter((sql) => !COMPOSED_STATEMENTS.has(sql));
expect(
uncomposed.length,
`${f} export '${name}' has ${uncomposed.length} statement(s) that never run — spread it into `
+ `buildUpgradeDdl() or buildCatalogDdl() in index.js. First: ${uncomposed[0]?.slice(0, 120)}`,
).toBe(0);
}
});

it('every non-test module has a backtick-wrapped README row', () => {
// Require the documented-row form (`module.js`) rather than a bare
// substring, so a name appearing only in prose can't satisfy the guard.
for (const f of MODULE_FILES) {
expect(README_SRC, `missing README row for db/schema/${f}`).toContain('`' + f + '`');
}
});
});
// @vitest-environment node
10 changes: 6 additions & 4 deletions server/lib/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ const BARREL_SRC = readFileSync(join(HERE, 'index.js'), 'utf8');
const README_SRC = readFileSync(join(HERE, 'README.md'), 'utf8');

const sourceFiles = readdirSync(HERE)
.filter((f) => f.endsWith('.js') && !f.endsWith('.test.js') && f !== 'index.js');
.filter((f) => (f.endsWith('.js') || f.endsWith('.jsx'))
&& !f.endsWith('.test.js') && !f.endsWith('.test.jsx')
&& f !== 'index.js');

// The barrel is the machine-checkable enumeration of every public surface in
// `server/lib/`. If a `export * from './foo.js'` line points to a non-existent
Expand All @@ -18,7 +20,7 @@ const sourceFiles = readdirSync(HERE)
// a README row. Both halves keep the discovery contract in AGENTS.md honest.

describe('server/lib/ barrel', () => {
it('re-exports every non-test .js file from index.js', () => {
it('re-exports every non-test source file from index.js', () => {
// The `import * as barrel` above is the load: a star-export of a missing
// module throws before this body runs. The length check keeps `barrel` live
// so an unused-import lint can't delete that load.
Expand All @@ -30,12 +32,12 @@ describe('server/lib/ barrel', () => {
}
});

it('every non-test .js file has a README row', () => {
it('every non-test source file has a README row', () => {
// Forces the catalog parity: a new helper must also get a one-line
// README entry. Looser match (filename anywhere in the README) so the
// table format can evolve without breaking this guard.
for (const f of sourceFiles) {
const base = f.replace(/\.js$/, '');
const base = f.replace(/\.jsx?$/, '');
expect(README_SRC, `missing README entry for ${f}`).toContain(base);
}
});
Expand Down