Commit 652c29ccea7f286a8ef85cdb5bb5c30e33a4b3e0

Authored by yibo
1 parent 4ccccfc3

Fix: uploads-route.test.js was deleting the real uploads/ directory

Spawned server.js with cwd: __dirname (the real repo path, not an
isolated copy), so its own test.after() cleanup — rm -rf on
"uploads/" — wiped every real uploaded file in the repo on every
`node --test` run, not just what the test itself created. Discovered
after running the suite several times today silently deleted this
session's real character/garment/dance-ref source images (the
generated dance videos in library.json were unaffected — those proxy
from H3, not served from disk — but admin/dance.html thumbnails broke
until the images were re-uploaded).

Now spawns from an fs.mkdtempSync isolated copy and cleans up only
that tmpdir, matching the pattern already used correctly in
dressup-generate.test.js and dance-channel.test.js.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011Deddn3sM3oQnmvqWSMb4y
Showing 1 changed file with 16 additions and 5 deletions
uploads-route.test.js
@@ -6,6 +6,7 @@ const test = require(&quot;node:test&quot;); @@ -6,6 +6,7 @@ const test = require(&quot;node:test&quot;);
6 const assert = require("node:assert/strict"); 6 const assert = require("node:assert/strict");
7 const fs = require("node:fs"); 7 const fs = require("node:fs");
8 const path = require("node:path"); 8 const path = require("node:path");
  9 +const os = require("node:os");
9 const { spawn } = require("node:child_process"); 10 const { spawn } = require("node:child_process");
10 const http = require("node:http"); 11 const http = require("node:http");
11 12
@@ -37,9 +38,22 @@ function waitForServer(url, timeoutMs = 10000) { @@ -37,9 +38,22 @@ function waitForServer(url, timeoutMs = 10000) {
37 } 38 }
38 39
39 let child; 40 let child;
  41 +let cwd;
40 test.before(async () => { 42 test.before(async () => {
  43 + // server.js resolves uploads/ relative to its own __dirname, not PORT —
  44 + // a past version of this test spawned server.js with cwd: __dirname
  45 + // (the real repo) and then rm -rf'd the whole uploads/ directory in
  46 + // test.after as "cleanup", which wiped real uploaded content on every
  47 + // `node --test` run. Spawn from an isolated tmpdir copy instead, same
  48 + // pattern as dressup-generate.test.js / dance-channel.test.js, so this
  49 + // test can never touch the real repo's uploads/.
  50 + cwd = fs.mkdtempSync(path.join(os.tmpdir(), "uploads-route-test-"));
  51 + for (const f of ["server.js", "ingest.js", "uploads.js", "store.js", "dressup-store.js", "prompt-template.js"]) {
  52 + fs.copyFileSync(path.join(__dirname, f), path.join(cwd, f));
  53 + }
  54 +
41 child = spawn(process.execPath, ["server.js"], { 55 child = spawn(process.execPath, ["server.js"], {
42 - cwd: __dirname, 56 + cwd,
43 // H3_CONSOLE_BASE cleared: this test only exercises the upload route, 57 // H3_CONSOLE_BASE cleared: this test only exercises the upload route,
44 // no need for the spawned copy to also hit the real H3 Console / 58 // no need for the spawned copy to also hit the real H3 Console /
45 // race the live pm2 instance on library.json's periodic sync. 59 // race the live pm2 instance on library.json's periodic sync.
@@ -58,10 +72,7 @@ test.before(async () =&gt; { @@ -58,10 +72,7 @@ test.before(async () =&gt; {
58 72
59 test.after(() => { 73 test.after(() => {
60 child.kill(); 74 child.kill();
61 - // uploads/ written by this test run lands under the real repo path  
62 - // (server.js resolves it relative to __dirname, not PORT) — clean it up.  
63 - const dir = path.join(__dirname, "uploads");  
64 - fs.rmSync(dir, { recursive: true, force: true }); 75 + fs.rmSync(cwd, { recursive: true, force: true });
65 }); 76 });
66 77
67 test("uploading a real mp4 via /api/admin/upload-video returns a URL that GET /uploads/* serves back as video/mp4", async () => { 78 test("uploading a real mp4 via /api/admin/upload-video returns a URL that GET /uploads/* serves back as video/mp4", async () => {