/** * Every bound method the suite names by hand must exist. * * A binding call carries only a method id, so a spec that names a method * the Go side no longer has fails at *runtime*, in whichever spec * happens to call it, with `unknown bound method name` — and nothing * before that. `library.Library.GetTracks` was replaced by * `GetTrackTable` (#281) and four specs kept calling the old name: the * suite reported twenty failures across transport, bottom-bar and * reduced-motion specs, none of which mention the library list. * * The known names come from `support/method-ids.mjs`, which derives them * from the generated bindings tree that `make bindings-check` keeps * current — so this cannot disagree with what the app can answer. * * It asserts first that it read something: a glob that matched nothing * would pass over an empty list. */ import { readFileSync, readdirSync } from 'node:fs'; import { fileURLToPath } from 'node:url'; import { dirname, join } from 'node:path'; import { test, expect } from '../support/fixtures.js'; import { methodIDs } from '../support/method-ids.mjs'; const here = dirname(fileURLToPath(import.meta.url)); /** The specs, and the harness that calls bindings on their behalf. */ const DIRS = [here, join(here, '..', 'support')]; function sources(): Array<[string, string]> { const out: Array<[string, string]> = []; for (const dir of DIRS) { for (const entry of readdirSync(dir)) { if (!entry.endsWith('.ts')) continue; if (entry.endsWith('.spec.ts') && entry === 'binding-names.spec.ts') continue; const path = join(dir, entry); out.push([path, readFileSync(path, 'utf8')]); } } return out; } /** `'library.Library.GetTrackTable'` — package, service, method. */ const NAMED_BINDING = /'([a-z][A-Za-z0-9]*\.[A-Z]\w*\.[A-Z]\w*)'/g; /** * Names that are deliberately not bindings. * * `harness.spec.ts` calls an unknown method on purpose, to assert that * the bridge rejects with a ReferenceError naming it rather than * hanging — that spec is the reason a bad call is loud. */ const DELIBERATE = new Set(['queue.Queue.Nope']); test('every binding named by a spec exists', () => { // `methodIDs()` is the id -> name map the recorder names calls with. const known = new Set(methodIDs().values()); const files = sources(); // A sweep over an empty glob passes and proves nothing. expect(files.length, 'specs and support files read').toBeGreaterThan(20); expect(known.size, 'bound method names derived').toBeGreaterThan(100); const unknown: string[] = []; for (const [path, source] of files) { for (const [, name] of source.matchAll(NAMED_BINDING)) { if (known.has(name!) || DELIBERATE.has(name!)) continue; unknown.push(`${name} (${path.split('/').pop()})`); } } expect([...new Set(unknown)]).toEqual([]); });