* test(obsidivm): RED — bridge should give an actionable error when the service is down The obsidivm:* benchmarks fetch from the OBSIDIVM range service; when it is not running the bridge throws a bare 'fetch failed' (surfaced as 'FATAL: fetch failed'), naming neither the service, the URL, nor how to start it. Pins that a connection failure becomes an actionable, self-service error. Fails on main: the message is 'fetch failed'. Refs #156 * fix(obsidivm): actionable error when the range service is unreachable The obsidivm:* benchmarks fetch from the OBSIDIVM range service; when it is not running the bridge's shared call() let the raw 'fetch failed' propagate, which surfaced as an opaque 'FATAL: fetch failed' (#156) naming neither the service, the URL, nor how to start it — even in --hunter stub, whose grading step still needs it. Wrap the fetch in call() (the single choke point for every OBSIDIVM request) so a connection failure becomes: 'OBSIDIVM service unreachable at <url> ... Start it per docs/OBSIDIVM.md (python3 range.py, listens on :4200), or set OBSIDIVM_URL', flagged err.unreachable to distinguish it from an HTTP-status error; a timeout is reported distinctly. Clarify in the bench --help/usage that every hunter mode (stub included) needs the OBSIDIVM service, live/t3mp3st also a key. Adds scripts/obsidivm-bridge.d.mts so the new test types cleanly. Closes #156 * fix(obsidivm): distinguish invalid-URL from unreachable; test timeout path Adversarial review of the diff found two gaps: - A malformed OBSIDIVM_URL (ERR_INVALID_URL) was folded into the 'service unreachable — is it running?' path, misdirecting the user to start a service when the real fix is the URL. Now branched to a distinct 'Invalid OBSIDIVM base URL … set OBSIDIVM_URL' error, no err.unreachable flag. - The timeout (AbortError) branch had no coverage. Added a test using a never-responding TCP server + short timeoutMs asserting 'timed out after Nms' and NOT unreachable; and a test for the invalid-URL branch. Also completed the .d.mts stub with the cloudgoat* methods it was missing. Refs #156
This commit is contained in:
1 parent
c352f34843
commit
41f0c14039
4 files changed
+163
-8
No files matched your search
@@ -31,12 +31,40 @@ export function obsidivm({ baseUrl, timeoutMs } = {}) {
|
||||
const ctrl = new AbortController();
|
||||
const timer = setTimeout(() => ctrl.abort(), t);
|
||||
try {
|
||||
const res = await fetch(url, {
|
||||
method,
|
||||
headers: body ? { 'content-type': 'application/json' } : {},
|
||||
body: body ? JSON.stringify(body) : undefined,
|
||||
signal: ctrl.signal,
|
||||
});
|
||||
let res;
|
||||
try {
|
||||
res = await fetch(url, {
|
||||
method,
|
||||
headers: body ? { 'content-type': 'application/json' } : {},
|
||||
body: body ? JSON.stringify(body) : undefined,
|
||||
signal: ctrl.signal,
|
||||
});
|
||||
} catch (e) {
|
||||
// The fetch itself failed — distinct from an HTTP-status error below. A
|
||||
// bare `fetch failed` here is the #156 dead-end; make it self-service.
|
||||
// Three sub-cases, kept distinct so the message points at the real fix:
|
||||
// AbortError → the request timed out
|
||||
// ERR_INVALID_URL → OBSIDIVM_URL is malformed (a config error, NOT a
|
||||
// down service — don't send the user to start one)
|
||||
// otherwise → nothing is listening on the base URL
|
||||
if (e?.name === 'AbortError') {
|
||||
throw new Error(`OBSIDIVM ${method} ${path} timed out after ${t}ms (${base}).`);
|
||||
}
|
||||
if (e?.cause?.code === 'ERR_INVALID_URL') {
|
||||
throw new Error(
|
||||
`Invalid OBSIDIVM base URL ${JSON.stringify(base)} — set OBSIDIVM_URL (or --obsidivm) ` +
|
||||
`to a full URL like http://127.0.0.1:4200.`,
|
||||
);
|
||||
}
|
||||
const err = new Error(
|
||||
`OBSIDIVM service unreachable at ${base} — ${method} ${path} failed ` +
|
||||
`(${e?.cause?.code || e?.message || 'network error'}). Is it running? ` +
|
||||
`Start it per docs/OBSIDIVM.md (python3 range.py, listens on :4200), or set OBSIDIVM_URL.`,
|
||||
);
|
||||
err.unreachable = true;
|
||||
err.cause = e;
|
||||
throw err;
|
||||
}
|
||||
const text = await res.text();
|
||||
let parsed;
|
||||
try { parsed = JSON.parse(text); } catch { parsed = { raw: text }; }
|
||||
|
||||
Reference in new issue
Block a user