JSON Patch Test Suite
This package (`json-patch-test-suite`) serves as the official and community-maintained collection of test cases designed to validate implementations of the IETF JSON Patch specification (RFC 6902). It does not provide an actual JSON Patch implementation, but rather offers a standardized data set comprising source documents, applicable patches, and either the expected resulting document or a description of an anticipated error. The current stable version is 1.1.0. As a static test suite for a well-established RFC, its release cadence is infrequent, primarily occurring when new edge cases are identified or the RFC itself undergoes revisions. Its primary value lies in offering a neutral, comprehensive, and widely-adopted benchmark that allows developers to rigorously test their JSON Patch libraries for strict conformance to the RFC 6902 standard, ensuring interoperability and correctness across various programming languages and environments. This helps to prevent divergent interpretations of the specification.
Common errors
-
Cannot find module 'json-patch-test-suite/tests.json'
cause Your TypeScript configuration or JavaScript bundler is not set up to correctly resolve `.json` file imports, or the module resolution path is incorrect.fixFor TypeScript, add `"resolveJsonModule": true` and `"esModuleInterop": true` to the `compilerOptions` in your `tsconfig.json`. If using a bundler (like Webpack), ensure your configuration includes a JSON loader (often enabled by default). Double-check the exact import path: `import tests from 'json-patch-test-suite/tests.json'`. -
SyntaxError: Unexpected token '{' at <module-path>/node_modules/json-patch-test-suite/tests.jsoncause This typically occurs in older Node.js or CommonJS environments when `require()` or `import` attempts to parse a JSON file as a JavaScript module, or if the `require()` call does not specify the full `.json` extension.fixEnsure that your `require()` call explicitly targets the `.json` file extension (e.g., `const tests = require('json-patch-test-suite/tests.json')`). If using ESM in Node.js, confirm your `package.json` contains `"type": "module"` or use the `.mjs` extension for your module files, and configure your bundler/TypeScript to resolve JSON modules.
Warnings
- gotcha Direct JSON file imports require specific configuration in modern JavaScript environments.
- gotcha The `error` field in test records provides a *suggested* error message, not a definitive string for comparison.
- gotcha This package provides only JSON Patch test data, not an implementation of the RFC 6902 specification.
Install
-
npm install json-patch-test-suite -
yarn add json-patch-test-suite -
pnpm add json-patch-test-suite
Imports
- tests
import { tests } from 'json-patch-test-suite'import tests from 'json-patch-test-suite/tests.json'
- specTests
const specTests = require('json-patch-test-suite')import specTests from 'json-patch-test-suite/spec_tests.json'
- tests
const tests = require('json-patch-test-suite')const tests = require('json-patch-test-suite/tests.json')
Quickstart
import * as fs from 'fs';
import * as path from 'path';
// IMPORTANT: This 'applyPatch' function is a placeholder.
// You must integrate your actual JSON Patch implementation here
// (e.g., from 'fast-json-patch', 'json-patch', etc.)
const applyPatch = (doc: any, patch: any[]): any => {
// In a real scenario, this would apply the patch and return the new document.
// For this example, we'll simulate a success or throw an error based on patch content.
if (!doc || !patch) throw new Error('Invalid input for patch application.');
if (patch.some((op: any) => op.path === '/invalid/path' && op.op === 'add')) {
throw new Error('Simulated patch error: Invalid path operation.');
}
return { ...doc, patched: true }; // Simulate a successful patch
};
interface TestRecord {
doc: any;
patch: any[];
expected?: any;
error?: string;
comment?: string;
disabled?: boolean;
}
// Dynamically resolve the path to the installed json-patch-test-suite package.
// This is generally safer than assuming 'node_modules' directly.
const resolvePackagePath = (packageName: string) => {
try {
return path.dirname(require.resolve(packageName + '/package.json'));
} catch (e) {
throw new Error(`Could not find package ${packageName}. Is it installed?`);
}
};
const suitePackagePath = resolvePackagePath('json-patch-test-suite');
const testsPath = path.join(suitePackagePath, 'tests.json');
const testSuite: TestRecord[] = JSON.parse(fs.readFileSync(testsPath, 'utf8'));
let passedTests = 0;
let failedTests = 0;
console.log(`Running ${testSuite.length} JSON Patch tests from the suite...\n`);
testSuite.forEach((test, index) => {
if (test.disabled) {
// console.log(`Skipping disabled test [${index + 1}]: ${test.comment}`);
return;
}
try {
const result = applyPatch(test.doc, test.patch);
if (test.expected !== undefined) {
if (JSON.stringify(result) === JSON.stringify(test.expected)) {
passedTests++;
} else {
failedTests++;
console.error(`FAIL [${index + 1}]: ${test.comment || 'Unnamed test'}`);
console.error(' Doc:', JSON.stringify(test.doc));
console.error(' Patch:', JSON.stringify(test.patch));
console.error(' Expected:', JSON.stringify(test.expected));
console.error(' Got:', JSON.stringify(result));
console.error('--------------------------------------------------');
}
} else if (test.error !== undefined) {
failedTests++;
console.error(`FAIL [${index + 1}]: ${test.comment || 'Unnamed test'} (Expected error, but patch succeeded)`);
console.error('--------------------------------------------------');
}
// If no expected/error, assume success if no exception
} catch (e: any) {
if (test.error !== undefined) {
passedTests++; // We expected an error, and got one. Good enough for basic check.
} else {
failedTests++;
console.error(`FAIL [${index + 1}]: ${test.comment || 'Unnamed test'} (Unexpected error)`);
console.error(' Error:', e.message);
console.error(' Doc:', JSON.stringify(test.doc));
console.error(' Patch:', JSON.stringify(test.patch));
console.error('--------------------------------------------------');
}
}
});
console.log(`\nTests finished: ${passedTests} passed, ${failedTests} failed.`);
if (failedTests > 0) {
process.exit(1);
}