test(commands): cover the where filter command (#121)

Adds characterization tests for src/commands/stdlib/where.ts, which shipped
with no direct coverage. Exercises the real command through parsePipeline +
runPipeline: literal coercion (number/boolean/null), the = to == normalization,
dotted-path resolution with non-object safety, all six comparison operators,
the loose-equality/missing-path quirk, and both throw paths (missing expression,
operator-less expression). No production code changes.
This commit is contained in:
Krasimir Kralev
2026-08-13 12:29:32 -07:00
committed by GitHub
parent 096f5fedcb
commit afb198a96b
+106
View File
@@ -0,0 +1,106 @@
import test from "node:test";
import assert from "node:assert/strict";
import { runPipeline } from "../src/runtime.js";
import { createDefaultRegistry } from "../src/commands/registry.js";
import { parsePipeline } from "../src/parser.js";
async function run(pipelineText: string, input: any[]) {
const pipeline = parsePipeline(pipelineText);
const registry = createDefaultRegistry();
const res = await runPipeline({
pipeline,
registry,
stdin: process.stdin,
stdout: process.stdout,
stderr: process.stderr,
env: process.env,
mode: "tool",
input: (async function* () {
for (const x of input) yield x;
})(),
});
return res.items;
}
test("where coerces a numeric literal and filters with >=", async () => {
const input = [{ n: 10 }, { n: 30 }, { n: 50 }];
const out = await run("where n>=30", input);
assert.deepEqual(out, [{ n: 30 }, { n: 50 }]);
});
test("where normalizes a single = to == and coerces true", async () => {
const input = [
{ unread: true, id: 1 },
{ unread: false, id: 2 },
];
const out = await run("where unread=true", input);
assert.deepEqual(out, [{ unread: true, id: 1 }]);
});
test("where coerces the false literal to a boolean", async () => {
const input = [
{ unread: true, id: 1 },
{ unread: false, id: 2 },
];
const out = await run("where unread=false", input);
assert.deepEqual(out, [{ unread: false, id: 2 }]);
});
test("where resolves a dotted path with ==", async () => {
const input = [{ user: { id: "u1" } }, { user: { id: "u2" } }];
const out = await run("where user.id==u1", input);
assert.deepEqual(out, [{ user: { id: "u1" } }]);
});
test("where keeps a non-coercible right-hand side as a string", async () => {
const input = [
{ status: "active", id: 1 },
{ status: "closed", id: 2 },
];
const out = await run("where status==active", input);
assert.deepEqual(out, [{ status: "active", id: 1 }]);
});
test("where supports the != operator", async () => {
const input = [
{ kind: "spam", id: 1 },
{ kind: "ham", id: 2 },
];
const out = await run("where kind!=spam", input);
assert.deepEqual(out, [{ kind: "ham", id: 2 }]);
});
test("where supports the strict ordering operators < and >", async () => {
const input = [{ n: 1 }, { n: 5 }, { n: 9 }];
assert.deepEqual(await run("where n<5", input), [{ n: 1 }]);
assert.deepEqual(await run("where n>5", input), [{ n: 9 }]);
});
test("where supports the inclusive ordering operator <=", async () => {
const input = [{ n: 1 }, { n: 5 }, { n: 9 }];
const out = await run("where n<=5", input);
assert.deepEqual(out, [{ n: 1 }, { n: 5 }]);
});
test("where coerces the null literal and matches missing paths via loose equality", async () => {
// getPath returns undefined for the missing key, and undefined == null is true,
// so both the explicit-null and the missing-key rows pass; the numeric 0 does not.
const input = [{ x: null, id: 1 }, { x: 0, id: 3 }, { id: 2 }];
const out = await run("where x=null", input);
assert.deepEqual(out, [{ x: null, id: 1 }, { id: 2 }]);
});
test("where returns undefined (excluding the row) when a dotted path hits a non-object", async () => {
const input = [{ a: { b: 1 } }, { a: 5 }];
const out = await run("where a.b==1", input);
assert.deepEqual(out, [{ a: { b: 1 } }]);
});
test("where throws when the expression is missing", async () => {
await assert.rejects(run("where", [{ n: 1 }]), /where requires an expression/);
});
test("where throws on an expression with no operator", async () => {
await assert.rejects(run("where garbage", [{ n: 1 }]), /Invalid where expression/);
});