mirror of
https://github.com/affaan-m/ECC.git
synced 2026-11-07 08:35:54 +00:00
Verify and address PR #3809 discussions 4419228221, 4419257840 and 4419257844. Iterate string-array assertions through sparse positions, validate the declared example identifier grammar before request construction, and refresh Turkish/Chinese reviewer references. Regenerate the canonical Pi copy. Compile and execute the actual typed fixture; keep published validation bodies aligned and prove invalid identifiers never call fetch. RED: 28 pass, 2 fail. GREEN: 30 pass, 0 fail. Strict tsc, changed ESLint/Markdownlint, Unicode/agent/skill validators and deterministic Pi checks pass. Parent independently reviewed all six paths. Registry rebinding is deferred to the combined integration.
325 lines
13 KiB
JavaScript
325 lines
13 KiB
JavaScript
'use strict';
|
|
|
|
// Tests for the typescript-patterns skill: the SKILL.md examples must match
|
|
// their strict-TypeScript claims. Compile the real fixture and execute its
|
|
// emitted functions so the type checks and runtime regressions share one source.
|
|
|
|
const assert = require('assert');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const ts = require('typescript');
|
|
|
|
const repoRoot = path.resolve(__dirname, '..', '..');
|
|
const skillPath = path.join(repoRoot, 'skills', 'typescript-patterns', 'SKILL.md');
|
|
const skill = fs.readFileSync(skillPath, 'utf8');
|
|
|
|
let passed = 0;
|
|
let failed = 0;
|
|
const pending = [];
|
|
|
|
function test(name, fn) {
|
|
try {
|
|
const result = fn();
|
|
if (result && typeof result.then === 'function') {
|
|
pending.push(result.then(
|
|
() => { passed += 1; console.log(` ✓ ${name}`); },
|
|
error => { failed += 1; console.log(` ✗ ${name}`); console.log(` ${error.message}`); }
|
|
));
|
|
return;
|
|
}
|
|
passed += 1;
|
|
console.log(` ✓ ${name}`);
|
|
} catch (error) {
|
|
failed += 1;
|
|
console.log(` ✗ ${name}`);
|
|
console.log(` ${error.message}`);
|
|
}
|
|
}
|
|
|
|
console.log('\n=== TypeScript patterns skill tests ===\n');
|
|
|
|
// ---- Skill document claims ---------------------------------------------
|
|
|
|
test('loadUser validates instead of asserting the payload', () => {
|
|
assert.doesNotMatch(skill, /response\.json\(\) as Promise<User>/);
|
|
assert.match(skill, /function isUser\(value: unknown\): value is User/);
|
|
});
|
|
|
|
test('config example validates every consumed field', () => {
|
|
assert.doesNotMatch(skill, /value as Config/);
|
|
assert.match(skill, /toPositiveInt\(raw\['timeout'\]/);
|
|
assert.match(skill, /toPositiveInt\(raw\['retries'\]/);
|
|
assert.match(skill, /DEFAULT_CONFIG/);
|
|
});
|
|
|
|
test('concurrency example narrows indexed access', () => {
|
|
assert.doesNotMatch(skill, /mapper\(items\[index\]\)/);
|
|
assert.match(skill, /Number\.isInteger\(limit\) \|\| limit < 1/);
|
|
assert.match(skill, /noUncheckedIndexedAccess/);
|
|
});
|
|
|
|
test('async error prose states the real semantics', () => {
|
|
assert.match(skill, /always surfaces throws as rejections/);
|
|
assert.doesNotMatch(skill, /lets them escape as rejected promises inconsistently/);
|
|
});
|
|
|
|
// ---- Compile and execute the actual strict TypeScript fixture ----------
|
|
|
|
const fixturePath = path.join(repoRoot, 'tests', 'fixtures', 'typescript-patterns', 'examples.ts');
|
|
const configPath = path.join(path.dirname(fixturePath), 'tsconfig.json');
|
|
const config = ts.readConfigFile(configPath, ts.sys.readFile);
|
|
const parsedConfig = ts.parseJsonConfigFileContent(config.config, ts.sys, path.dirname(configPath));
|
|
const program = ts.createProgram(parsedConfig.fileNames, parsedConfig.options);
|
|
|
|
test('actual fixture compiles with the recommended index-signature baseline', () => {
|
|
assert.strictEqual(parsedConfig.options.noPropertyAccessFromIndexSignature, true);
|
|
const diagnostics = [config.error, ...parsedConfig.errors, ...ts.getPreEmitDiagnostics(program)].filter(Boolean);
|
|
assert.deepStrictEqual(diagnostics.map(d => ts.flattenDiagnosticMessageText(d.messageText, '\n')), []);
|
|
});
|
|
|
|
test('compiled validation and concurrency bodies match the published examples', () => {
|
|
const printer = ts.createPrinter({ removeComments: true });
|
|
function bodies(source, filename) {
|
|
const file = ts.createSourceFile(filename, source, ts.ScriptTarget.Latest, true);
|
|
const found = new Map();
|
|
function visit(node) {
|
|
if (ts.isFunctionDeclaration(node) && node.name && node.body) {
|
|
found.set(node.name.text, printer.printNode(ts.EmitHint.Unspecified, node.body, file));
|
|
}
|
|
ts.forEachChild(node, visit);
|
|
}
|
|
visit(file);
|
|
return found;
|
|
}
|
|
const documented = [...skill.matchAll(/```typescript\n([\s\S]*?)```/g)].map(match => match[1]).join('\n');
|
|
const docs = bodies(documented, 'skill.ts');
|
|
const fixtures = bodies(fs.readFileSync(fixturePath, 'utf8'), fixturePath);
|
|
for (const name of ['parseConfig', 'assertIsStringArray', 'assertUserId', 'isUser', 'loadUser', 'mapWithConcurrency']) {
|
|
assert.ok(docs.has(name), `${name} is missing from the skill`);
|
|
const fixtureBody = name === 'loadUser' ? fixtures.get(name).replace(/\bfetchJson\b/g, 'fetch') : fixtures.get(name);
|
|
assert.strictEqual(fixtureBody, docs.get(name), `${name} fixture drifted from the published example`);
|
|
}
|
|
});
|
|
|
|
const emitted = ts.transpileModule(fs.readFileSync(fixturePath, 'utf8'), {
|
|
compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.CommonJS },
|
|
}).outputText;
|
|
const fixtureExports = {};
|
|
// Only checked-in fixture code is evaluated; no filesystem or network access.
|
|
new Function('exports', emitted)(fixtureExports);
|
|
const { parseConfig } = fixtureExports.ConfigExample;
|
|
const { assertIsStringArray } = fixtureExports.AssertionExample;
|
|
const { loadUser } = fixtureExports.UserExample;
|
|
const { mapWithConcurrency } = fixtureExports.ConcurrencyExample;
|
|
|
|
const okFetch = payload => async () => ({ ok: true, status: 200, json: async () => payload });
|
|
const badFetch = async () => ({ ok: false, status: 404, json: async () => ({}) });
|
|
|
|
// ---- parseConfig --------------------------------------------------------
|
|
|
|
test('parseConfig accepts a valid configuration', () => {
|
|
assert.deepStrictEqual(parseConfig({ timeout: 1000, retries: 5 }), { timeout: 1000, retries: 5 });
|
|
});
|
|
|
|
test('parseConfig rejects a string-valued retries field', () => {
|
|
assert.throws(() => parseConfig({ timeout: 1000, retries: '3' }), TypeError);
|
|
});
|
|
|
|
test('parseConfig applies declared defaults for missing fields', () => {
|
|
assert.deepStrictEqual(parseConfig({}), { timeout: 30_000, retries: 3 });
|
|
assert.deepStrictEqual(parseConfig(null), { timeout: 30_000, retries: 3 });
|
|
});
|
|
|
|
test('parseConfig rejects zero and negative values', () => {
|
|
assert.throws(() => parseConfig({ timeout: 0, retries: 3 }), TypeError);
|
|
assert.throws(() => parseConfig({ timeout: 1000, retries: -1 }), TypeError);
|
|
assert.throws(() => parseConfig({ timeout: 1.5, retries: 3 }), TypeError);
|
|
});
|
|
|
|
// ---- assertIsStringArray -------------------------------------------------
|
|
|
|
test('assertIsStringArray rejects wholly and partially sparse arrays', () => {
|
|
assert.throws(() => assertIsStringArray(new Array(3)), TypeError);
|
|
const partial = ['first', 'second'];
|
|
delete partial[1];
|
|
assert.throws(() => assertIsStringArray(partial), TypeError);
|
|
});
|
|
|
|
test('assertIsStringArray accepts dense strings and rejects present non-strings', () => {
|
|
assert.doesNotThrow(() => assertIsStringArray([]));
|
|
assert.doesNotThrow(() => assertIsStringArray(['', 'hello']));
|
|
for (const invalid of [['hello', undefined], ['hello', 1], null, 'hello']) {
|
|
assert.throws(() => assertIsStringArray(invalid), TypeError);
|
|
}
|
|
});
|
|
|
|
// ---- loadUser / isUser ----------------------------------------------------
|
|
|
|
test('loadUser returns a valid user', async () => {
|
|
const user = await loadUser('u_1', okFetch({ id: 'u_1', email: 'a@example.com' }));
|
|
assert.deepStrictEqual(user, { id: 'u_1', email: 'a@example.com' });
|
|
});
|
|
|
|
test('loadUser rejects invalid identifiers without making a request', async () => {
|
|
const invalidIds = ['', '../profile', '..', 'a/b', 'a\\b', '%2e%2e', 'u_1?admin=1', 'u_1#bio', 'u_1\n', 'u_\u{1F600}', 1, null];
|
|
for (const userId of invalidIds) {
|
|
let requests = 0;
|
|
const fetchJson = async () => {
|
|
requests += 1;
|
|
return { ok: true, status: 200, json: async () => ({ id: 'u_1', email: 'a@example.com' }) };
|
|
};
|
|
await assert.rejects(loadUser(userId, fetchJson), TypeError);
|
|
assert.strictEqual(requests, 0, `request made for invalid ID ${JSON.stringify(userId)}`);
|
|
}
|
|
});
|
|
|
|
test('loadUser preserves valid identifiers as one endpoint segment', async () => {
|
|
for (const userId of ['u_1', 'User-123', '42']) {
|
|
let requested;
|
|
const user = await loadUser(userId, async url => {
|
|
requested = url;
|
|
return { ok: true, status: 200, json: async () => ({ id: userId, email: 'a@example.com' }) };
|
|
});
|
|
assert.strictEqual(requested, `/users/${userId}`);
|
|
assert.strictEqual(user.id, userId);
|
|
}
|
|
});
|
|
|
|
test('loadUser rejects a response missing email', async () => {
|
|
await assert.rejects(loadUser('u_1', okFetch({ id: 'u_1' })), TypeError);
|
|
});
|
|
|
|
test('loadUser rejects fields with incorrect types', async () => {
|
|
await assert.rejects(loadUser('u_1', okFetch({ id: 1, email: 'a@example.com' })), TypeError);
|
|
await assert.rejects(loadUser('u_1', okFetch({ id: 'u_1', email: 'not-an-email' })), TypeError);
|
|
});
|
|
|
|
test('loadUser rejects null, arrays, and other malformed payloads', async () => {
|
|
await assert.rejects(loadUser('u_1', okFetch(null)), TypeError);
|
|
await assert.rejects(loadUser('u_1', okFetch([{ id: 'u_1', email: 'a@example.com' }])), TypeError);
|
|
await assert.rejects(loadUser('u_1', okFetch('oops')), TypeError);
|
|
});
|
|
|
|
test('loadUser rejects failed fetches and invalid JSON', async () => {
|
|
await assert.rejects(loadUser('u_1', badFetch), /Failed to load user/);
|
|
await assert.rejects(loadUser('u_1', async () => { throw new Error('bad json'); }), /bad json/);
|
|
});
|
|
|
|
// ---- mapWithConcurrency ----------------------------------------------------
|
|
|
|
test('mapWithConcurrency handles an empty input array', async () => {
|
|
assert.deepStrictEqual(await mapWithConcurrency([], 4, async x => x), []);
|
|
});
|
|
|
|
test('mapWithConcurrency maps with a valid limit', async () => {
|
|
assert.deepStrictEqual(await mapWithConcurrency([1, 2, 3], 2, async x => x * 2), [2, 4, 6]);
|
|
});
|
|
|
|
test('mapWithConcurrency accepts limits greater than the input length', async () => {
|
|
assert.deepStrictEqual(await mapWithConcurrency([1, 2], 10, async x => x + 1), [2, 3]);
|
|
});
|
|
|
|
test('mapWithConcurrency rejects zero and negative limits', async () => {
|
|
await assert.rejects(mapWithConcurrency([1], 0, async x => x), RangeError);
|
|
await assert.rejects(mapWithConcurrency([1], -2, async x => x), RangeError);
|
|
});
|
|
|
|
test('mapWithConcurrency rejects fractional and non-finite limits', async () => {
|
|
await assert.rejects(mapWithConcurrency([1], 1.5, async x => x), RangeError);
|
|
await assert.rejects(mapWithConcurrency([1], NaN, async x => x), RangeError);
|
|
await assert.rejects(mapWithConcurrency([1], Infinity, async x => x), RangeError);
|
|
});
|
|
|
|
test('mapWithConcurrency maps a present undefined element', async () => {
|
|
const seen = [];
|
|
const out = await mapWithConcurrency([undefined, 1], 2, async x => {
|
|
seen.push(x);
|
|
return x === undefined ? 0 : x;
|
|
});
|
|
assert.deepStrictEqual(seen, [undefined, 1]);
|
|
assert.deepStrictEqual(out, [0, 1]);
|
|
});
|
|
|
|
test('mapWithConcurrency skips only genuine sparse-array holes', async () => {
|
|
// Index 1 is a real hole; index 0 holds a present `undefined` value.
|
|
const input = new Array(3);
|
|
input[0] = undefined;
|
|
input[2] = 3;
|
|
const seen = [];
|
|
const out = await mapWithConcurrency(input, 3, async x => {
|
|
seen.push(x);
|
|
return x === undefined ? 'was-undefined' : x;
|
|
});
|
|
// The hole never reaches the mapper; the present undefined does.
|
|
assert.deepStrictEqual(seen, [undefined, 3]);
|
|
assert.strictEqual(out[0], 'was-undefined');
|
|
assert.strictEqual(out[2], 3);
|
|
assert.ok(!(1 in out), 'hole at index 1 should stay a hole in the result, not become a value');
|
|
assert.strictEqual(out.length, 3);
|
|
});
|
|
|
|
function deferred() {
|
|
let release;
|
|
const promise = new Promise(resolve => { release = resolve; });
|
|
return { promise, release };
|
|
}
|
|
|
|
test('mapWithConcurrency preserves order when mappers complete out of their input order', async () => {
|
|
const gates = [deferred(), deferred(), deferred(), deferred()];
|
|
const started = [];
|
|
const completed = [];
|
|
const resultPromise = mapWithConcurrency([0, 1, 2, 3], 4, async x => {
|
|
started.push(x);
|
|
await gates[x].promise;
|
|
completed.push(x);
|
|
return x * 10;
|
|
});
|
|
// No gate has been released yet, so having every mapper started proves
|
|
// concurrency happened before any completion.
|
|
await Promise.resolve();
|
|
assert.deepStrictEqual(started, [0, 1, 2, 3]);
|
|
// Release the gates in reverse input order and record the actual
|
|
// completion order; determinism comes from the gates, not timers.
|
|
for (let i = 3; i >= 0; i -= 1) {
|
|
gates[i].release();
|
|
}
|
|
const out = await resultPromise;
|
|
assert.deepStrictEqual(completed, [3, 2, 1, 0]);
|
|
// The completion order differs from input order, yet results still land
|
|
// in input order.
|
|
assert.deepStrictEqual(out, [0, 10, 20, 30]);
|
|
});
|
|
|
|
test('mapWithConcurrency propagates mapper rejection', async () => {
|
|
await assert.rejects(
|
|
mapWithConcurrency([1, 2, 3], 2, async x => { if (x === 2) throw new Error('boom'); return x; }),
|
|
/boom/
|
|
);
|
|
});
|
|
|
|
test('mapWithConcurrency never exceeds the configured limit', async () => {
|
|
let active = 0;
|
|
let maxActive = 0;
|
|
await mapWithConcurrency([1, 2, 3, 4, 5, 6], 2, async x => {
|
|
active += 1;
|
|
maxActive = Math.max(maxActive, active);
|
|
await new Promise(resolve => setTimeout(resolve, 5));
|
|
active -= 1;
|
|
return x;
|
|
});
|
|
assert.ok(maxActive <= 2, `peak concurrency ${maxActive} exceeded limit 2`);
|
|
assert.ok(maxActive > 1, 'workers never overlapped; pool may be serial');
|
|
});
|
|
|
|
test('async functions surface throws as rejections, sync returns do not', async () => {
|
|
const { asyncFails, lazyFails } = fixtureExports.AsyncErrorsExample;
|
|
await assert.rejects(asyncFails(), /becomes a rejection/);
|
|
assert.throws(() => lazyFails(), /throws synchronously/);
|
|
});
|
|
|
|
Promise.allSettled(pending).then(() => {
|
|
console.log(`\nPassed: ${passed}`);
|
|
console.log(`Failed: ${failed}`);
|
|
process.exit(failed > 0 ? 1 : 0);
|
|
});
|