Skip to content

Commit 8be967c

Browse files
GabrielDraporclaude
andcommitted
test(self-host): make code lookup robust for containerised runs
Verifying against Docker surfaced a harness race: a container's stdout reaches the tailed log file with a lag, and reruns leave older codes behind. The verifier now polls for the code and takes the newest match, and accepts OVID_DB=external when the caller seeded the database (as inside a container). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HTapNECBpagy5qnQpGhtKF
1 parent 4d7a676 commit 8be967c

1 file changed

Lines changed: 27 additions & 3 deletions

File tree

scripts/verify-selfhost.mjs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ const j = async (u, o) => {
2828
// Seed a small bilingual book directly through SQLite so the read path can be
2929
// checked without needing an LLM key or a real import.
3030
async function seedBook() {
31+
// OVID_DB=external: the caller seeded already (e.g. the book was inserted
32+
// inside a container), so just use what's there.
33+
if (DB === 'external') return;
3134
const { DatabaseSync } = await import('node:sqlite');
3235
const d = new DatabaseSync(DB);
3336
const existing = d
@@ -87,6 +90,29 @@ async function seedBook() {
8790
}
8891
await seedBook();
8992

93+
/**
94+
* Read the newest sign-in code for an address out of the server log.
95+
* Log tailing is asynchronous (a container's stdout reaches the file with a
96+
* lag), so poll briefly; always take the *last* match, since reruns leave
97+
* older codes behind.
98+
*/
99+
async function readLoginCode(fs, email, timeoutMs = 5000) {
100+
const escaped = email.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
101+
const deadline = Date.now() + timeoutMs;
102+
for (;;) {
103+
let text = '';
104+
try {
105+
text = fs.readFileSync(LOG, 'utf8');
106+
} catch {
107+
/* log not created yet */
108+
}
109+
const matches = [...text.matchAll(new RegExp(escaped + ': (\\d{6})', 'g'))];
110+
if (matches.length) return matches[matches.length - 1][1];
111+
if (Date.now() > deadline) return null;
112+
await new Promise((r) => setTimeout(r, 200));
113+
}
114+
}
115+
90116
console.log('\n[1] 书架与书籍');
91117
let r = await j('/api/v2/books');
92118
ck(
@@ -140,9 +166,7 @@ const st = await fetch(BASE + '/api/auth/email/start', {
140166
body: JSON.stringify({ email: 'reader2@example.com' }),
141167
});
142168
const fs = await import('node:fs');
143-
const code = (fs
144-
.readFileSync(LOG, 'utf8')
145-
.match(/reader2@example\.com: (\d{6})/) || [])[1];
169+
const code = await readLoginCode(fs, 'reader2@example.com');
146170
const vr = await fetch(BASE + '/api/auth/email/verify', {
147171
method: 'POST',
148172
headers: { 'Content-Type': 'application/json' },

0 commit comments

Comments
 (0)