From 89f80d30e92165b0061f900ee0e9ebeb4ade10af Mon Sep 17 00:00:00 2001 From: "hivecommons-hive[bot]" <280983584+hivecommons-hive[bot]@users.noreply.github.com> Date: Sat, 26 Sep 2026 12:24:12 -0400 Subject: [PATCH] [quality] pin package entry export surface: index.test.ts Adds src/index.test.ts covering dist/index.js (the package.json main entry), which previously never loaded in the test suite: - exact Object.keys match of the runtime export surface - callable check for every function export - AUTODETECT_VARS shape check - identity check: each re-export === its source-module symbol - smoke round-trip (parseVars + expand) through the entry point Compiled dist/index.test.{js,js.map,d.ts} committed per CI dist-sync gate. Refs #48 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: hivecommons-hive[bot] <280983584+hivecommons-hive[bot]@users.noreply.github.com> --- dist/index.test.d.ts | 1 + dist/index.test.js | 59 +++++++++++++++++++++++++++++++++ dist/index.test.js.map | 1 + src/index.test.ts | 74 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 135 insertions(+) create mode 100644 dist/index.test.d.ts create mode 100644 dist/index.test.js create mode 100644 dist/index.test.js.map create mode 100644 src/index.test.ts diff --git a/dist/index.test.d.ts b/dist/index.test.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/dist/index.test.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/dist/index.test.js b/dist/index.test.js new file mode 100644 index 0000000..700795d --- /dev/null +++ b/dist/index.test.js @@ -0,0 +1,59 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import * as api from './index.js'; +// package.json "main" points at dist/index.js — this file is the public API +// surface consumers get from `import ... from '@hivecommons/promptargs'`. +// Pin every runtime export so a refactor of the barrel (or of a re-exported +// module) that drops or renames a symbol fails the suite instead of shipping +// a silent breaking change. +const EXPECTED_FUNCTION_EXPORTS = [ + 'parseVars', + 'expand', + 'autodetect', + 'autodetectAll', + 'loadTemplates', + 'findTemplate', + 'resolve', + 'renderStatus', +].sort(); +test('index exports exactly the documented runtime API', () => { + const actual = Object.keys(api).sort(); + const expected = [...EXPECTED_FUNCTION_EXPORTS, 'AUTODETECT_VARS'].sort(); + assert.deepStrictEqual(actual, expected); +}); +test('every function export is callable', () => { + for (const name of EXPECTED_FUNCTION_EXPORTS) { + assert.strictEqual(typeof api[name], 'function', `expected export "${name}" to be a function`); + } +}); +test('AUTODETECT_VARS is a non-empty list of var names', () => { + assert.ok(Array.isArray(api.AUTODETECT_VARS)); + assert.ok(api.AUTODETECT_VARS.length > 0); + for (const v of api.AUTODETECT_VARS) { + assert.strictEqual(typeof v, 'string'); + assert.ok(v.length > 0); + } +}); +test('entry-point exports are the same objects as their source modules', async () => { + const parser = await import('./parser.js'); + const autodetectMod = await import('./autodetect.js'); + const loader = await import('./loader.js'); + const resolver = await import('./resolver.js'); + const status = await import('./status.js'); + assert.strictEqual(api.parseVars, parser.parseVars); + assert.strictEqual(api.expand, parser.expand); + assert.strictEqual(api.autodetect, autodetectMod.autodetect); + assert.strictEqual(api.autodetectAll, autodetectMod.autodetectAll); + assert.strictEqual(api.AUTODETECT_VARS, autodetectMod.AUTODETECT_VARS); + assert.strictEqual(api.loadTemplates, loader.loadTemplates); + assert.strictEqual(api.findTemplate, loader.findTemplate); + assert.strictEqual(api.resolve, resolver.resolve); + assert.strictEqual(api.renderStatus, status.renderStatus); +}); +test('smoke: exported parseVars + expand round-trip through the entry point', () => { + const vars = api.parseVars('Hello {{name}}, focus on {{focus}}'); + assert.deepStrictEqual(vars.map((v) => v.name), ['name', 'focus']); + const out = api.expand('Hello {{name}}', { name: 'World' }); + assert.strictEqual(out, 'Hello World'); +}); +//# sourceMappingURL=index.test.js.map \ No newline at end of file diff --git a/dist/index.test.js.map b/dist/index.test.js.map new file mode 100644 index 0000000..173898f --- /dev/null +++ b/dist/index.test.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/src/index.test.ts b/src/index.test.ts new file mode 100644 index 0000000..737bd45 --- /dev/null +++ b/src/index.test.ts @@ -0,0 +1,74 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; + +import * as api from './index.js'; + +// package.json "main" points at dist/index.js — this file is the public API +// surface consumers get from `import ... from '@hivecommons/promptargs'`. +// Pin every runtime export so a refactor of the barrel (or of a re-exported +// module) that drops or renames a symbol fails the suite instead of shipping +// a silent breaking change. + +const EXPECTED_FUNCTION_EXPORTS = [ + 'parseVars', + 'expand', + 'autodetect', + 'autodetectAll', + 'loadTemplates', + 'findTemplate', + 'resolve', + 'renderStatus', +].sort(); + +test('index exports exactly the documented runtime API', () => { + const actual = Object.keys(api).sort(); + const expected = [...EXPECTED_FUNCTION_EXPORTS, 'AUTODETECT_VARS'].sort(); + assert.deepStrictEqual(actual, expected); +}); + +test('every function export is callable', () => { + for (const name of EXPECTED_FUNCTION_EXPORTS) { + assert.strictEqual( + typeof (api as Record)[name], + 'function', + `expected export "${name}" to be a function`, + ); + } +}); + +test('AUTODETECT_VARS is a non-empty list of var names', () => { + assert.ok(Array.isArray(api.AUTODETECT_VARS)); + assert.ok(api.AUTODETECT_VARS.length > 0); + for (const v of api.AUTODETECT_VARS) { + assert.strictEqual(typeof v, 'string'); + assert.ok(v.length > 0); + } +}); + +test('entry-point exports are the same objects as their source modules', async () => { + const parser = await import('./parser.js'); + const autodetectMod = await import('./autodetect.js'); + const loader = await import('./loader.js'); + const resolver = await import('./resolver.js'); + const status = await import('./status.js'); + + assert.strictEqual(api.parseVars, parser.parseVars); + assert.strictEqual(api.expand, parser.expand); + assert.strictEqual(api.autodetect, autodetectMod.autodetect); + assert.strictEqual(api.autodetectAll, autodetectMod.autodetectAll); + assert.strictEqual(api.AUTODETECT_VARS, autodetectMod.AUTODETECT_VARS); + assert.strictEqual(api.loadTemplates, loader.loadTemplates); + assert.strictEqual(api.findTemplate, loader.findTemplate); + assert.strictEqual(api.resolve, resolver.resolve); + assert.strictEqual(api.renderStatus, status.renderStatus); +}); + +test('smoke: exported parseVars + expand round-trip through the entry point', () => { + const vars = api.parseVars('Hello {{name}}, focus on {{focus}}'); + assert.deepStrictEqual( + vars.map((v: { name: string }) => v.name), + ['name', 'focus'], + ); + const out = api.expand('Hello {{name}}', { name: 'World' }); + assert.strictEqual(out, 'Hello World'); +});