Skip to content

Commit d255297

Browse files
authored
test(parsers): gate LANGUAGE_REGISTRY ↔ NATIVE_SUPPORTED_EXTENSIONS parity (#1154)
* test(parsers): gate LANGUAGE_REGISTRY ↔ NATIVE_SUPPORTED_EXTENSIONS parity Closes #1121. The drift guard between NATIVE_SUPPORTED_EXTENSIONS and parser_registry.rs covered link 2 ↔ 3, but link 1 ↔ 2 (LANGUAGE_REGISTRY ↔ NATIVE_SUPPORTED_EXTENSIONS) had no test. A WASM-only language added to the registry would silently degrade the native engine without flagging. Adds three tests with an explicit WASM_ONLY_ALLOWLIST (currently empty) so the allowlist itself can't rot — entries must reference a real registry extension and must not duplicate a language that's already been ported. * fix(tests): normalize WASM_ONLY_ALLOWLIST case in parity checks (#1154) Lowercase allowlist entries before comparing against `registryExts` and `NATIVE_SUPPORTED_EXTENSIONS` so a mixed-case future entry can't slip past the staleness and orphan checks. Both reference sets are already lowercased, so without this normalization a capitalized allowlist entry would be silently skipped by test 2 and falsely flagged by test 3.
1 parent 46e7a42 commit d255297

1 file changed

Lines changed: 82 additions & 0 deletions

File tree

tests/parsers/native-drop-classification.test.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { describe, expect, it } from 'vitest';
55
import {
66
classifyNativeDrops,
77
formatDropExtensionSummary,
8+
LANGUAGE_REGISTRY,
89
NATIVE_SUPPORTED_EXTENSIONS,
910
} from '../../src/domain/parser.js';
1011

@@ -203,3 +204,84 @@ describe('NATIVE_SUPPORTED_EXTENSIONS drift guard', () => {
203204
).toEqual([]);
204205
});
205206
});
207+
208+
/**
209+
* Parity gate for `LANGUAGE_REGISTRY` ↔ `NATIVE_SUPPORTED_EXTENSIONS`.
210+
*
211+
* Acceptance criterion from #1071 (tracked in #1121): a CI gate prevents
212+
* future drift between the JS `LANGUAGE_REGISTRY` and the Rust extractor
213+
* coverage. The existing drift guard above covers
214+
* `NATIVE_SUPPORTED_EXTENSIONS ↔ parser_registry.rs`, but the link from
215+
* `LANGUAGE_REGISTRY` (the source of truth for languages we support at all)
216+
* to `NATIVE_SUPPORTED_EXTENSIONS` (the hand-maintained mirror of the Rust
217+
* enum) had no test — silently adding a WASM-only language would degrade the
218+
* native engine without flagging the regression.
219+
*
220+
* This test closes that gap. Every extension declared in `LANGUAGE_REGISTRY`
221+
* must either:
222+
* 1. Be present in `NATIVE_SUPPORTED_EXTENSIONS` (i.e. a Rust extractor
223+
* exists), or
224+
* 2. Appear in `WASM_ONLY_ALLOWLIST` below, with a comment explaining why
225+
* the language is intentionally WASM-only.
226+
*
227+
* Adding an extension to the allowlist is a deliberate choice: prefer porting
228+
* the extractor to Rust. The allowlist exists so a contributor can land a
229+
* WASM-only grammar (e.g. while a Rust port is in flight) without bypassing
230+
* the gate entirely, but every entry should have a tracking issue.
231+
*/
232+
describe('LANGUAGE_REGISTRY ↔ NATIVE_SUPPORTED_EXTENSIONS parity', () => {
233+
// Extensions intentionally left WASM-only. Currently empty: every language
234+
// in `LANGUAGE_REGISTRY` has a corresponding Rust extractor. If you must
235+
// add an entry, include a comment with the language id and the issue
236+
// tracking the Rust port.
237+
const WASM_ONLY_ALLOWLIST: ReadonlySet<string> = new Set<string>();
238+
239+
it('every LANGUAGE_REGISTRY extension has a Rust extractor or is on the allowlist', () => {
240+
const registryExts = new Set<string>();
241+
for (const entry of LANGUAGE_REGISTRY) {
242+
for (const ext of entry.extensions) {
243+
registryExts.add(ext.toLowerCase());
244+
}
245+
}
246+
const missingFromNative = [...registryExts]
247+
.filter((ext) => !NATIVE_SUPPORTED_EXTENSIONS.has(ext))
248+
.filter((ext) => !WASM_ONLY_ALLOWLIST.has(ext))
249+
.sort();
250+
expect(
251+
missingFromNative,
252+
`LANGUAGE_REGISTRY extensions without a Rust extractor (and not on WASM_ONLY_ALLOWLIST): ${missingFromNative.join(
253+
', ',
254+
)}. Either port the extractor to Rust and add the extension to NATIVE_SUPPORTED_EXTENSIONS, or add it to WASM_ONLY_ALLOWLIST with a justification.`,
255+
).toEqual([]);
256+
});
257+
258+
it('WASM_ONLY_ALLOWLIST does not list extensions that already have a Rust extractor', () => {
259+
// Catches stale allowlist entries: once a language is ported to Rust the
260+
// allowlist line should be deleted, not left behind as dead config.
261+
const stale = [...WASM_ONLY_ALLOWLIST]
262+
.map((ext) => ext.toLowerCase())
263+
.filter((ext) => NATIVE_SUPPORTED_EXTENSIONS.has(ext));
264+
expect(
265+
stale,
266+
`WASM_ONLY_ALLOWLIST entries that already have a Rust extractor — remove them: ${stale.join(', ')}`,
267+
).toEqual([]);
268+
});
269+
270+
it('WASM_ONLY_ALLOWLIST only references extensions that LANGUAGE_REGISTRY declares', () => {
271+
// Catches typos and dead entries: an allowlist line for an extension no
272+
// longer in the registry is silently useless.
273+
const registryExts = new Set<string>();
274+
for (const entry of LANGUAGE_REGISTRY) {
275+
for (const ext of entry.extensions) {
276+
registryExts.add(ext.toLowerCase());
277+
}
278+
}
279+
const orphans = [...WASM_ONLY_ALLOWLIST]
280+
.map((ext) => ext.toLowerCase())
281+
.filter((ext) => !registryExts.has(ext));
282+
expect(
283+
orphans,
284+
`WASM_ONLY_ALLOWLIST entries not declared in LANGUAGE_REGISTRY — likely a typo: ${orphans.join(', ')}`,
285+
).toEqual([]);
286+
});
287+
});

0 commit comments

Comments
 (0)