// ide-extension-parser-entities.test.mjs — Regression tests for XML numeric
// character references in plugin.xml.
//
// parsePluginXml documents a no-throw contract: "Malformed input returns
// { manifest: null, warnings: [...] } rather than throwing." decodeEntities
// guarded parseInt with Number.isFinite, which does not bound the value, so
// String.fromCodePoint raised RangeError for any code point above 0x10FFFF —
// a one-token hostile plugin.xml that aborts the parse instead of being
// reported.
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { parsePluginXml } from '../../scanners/lib/ide-extension-parser.mjs';
function xmlWithName(name) {
return `com.example.p${name}v`;
}
describe('ide-extension-parser — numeric character references', () => {
const outOfRange = [
['hex, just past the Unicode maximum', ''],
['decimal, just past the Unicode maximum', ''],
['hex, far out of range', ''],
['decimal, far out of range', ''],
];
for (const [label, ref] of outOfRange) {
it(`does not throw on an out-of-range reference (${label})`, () => {
assert.doesNotThrow(() => parsePluginXml(xmlWithName(`Bad${ref}Name`)));
});
it(`leaves an out-of-range reference literal (${label})`, () => {
const { manifest } = parsePluginXml(xmlWithName(`Bad${ref}Name`));
assert.ok(manifest, 'manifest should still parse');
assert.ok(
manifest.name.includes(ref),
`undecodable reference should be left as-is, got: ${manifest.name}`
);
});
}
it('still decodes valid numeric references', () => {
const { manifest } = parsePluginXml(xmlWithName('AB'));
assert.ok(manifest);
assert.equal(manifest.name, 'AB');
});
it('still decodes the maximum valid code point', () => {
const { manifest } = parsePluginXml(xmlWithName('x'));
assert.ok(manifest);
assert.equal(manifest.name, 'x\u{10FFFF}');
});
it('still decodes named entities', () => {
const { manifest } = parsePluginXml(xmlWithName('A&B<C'));
assert.ok(manifest);
assert.equal(manifest.name, 'A&B