From 2dca43177b9e66a7c4632270359210f19ed0b8a5 Mon Sep 17 00:00:00 2001 From: "Sharad." Date: Sat, 12 Sep 2026 14:20:44 +0000 Subject: [PATCH] Fix contains scorer passing on empty needle Reject empty substrings so misconfigured value: "" does not trivially pass. Fixes #28 Co-authored-by: Sharad. --- src/scorers/contains.ts | 4 ++-- tests/scorers.test.ts | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/scorers/contains.ts b/src/scorers/contains.ts index ae4e64f..bf219d6 100644 --- a/src/scorers/contains.ts +++ b/src/scorers/contains.ts @@ -18,7 +18,7 @@ function asList(value: unknown): string[] { export const containsScorer: Scorer = { type: "contains", score(spec: ScorerSpec, ctx: ScoreContext) { - const needles = [...asList(spec.value), ...asList(spec.values)]; + const needles = [...asList(spec.value), ...asList(spec.values)].filter((n) => n.length > 0); if (needles.length === 0) { return result(spec, { score: 0, passed: false, reason: "no value(s) provided" }); } @@ -44,7 +44,7 @@ export const containsScorer: Scorer = { export const notContainsScorer: Scorer = { type: "not-contains", score(spec: ScorerSpec, ctx: ScoreContext) { - const needles = [...asList(spec.value), ...asList(spec.values)]; + const needles = [...asList(spec.value), ...asList(spec.values)].filter((n) => n.length > 0); if (needles.length === 0) { return result(spec, { score: 0, passed: false, reason: "no value(s) provided" }); } diff --git a/tests/scorers.test.ts b/tests/scorers.test.ts index 78bf373..f2c0102 100644 --- a/tests/scorers.test.ts +++ b/tests/scorers.test.ts @@ -68,6 +68,10 @@ describe("regex", () => { }); describe("contains / not-contains", () => { + it("fails when needle is an empty string", async () => { + const r = await run(containsScorer, { type: "contains", value: "" }, ctx("anything")); + expect(r.passed).toBe(false); + }); it("gives partial credit when some substrings missing", async () => { const r = await run(containsScorer, { type: "contains", values: ["a", "b", "c"] }, ctx("a and b")); expect(r.passed).toBe(false);