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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"desktop:build": "tauri build",
"typecheck": "tsc --noEmit",
"check:auth": "node --experimental-strip-types scripts/check-auth-invariant.ts",
"test": "node --test --experimental-strip-types --test-reporter=spec 'scripts/**/*.test.ts' 'src/**/*.test.ts'",
"test": "node --import ./scripts/register-test-aliases.mjs --test --experimental-strip-types --test-reporter=spec 'scripts/**/*.test.ts' 'src/**/*.test.ts'",
"lint": "eslint .",
"format": "prettier --write ."
},
Expand Down
8 changes: 8 additions & 0 deletions scripts/register-test-aliases.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Loaded via `node --import` before the test runner starts. Registers the `@/*` alias resolve
* hook (test-alias-hook.mjs) so `.server.ts` unit tests can import modules that use the app's
* `@/...` import alias, which only Vite/tsc understand natively.
*/
import { register } from "node:module";

register("./test-alias-hook.mjs", import.meta.url);
26 changes: 26 additions & 0 deletions scripts/test-alias-hook.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Node module customization hook: resolves the `@/*` -> `./src/*` alias that Vite and tsconfig
* already define (tsconfig.json compilerOptions.paths), so `.server.ts` modules that import
* sibling app code via `@/...` can be loaded directly by `node --test` (which has no concept of
* bundler path aliases). Registered by scripts/register-test-aliases.mjs.
*/
import { existsSync } from "node:fs";
import { fileURLToPath, pathToFileURL } from "node:url";

const SRC_ROOT = new URL("../src/", import.meta.url);

function resolveAliasedPath(rel) {
const asIs = new URL(rel, SRC_ROOT);
if (existsSync(fileURLToPath(asIs))) return asIs;
const withExt = new URL(`${rel}.ts`, SRC_ROOT);
if (existsSync(fileURLToPath(withExt))) return withExt;
return asIs;
}

export async function resolve(specifier, context, nextResolve) {
if (specifier.startsWith("@/")) {
const target = resolveAliasedPath(specifier.slice(2));
return nextResolve(pathToFileURL(fileURLToPath(target)).href, context);
}
return nextResolve(specifier, context);
}
56 changes: 38 additions & 18 deletions src/lib/server/library-storage.server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,43 @@ process.env.AGENCY_LIBRARY_ROOT = await mkdtemp(join(tmpdir(), "clippy-lib-"));
delete process.env.SUPABASE_SERVICE_ROLE_KEY;
delete process.env.SUPABASE_SECRET_KEY;

const { writeLibraryFile, backendFromStorageKey, storagePath } =
await import("./library-storage.server.ts");
// library-storage.server.ts statically imports @/lib/server/app-settings.server, which
// transitively imports src/lib/db.ts. db.ts calls Vite's `import.meta.glob(...)` at module top
// level to inline migrations/*.sql at build time — a bundler-only construct with no meaning
// under plain `node --test`, even with the @/ alias resolved (see scripts/test-alias-hook.mjs).
// Fixing that would mean changing db.ts's migration loading for every caller, well outside this
// file's scope. Real coverage of writeLibraryFile/backendFromStorageKey runs under the real Vite
// runtime via the Task 10 live-app integration check (docs/superpowers/plans/2026-09-16-clips-to-library.md).
let mod: typeof import("./library-storage.server.ts") | null = null;
let loadError: Error | null = null;
try {
mod = await import("./library-storage.server.ts");
} catch (error) {
loadError = error instanceof Error ? error : new Error(String(error));
}

test("backendFromStorageKey reads the prefix", () => {
assert.equal(backendFromStorageKey("supabase:a/b.mp4"), "supabase");
assert.equal(backendFromStorageKey("s3:a/b.mp4"), "s3");
assert.equal(backendFromStorageKey("/tmp/agency-library/a/b.mp4"), "local");
});
if (!mod) {
test("library-storage.server.ts (skipped: unloadable under node --test)", {
skip: `Cannot load this module outside Vite: ${loadError?.message.split("\n")[0]}`,
}, () => {});
} else {
const { writeLibraryFile, backendFromStorageKey, storagePath } = mod;

test("writeLibraryFile streams a 20 MB file into the local backend without loading it", async () => {
const src = join(process.env.AGENCY_LIBRARY_ROOT!, "src.bin");
await writeFile(src, Buffer.alloc(20 * 1024 * 1024, 7));
const before = process.memoryUsage().heapUsed;
const result = await writeLibraryFile("asset-1/v1.mp4", src, "video/mp4");
const after = process.memoryUsage().heapUsed;
assert.equal(result, storagePath("asset-1/v1.mp4"));
assert.equal((await stat(result)).size, 20 * 1024 * 1024);
assert.ok(after - before < 15 * 1024 * 1024, "file was buffered into memory");
assert.equal((await readFile(result)).subarray(0, 4).toString("hex"), "07070707");
});
test("backendFromStorageKey reads the prefix", () => {
assert.equal(backendFromStorageKey("supabase:a/b.mp4"), "supabase");
assert.equal(backendFromStorageKey("s3:a/b.mp4"), "s3");
assert.equal(backendFromStorageKey("/tmp/agency-library/a/b.mp4"), "local");
});

test("writeLibraryFile streams a 20 MB file into the local backend without loading it", async () => {
const src = join(process.env.AGENCY_LIBRARY_ROOT!, "src.bin");
await writeFile(src, Buffer.alloc(20 * 1024 * 1024, 7));
const before = process.memoryUsage().heapUsed;
const result = await writeLibraryFile("asset-1/v1.mp4", src, "video/mp4");
const after = process.memoryUsage().heapUsed;
assert.equal(result, storagePath("asset-1/v1.mp4"));
assert.equal((await stat(result)).size, 20 * 1024 * 1024);
assert.ok(after - before < 15 * 1024 * 1024, "file was buffered into memory");
assert.equal((await readFile(result)).subarray(0, 4).toString("hex"), "07070707");
});
}
Loading