UNPKG

apiveritas

Version:

Lightweight CLI tool for consumer-driven API contract testing via JSON schema and payload comparisons.

81 lines (69 loc) 2.82 kB
/** * @file BasicComparator.ts * @author Mario Galea * @description * Provides a simple recursive comparison of two JSON-like objects, * optionally enforcing strict value matching. */ export class BasicComparator { private strictValues: boolean; /** * Creates an instance of BasicComparator. * * @param strictValues - If true, value mismatches are considered differences; otherwise, they are flagged but tolerated. */ constructor(strictValues: boolean = true) { this.strictValues = strictValues; } /** * Recursively compares two JSON-like objects and returns an array of human-readable difference strings. * * @param oldData - The original (expected) data object. * @param newData - The new (actual) data object to compare against the original. * @param pathPrefix - Internal use for recursion; tracks the nested path of the current key being compared. * @returns An array of difference messages. Empty if objects match (under configured tolerance). */ compare(oldData: any, newData: any, pathPrefix: string = ''): string[] { const diffs: string[] = []; if (typeof oldData !== 'object' || oldData === null) { diffs.push(`X Old data is not a valid object at ${this.formatPath(pathPrefix)}. Got: ${JSON.stringify(oldData)}`); return diffs; } if (typeof newData !== 'object' || newData === null) { diffs.push(`X New data is not a valid object at ${this.formatPath(pathPrefix)}. Got: ${JSON.stringify(newData)}`); return diffs; } for (const key in oldData) { const fullPath = pathPrefix ? `${pathPrefix}.${key}` : key; if (!(key in newData)) { diffs.push(`Missing key in new data: ${fullPath}`); continue; } if (typeof oldData[key] !== typeof newData[key]) { diffs.push( `Type mismatch at ${fullPath}: expected ${typeof oldData[key]}, got ${typeof newData[key]}` ); continue; } if (typeof oldData[key] === 'object' && oldData[key] !== null) { diffs.push(...this.compare(oldData[key], newData[key], fullPath)); } else if (oldData[key] !== newData[key]) { if (this.strictValues) { diffs.push(`X Value mismatch at ${fullPath}: "${oldData[key]}" vs "${newData[key]}"`); } else { diffs.push(`IGNORED:: ! Value mismatch at ${fullPath}: "${oldData[key]}" vs "${newData[key]}"`); } } } return diffs; } /** * Formats a path for display. If the path is empty, returns a root symbol. * * @param path - The current nested key path. * @returns A formatted string path or `#` if root. */ private formatPath(path: string): string { return path || '#'; } }