Files
vdm/extension/tests/lint/no-download-logic.test.ts
samiandClaude Sonnet 5 2cb1959bff ext: no-download-logic ESLint gate in the extension-lint CI job
CLAUDE.md §3's rule was prose only for extension/ (GUI already has
gui_no_download_logic as a ctest). eslint.config.mjs adds a
no-restricted-syntax/no-restricted-globals rule banning fetch/XHR/Request,
ReadableStream.getReader, Range/Content-Range header construction, and
IndexedDB in src/**/*.ts. Verified red on a planted violation (fetch +
Range header + stream reader) and green on ordinary code; that check is
now a permanent regression test (tests/lint/no-download-logic.test.ts)
rather than a one-off manual run. Wired into the existing extension-lint
job in .github/workflows/ci.yml, ahead of web-ext lint.

Generated protocol code (src/shared/protocol/**) is excluded from lint
entirely — it must never be hand-edited, so flagging it as fixable would
be a lie.

Answers gui/docs/ext-requests-m1.md.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Ed8KEmAW48v4YHdxLtqsMB
2026-09-11 12:25:19 +04:00

41 lines
1.6 KiB
TypeScript

// Regression test for the "no download logic in extension/" ESLint gate
// (eslint.config.mjs, answering gui/docs/ext-requests-m1.md). Runs ESLint's Node API
// directly against fixture source so a future edit to the rule set can't silently stop
// catching the patterns it was written for.
import { ESLint } from 'eslint';
import { describe, expect, it } from 'vitest';
async function lint(code: string): Promise<number> {
const eslint = new ESLint({ cwd: new URL('../..', import.meta.url).pathname });
// Path only needs to match the `files: ['src/**/*.ts']` glob in eslint.config.mjs.
const [result] = await eslint.lintText(code, { filePath: 'src/background/__fixture.ts' });
return result.messages.filter((m) => m.severity === 2).length;
}
describe('no-download-logic ESLint gate', () => {
it('goes red on fetch() + a hand-built Range header + a stream reader', async () => {
const errors = await lint(`
export async function grabBytes(url: string) {
const res = await fetch(url, { headers: { Range: 'bytes=0-1023' } });
const reader = res.body!.getReader();
return reader.read();
}
`);
expect(errors).toBeGreaterThan(0);
});
it('goes red on XMLHttpRequest', async () => {
const errors = await lint(`const x = new XMLHttpRequest();`);
expect(errors).toBeGreaterThan(0);
});
it('stays green on ordinary transport/RPC code', async () => {
const errors = await lint(`
export function greet(name: string): string {
return \`hello \${name}\`;
}
`);
expect(errors).toBe(0);
});
});