test/client-invariants.test.js
Rama master · réplica de lectura del repositorio original.
// Invariant pin for src/client.js — the boundaries a silent regression would hide.
//
// WHY THIS FILE IS ONE PLACE
//
// src/client.js is ~73 KB of transport behaviour whose assertions live scattered across
// client-content, client-panel-retry, stream-stall, stream-error,
// report-error-transient-guard, v2069/v2070/v2074/v2079-issue-fixes and
// workspace-stub-108 — eight partial files for one module, which is why it carried no
// mutation spec at all. test/mutations/client-invariants.json pins the invariants below
// through THIS file only, so a mutation verdict never depends on cross-file aggregation.
//
// WHY THESE ASSERTIONS
//
// - WebFetch auto-approve. This decides whether a model-initiated web fetch executes
// with no prompt in front of the user. Two independent ways to get it wrong: the
// feature turning itself on without the enable flag, and the declared request origin
// not binding the URL that is actually allowed through.
// - Binary / image placeholders. Cascade has no image channel in the text history it
// replays, so a block that falls through to JSON.stringify ships the caller's base64
// body into the prompt.
// - contentToString object fallback. Returning '' instead of the serialized block does
// not throw: it deletes a whole turn from the prompt and the model answers a
// different conversation.
// - CASCADE_COMPACT_CLAUDE_SYSTEM=0. The documented escape hatch; if it stops working
// the operator has no way back to "send the system prompt as written".
// - isCascadeTransportError. Feeds isUpstreamTransientError and the warmup recovery
// path: a false negative turns a recoverable HTTP/2 cancel into a failed request,
// and a lost "panel state" token leaves the LS session poisoned.
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import {
isCascadeTransportError,
isReadUrlAutoApproveAllowed,
compactSystemPromptForCascade,
contentToString,
} from '../src/client.js';
const ENV_AUTO_APPROVE = 'WINDSURFAPI_NATIVE_TOOL_BRIDGE_WEBFETCH_AUTO_APPROVE';
const ENV_AUTO_APPROVE_ORIGINS = 'WINDSURFAPI_NATIVE_TOOL_BRIDGE_WEBFETCH_AUTO_APPROVE_ORIGINS';
/** Set env vars for one call, then restore exactly (including "was unset"). */
function withEnv(vars, fn) {
const saved = new Map();
for (const [name, value] of Object.entries(vars)) {
saved.set(name, Object.prototype.hasOwnProperty.call(process.env, name)
? process.env[name]
: undefined);
if (value === undefined) delete process.env[name];
else process.env[name] = value;
}
try {
return fn();
} finally {
for (const [name, value] of saved) {
if (value === undefined) delete process.env[name];
else process.env[name] = value;
}
}
}
describe('WebFetch auto-approve is opt-in and origin-bound', () => {
it('stays OFF when the enable flag is unset even though origins are configured', () => {
withEnv({ [ENV_AUTO_APPROVE]: undefined, [ENV_AUTO_APPROVE_ORIGINS]: 'https://example.com' }, () => {
assert.equal(isReadUrlAutoApproveAllowed('https://example.com/a', ''), false);
assert.equal(isReadUrlAutoApproveAllowed('https://example.com/a', 'https://example.com'), false);
});
});
it('stays OFF for a value that is not exactly "1"', () => {
withEnv({ [ENV_AUTO_APPROVE]: 'true', [ENV_AUTO_APPROVE_ORIGINS]: 'https://example.com' }, () => {
assert.equal(isReadUrlAutoApproveAllowed('https://example.com/a', ''), false);
});
});
it('refuses when the origin the request declares is not the URL origin', () => {
withEnv({ [ENV_AUTO_APPROVE]: '1', [ENV_AUTO_APPROVE_ORIGINS]: 'https://example.com' }, () => {
assert.equal(isReadUrlAutoApproveAllowed('https://example.com/a', 'https://evil.example'), false);
assert.equal(isReadUrlAutoApproveAllowed('https://example.com/a', 'https://example.com'), true);
});
});
it('treats a bare-origin allowlist entry as the whole origin, not one exact URL', () => {
withEnv({ [ENV_AUTO_APPROVE]: '1', [ENV_AUTO_APPROVE_ORIGINS]: 'https://example.com' }, () => {
assert.equal(isReadUrlAutoApproveAllowed('https://example.com/deep/path?q=1', ''), true);
assert.equal(isReadUrlAutoApproveAllowed('https://example.com/#frag', ''), true);
});
});
it('does not extend a path-scoped allowlist entry to its children', () => {
withEnv({ [ENV_AUTO_APPROVE]: '1', [ENV_AUTO_APPROVE_ORIGINS]: 'https://docs.example.com/path' }, () => {
assert.equal(isReadUrlAutoApproveAllowed('https://docs.example.com/path', ''), true);
assert.equal(isReadUrlAutoApproveAllowed('https://docs.example.com/path/child', ''), false);
assert.equal(isReadUrlAutoApproveAllowed('https://docs.example.com/other', ''), false);
});
});
it('requires an allowlist entry and a canonical http(s) URL', () => {
withEnv({ [ENV_AUTO_APPROVE]: '1', [ENV_AUTO_APPROVE_ORIGINS]: '' }, () => {
assert.equal(isReadUrlAutoApproveAllowed('https://example.com/a', ''), false);
});
withEnv({ [ENV_AUTO_APPROVE]: '1', [ENV_AUTO_APPROVE_ORIGINS]: 'https://example.com' }, () => {
assert.equal(isReadUrlAutoApproveAllowed('ftp://example.com/a', ''), false);
assert.equal(isReadUrlAutoApproveAllowed('https://user:pw@example.com/a', ''), false);
assert.equal(isReadUrlAutoApproveAllowed('', ''), false);
});
});
});
describe('Cascade text history cannot carry binary or image bodies', () => {
it('replaces an unknown block whose data field is a long base64 body', () => {
const base64 = 'A'.repeat(200);
assert.equal(
contentToString([{ type: 'audio', data: base64 }]),
'[Binary content omitted from text history]',
);
});
it('replaces an image block that carries no image type field', () => {
assert.equal(
contentToString([{ media_type: 'image/png', data: 'AAAA' }]),
'[Image omitted from text history]',
);
assert.equal(
contentToString([{ source: { type: 'base64', media_type: 'image/png', data: 'AAAA' } }]),
'[Image omitted from text history]',
);
});
it('keeps plain text blocks, images and unknown blocks distinct', () => {
const out = contentToString([
{ type: 'text', text: 'alpha' },
{ type: 'image', image_url: 'https://example.com/x.png' },
{ type: 'tool_result', content: 'beta' },
]);
assert.ok(out.startsWith('alpha'));
assert.ok(out.includes('[Image omitted from text history]'));
assert.ok(out.includes('"tool_result"'));
});
it('serializes a non-string, non-array content instead of dropping the turn', () => {
assert.equal(contentToString({ type: 'text', text: 'hello' }), '{"type":"text","text":"hello"}');
assert.equal(contentToString(7), '7');
assert.equal(contentToString(null), '');
assert.equal(contentToString(undefined), '');
assert.equal(contentToString('plain'), 'plain');
});
});
describe('Claude Code system-prompt compaction keeps its escape hatch', () => {
const CC_PROMPT = [
"You are Claude Code, Anthropic's official CLI for Claude.",
'Tool protocol details: content_block tool_use tool_result '.repeat(120),
'Working directory: /srv/distinctive-project',
'Platform: linux',
].join('\n');
it('compacts a long Claude Code prompt while keeping the environment facts', () => {
withEnv({ CASCADE_COMPACT_CLAUDE_SYSTEM: undefined }, () => {
const out = compactSystemPromptForCascade(CC_PROMPT);
assert.ok(out.length < 2000, `expected a compact prompt, got ${out.length} chars`);
assert.ok(out.includes('Working directory: /srv/distinctive-project'));
});
});
it('returns the caller prompt nearly verbatim when the switch is "0"', () => {
withEnv({ CASCADE_COMPACT_CLAUDE_SYSTEM: '0' }, () => {
const out = compactSystemPromptForCascade(CC_PROMPT);
assert.ok(
out.length > CC_PROMPT.length * 0.9,
`expected the un-compacted prompt (${CC_PROMPT.length} chars), got ${out.length}`,
);
assert.ok(out.includes('Tool protocol details'));
assert.ok(out.includes('Working directory: /srv/distinctive-project'));
});
});
});
describe('Cascade transport classification covers the recovery tokens', () => {
it('classifies HTTP/2 cancellation, session loss and panel-state loss as transport', () => {
assert.equal(isCascadeTransportError(new Error('panel state not found for session abc')), true);
assert.equal(isCascadeTransportError(new Error('Http2Session closed with error')), true);
assert.equal(isCascadeTransportError(new Error('The pending stream has been canceled')), true);
assert.equal(
isCascadeTransportError(Object.assign(new Error('unauthenticated'), { code: 'ERR_HTTP2_STREAM_ERROR' })),
true,
);
});
it('does not classify a model-level refusal as transport', () => {
assert.equal(isCascadeTransportError(new Error('permission_denied: model unavailable')), false);
assert.equal(isCascadeTransportError(new Error('untrusted workspace')), false);
});
});