ktg-plugin-marketplace/plugins/ultraplan-local/examples/02-real-cli/tests/tally.test.mjs
Kjell Tore Guttormsen c8146c143d feat(ultraplan-local): tally CLI baseline fixture for examples/02-real-cli (Spor B B2) [skip-docs]
Adds the runnable counterpart to examples/01-add-verbose-flag (which is
artifacts-only). The fixture is the measurement target for Spor B's
end-to-end pipeline run (B3) and Spor C's cache-prefix experiment.

Baseline:
- tally.mjs (80 lines, hand-rolled argv parser, zero deps)
- 3 flags: --json, -i/--ignore-case, --lines + --help
- Exit codes: 0 success, 1 file error, 2 invalid argv
- 10 node:test cases, all green (~2.2s wall-clock)
- Deterministic fixtures: sample.txt (foo×7, Foo×1, regex fo+×9) +
  poem.txt (--lines vs total distinction)
- REGENERATED.md skeleton (B3 fills the pipeline walk-through)

Brief preconditions verified:
- grep -c 'foo' sample.txt = 4 (>= 1)
- regex /fo+/g count = 9 (> grep count)
- Brief assumptions for B3 SC #1, #3 hold

This is the first runnable example in plugins/ultraplan-local/examples/.
Next: B3 runs /ultraresearch-local + /ultraplan-local + /ultraexecute-local
against the brief to add --regex/-r, then verifies all 10 Success Criteria.
2026-05-04 20:18:57 +02:00

93 lines
3.1 KiB
JavaScript

import { test } from 'node:test';
import assert from 'node:assert/strict';
import { spawnSync } from 'node:child_process';
import { fileURLToPath } from 'node:url';
import { dirname, resolve } from 'node:path';
const here = dirname(fileURLToPath(import.meta.url));
const TALLY = resolve(here, '..', 'tally.mjs');
const SAMPLE = resolve(here, '..', 'fixtures', 'sample.txt');
const POEM = resolve(here, '..', 'fixtures', 'poem.txt');
function run(...args) {
return spawnSync('node', [TALLY, ...args], { encoding: 'utf8' });
}
test('plain count: tally foo sample.txt prints 7', () => {
const r = run('foo', SAMPLE);
assert.equal(r.status, 0);
assert.equal(r.stdout.trim(), '7');
assert.equal(r.stderr, '');
});
test('JSON output: tally --json foo sample.txt parses with count 7', () => {
const r = run('--json', 'foo', SAMPLE);
assert.equal(r.status, 0);
const parsed = JSON.parse(r.stdout);
assert.equal(parsed.count, 7);
assert.equal(parsed.pattern, 'foo');
assert.equal(parsed.flags.json, true);
assert.equal(parsed.flags.ignoreCase, false);
assert.equal(parsed.flags.lines, false);
});
test('case-sensitive default: tally Foo sample.txt prints 1', () => {
const r = run('Foo', SAMPLE);
assert.equal(r.status, 0);
assert.equal(r.stdout.trim(), '1');
});
test('case-insensitive: tally -i Foo == tally -i foo (and exceeds case-sensitive)', () => {
const ri1 = run('-i', 'Foo', SAMPLE);
const ri2 = run('-i', 'foo', SAMPLE);
const rcs = run('foo', SAMPLE);
assert.equal(ri1.status, 0);
assert.equal(ri2.status, 0);
assert.equal(ri1.stdout, ri2.stdout);
assert.ok(Number(ri1.stdout) > Number(rcs.stdout));
});
test('--lines mode: tally --lines foo poem.txt prints 3 (not total occurrences 4)', () => {
const lines = run('--lines', 'foo', POEM);
const total = run('foo', POEM);
assert.equal(lines.status, 0);
assert.equal(total.status, 0);
assert.equal(lines.stdout.trim(), '3');
assert.equal(total.stdout.trim(), '4');
});
test('flag in last position: tally foo sample.txt --json equals tally --json foo sample.txt', () => {
const last = run('foo', SAMPLE, '--json');
const first = run('--json', 'foo', SAMPLE);
assert.equal(last.status, 0);
assert.equal(first.status, 0);
assert.equal(last.stdout, first.stdout);
});
test('missing argument: tally foo exits 2 with stderr', () => {
const r = run('foo');
assert.equal(r.status, 2);
assert.match(r.stderr, /^tally: /);
assert.equal(r.stdout, '');
});
test('unknown flag: tally --unknown foo sample.txt exits 2 with stderr', () => {
const r = run('--unknown', 'foo', SAMPLE);
assert.equal(r.status, 2);
assert.match(r.stderr, /^tally: /);
assert.equal(r.stdout, '');
});
test('file not found: tally foo /does/not/exist exits 1 with stderr', () => {
const r = run('foo', '/does/not/exist');
assert.equal(r.status, 1);
assert.match(r.stderr, /^tally: /);
assert.equal(r.stdout, '');
});
test('--help: stdout contains "Usage:", exit 0', () => {
const r = run('--help');
assert.equal(r.status, 0);
assert.match(r.stdout, /Usage:/);
assert.match(r.stdout, /--ignore-case/);
});