UNPKG

pgsql-test

Version:

pgsql-test offers isolated, role-aware, and rollback-friendly PostgreSQL environments for integration tests — giving developers realistic test coverage without external state pollution

129 lines (128 loc) 5.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.snapshot = exports.createSnapshot = exports.prune = exports.defaultPruners = exports.composePruners = exports.pruneTokens = exports.prunePeoplestamps = exports.pruneSchemas = exports.pruneHashes = exports.pruneUUIDs = exports.pruneIdArrays = exports.pruneIds = exports.pruneDates = exports.formatPgError = exports.formatPgErrorFields = exports.extractPgErrorFields = void 0; exports.getErrorCode = getErrorCode; const types_1 = require("@pgpmjs/types"); Object.defineProperty(exports, "extractPgErrorFields", { enumerable: true, get: function () { return types_1.extractPgErrorFields; } }); Object.defineProperty(exports, "formatPgErrorFields", { enumerable: true, get: function () { return types_1.formatPgErrorFields; } }); Object.defineProperty(exports, "formatPgError", { enumerable: true, get: function () { return types_1.formatPgError; } }); /** * Extract the error code from an error message. * * Enhanced error messages from PgTestClient include additional context on subsequent lines * (Where, Query, Values, etc.). This function returns only the first line, which contains * the actual error code raised by PostgreSQL. * * @param message - The error message (may contain multiple lines with debug context) * @returns The first line of the error message (the error code) * * @example * // Error message with enhanced context: * // "NONEXISTENT_TYPE\nWhere: PL/pgSQL function...\nQuery: INSERT INTO..." * getErrorCode(err.message) // => "NONEXISTENT_TYPE" */ function getErrorCode(message) { return message.split('\n')[0]; } const uuidRegexp = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; const idReplacement = (v, idHash) => { if (!v) return v; if (!idHash) return '[ID]'; const key = String(v); return idHash[key] !== undefined ? `[ID-${idHash[key]}]` : '[ID]'; }; function mapValues(obj, fn) { return Object.entries(obj).reduce((acc, [key, value]) => { acc[key] = fn(value, key); return acc; }, {}); } const pruneDates = (row) => mapValues(row, (v, k) => { if (!v) { return v; } if (v instanceof Date) { return '[DATE]'; } else if (typeof v === 'string' && /(_at|At)$/.test(k) && /^20[0-9]{2}-[0-9]{2}-[0-9]{2}/.test(v)) { return '[DATE]'; } return v; }); exports.pruneDates = pruneDates; const pruneIds = (row, idHash) => mapValues(row, (v, k) => (k === 'id' || (typeof k === 'string' && k.endsWith('_id'))) && (typeof v === 'string' || typeof v === 'number') ? idReplacement(v, idHash) : v); exports.pruneIds = pruneIds; const pruneIdArrays = (row) => mapValues(row, (v, k) => typeof k === 'string' && k.endsWith('_ids') && Array.isArray(v) ? `[UUIDs-${v.length}]` : v); exports.pruneIdArrays = pruneIdArrays; const pruneUUIDs = (row) => mapValues(row, (v, k) => { if (typeof v !== 'string') { return v; } if (['uuid', 'queue_name'].includes(k) && uuidRegexp.test(v)) { return '[UUID]'; } if (k === 'gravatar' && /^[0-9a-f]{32}$/i.test(v)) { return '[gUUID]'; } return v; }); exports.pruneUUIDs = pruneUUIDs; const pruneHashes = (row) => mapValues(row, (v, k) => typeof k === 'string' && k.endsWith('_hash') && typeof v === 'string' && v.startsWith('$') ? '[hash]' : v); exports.pruneHashes = pruneHashes; const pruneSchemas = (row) => mapValues(row, (v, k) => typeof v === 'string' && /^zz-/.test(v) ? '[schemahash]' : v); exports.pruneSchemas = pruneSchemas; const prunePeoplestamps = (row) => mapValues(row, (v, k) => k.endsWith('_by') && typeof v === 'string' ? '[peoplestamp]' : v); exports.prunePeoplestamps = prunePeoplestamps; const pruneTokens = (row) => mapValues(row, (v, k) => (k === 'token' || k.endsWith('_token')) && typeof v === 'string' ? '[token]' : v); exports.pruneTokens = pruneTokens; const composePruners = (...pruners) => (row) => pruners.reduce((acc, pruner) => pruner(acc), row); exports.composePruners = composePruners; // Default pruners used by prune/snapshot (without IdHash) exports.defaultPruners = [ exports.pruneTokens, exports.prunePeoplestamps, exports.pruneDates, exports.pruneIdArrays, exports.pruneUUIDs, exports.pruneHashes ]; // Compose pruners and apply pruneIds with IdHash support const prune = (row, idHash) => { const pruned = (0, exports.composePruners)(...exports.defaultPruners)(row); return (0, exports.pruneIds)(pruned, idHash); }; exports.prune = prune; // Factory to create a snapshot function with custom pruners const createSnapshot = (pruners) => { const pruneFn = (0, exports.composePruners)(...pruners); const snap = (obj, idHash) => { if (Array.isArray(obj)) { return obj.map((el) => snap(el, idHash)); } else if (obj && typeof obj === 'object') { const pruned = pruneFn(obj); const prunedWithIds = (0, exports.pruneIds)(pruned, idHash); return mapValues(prunedWithIds, (v) => snap(v, idHash)); } return obj; }; return snap; }; exports.createSnapshot = createSnapshot; exports.snapshot = (0, exports.createSnapshot)(exports.defaultPruners);