import { isEmptyObject, isEqual, isNil, isPlainObject } from "es-toolkit"; import { isArray } from "es-toolkit/compat"; import type { ExpectOperator, ExpectValue } from "../types"; const OPERATOR_KEYS = new Set(["contains", "empty", "equals", "exists", "gt", "gte", "lt", "lte", "match"]); export function applyOperator(actual: unknown, op: ExpectOperator): boolean { for (const [key, expected] of Object.entries(op)) { if (expected === undefined) continue; switch (key) { case "contains": if (!String(actual).includes(expected as string)) return false; break; case "empty": { const isEmpty = isNil(actual) || actual === "" || (isArray(actual) && actual.length === 0) || isEmptyObject(actual); if (expected !== isEmpty) return false; break; } case "equals": if (!isEqual(actual, expected)) return false; break; case "exists": if (expected) { if (actual === undefined) return false; } else { if (actual !== undefined) return false; } break; case "gt": if (!(Number(actual) > (expected as number))) return false; break; case "gte": if (!(Number(actual) >= (expected as number))) return false; break; case "lt": if (!(Number(actual) < (expected as number))) return false; break; case "lte": if (!(Number(actual) <= (expected as number))) return false; break; case "match": if (!new RegExp(expected as string).test(String(actual))) return false; break; } } return true; } export function checkExpectValue(actual: unknown, expected: ExpectValue): boolean { if (isPlainObject(expected) && Object.keys(expected).some((key) => OPERATOR_KEYS.has(key))) { return applyOperator(actual, expected as ExpectOperator); } return applyOperator(actual, { equals: expected as Exclude }); } export function evaluateJsonPath(json: unknown, path: string): unknown { if (!path.startsWith("$.")) return undefined; const segments = path.slice(2).split("."); let current: unknown = json; for (const seg of segments) { const bracketMatch = /^(.+?)\[(\d+)\]$/.exec(seg); if (bracketMatch) { current = (current as Record)?.[bracketMatch[1]!]; const idx = parseInt(bracketMatch[2]!, 10); if (!isArray(current) || idx >= current.length) return undefined; current = current[idx]; } else { if (current === null || current === undefined) return undefined; current = (current as Record)[seg]; } } return current; }