diff --git a/tests/performance/employment_separation_acceptance_cardinality.test.mjs b/tests/performance/employment_separation_acceptance_cardinality.test.mjs new file mode 100644 index 000000000..a4f717aa4 --- /dev/null +++ b/tests/performance/employment_separation_acceptance_cardinality.test.mjs @@ -0,0 +1,116 @@ +import assert from "node:assert/strict"; +import { createHash } from "node:crypto"; +import test from "node:test"; + +import { validateEmploymentSeparationAcceptance } from "./employment_separation_acceptance_contract.mjs"; +import { + acceptanceFixtureBytes, + acceptanceFixtureSha256, + acceptanceLoadModel, +} from "./employment_separation_acceptance_fixture_test_support.mjs"; + +const PROFILE_PRECONDITIONS = Object.freeze({ + first_commit: "active_current_expected_version", + replay: "same_key_same_semantics_already_committed", + rejection: "expected_version_stale_or_semantic_conflict", + contention: "active_current_expected_version", +}); +const FIXTURE_BYTES = acceptanceFixtureBytes(); +const FIXTURE_SHA256 = acceptanceFixtureSha256(FIXTURE_BYTES); + +function performanceResult(profile, iterations) { + const trendName = profile === "contention" + ? "employment_separation_contention_duration_ms" + : "employment_separation_first_commit_duration_ms"; + const latencySamples = profile === "contention" ? iterations * 2 : iterations; + return { + schema_version: "orgmetra.employment_separation.performance_result.v1", + candidate_sha: "a".repeat(40), + fixture_sha256: FIXTURE_SHA256, + selected_profile: profile, + expected_iterations: iterations, + completed_iterations: iterations, + sample_complete: true, + completed_at: "2026-09-13T04:10:00Z", + load_model: acceptanceLoadModel(iterations), + dataset_id: "dataset:employment-separation-perf-1", + clearance_reference: "data_clearance:perf-2026-09", + preparation_protocol_reference: "protocol:employment-separation-perf-v1", + prepared_state_evidence_reference: "evidence:prepared-state-perf-1", + resource_evidence_reference: "metrics:employment-separation-perf-1", + profile_preconditions: { ...PROFILE_PRECONDITIONS }, + minimum_non_contending_records: 1000, + minimum_contention_pairs: 100, + k6: { + metrics: { + iterations: { values: { count: iterations } }, + checks: { values: { rate: 1, passes: iterations, fails: 0 } }, + employment_separation_unexpected_response: { values: { rate: 0, passes: 0, fails: iterations } }, + employment_separation_latency_samples: { values: { count: latencySamples } }, + [trendName]: { + values: profile === "contention" + ? { "p(50)": 12, "p(95)": 80, "p(99)": 120, max: 200, count: latencySamples } + : { "p(50)": 8.1, "p(95)": 18.4, "p(99)": 19.7, max: 22.3, count: latencySamples }, + }, + }, + }, + }; +} + +function runtimeEvidence(resultArtifact, profile, iterations) { + return { + schema_version: "orgmetra.employment_separation.runtime_evidence.v1", + candidate_sha: "a".repeat(40), + observed_service_sha: "a".repeat(40), + selected_profile: profile, + performance_result_sha256: createHash("sha256").update(resultArtifact).digest("hex"), + fixture_sha256: FIXTURE_SHA256, + environment_reference: "environment:perf-staging-1", + deployment_reference: "deployment:orgmetra-people-a1", + observer_reference: "observer:perf-runtime-1", + load_observation_reference: "evidence:perf-load-observation-1", + observed_load_model: acceptanceLoadModel(iterations), + resource_evidence_reference: "metrics:employment-separation-perf-1", + observed_at: "2026-09-13T04:10:01Z", + host_cpu_percent_p95: 42.5, + host_rss_bytes_max: 536870912, + db_pool_acquire_p95_ms: 1.2, + db_pool_in_use_max: 18, + db_pool_waiters_max: 2, + db_connections_max: 20, + residual_http_tasks: 0, + residual_db_sessions: 0, + residual_open_transactions: 0, + residual_sockets: 0, + residual_background_workers: 0, + residual_pool_checkouts: 0, + residual_pool_waiters: 0, + }; +} + +function assertRejected(result, pattern) { + const artifact = Buffer.from(`${JSON.stringify(result, null, 2)}\n`, "utf8"); + assert.throws( + () => validateEmploymentSeparationAcceptance( + artifact, + runtimeEvidence(artifact, result.selected_profile, result.expected_iterations), + FIXTURE_BYTES, + ), + pattern, + ); +} + +test("requires at least 1000 ordinary buyer-path iterations", () => { + assertRejected(performanceResult("first_commit", 1), /at least 1000 iterations/); +}); + +test("requires at least 100 contention pairs", () => { + assertRejected(performanceResult("contention", 99), /at least 100 iterations/); +}); + +test("requires fixed cardinality declarations in the result artifact", () => { + const result = performanceResult("first_commit", 1000); + result.minimum_non_contending_records = 1; + result.minimum_contention_pairs = 1; + assertRejected(result, /minimum_non_contending_records must equal 1000/); +}); diff --git a/tests/performance/employment_separation_acceptance_check.mjs b/tests/performance/employment_separation_acceptance_check.mjs new file mode 100644 index 000000000..c68e9c8e1 --- /dev/null +++ b/tests/performance/employment_separation_acceptance_check.mjs @@ -0,0 +1,44 @@ +import { readFile } from "node:fs/promises"; + +import { validateEmploymentSeparationAcceptance } from "./employment_separation_acceptance_contract.mjs"; +import { requireCommercialPerformanceAuthorities } from "./employment_separation_commercial_owner_gate.mjs"; +import { validatePinnedK6AcceptanceEvidence } from "./employment_separation_k6_evidence_contract.mjs"; +import { parseRuntimeEvidenceArtifact } from "./employment_separation_runtime_evidence_artifact.mjs"; + +async function main() { + // Byte/provenance attestation and deployed-candidate identity are separate + // trust boundaries. Surface every unresolved owner gap before touching caller + // paths so resolving one authority cannot accidentally enable acceptance while + // the other remains self-asserted. + requireCommercialPerformanceAuthorities(); + + const [resultPath, runtimeEvidencePath, fixturePath] = process.argv.slice(2); + if (!resultPath || !runtimeEvidencePath || !fixturePath || process.argv.length !== 5) { + throw new Error("usage: node employment_separation_acceptance_check.mjs "); + } + const [resultBytes, runtimeBytes, fixtureBytes] = await Promise.all([ + readFile(resultPath), + readFile(runtimeEvidencePath), + readFile(fixturePath), + ]); + const runtimeDocument = parseRuntimeEvidenceArtifact(runtimeBytes); + const evidenceContract = validateEmploymentSeparationAcceptance( + resultBytes, + runtimeDocument.parsed, + fixtureBytes, + ); + // Structural validation owns the bounded/strict result parser. Run it before + // this secondary pinned-runtime interpretation so duplicate members, nesting + // abuse, and oversized result artifacts cannot reach ordinary JSON.parse first. + const k6Evidence = validatePinnedK6AcceptanceEvidence(resultBytes, runtimeDocument.parsed); + process.stdout.write(`${JSON.stringify({ + ...evidenceContract, + ...k6Evidence, + runtime_evidence_sha256: runtimeDocument.sha256, + }, null, 2)}\n`); +} + +main().catch((error) => { + process.stderr.write(`${error.message}\n`); + process.exitCode = 1; +}); diff --git a/tests/performance/employment_separation_acceptance_cli_digest.test.mjs b/tests/performance/employment_separation_acceptance_cli_digest.test.mjs new file mode 100644 index 000000000..2aee8c315 --- /dev/null +++ b/tests/performance/employment_separation_acceptance_cli_digest.test.mjs @@ -0,0 +1,34 @@ +import assert from "node:assert/strict"; +import { spawnSync } from "node:child_process"; +import { fileURLToPath } from "node:url"; +import test from "node:test"; + +const acceptanceCheck = fileURLToPath(new URL("./employment_separation_acceptance_check.mjs", import.meta.url)); + +const PERFORMANCE_ATTESTATION_GAP_PATTERN = /authenticated performance-evidence attestation.*ContextualWisdomLab\/.github#2162/; +const DEPLOYMENT_IDENTITY_GAP_PATTERN = /authenticated deployed-candidate evidence.*ContextualWisdomLab\/Orgmetra#395/; + +function assertCommercialOwnerGaps(stderr) { + assert.match(stderr, PERFORMANCE_ATTESTATION_GAP_PATTERN); + assert.match(stderr, DEPLOYMENT_IDENTITY_GAP_PATTERN); +} + +test("commercial acceptance fails closed before trusting a caller-supplied result digest", () => { + const result = spawnSync( + process.execPath, + [acceptanceCheck, "result.json", "runtime.json", "fixture.json", "a".repeat(64)], + { encoding: "utf8" }, + ); + assert.notEqual(result.status, 0); + assertCommercialOwnerGaps(result.stderr); +}); + +test("commercial acceptance cannot be restored by substituting both result bytes and digest locally", () => { + const result = spawnSync( + process.execPath, + [acceptanceCheck, "substituted-result.json", "substituted-runtime.json", "fixture.json", "b".repeat(64)], + { encoding: "utf8" }, + ); + assert.notEqual(result.status, 0); + assertCommercialOwnerGaps(result.stderr); +}); diff --git a/tests/performance/employment_separation_acceptance_contract.mjs b/tests/performance/employment_separation_acceptance_contract.mjs new file mode 100644 index 000000000..4155a7a45 --- /dev/null +++ b/tests/performance/employment_separation_acceptance_contract.mjs @@ -0,0 +1,501 @@ +import { createHash } from "node:crypto"; +import { TextDecoder } from "node:util"; + +import { validatePerformanceFixture } from "./employment_separation_fixture_contract.mjs"; +import { validatePerformanceLoadModel } from "./employment_separation_run_contract.mjs"; +import { parseStrictJsonText } from "./strict_json_artifact.mjs"; + +const RESULT_SCHEMA = "orgmetra.employment_separation.performance_result.v1"; +const RUNTIME_SCHEMA = "orgmetra.employment_separation.runtime_evidence.v1"; +const SHA_PATTERN = /^[0-9a-f]{40}$/; +const SHA256_PATTERN = /^[0-9a-f]{64}$/; +const REFERENCE_PATTERN = /^[a-z][a-z0-9_]*:[A-Za-z0-9][A-Za-z0-9._~-]*$/; +const UTC_TIMESTAMP_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/; +const MINIMUM_NON_CONTENDING_RECORDS = 1000; +const MINIMUM_CONTENTION_PAIRS = 100; +const MAXIMUM_RESULT_ARTIFACT_BYTES = 1024 * 1024; +const MAXIMUM_FIXTURE_ARTIFACT_BYTES = 8 * 1024 * 1024; +const PROFILE_NAMES = Object.freeze(["first_commit", "replay", "rejection", "contention"]); +const PROFILE_PRECONDITIONS = Object.freeze({ + first_commit: "active_current_expected_version", + replay: "same_key_same_semantics_already_committed", + rejection: "expected_version_stale_or_semantic_conflict", + contention: "active_current_expected_version", +}); +const PROFILE_TRENDS = Object.freeze({ + first_commit: "employment_separation_first_commit_duration_ms", + replay: "employment_separation_replay_duration_ms", + rejection: "employment_separation_rejection_duration_ms", + contention: "employment_separation_contention_duration_ms", +}); +const RESIDUAL_FIELDS = Object.freeze([ + "residual_http_tasks", + "residual_db_sessions", + "residual_open_transactions", + "residual_sockets", + "residual_background_workers", + "residual_pool_checkouts", + "residual_pool_waiters", +]); +const RESULT_KEYS = Object.freeze([ + "candidate_sha", + "clearance_reference", + "completed_at", + "completed_iterations", + "dataset_id", + "expected_iterations", + "fixture_sha256", + "k6", + "load_model", + "minimum_contention_pairs", + "minimum_non_contending_records", + "preparation_protocol_reference", + "prepared_state_evidence_reference", + "profile_preconditions", + "resource_evidence_reference", + "sample_complete", + "schema_version", + "selected_profile", +]); +const RESULT_K6_IDENTITY_KEYS = Object.freeze([ + "k6_version", + "k6_image", + "k6_image_digest", + "k6_runner_identity", +]); +const RUNTIME_KEYS = Object.freeze([ + "candidate_sha", + "db_connections_max", + "db_pool_acquire_p95_ms", + "db_pool_in_use_max", + "db_pool_waiters_max", + "deployment_reference", + "environment_reference", + "fixture_sha256", + "host_cpu_percent_p95", + "host_rss_bytes_max", + "load_observation_reference", + "observed_at", + "observed_load_model", + "observed_service_sha", + "observer_reference", + "performance_result_sha256", + "residual_background_workers", + "residual_db_sessions", + "residual_http_tasks", + "residual_open_transactions", + "residual_pool_checkouts", + "residual_pool_waiters", + "residual_sockets", + "resource_evidence_reference", + "schema_version", + "selected_profile", +]); +const RUNTIME_K6_IDENTITY_KEYS = Object.freeze([ + "observed_k6_version", + "observed_k6_image", + "observed_k6_image_digest", + "observed_k6_runner_identity", +]); + +function fail(message) { + throw new Error(message); +} + +function plainObject(value, label) { + if (value === null || typeof value !== "object" || Array.isArray(value)) { + fail(`${label} must be an object`); + } + return value; +} + +function exactKeys(value, expected, label) { + const actual = Object.keys(value).sort(); + const wanted = [...expected].sort(); + if (actual.length !== wanted.length || actual.some((key, index) => key !== wanted[index])) { + fail(`${label} must contain exactly ${expected.join(", ")}`); + } +} + +function exactKeysWithOptionalGroup(value, required, optional, label) { + const actual = Object.keys(value); + const allowed = new Set([...required, ...optional]); + const requiredPresent = required.every((key) => Object.prototype.hasOwnProperty.call(value, key)); + const optionalPresent = optional.filter((key) => Object.prototype.hasOwnProperty.call(value, key)); + const hasUnknown = actual.some((key) => !allowed.has(key)); + const hasPartialOptionalGroup = optionalPresent.length !== 0 && optionalPresent.length !== optional.length; + if (!requiredPresent || hasUnknown || hasPartialOptionalGroup) { + fail(`${label} must contain exactly the required fields and either all or none of ${optional.join(", ")}`); + } +} + +function stringValue(value, label) { + if (typeof value !== "string" || value.trim() === "") fail(`${label} must be a non-empty string`); + return value; +} + +function validateOptionalStringGroup(value, keys, label) { + if (!Object.prototype.hasOwnProperty.call(value, keys[0])) return; + for (const key of keys) stringValue(value[key], `${label}.${key}`); +} + +function sha(value, label) { + const text = stringValue(value, label).toLowerCase(); + if (!SHA_PATTERN.test(text)) fail(`${label} must be a full Git commit SHA`); + return text; +} + +function sha256(value, label) { + const text = stringValue(value, label).toLowerCase(); + if (!SHA256_PATTERN.test(text)) fail(`${label} must be a SHA-256 digest`); + return text; +} + +function reference(value, label) { + const text = stringValue(value, label); + if (text.length > 200 || !REFERENCE_PATTERN.test(text)) fail(`${label} must be a namespaced opaque reference`); + return text; +} + +function utcTimestamp(value, label) { + const text = stringValue(value, label); + if (!UTC_TIMESTAMP_PATTERN.test(text)) { + fail(`${label} must be an RFC 3339 UTC timestamp`); + } + + const year = Number(text.slice(0, 4)); + const month = Number(text.slice(5, 7)); + const day = Number(text.slice(8, 10)); + const hour = Number(text.slice(11, 13)); + const minute = Number(text.slice(14, 16)); + const second = Number(text.slice(17, 19)); + const leapYear = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0); + const daysInMonth = [31, leapYear ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + if ( + year < 1 + || year > 9999 + || month < 1 + || month > 12 + || day < 1 + || day > daysInMonth[month - 1] + || hour > 23 + || minute > 59 + || second > 59 + ) { + fail(`${label} must be an RFC 3339 UTC timestamp`); + } + return text; +} + +function compareUtcTimestamps(left, right) { + const leftBody = left.slice(0, -1); + const rightBody = right.slice(0, -1); + const [leftSecond, leftFraction = ""] = leftBody.split("."); + const [rightSecond, rightFraction = ""] = rightBody.split("."); + if (leftSecond < rightSecond) return -1; + if (leftSecond > rightSecond) return 1; + + const precision = Math.max(leftFraction.length, rightFraction.length); + const normalizedLeft = leftFraction.padEnd(precision, "0"); + const normalizedRight = rightFraction.padEnd(precision, "0"); + if (normalizedLeft < normalizedRight) return -1; + if (normalizedLeft > normalizedRight) return 1; + return 0; +} + +function positiveInteger(value, label) { + if (!Number.isSafeInteger(value) || value < 1) fail(`${label} must be a positive safe integer`); + return value; +} + +function nonNegativeInteger(value, label) { + if (!Number.isSafeInteger(value) || value < 0) fail(`${label} must be a non-negative safe integer`); + return value; +} + +function finiteNumber(value, label, { minimum = 0, maximum = Number.POSITIVE_INFINITY } = {}) { + if (typeof value !== "number" || !Number.isFinite(value) || value < minimum || value > maximum) { + fail(`${label} must be a finite number between ${minimum} and ${maximum}`); + } + return value; +} + +function rawBytes(value, label) { + if (value instanceof Uint8Array) return value; + if (value instanceof ArrayBuffer) return new Uint8Array(value); + fail(`${label} must be supplied as raw bytes`); +} + +function decodeStrictUtf8(value, label, maximumBytes) { + const bytes = rawBytes(value, label); + if (bytes.byteLength > maximumBytes) { + fail(`${label} must not exceed ${maximumBytes} bytes`); + } + + let text; + try { + text = new TextDecoder("utf-8", { fatal: true }).decode(bytes); + } catch (error) { + throw new Error(`${label} must be valid UTF-8`, { cause: error }); + } + if (text.trim() === "") fail(`${label} must be non-empty JSON text`); + return { bytes, text }; +} + +function parseJsonArtifact(value, label, maximumBytes) { + const { bytes, text } = decodeStrictUtf8(value, label, maximumBytes); + const parsed = parseStrictJsonText(text, label); + return { + bytes, + digest: createHash("sha256").update(bytes).digest("hex"), + parsed, + }; +} + +function metric(data, name) { + const metrics = plainObject(data.k6, "result.k6").metrics; + const table = plainObject(metrics, "result.k6.metrics"); + return plainObject(table[name], `result.k6.metrics.${name}`); +} + +function metricValues(data, name) { + return plainObject(metric(data, name).values, `result.k6.metrics.${name}.values`); +} + +function sameLoadModel(observed, declared) { + for (const field of [ + "executor", + "target_rps", + "duration_seconds", + "preallocated_vus", + "max_vus", + "client_network_topology", + ]) { + if (observed[field] !== declared[field]) { + fail(`runtime.observed_load_model.${field} must match result.load_model.${field}`); + } + } +} + +function validateResult(result) { + exactKeysWithOptionalGroup(result, RESULT_KEYS, RESULT_K6_IDENTITY_KEYS, "result"); + validateOptionalStringGroup(result, RESULT_K6_IDENTITY_KEYS, "result"); + if (result.schema_version !== RESULT_SCHEMA) fail("result.schema_version is unsupported"); + const candidateSha = sha(result.candidate_sha, "result.candidate_sha"); + const fixtureSha256 = sha256(result.fixture_sha256, "result.fixture_sha256"); + const profile = stringValue(result.selected_profile, "result.selected_profile"); + const trendName = PROFILE_TRENDS[profile]; + if (!trendName) fail("result.selected_profile is unsupported"); + + reference(result.dataset_id, "result.dataset_id"); + reference(result.clearance_reference, "result.clearance_reference"); + reference(result.preparation_protocol_reference, "result.preparation_protocol_reference"); + reference(result.prepared_state_evidence_reference, "result.prepared_state_evidence_reference"); + reference(result.resource_evidence_reference, "result.resource_evidence_reference"); + const preconditions = plainObject(result.profile_preconditions, "result.profile_preconditions"); + exactKeys(preconditions, PROFILE_NAMES, "result.profile_preconditions"); + for (const profileName of PROFILE_NAMES) { + if (preconditions[profileName] !== PROFILE_PRECONDITIONS[profileName]) { + fail(`result.profile_preconditions.${profileName} must be ${PROFILE_PRECONDITIONS[profileName]}`); + } + } + + if (result.minimum_non_contending_records !== MINIMUM_NON_CONTENDING_RECORDS) { + fail(`result.minimum_non_contending_records must equal ${MINIMUM_NON_CONTENDING_RECORDS}`); + } + if (result.minimum_contention_pairs !== MINIMUM_CONTENTION_PAIRS) { + fail(`result.minimum_contention_pairs must equal ${MINIMUM_CONTENTION_PAIRS}`); + } + + const expectedIterations = positiveInteger(result.expected_iterations, "result.expected_iterations"); + const minimumIterations = profile === "contention" ? MINIMUM_CONTENTION_PAIRS : MINIMUM_NON_CONTENDING_RECORDS; + if (expectedIterations < minimumIterations) { + fail(`${profile} requires at least ${minimumIterations} iterations`); + } + const loadModel = validatePerformanceLoadModel(result.load_model, expectedIterations, profile); + const completedIterations = nonNegativeInteger(result.completed_iterations, "result.completed_iterations"); + if (result.sample_complete !== true || completedIterations !== expectedIterations) { + fail("result sample must be complete"); + } + const completedAt = utcTimestamp(result.completed_at, "result.completed_at"); + + const iterationValues = metricValues(result, "iterations"); + const metricIterations = nonNegativeInteger(iterationValues.count, "result.k6.metrics.iterations.values.count"); + if (metricIterations !== expectedIterations) fail("k6 iteration count must equal expected_iterations"); + + const expectedLatencySamples = profile === "contention" ? expectedIterations * 2 : expectedIterations; + if (!Number.isSafeInteger(expectedLatencySamples)) fail("expected latency sample count must be a safe integer"); + const latencySampleValues = metricValues(result, "employment_separation_latency_samples"); + const latencySampleCount = nonNegativeInteger( + latencySampleValues.count, + "result.k6.metrics.employment_separation_latency_samples.values.count", + ); + if (latencySampleCount !== expectedLatencySamples) { + fail(`latency sample count must equal ${expectedLatencySamples}`); + } + + const checkValues = metricValues(result, "checks"); + if (finiteNumber(checkValues.rate, "result.k6.metrics.checks.values.rate", { maximum: 1 }) !== 1) { + fail("k6 checks rate must equal 1"); + } + const checkPasses = nonNegativeInteger(checkValues.passes, "result.k6.metrics.checks.values.passes"); + const checkFailures = nonNegativeInteger(checkValues.fails, "result.k6.metrics.checks.values.fails"); + if (checkPasses !== expectedIterations || checkFailures !== 0) { + fail("k6 governed outcome checks must cover exactly expected_iterations with zero failures"); + } + + const unexpectedValues = metricValues(result, "employment_separation_unexpected_response"); + if (finiteNumber(unexpectedValues.rate, "result.k6.metrics.employment_separation_unexpected_response.values.rate", { maximum: 1 }) !== 0) { + fail("unexpected response rate must equal 0"); + } + const unexpectedPasses = nonNegativeInteger( + unexpectedValues.passes, + "result.k6.metrics.employment_separation_unexpected_response.values.passes", + ); + const unexpectedFailures = nonNegativeInteger( + unexpectedValues.fails, + "result.k6.metrics.employment_separation_unexpected_response.values.fails", + ); + if (unexpectedPasses !== 0 || unexpectedFailures !== expectedIterations) { + fail("governed outcome observations must cover exactly expected_iterations with zero unexpected responses"); + } + + const trendValues = metricValues(result, trendName); + const trendSampleCount = nonNegativeInteger(trendValues.count, `${trendName}.count`); + if (trendSampleCount !== expectedLatencySamples) { + fail(`Trend sample count must equal ${expectedLatencySamples}`); + } + const p50 = finiteNumber(trendValues["p(50)"], `${trendName}.p50`); + const p95 = finiteNumber(trendValues["p(95)"], `${trendName}.p95`); + const p99 = finiteNumber(trendValues["p(99)"], `${trendName}.p99`); + const maximum = finiteNumber(trendValues.max, `${trendName}.max`); + if (!(p50 <= p95 && p95 <= p99 && p99 <= maximum)) { + fail(`${trendName} percentile evidence must be monotonic`); + } + if (profile === "first_commit" && p95 > 20) fail("first_commit p95 must be <= 20 ms"); + + return { candidateSha, fixtureSha256, profile, p95, completedAt, expectedIterations, loadModel }; +} + +function parseAndValidateFixture(fixtureArtifact, result, validatedResult) { + const fixtureDocument = parseJsonArtifact( + fixtureArtifact, + "performance fixture", + MAXIMUM_FIXTURE_ARTIFACT_BYTES, + ); + if (fixtureDocument.digest !== validatedResult.fixtureSha256) { + fail("result.fixture_sha256 does not bind the supplied performance fixture"); + } + + const fixture = validatePerformanceFixture(fixtureDocument.parsed, { + minimumNonContendingRecords: MINIMUM_NON_CONTENDING_RECORDS, + minimumContentionPairs: MINIMUM_CONTENTION_PAIRS, + }); + if (fixture.candidate_sha.toLowerCase() !== validatedResult.candidateSha) { + fail("fixture.candidate_sha must match result.candidate_sha"); + } + if (compareUtcTimestamps(fixture.prepared_at, validatedResult.completedAt) > 0) { + fail("fixture.prepared_at must not follow result.completed_at"); + } + for (const field of [ + "dataset_id", + "clearance_reference", + "preparation_protocol_reference", + "prepared_state_evidence_reference", + "resource_evidence_reference", + ]) { + if (fixture[field] !== result[field]) fail(`fixture.${field} must match result.${field}`); + } + for (const profileName of PROFILE_NAMES) { + if (fixture.profile_preconditions[profileName] !== result.profile_preconditions[profileName]) { + fail(`fixture.profile_preconditions.${profileName} must match result.profile_preconditions.${profileName}`); + } + } + const expectedIterations = fixture.profiles[validatedResult.profile].length; + if (expectedIterations !== result.expected_iterations) { + fail("result.expected_iterations must equal the selected fixture profile cardinality"); + } + return fixtureDocument.digest; +} + +function validateRuntimeEvidence(runtime, resultDigest, result, validatedResult, fixtureDigest) { + exactKeysWithOptionalGroup(runtime, RUNTIME_KEYS, RUNTIME_K6_IDENTITY_KEYS, "runtime"); + validateOptionalStringGroup(runtime, RUNTIME_K6_IDENTITY_KEYS, "runtime"); + if (runtime.schema_version !== RUNTIME_SCHEMA) fail("runtime.schema_version is unsupported"); + const candidateSha = sha(runtime.candidate_sha, "runtime.candidate_sha"); + const observedServiceSha = sha(runtime.observed_service_sha, "runtime.observed_service_sha"); + if (candidateSha !== validatedResult.candidateSha) fail("runtime.candidate_sha must match result.candidate_sha"); + if (observedServiceSha !== validatedResult.candidateSha) fail("runtime.observed_service_sha must match candidate_sha"); + if (runtime.selected_profile !== validatedResult.profile) fail("runtime.selected_profile must match result.selected_profile"); + + const suppliedDigest = sha256(runtime.performance_result_sha256, "runtime.performance_result_sha256"); + if (suppliedDigest !== resultDigest) fail("runtime.performance_result_sha256 does not bind the supplied result artifact"); + const runtimeFixtureDigest = sha256(runtime.fixture_sha256, "runtime.fixture_sha256"); + if (runtimeFixtureDigest !== fixtureDigest || runtimeFixtureDigest !== validatedResult.fixtureSha256) { + fail("runtime.fixture_sha256 must match the exact validated performance fixture"); + } + + reference(runtime.environment_reference, "runtime.environment_reference"); + reference(runtime.deployment_reference, "runtime.deployment_reference"); + reference(runtime.observer_reference, "runtime.observer_reference"); + reference(runtime.load_observation_reference, "runtime.load_observation_reference"); + const observedLoadModel = validatePerformanceLoadModel( + runtime.observed_load_model, + validatedResult.expectedIterations, + validatedResult.profile, + ); + sameLoadModel(observedLoadModel, validatedResult.loadModel); + const resourceReference = reference(runtime.resource_evidence_reference, "runtime.resource_evidence_reference"); + if (resourceReference !== result.resource_evidence_reference) { + fail("runtime.resource_evidence_reference must match result.resource_evidence_reference"); + } + const observedAt = utcTimestamp(runtime.observed_at, "runtime.observed_at"); + if (compareUtcTimestamps(observedAt, validatedResult.completedAt) < 0) { + fail("runtime.observed_at must not precede result.completed_at"); + } + + finiteNumber(runtime.host_cpu_percent_p95, "runtime.host_cpu_percent_p95", { maximum: 100 }); + positiveInteger(runtime.host_rss_bytes_max, "runtime.host_rss_bytes_max"); + finiteNumber(runtime.db_pool_acquire_p95_ms, "runtime.db_pool_acquire_p95_ms"); + nonNegativeInteger(runtime.db_pool_in_use_max, "runtime.db_pool_in_use_max"); + nonNegativeInteger(runtime.db_pool_waiters_max, "runtime.db_pool_waiters_max"); + positiveInteger(runtime.db_connections_max, "runtime.db_connections_max"); + if (runtime.db_pool_in_use_max > runtime.db_connections_max) { + fail("runtime.db_pool_in_use_max cannot exceed runtime.db_connections_max"); + } + + for (const field of RESIDUAL_FIELDS) { + if (nonNegativeInteger(runtime[field], `runtime.${field}`) !== 0) { + fail(`runtime.${field} must be 0`); + } + } + + return suppliedDigest; +} + +export function validateEmploymentSeparationAcceptance(resultArtifact, runtimeEvidence, fixtureArtifact) { + const resultDocument = parseJsonArtifact( + resultArtifact, + "performance result", + MAXIMUM_RESULT_ARTIFACT_BYTES, + ); + const result = plainObject(resultDocument.parsed, "result"); + const runtime = plainObject(runtimeEvidence, "runtime"); + const validatedResult = validateResult(result); + const fixtureDigest = parseAndValidateFixture(fixtureArtifact, result, validatedResult); + const resultDigest = validateRuntimeEvidence( + runtime, + resultDocument.digest, + result, + validatedResult, + fixtureDigest, + ); + return { + structurally_valid: true, + candidate_sha: validatedResult.candidateSha, + selected_profile: validatedResult.profile, + fixture_sha256: fixtureDigest, + performance_result_sha256: resultDigest, + p95_ms: validatedResult.p95, + }; +} \ No newline at end of file diff --git a/tests/performance/employment_separation_acceptance_contract.test.mjs b/tests/performance/employment_separation_acceptance_contract.test.mjs new file mode 100644 index 000000000..573cdd2b8 --- /dev/null +++ b/tests/performance/employment_separation_acceptance_contract.test.mjs @@ -0,0 +1,198 @@ +import assert from "node:assert/strict"; +import { createHash } from "node:crypto"; +import test from "node:test"; + +import { validateEmploymentSeparationAcceptance } from "./employment_separation_acceptance_contract.mjs"; +import { + acceptanceFixtureBytes, + acceptanceFixtureSha256, + acceptanceLoadModel, +} from "./employment_separation_acceptance_fixture_test_support.mjs"; + +const FIXTURE_BYTES = acceptanceFixtureBytes(); +const FIXTURE_SHA256 = acceptanceFixtureSha256(FIXTURE_BYTES); + +function result() { + return { + schema_version: "orgmetra.employment_separation.performance_result.v1", + candidate_sha: "a".repeat(40), + fixture_sha256: FIXTURE_SHA256, + selected_profile: "first_commit", + expected_iterations: 1000, + completed_iterations: 1000, + sample_complete: true, + completed_at: "2026-09-13T04:10:00Z", + load_model: acceptanceLoadModel(1000), + dataset_id: "dataset:employment-separation-perf-1", + clearance_reference: "data_clearance:perf-2026-09", + preparation_protocol_reference: "protocol:employment-separation-perf-v1", + prepared_state_evidence_reference: "evidence:prepared-state-perf-1", + resource_evidence_reference: "metrics:employment-separation-perf-1", + profile_preconditions: { + first_commit: "active_current_expected_version", + replay: "same_key_same_semantics_already_committed", + rejection: "expected_version_stale_or_semantic_conflict", + contention: "active_current_expected_version", + }, + minimum_non_contending_records: 1000, + minimum_contention_pairs: 100, + k6: { + metrics: { + iterations: { values: { count: 1000, rate: 40 } }, + checks: { values: { rate: 1, passes: 1000, fails: 0 } }, + employment_separation_unexpected_response: { values: { rate: 0, passes: 0, fails: 1000 } }, + employment_separation_latency_samples: { values: { count: 1000, rate: 40 } }, + employment_separation_first_commit_duration_ms: { + values: { "p(50)": 8.1, "p(95)": 18.4, "p(99)": 19.7, max: 22.3, count: 1000 }, + }, + }, + }, + }; +} + +function render(value) { + return Buffer.from(`${JSON.stringify(value, null, 2)}\n`, "utf8"); +} + +function runtimeEvidence(resultArtifact) { + return { + schema_version: "orgmetra.employment_separation.runtime_evidence.v1", + candidate_sha: "a".repeat(40), + observed_service_sha: "a".repeat(40), + selected_profile: "first_commit", + performance_result_sha256: createHash("sha256").update(resultArtifact).digest("hex"), + fixture_sha256: FIXTURE_SHA256, + environment_reference: "environment:perf-staging-1", + deployment_reference: "deployment:orgmetra-people-a1", + observer_reference: "observer:perf-runtime-1", + load_observation_reference: "evidence:perf-load-observation-1", + observed_load_model: acceptanceLoadModel(1000), + resource_evidence_reference: "metrics:employment-separation-perf-1", + observed_at: "2026-09-13T04:10:01Z", + host_cpu_percent_p95: 42.5, + host_rss_bytes_max: 536870912, + db_pool_acquire_p95_ms: 1.2, + db_pool_in_use_max: 18, + db_pool_waiters_max: 2, + db_connections_max: 20, + residual_http_tasks: 0, + residual_db_sessions: 0, + residual_open_transactions: 0, + residual_sockets: 0, + residual_background_workers: 0, + residual_pool_checkouts: 0, + residual_pool_waiters: 0, + }; +} + +function evidencePair() { + const performance = result(); + const artifact = render(performance); + return { performance, artifact, runtime: runtimeEvidence(artifact) }; +} + +test("validates exact candidate evidence as structural evidence only", () => { + const { artifact, runtime } = evidencePair(); + const structuralEvidence = validateEmploymentSeparationAcceptance(artifact, runtime, FIXTURE_BYTES); + assert.deepEqual(structuralEvidence, { + structurally_valid: true, + candidate_sha: "a".repeat(40), + selected_profile: "first_commit", + fixture_sha256: FIXTURE_SHA256, + performance_result_sha256: runtime.performance_result_sha256, + p95_ms: 18.4, + }); + assert.equal(Object.hasOwn(structuralEvidence, "accepted"), false); +}); + +test("rejects a self-declared target when the observed service revision differs", () => { + const { artifact, runtime } = evidencePair(); + runtime.observed_service_sha = "b".repeat(40); + assert.throws(() => validateEmploymentSeparationAcceptance(artifact, runtime, FIXTURE_BYTES), /observed_service_sha must match candidate_sha/); +}); + +test("rejects a result artifact that is not the one observed by the runtime evidence", () => { + const { artifact, runtime } = evidencePair(); + runtime.performance_result_sha256 = "0".repeat(64); + assert.throws(() => validateEmploymentSeparationAcceptance(artifact, runtime, FIXTURE_BYTES), /performance_result_sha256/); +}); + +test("rejects declared load evidence that differs from the independent runtime observation", () => { + const { artifact, runtime } = evidencePair(); + runtime.observed_load_model.target_rps = 2; + runtime.observed_load_model.duration_seconds = 500; + assert.throws( + () => validateEmploymentSeparationAcceptance(artifact, runtime, FIXTURE_BYTES), + /observed_load_model.target_rps must match result.load_model.target_rps/, + ); +}); + +test("rejects first-commit evidence above the commercial p95 target", () => { + const { performance } = evidencePair(); + performance.k6.metrics.employment_separation_first_commit_duration_ms.values["p(95)"] = 20.001; + performance.k6.metrics.employment_separation_first_commit_duration_ms.values["p(99)"] = 21; + const artifact = render(performance); + assert.throws(() => validateEmploymentSeparationAcceptance(artifact, runtimeEvidence(artifact), FIXTURE_BYTES), /p95 must be <= 20 ms/); +}); + +test("rejects a closed workload model", () => { + const { performance } = evidencePair(); + performance.load_model.executor = "shared-iterations"; + const artifact = render(performance); + assert.throws(() => validateEmploymentSeparationAcceptance(artifact, runtimeEvidence(artifact), FIXTURE_BYTES), /constant-arrival-rate/); +}); + +test("rejects incomplete scheduled samples even when the completed subset is fast", () => { + const { performance } = evidencePair(); + performance.completed_iterations = 999; + performance.sample_complete = false; + performance.k6.metrics.iterations.values.count = 999; + performance.k6.metrics.employment_separation_latency_samples.values.count = 999; + performance.k6.metrics.employment_separation_first_commit_duration_ms.values.count = 999; + const artifact = render(performance); + assert.throws(() => validateEmploymentSeparationAcceptance(artifact, runtimeEvidence(artifact), FIXTURE_BYTES), /sample must be complete/); +}); + +test("rejects structural validity when post-run cleanup finds a run-scoped leak", () => { + const { artifact, runtime } = evidencePair(); + runtime.residual_open_transactions = 1; + assert.throws(() => validateEmploymentSeparationAcceptance(artifact, runtime, FIXTURE_BYTES), /residual_open_transactions must be 0/); +}); + +test("rejects missing CPU, memory, or pool observations instead of accepting latency alone", () => { + const { artifact, runtime } = evidencePair(); + runtime.db_pool_acquire_p95_ms = null; + assert.throws(() => validateEmploymentSeparationAcceptance(artifact, runtime, FIXTURE_BYTES), /db_pool_acquire_p95_ms/); +}); + +test("rejects byte-distinct result artifacts that collide after lossy UTF-8 decoding", () => { + const resultBytes = render(result()); + const malformedA = Buffer.concat([Buffer.from([0x80]), resultBytes]); + const malformedB = Buffer.concat([Buffer.from([0x81]), resultBytes]); + assert.equal(malformedA.toString("utf8"), malformedB.toString("utf8")); + assert.notEqual( + createHash("sha256").update(malformedA).digest("hex"), + createHash("sha256").update(malformedB).digest("hex"), + ); + + for (const malformed of [malformedA, malformedB]) { + const runtime = runtimeEvidence(malformed); + assert.throws( + () => validateEmploymentSeparationAcceptance(malformed, runtime, FIXTURE_BYTES), + /valid UTF-8/, + ); + } +}); + +test("local validator cannot emit a commercial acceptance receipt from caller-consistent replacement artifacts", () => { + const replacement = result(); + replacement.dataset_id = "dataset:employment-separation-perf-substituted"; + const artifact = render(replacement); + const structuralEvidence = validateEmploymentSeparationAcceptance( + artifact, + runtimeEvidence(artifact), + FIXTURE_BYTES, + ); + assert.equal(structuralEvidence.structurally_valid, true); + assert.equal(Object.hasOwn(structuralEvidence, "accepted"), false); +}); diff --git a/tests/performance/employment_separation_acceptance_edge.test.mjs b/tests/performance/employment_separation_acceptance_edge.test.mjs new file mode 100644 index 000000000..6543c9ed9 --- /dev/null +++ b/tests/performance/employment_separation_acceptance_edge.test.mjs @@ -0,0 +1,269 @@ +import assert from "node:assert/strict"; +import { createHash } from "node:crypto"; +import test from "node:test"; + +import { validateEmploymentSeparationAcceptance } from "./employment_separation_acceptance_contract.mjs"; +import { + acceptanceFixtureBytes, + acceptanceFixtureSha256, + acceptanceLoadModel, +} from "./employment_separation_acceptance_fixture_test_support.mjs"; + +const TREND_BY_PROFILE = { + first_commit: "employment_separation_first_commit_duration_ms", + replay: "employment_separation_replay_duration_ms", + rejection: "employment_separation_rejection_duration_ms", + contention: "employment_separation_contention_duration_ms", +}; +const PROFILE_PRECONDITIONS = Object.freeze({ + first_commit: "active_current_expected_version", + replay: "same_key_same_semantics_already_committed", + rejection: "expected_version_stale_or_semantic_conflict", + contention: "active_current_expected_version", +}); +const FIXTURE_BYTES = acceptanceFixtureBytes(); +const FIXTURE_SHA256 = acceptanceFixtureSha256(FIXTURE_BYTES); + +function result(profile = "first_commit") { + const iterations = profile === "contention" ? 100 : 1000; + const latencySamples = profile === "contention" ? iterations * 2 : iterations; + const trend = profile === "first_commit" + ? { "p(50)": 8, "p(95)": 18, "p(99)": 19, max: 22, count: latencySamples } + : { "p(50)": 30, "p(95)": 80, "p(99)": 100, max: 120, count: latencySamples }; + return { + schema_version: "orgmetra.employment_separation.performance_result.v1", + candidate_sha: "a".repeat(40), + fixture_sha256: FIXTURE_SHA256, + selected_profile: profile, + expected_iterations: iterations, + completed_iterations: iterations, + sample_complete: true, + completed_at: "2026-09-13T04:10:00Z", + load_model: acceptanceLoadModel(iterations), + dataset_id: "dataset:employment-separation-perf-1", + clearance_reference: "data_clearance:perf-2026-09", + preparation_protocol_reference: "protocol:employment-separation-perf-v1", + prepared_state_evidence_reference: "evidence:prepared-state-perf-1", + resource_evidence_reference: "metrics:employment-separation-perf-1", + profile_preconditions: { ...PROFILE_PRECONDITIONS }, + minimum_non_contending_records: 1000, + minimum_contention_pairs: 100, + k6: { + metrics: { + iterations: { values: { count: iterations } }, + checks: { values: { rate: 1, passes: iterations, fails: 0 } }, + employment_separation_unexpected_response: { values: { rate: 0, passes: 0, fails: iterations } }, + employment_separation_latency_samples: { values: { count: latencySamples } }, + [TREND_BY_PROFILE[profile]]: { values: trend }, + }, + }, + }; +} + +function runtime(resultArtifact, profile = "first_commit") { + const iterations = profile === "contention" ? 100 : 1000; + return { + schema_version: "orgmetra.employment_separation.runtime_evidence.v1", + candidate_sha: "a".repeat(40), + observed_service_sha: "a".repeat(40), + selected_profile: profile, + performance_result_sha256: createHash("sha256").update(resultArtifact).digest("hex"), + fixture_sha256: FIXTURE_SHA256, + environment_reference: "environment:perf-staging-1", + deployment_reference: "deployment:orgmetra-people-a1", + observer_reference: "observer:perf-runtime-1", + load_observation_reference: "evidence:perf-load-observation-1", + observed_load_model: acceptanceLoadModel(iterations), + resource_evidence_reference: "metrics:employment-separation-perf-1", + observed_at: "2026-09-13T04:10:01Z", + host_cpu_percent_p95: 42, + host_rss_bytes_max: 536870912, + db_pool_acquire_p95_ms: 1, + db_pool_in_use_max: 18, + db_pool_waiters_max: 2, + db_connections_max: 20, + residual_http_tasks: 0, + residual_db_sessions: 0, + residual_open_transactions: 0, + residual_sockets: 0, + residual_background_workers: 0, + residual_pool_checkouts: 0, + residual_pool_waiters: 0, + }; +} + +function render(value) { + return Buffer.from(`${JSON.stringify(value, null, 2)}\n`, "utf8"); +} + +function rejectResult(mutate, pattern = /./, profile = "first_commit") { + const value = result(profile); + mutate(value); + const artifact = render(value); + assert.throws(() => validateEmploymentSeparationAcceptance(artifact, runtime(artifact, profile), FIXTURE_BYTES), pattern); +} + +function rejectRuntime(mutate, pattern = /./, profile = "first_commit") { + const value = result(profile); + const artifact = render(value); + const evidence = runtime(artifact, profile); + mutate(evidence); + assert.throws(() => validateEmploymentSeparationAcceptance(artifact, evidence, FIXTURE_BYTES), pattern); +} + +test("rejects malformed result and runtime containers", () => { + assert.throws(() => validateEmploymentSeparationAcceptance("", {}, FIXTURE_BYTES), /raw bytes/); + assert.throws(() => validateEmploymentSeparationAcceptance(4, {}, FIXTURE_BYTES), /raw bytes/); + assert.throws(() => validateEmploymentSeparationAcceptance(Buffer.alloc(0), {}, FIXTURE_BYTES), /non-empty JSON text/); + assert.throws(() => validateEmploymentSeparationAcceptance(Buffer.from("not json", "utf8"), {}, FIXTURE_BYTES), /valid JSON/); + assert.throws(() => validateEmploymentSeparationAcceptance(Buffer.from("[]", "utf8"), {}, FIXTURE_BYTES), /result must be an object/); + const artifact = render(result()); + assert.throws(() => validateEmploymentSeparationAcceptance(artifact, [], FIXTURE_BYTES), /runtime must be an object/); + assert.throws(() => validateEmploymentSeparationAcceptance(artifact, null, FIXTURE_BYTES), /runtime must be an object/); +}); + +test("rejects invalid result authority and cardinality metadata", () => { + rejectResult((value) => { value.schema_version = "v0"; }, /schema_version/); + rejectResult((value) => { value.candidate_sha = ""; }, /non-empty string/); + rejectResult((value) => { value.candidate_sha = "z".repeat(40); }, /full Git commit SHA/); + rejectResult((value) => { value.fixture_sha256 = "bad"; }, /SHA-256 digest/); + rejectResult((value) => { value.selected_profile = null; }, /non-empty string/); + rejectResult((value) => { value.selected_profile = "unknown"; }, /unsupported/); + rejectResult((value) => { value.minimum_non_contending_records = 999; }, /must equal 1000/); + rejectResult((value) => { value.minimum_contention_pairs = 99; }, /must equal 100/); + rejectResult((value) => { value.expected_iterations = 1.5; }, /positive safe integer/); + rejectResult((value) => { value.expected_iterations = 0; }, /positive safe integer/); + rejectResult((value) => { value.completed_iterations = 1.5; }, /non-negative safe integer/); + rejectResult((value) => { value.completed_iterations = -1; }, /non-negative safe integer/); + rejectResult((value) => { value.sample_complete = false; }, /sample must be complete/); + rejectResult((value) => { value.completed_iterations = 999; }, /sample must be complete/); +}); + +test("rejects invalid load-model and client-network evidence", () => { + rejectResult((value) => { delete value.load_model; }, /load_model must be an object/); + rejectResult((value) => { value.load_model.executor = "shared-iterations"; }, /constant-arrival-rate/); + rejectResult((value) => { value.load_model.duration_seconds = 999; }, /must equal expectedIterations exactly/); + rejectResult((value) => { value.load_model.max_vus = 0; }, /positive safe integer/); + rejectResult((value) => { value.load_model.client_network_topology = "https_mitm_proxy"; }, /client_network_topology/); +}); + +test("rejects invalid timestamps and result evidence references", () => { + rejectResult((value) => { value.completed_at = "nope"; }, /UTC timestamp/); + rejectResult((value) => { value.completed_at = "2026-13-40T04:10:00Z"; }, /UTC timestamp/); + rejectResult((value) => { value.resource_evidence_reference = ""; }, /non-empty string/); + rejectResult((value) => { value.resource_evidence_reference = "not namespaced"; }, /namespaced opaque reference/); + rejectResult((value) => { value.resource_evidence_reference = `metrics:${"x".repeat(201)}`; }, /namespaced opaque reference/); +}); + +test("rejects malformed k6 metric containers and iteration evidence", () => { + rejectResult((value) => { value.k6 = null; }, /result.k6 must be an object/); + rejectResult((value) => { value.k6.metrics = []; }, /result.k6.metrics must be an object/); + rejectResult((value) => { delete value.k6.metrics.iterations; }, /iterations must be an object/); + rejectResult((value) => { value.k6.metrics.iterations.values = []; }, /iterations.values must be an object/); + rejectResult((value) => { value.k6.metrics.iterations.values.count = 999; }, /iteration count/); + rejectResult((value) => { delete value.k6.metrics.employment_separation_latency_samples; }, /latency_samples must be an object/); + rejectResult((value) => { value.k6.metrics.employment_separation_latency_samples.values.count = 999; }, /latency sample count/); + rejectResult( + (value) => { value.k6.metrics.employment_separation_latency_samples.values.count = 199; }, + /latency sample count/, + "contention", + ); + rejectResult((value) => { value.k6.metrics.employment_separation_first_commit_duration_ms.values.count = 999; }, /Trend sample count/); + rejectResult( + (value) => { value.k6.metrics.employment_separation_contention_duration_ms.values.count = 199; }, + /Trend sample count/, + "contention", + ); +}); + +test("rejects invalid success and unexpected-response rates", () => { + rejectResult((value) => { value.k6.metrics.checks.values.rate = 0.5; }, /checks rate must equal 1/); + rejectResult((value) => { value.k6.metrics.checks.values.rate = "1"; }, /finite number/); + rejectResult((value) => { value.k6.metrics.checks.values.rate = -0.1; }, /finite number/); + rejectResult((value) => { value.k6.metrics.checks.values.rate = 1.1; }, /finite number/); + rejectResult((value) => { value.k6.metrics.employment_separation_unexpected_response.values.rate = 0.1; }, /unexpected response rate/); +}); + +test("rejects invalid or non-monotonic latency distributions", () => { + const metric = (value) => value.k6.metrics.employment_separation_first_commit_duration_ms.values; + rejectResult((value) => { metric(value)["p(50)"] = null; }, /finite number/); + rejectResult((value) => { metric(value)["p(50)"] = -1; }, /finite number/); + rejectResult((value) => { metric(value)["p(50)"] = 19; metric(value)["p(95)"] = 18; }, /monotonic/); + rejectResult((value) => { metric(value)["p(95)"] = 20; metric(value)["p(99)"] = 19; }, /monotonic/); + rejectResult((value) => { metric(value)["p(99)"] = 23; metric(value).max = 22; }, /monotonic/); +}); + +test("accepts non-first profiles without applying the first-commit latency target", () => { + for (const profile of ["replay", "rejection", "contention"]) { + const value = result(profile); + const artifact = render(value); + const accepted = validateEmploymentSeparationAcceptance(artifact, runtime(artifact, profile), FIXTURE_BYTES); + assert.equal(accepted.selected_profile, profile); + assert.equal(accepted.p95_ms, 80); + } +}); + +test("rejects malformed runtime authority and artifact binding", () => { + rejectRuntime((value) => { value.schema_version = "v0"; }, /schema_version/); + rejectRuntime((value) => { value.candidate_sha = "b".repeat(40); }, /candidate_sha must match/); + rejectRuntime((value) => { value.candidate_sha = "bad"; }, /full Git commit SHA/); + rejectRuntime((value) => { value.observed_service_sha = "b".repeat(40); }, /observed_service_sha/); + rejectRuntime((value) => { value.observed_service_sha = "bad"; }, /full Git commit SHA/); + rejectRuntime((value) => { value.selected_profile = "replay"; }, /selected_profile/); + rejectRuntime((value) => { value.performance_result_sha256 = "bad"; }, /SHA-256 digest/); + rejectRuntime((value) => { value.performance_result_sha256 = "0".repeat(64); }, /does not bind/); + rejectRuntime((value) => { value.fixture_sha256 = "bad"; }, /SHA-256 digest/); + rejectRuntime((value) => { value.fixture_sha256 = "0".repeat(64); }, /exact validated performance fixture/); +}); + +test("rejects invalid runtime load observation", () => { + rejectRuntime((value) => { value.load_observation_reference = "bad"; }, /load_observation_reference/); + rejectRuntime((value) => { delete value.observed_load_model; }, /load_model must be an object/); + rejectRuntime((value) => { value.observed_load_model.executor = "shared-iterations"; }, /constant-arrival-rate/); + rejectRuntime((value) => { + value.observed_load_model.target_rps = 2; + value.observed_load_model.duration_seconds = 500; + }, /observed_load_model.target_rps must match/); +}); + +test("rejects invalid runtime references and observation time", () => { + rejectRuntime((value) => { value.environment_reference = ""; }, /non-empty string/); + rejectRuntime((value) => { value.environment_reference = "no namespace"; }, /namespaced opaque reference/); + rejectRuntime((value) => { value.deployment_reference = "bad"; }, /namespaced opaque reference/); + rejectRuntime((value) => { value.observer_reference = "bad"; }, /namespaced opaque reference/); + rejectRuntime((value) => { value.resource_evidence_reference = "metrics:different"; }, /must match result.resource_evidence_reference/); + rejectRuntime((value) => { value.observed_at = "nope"; }, /UTC timestamp/); + rejectRuntime((value) => { value.observed_at = "2026-13-40T04:10:01Z"; }, /UTC timestamp/); + rejectRuntime((value) => { value.observed_at = "2026-09-13T04:09:59Z"; }, /must not precede/); +}); + +test("rejects invalid host and pool measurements", () => { + rejectRuntime((value) => { value.host_cpu_percent_p95 = "42"; }, /finite number/); + rejectRuntime((value) => { value.host_cpu_percent_p95 = -1; }, /finite number/); + rejectRuntime((value) => { value.host_cpu_percent_p95 = 101; }, /finite number/); + rejectRuntime((value) => { value.host_rss_bytes_max = 0; }, /positive safe integer/); + rejectRuntime((value) => { value.host_rss_bytes_max = 1.5; }, /positive safe integer/); + rejectRuntime((value) => { value.db_pool_acquire_p95_ms = -1; }, /finite number/); + rejectRuntime((value) => { value.db_pool_in_use_max = -1; }, /non-negative safe integer/); + rejectRuntime((value) => { value.db_pool_in_use_max = 1.5; }, /non-negative safe integer/); + rejectRuntime((value) => { value.db_pool_waiters_max = -1; }, /non-negative safe integer/); + rejectRuntime((value) => { value.db_connections_max = 0; }, /positive safe integer/); + rejectRuntime((value) => { value.db_pool_in_use_max = 21; }, /cannot exceed/); +}); + +test("rejects every residual resource and invalid residual counters", () => { + const fields = [ + "residual_http_tasks", + "residual_db_sessions", + "residual_open_transactions", + "residual_sockets", + "residual_background_workers", + "residual_pool_checkouts", + "residual_pool_waiters", + ]; + for (const field of fields) { + rejectRuntime((value) => { value[field] = 1; }, new RegExp(`${field} must be 0`)); + } + rejectRuntime((value) => { value.residual_http_tasks = -1; }, /non-negative safe integer/); + rejectRuntime((value) => { value.residual_http_tasks = 1.5; }, /non-negative safe integer/); +}); diff --git a/tests/performance/employment_separation_acceptance_exact_keys.test.mjs b/tests/performance/employment_separation_acceptance_exact_keys.test.mjs new file mode 100644 index 000000000..c11b3adf5 --- /dev/null +++ b/tests/performance/employment_separation_acceptance_exact_keys.test.mjs @@ -0,0 +1,112 @@ +import assert from "node:assert/strict"; +import { createHash } from "node:crypto"; +import test from "node:test"; + +import { validateEmploymentSeparationAcceptance } from "./employment_separation_acceptance_contract.mjs"; +import { + acceptanceFixtureBytes, + acceptanceFixtureSha256, + acceptanceLoadModel, +} from "./employment_separation_acceptance_fixture_test_support.mjs"; + +const FIXTURE_BYTES = acceptanceFixtureBytes(); +const FIXTURE_SHA256 = acceptanceFixtureSha256(FIXTURE_BYTES); + +function performanceResult() { + return { + schema_version: "orgmetra.employment_separation.performance_result.v1", + candidate_sha: "a".repeat(40), + fixture_sha256: FIXTURE_SHA256, + selected_profile: "first_commit", + expected_iterations: 1000, + completed_iterations: 1000, + sample_complete: true, + completed_at: "2026-09-13T04:10:00Z", + load_model: acceptanceLoadModel(1000), + dataset_id: "dataset:employment-separation-perf-1", + clearance_reference: "data_clearance:perf-2026-09", + preparation_protocol_reference: "protocol:employment-separation-perf-v1", + prepared_state_evidence_reference: "evidence:prepared-state-perf-1", + resource_evidence_reference: "metrics:employment-separation-perf-1", + profile_preconditions: { + first_commit: "active_current_expected_version", + replay: "same_key_same_semantics_already_committed", + rejection: "expected_version_stale_or_semantic_conflict", + contention: "active_current_expected_version", + }, + minimum_non_contending_records: 1000, + minimum_contention_pairs: 100, + k6: { + metrics: { + iterations: { values: { count: 1000, rate: 40 } }, + checks: { values: { rate: 1, passes: 1000, fails: 0 } }, + employment_separation_unexpected_response: { values: { rate: 0, passes: 0, fails: 1000 } }, + employment_separation_latency_samples: { values: { count: 1000, rate: 40 } }, + employment_separation_first_commit_duration_ms: { + values: { "p(50)": 8.1, "p(95)": 18.4, "p(99)": 19.7, max: 22.3, count: 1000 }, + }, + }, + }, + }; +} + +function artifact(result) { + return Buffer.from(`${JSON.stringify(result, null, 2)}\n`, "utf8"); +} + +function runtimeEvidence(resultArtifact) { + return { + schema_version: "orgmetra.employment_separation.runtime_evidence.v1", + candidate_sha: "a".repeat(40), + observed_service_sha: "a".repeat(40), + selected_profile: "first_commit", + performance_result_sha256: createHash("sha256").update(resultArtifact).digest("hex"), + fixture_sha256: FIXTURE_SHA256, + environment_reference: "environment:perf-staging-1", + deployment_reference: "deployment:orgmetra-people-a1", + observer_reference: "observer:perf-runtime-1", + load_observation_reference: "evidence:perf-load-observation-1", + observed_load_model: acceptanceLoadModel(1000), + resource_evidence_reference: "metrics:employment-separation-perf-1", + observed_at: "2026-09-13T04:10:01Z", + host_cpu_percent_p95: 42.5, + host_rss_bytes_max: 536870912, + db_pool_acquire_p95_ms: 1.2, + db_pool_in_use_max: 18, + db_pool_waiters_max: 2, + db_connections_max: 20, + residual_http_tasks: 0, + residual_db_sessions: 0, + residual_open_transactions: 0, + residual_sockets: 0, + residual_background_workers: 0, + residual_pool_checkouts: 0, + residual_pool_waiters: 0, + }; +} + +test("rejects undeclared top-level result evidence fields", () => { + const result = performanceResult(); + result.unreviewed_extension = "must-not-be-ignored"; + const resultArtifact = artifact(result); + + assert.throws( + () => validateEmploymentSeparationAcceptance( + resultArtifact, + runtimeEvidence(resultArtifact), + FIXTURE_BYTES, + ), + /result must contain exactly/, + ); +}); + +test("rejects undeclared top-level runtime evidence fields", () => { + const resultArtifact = artifact(performanceResult()); + const runtime = runtimeEvidence(resultArtifact); + runtime.unreviewed_extension = "must-not-be-ignored"; + + assert.throws( + () => validateEmploymentSeparationAcceptance(resultArtifact, runtime, FIXTURE_BYTES), + /runtime must contain exactly/, + ); +}); diff --git a/tests/performance/employment_separation_acceptance_fixture_binding.test.mjs b/tests/performance/employment_separation_acceptance_fixture_binding.test.mjs new file mode 100644 index 000000000..4473f5bc2 --- /dev/null +++ b/tests/performance/employment_separation_acceptance_fixture_binding.test.mjs @@ -0,0 +1,141 @@ +import assert from "node:assert/strict"; +import { createHash } from "node:crypto"; +import test from "node:test"; + +import { validateEmploymentSeparationAcceptance } from "./employment_separation_acceptance_contract.mjs"; +import { + ACCEPTANCE_CANDIDATE_SHA, + ACCEPTANCE_PROFILE_PRECONDITIONS, + acceptanceFixtureBytes, + acceptanceFixtureSha256, + acceptanceLoadModel, +} from "./employment_separation_acceptance_fixture_test_support.mjs"; + +function performanceResult(fixtureSha256, { includeFixtureDigest = true } = {}) { + const value = { + schema_version: "orgmetra.employment_separation.performance_result.v1", + candidate_sha: ACCEPTANCE_CANDIDATE_SHA, + selected_profile: "first_commit", + expected_iterations: 1000, + completed_iterations: 1000, + sample_complete: true, + completed_at: "2026-09-13T04:10:00Z", + load_model: acceptanceLoadModel(1000), + dataset_id: "dataset:employment-separation-perf-1", + clearance_reference: "data_clearance:perf-2026-09", + preparation_protocol_reference: "protocol:employment-separation-perf-v1", + prepared_state_evidence_reference: "evidence:prepared-state-perf-1", + resource_evidence_reference: "metrics:employment-separation-perf-1", + profile_preconditions: { ...ACCEPTANCE_PROFILE_PRECONDITIONS }, + minimum_non_contending_records: 1000, + minimum_contention_pairs: 100, + k6: { + metrics: { + iterations: { values: { count: 1000 } }, + checks: { values: { rate: 1, passes: 1000, fails: 0 } }, + employment_separation_unexpected_response: { values: { rate: 0, passes: 0, fails: 1000 } }, + employment_separation_latency_samples: { values: { count: 1000 } }, + employment_separation_first_commit_duration_ms: { + values: { "p(50)": 8, "p(95)": 18, "p(99)": 19, max: 22, count: 1000 }, + }, + }, + }, + }; + if (includeFixtureDigest) value.fixture_sha256 = fixtureSha256; + return value; +} + +function runtimeEvidence(resultArtifact, fixtureSha256) { + return { + schema_version: "orgmetra.employment_separation.runtime_evidence.v1", + candidate_sha: ACCEPTANCE_CANDIDATE_SHA, + observed_service_sha: ACCEPTANCE_CANDIDATE_SHA, + selected_profile: "first_commit", + performance_result_sha256: createHash("sha256").update(resultArtifact).digest("hex"), + fixture_sha256: fixtureSha256, + environment_reference: "environment:perf-staging-1", + deployment_reference: "deployment:orgmetra-people-a1", + observer_reference: "observer:perf-runtime-1", + load_observation_reference: "evidence:perf-load-observation-1", + observed_load_model: acceptanceLoadModel(1000), + resource_evidence_reference: "metrics:employment-separation-perf-1", + observed_at: "2026-09-13T04:10:01Z", + host_cpu_percent_p95: 42, + host_rss_bytes_max: 536870912, + db_pool_acquire_p95_ms: 1, + db_pool_in_use_max: 18, + db_pool_waiters_max: 2, + db_connections_max: 20, + residual_http_tasks: 0, + residual_db_sessions: 0, + residual_open_transactions: 0, + residual_sockets: 0, + residual_background_workers: 0, + residual_pool_checkouts: 0, + residual_pool_waiters: 0, + }; +} + +function render(value) { + return Buffer.from(`${JSON.stringify(value, null, 2)}\n`, "utf8"); +} + +test("rejects acceptance without an exact fixture digest", () => { + const fixtureBytes = acceptanceFixtureBytes(); + const fixtureSha256 = acceptanceFixtureSha256(fixtureBytes); + const resultArtifact = render(performanceResult(fixtureSha256, { includeFixtureDigest: false })); + assert.throws( + () => validateEmploymentSeparationAcceptance(resultArtifact, runtimeEvidence(resultArtifact, fixtureSha256), fixtureBytes), + /fixture_sha256/, + ); +}); + +test("rejects runtime evidence bound to a different fixture digest", () => { + const fixtureBytes = acceptanceFixtureBytes(); + const fixtureSha256 = acceptanceFixtureSha256(fixtureBytes); + const resultArtifact = render(performanceResult(fixtureSha256)); + assert.throws( + () => validateEmploymentSeparationAcceptance(resultArtifact, runtimeEvidence(resultArtifact, "0".repeat(64)), fixtureBytes), + /fixture_sha256/, + ); +}); + +test("rejects a fixture whose exact bytes are not right-cleared", () => { + const fixtureBytes = acceptanceFixtureBytes({ rightCleared: false }); + const fixtureSha256 = acceptanceFixtureSha256(fixtureBytes); + const resultArtifact = render(performanceResult(fixtureSha256)); + assert.throws( + () => validateEmploymentSeparationAcceptance(resultArtifact, runtimeEvidence(resultArtifact, fixtureSha256), fixtureBytes), + /right_cleared/, + ); +}); + +test("rejects a fixture whose exact bytes are synthetic", () => { + const fixtureBytes = acceptanceFixtureBytes({ synthetic: true }); + const fixtureSha256 = acceptanceFixtureSha256(fixtureBytes); + const resultArtifact = render(performanceResult(fixtureSha256)); + assert.throws( + () => validateEmploymentSeparationAcceptance(resultArtifact, runtimeEvidence(resultArtifact, fixtureSha256), fixtureBytes), + /synthetic/, + ); +}); + +test("rejects byte-distinct fixture artifacts that collide after lossy UTF-8 decoding", () => { + const fixtureBytes = acceptanceFixtureBytes(); + const malformedA = Buffer.concat([Buffer.from([0x80]), fixtureBytes]); + const malformedB = Buffer.concat([Buffer.from([0x81]), fixtureBytes]); + assert.equal(malformedA.toString("utf8"), malformedB.toString("utf8")); + assert.notEqual( + createHash("sha256").update(malformedA).digest("hex"), + createHash("sha256").update(malformedB).digest("hex"), + ); + + for (const malformed of [malformedA, malformedB]) { + const fixtureSha256 = createHash("sha256").update(malformed).digest("hex"); + const resultArtifact = render(performanceResult(fixtureSha256)); + assert.throws( + () => validateEmploymentSeparationAcceptance(resultArtifact, runtimeEvidence(resultArtifact, fixtureSha256), malformed), + /valid UTF-8/, + ); + } +}); diff --git a/tests/performance/employment_separation_acceptance_fixture_test_support.mjs b/tests/performance/employment_separation_acceptance_fixture_test_support.mjs new file mode 100644 index 000000000..d09eea460 --- /dev/null +++ b/tests/performance/employment_separation_acceptance_fixture_test_support.mjs @@ -0,0 +1,95 @@ +import { createHash } from "node:crypto"; + +import { + PERFORMANCE_CLIENT_NETWORK_TOPOLOGY, + approvedPerformanceLoadModel, +} from "./employment_separation_run_contract.mjs"; + +export const ACCEPTANCE_CANDIDATE_SHA = "a".repeat(40); +export const ACCEPTANCE_PROFILE_PRECONDITIONS = Object.freeze({ + first_commit: "active_current_expected_version", + replay: "same_key_same_semantics_already_committed", + rejection: "expected_version_stale_or_semantic_conflict", + contention: "active_current_expected_version", +}); + +function uuid(value) { + const tail = value.toString(16).padStart(12, "0"); + return `00000000-0000-4000-8000-${tail}`; +} + +function command(index, keySuffix = "only") { + return { + actor_reference: `actor:perf${index}`, + idempotency_key: `employment-separation-perf-${index}-${keySuffix}`, + tenant_record_id: uuid(900000), + payload: { + confirmation_reference: `confirmation:perf${index}`, + employment_record_id: uuid(100000 + index), + evidence_reference: `evidence:perf${index}`, + evidence_version_code: "v1", + expected_employment_record_version_id: uuid(500000 + index), + person_record_id: uuid(300000 + index), + separation_effective_on: "2026-09-01", + separation_reason_code: "voluntary_resignation", + }, + }; +} + +function records(start, count) { + return Array.from({ length: count }, (_, offset) => command(start + offset)); +} + +export function acceptanceLoadModel(expectedIterations) { + if (expectedIterations === 1000) return { ...approvedPerformanceLoadModel("first_commit") }; + if (expectedIterations === 100) return { ...approvedPerformanceLoadModel("contention") }; + return { + executor: "constant-arrival-rate", + target_rps: 1, + duration_seconds: expectedIterations, + preallocated_vus: 1, + max_vus: 1, + client_network_topology: PERFORMANCE_CLIENT_NETWORK_TOPOLOGY, + }; +} + +export function acceptanceFixture({ rightCleared = true, synthetic = false } = {}) { + const contentionStart = 4000; + return { + candidate_sha: ACCEPTANCE_CANDIDATE_SHA, + clearance_reference: "data_clearance:perf-2026-09", + dataset_id: "dataset:employment-separation-perf-1", + prepared_at: "2026-09-13T04:00:00Z", + prepared_state_evidence_reference: "evidence:prepared-state-perf-1", + preparation_protocol_reference: "protocol:employment-separation-perf-v1", + profile_preconditions: { ...ACCEPTANCE_PROFILE_PRECONDITIONS }, + profiles: { + first_commit: records(1, 1000), + replay: records(1001, 1000), + rejection: records(2001, 1000), + contention: Array.from({ length: 100 }, (_, offset) => { + const index = contentionStart + offset; + return { + left: command(index, "left"), + right: command(index, "right"), + }; + }), + }, + resource_evidence_reference: "metrics:employment-separation-perf-1", + right_cleared: rightCleared, + schema_version: "orgmetra.employment_separation.performance_fixture.v1", + synthetic, + }; +} + +export function acceptanceFixtureText(options) { + return `${JSON.stringify(acceptanceFixture(options), null, 2)}\n`; +} + +export function acceptanceFixtureBytes(options) { + return Buffer.from(acceptanceFixtureText(options), "utf8"); +} + +export function acceptanceFixtureSha256(value) { + return createHash("sha256").update(value).digest("hex"); +} diff --git a/tests/performance/employment_separation_acceptance_latency_samples.test.mjs b/tests/performance/employment_separation_acceptance_latency_samples.test.mjs new file mode 100644 index 000000000..59e5cd3e6 --- /dev/null +++ b/tests/performance/employment_separation_acceptance_latency_samples.test.mjs @@ -0,0 +1,115 @@ +import assert from "node:assert/strict"; +import { createHash } from "node:crypto"; +import test from "node:test"; + +import { validateEmploymentSeparationAcceptance } from "./employment_separation_acceptance_contract.mjs"; +import { + acceptanceFixtureBytes, + acceptanceFixtureSha256, + acceptanceLoadModel, +} from "./employment_separation_acceptance_fixture_test_support.mjs"; + +const candidateSha = "a".repeat(40); +const FIXTURE_BYTES = acceptanceFixtureBytes(); +const FIXTURE_SHA256 = acceptanceFixtureSha256(FIXTURE_BYTES); + +function performanceResult(latencySamples = 1000, trendSamples = latencySamples) { + return { + schema_version: "orgmetra.employment_separation.performance_result.v1", + candidate_sha: candidateSha, + fixture_sha256: FIXTURE_SHA256, + selected_profile: "first_commit", + expected_iterations: 1000, + completed_iterations: 1000, + sample_complete: true, + completed_at: "2026-09-13T04:10:00Z", + load_model: acceptanceLoadModel(1000), + dataset_id: "dataset:employment-separation-perf-1", + clearance_reference: "data_clearance:perf-2026-09", + preparation_protocol_reference: "protocol:employment-separation-perf-v1", + prepared_state_evidence_reference: "evidence:prepared-state-perf-1", + resource_evidence_reference: "metrics:employment-separation-perf-1", + profile_preconditions: { + first_commit: "active_current_expected_version", + replay: "same_key_same_semantics_already_committed", + rejection: "expected_version_stale_or_semantic_conflict", + contention: "active_current_expected_version", + }, + minimum_non_contending_records: 1000, + minimum_contention_pairs: 100, + k6: { + metrics: { + iterations: { values: { count: 1000 } }, + checks: { values: { rate: 1, passes: 1000, fails: 0 } }, + employment_separation_unexpected_response: { values: { rate: 0, passes: 0, fails: 1000 } }, + employment_separation_latency_samples: { values: { count: latencySamples } }, + employment_separation_first_commit_duration_ms: { + values: { "p(50)": 8, "p(95)": 18, "p(99)": 19, max: 22, count: trendSamples }, + }, + }, + }, + }; +} + +function render(value) { + return Buffer.from(`${JSON.stringify(value, null, 2)}\n`, "utf8"); +} + +function runtimeEvidence(resultArtifact) { + return { + schema_version: "orgmetra.employment_separation.runtime_evidence.v1", + candidate_sha: candidateSha, + observed_service_sha: candidateSha, + selected_profile: "first_commit", + performance_result_sha256: createHash("sha256").update(resultArtifact).digest("hex"), + fixture_sha256: FIXTURE_SHA256, + environment_reference: "environment:perf-staging-1", + deployment_reference: "deployment:orgmetra-people-a1", + observer_reference: "observer:perf-runtime-1", + load_observation_reference: "evidence:perf-load-observation-1", + observed_load_model: acceptanceLoadModel(1000), + resource_evidence_reference: "metrics:employment-separation-perf-1", + observed_at: "2026-09-13T04:10:01Z", + host_cpu_percent_p95: 42, + host_rss_bytes_max: 536870912, + db_pool_acquire_p95_ms: 1, + db_pool_in_use_max: 18, + db_pool_waiters_max: 2, + db_connections_max: 20, + residual_http_tasks: 0, + residual_db_sessions: 0, + residual_open_transactions: 0, + residual_sockets: 0, + residual_background_workers: 0, + residual_pool_checkouts: 0, + residual_pool_waiters: 0, + }; +} + +test("rejects a complete iteration count with an incomplete latency counter", () => { + const artifact = render(performanceResult(999, 1000)); + assert.throws( + () => validateEmploymentSeparationAcceptance(artifact, runtimeEvidence(artifact), FIXTURE_BYTES), + /latency sample count/, + ); +}); + +test("rejects a complete counter when the measured Trend itself is truncated", () => { + const artifact = render(performanceResult(1000, 999)); + assert.throws( + () => validateEmploymentSeparationAcceptance(artifact, runtimeEvidence(artifact), FIXTURE_BYTES), + /Trend sample count/, + ); +}); + +test("validates latency evidence structurally only when every expected request contributed to the measured Trend", () => { + const artifact = render(performanceResult(1000, 1000)); + const structuralEvidence = validateEmploymentSeparationAcceptance( + artifact, + runtimeEvidence(artifact), + FIXTURE_BYTES, + ); + assert.equal(structuralEvidence.structurally_valid, true); + assert.equal(Object.hasOwn(structuralEvidence, "accepted"), false); + assert.equal(structuralEvidence.p95_ms, 18); +}); diff --git a/tests/performance/employment_separation_acceptance_outcome_cardinality.test.mjs b/tests/performance/employment_separation_acceptance_outcome_cardinality.test.mjs new file mode 100644 index 000000000..9005f69d7 --- /dev/null +++ b/tests/performance/employment_separation_acceptance_outcome_cardinality.test.mjs @@ -0,0 +1,103 @@ +import assert from "node:assert/strict"; +import { createHash } from "node:crypto"; +import test from "node:test"; + +import { validateEmploymentSeparationAcceptance } from "./employment_separation_acceptance_contract.mjs"; +import { + acceptanceFixtureBytes, + acceptanceFixtureSha256, + acceptanceLoadModel, +} from "./employment_separation_acceptance_fixture_test_support.mjs"; + +const FIXTURE_BYTES = acceptanceFixtureBytes(); +const FIXTURE_SHA256 = acceptanceFixtureSha256(FIXTURE_BYTES); + +function result() { + return { + schema_version: "orgmetra.employment_separation.performance_result.v1", + candidate_sha: "a".repeat(40), + fixture_sha256: FIXTURE_SHA256, + selected_profile: "first_commit", + expected_iterations: 1000, + completed_iterations: 1000, + sample_complete: true, + completed_at: "2026-09-13T04:10:00Z", + load_model: acceptanceLoadModel(1000), + dataset_id: "dataset:employment-separation-perf-1", + clearance_reference: "data_clearance:perf-2026-09", + preparation_protocol_reference: "protocol:employment-separation-perf-v1", + prepared_state_evidence_reference: "evidence:prepared-state-perf-1", + resource_evidence_reference: "metrics:employment-separation-perf-1", + profile_preconditions: { + first_commit: "active_current_expected_version", + replay: "same_key_same_semantics_already_committed", + rejection: "expected_version_stale_or_semantic_conflict", + contention: "active_current_expected_version", + }, + minimum_non_contending_records: 1000, + minimum_contention_pairs: 100, + k6: { + metrics: { + iterations: { values: { count: 1000, rate: 40 } }, + checks: { values: { rate: 1, passes: 1000, fails: 0 } }, + employment_separation_unexpected_response: { values: { rate: 0, passes: 0, fails: 1000 } }, + employment_separation_latency_samples: { values: { count: 1000, rate: 40 } }, + employment_separation_first_commit_duration_ms: { + values: { "p(50)": 8.1, "p(95)": 18.4, "p(99)": 19.7, max: 22.3, count: 1000 }, + }, + }, + }, + }; +} + +function render(value) { + return Buffer.from(`${JSON.stringify(value, null, 2)}\n`, "utf8"); +} + +function runtimeEvidence(resultArtifact) { + return { + schema_version: "orgmetra.employment_separation.runtime_evidence.v1", + candidate_sha: "a".repeat(40), + observed_service_sha: "a".repeat(40), + selected_profile: "first_commit", + performance_result_sha256: createHash("sha256").update(resultArtifact).digest("hex"), + fixture_sha256: FIXTURE_SHA256, + environment_reference: "environment:perf-staging-1", + deployment_reference: "deployment:orgmetra-people-a1", + observer_reference: "observer:perf-runtime-1", + load_observation_reference: "evidence:perf-load-observation-1", + observed_load_model: acceptanceLoadModel(1000), + resource_evidence_reference: "metrics:employment-separation-perf-1", + observed_at: "2026-09-13T04:10:01Z", + host_cpu_percent_p95: 42.5, + host_rss_bytes_max: 536870912, + db_pool_acquire_p95_ms: 1.2, + db_pool_in_use_max: 18, + db_pool_waiters_max: 2, + db_connections_max: 20, + residual_http_tasks: 0, + residual_db_sessions: 0, + residual_open_transactions: 0, + residual_sockets: 0, + residual_background_workers: 0, + residual_pool_checkouts: 0, + residual_pool_waiters: 0, + }; +} + +function validate(performance) { + const artifact = render(performance); + return validateEmploymentSeparationAcceptance(artifact, runtimeEvidence(artifact), FIXTURE_BYTES); +} + +test("rejects perfect check rate when governed outcome checks cover only a subset of iterations", () => { + const performance = result(); + performance.k6.metrics.checks.values.passes = 1; + assert.throws(() => validate(performance), /checks.*expected_iterations|governed outcome checks/i); +}); + +test("rejects zero unexpected-response rate when governed outcome observations cover only a subset of iterations", () => { + const performance = result(); + performance.k6.metrics.employment_separation_unexpected_response.values.fails = 1; + assert.throws(() => validate(performance), /unexpected response.*expected_iterations|governed outcome observations/i); +}); diff --git a/tests/performance/employment_separation_acceptance_provenance.test.mjs b/tests/performance/employment_separation_acceptance_provenance.test.mjs new file mode 100644 index 000000000..f83443052 --- /dev/null +++ b/tests/performance/employment_separation_acceptance_provenance.test.mjs @@ -0,0 +1,141 @@ +import assert from "node:assert/strict"; +import { createHash } from "node:crypto"; +import test from "node:test"; + +import { validateEmploymentSeparationAcceptance } from "./employment_separation_acceptance_contract.mjs"; +import { validateRunnerResultDigest } from "./employment_separation_runner_result_digest.mjs"; +import { + acceptanceFixtureBytes, + acceptanceFixtureSha256, + acceptanceLoadModel, +} from "./employment_separation_acceptance_fixture_test_support.mjs"; + +const PROFILE_PRECONDITIONS = Object.freeze({ + first_commit: "active_current_expected_version", + replay: "same_key_same_semantics_already_committed", + rejection: "expected_version_stale_or_semantic_conflict", + contention: "active_current_expected_version", +}); +const FIXTURE_BYTES = acceptanceFixtureBytes(); +const FIXTURE_SHA256 = acceptanceFixtureSha256(FIXTURE_BYTES); + +function result() { + return { + schema_version: "orgmetra.employment_separation.performance_result.v1", + candidate_sha: "a".repeat(40), + fixture_sha256: FIXTURE_SHA256, + selected_profile: "first_commit", + expected_iterations: 1000, + completed_iterations: 1000, + sample_complete: true, + completed_at: "2026-09-13T04:10:00Z", + load_model: acceptanceLoadModel(1000), + dataset_id: "dataset:employment-separation-perf-1", + clearance_reference: "data_clearance:perf-2026-09", + preparation_protocol_reference: "protocol:employment-separation-perf-v1", + prepared_state_evidence_reference: "evidence:prepared-state-perf-1", + resource_evidence_reference: "metrics:employment-separation-perf-1", + profile_preconditions: { ...PROFILE_PRECONDITIONS }, + minimum_non_contending_records: 1000, + minimum_contention_pairs: 100, + k6: { + metrics: { + iterations: { values: { count: 1000 } }, + checks: { values: { rate: 1, passes: 1000, fails: 0 } }, + employment_separation_unexpected_response: { values: { rate: 0, passes: 0, fails: 1000 } }, + employment_separation_latency_samples: { values: { count: 1000 } }, + employment_separation_first_commit_duration_ms: { + values: { "p(50)": 8, "p(95)": 18, "p(99)": 19, max: 22, count: 1000 }, + }, + }, + }, + }; +} + +function render(value) { + return Buffer.from(`${JSON.stringify(value, null, 2)}\n`, "utf8"); +} + +function runtime(resultArtifact) { + return { + schema_version: "orgmetra.employment_separation.runtime_evidence.v1", + candidate_sha: "a".repeat(40), + observed_service_sha: "a".repeat(40), + selected_profile: "first_commit", + performance_result_sha256: createHash("sha256").update(resultArtifact).digest("hex"), + fixture_sha256: FIXTURE_SHA256, + environment_reference: "environment:perf-staging-1", + deployment_reference: "deployment:orgmetra-people-a1", + observer_reference: "observer:perf-runtime-1", + load_observation_reference: "evidence:perf-load-observation-1", + observed_load_model: acceptanceLoadModel(1000), + resource_evidence_reference: "metrics:employment-separation-perf-1", + observed_at: "2026-09-13T04:10:01Z", + host_cpu_percent_p95: 42, + host_rss_bytes_max: 536870912, + db_pool_acquire_p95_ms: 1, + db_pool_in_use_max: 18, + db_pool_waiters_max: 2, + db_connections_max: 20, + residual_http_tasks: 0, + residual_db_sessions: 0, + residual_open_transactions: 0, + residual_sockets: 0, + residual_background_workers: 0, + residual_pool_checkouts: 0, + residual_pool_waiters: 0, + }; +} + +function reject(mutate, pattern) { + const value = result(); + mutate(value); + const artifact = render(value); + assert.throws(() => validateEmploymentSeparationAcceptance(artifact, runtime(artifact), FIXTURE_BYTES), pattern); +} + +test("requires right-cleared dataset and preparation provenance references", () => { + for (const field of [ + "dataset_id", + "clearance_reference", + "preparation_protocol_reference", + "prepared_state_evidence_reference", + ]) { + reject((value) => { delete value[field]; }, new RegExp(`result\\.${field}`)); + reject((value) => { value[field] = "not namespaced"; }, new RegExp(`result\\.${field}`)); + } +}); + +test("requires exact profile-precondition vocabulary", () => { + reject((value) => { delete value.profile_preconditions; }, /result\.profile_preconditions/); + reject((value) => { value.profile_preconditions = []; }, /result\.profile_preconditions/); + reject((value) => { delete value.profile_preconditions.replay; }, /exactly first_commit, replay, rejection, contention/); + reject((value) => { value.profile_preconditions.replay = "already_committed_maybe"; }, /profile_preconditions\.replay/); + reject((value) => { value.profile_preconditions.extra = "unexpected"; }, /exactly first_commit, replay, rejection, contention/); +}); + +test("requires exact open-load and direct-network provenance", () => { + reject((value) => { delete value.load_model; }, /load_model must be an object/); + reject((value) => { value.load_model.executor = "shared-iterations"; }, /constant-arrival-rate/); + reject((value) => { value.load_model.client_network_topology = "https_mitm_proxy"; }, /client_network_topology/); +}); + +test("binds later acceptance to the exact digest emitted by the benchmark runner", () => { + const artifact = render(result()); + const digest = createHash("sha256").update(artifact).digest("hex"); + assert.equal(validateRunnerResultDigest(artifact, digest), digest); + + const substituted = Buffer.concat([artifact, Buffer.from(" ", "utf8")]); + assert.throws( + () => validateRunnerResultDigest(substituted, digest), + /runner result digest does not bind the supplied performance result/, + ); +}); + +test("rejects malformed runner result digests before acceptance", () => { + const artifact = render(result()); + assert.throws( + () => validateRunnerResultDigest(artifact, "not-a-sha256"), + /runner result digest must be a SHA-256 digest/, + ); +}); diff --git a/tests/performance/employment_separation_acceptance_timestamp.test.mjs b/tests/performance/employment_separation_acceptance_timestamp.test.mjs new file mode 100644 index 000000000..585ac42b5 --- /dev/null +++ b/tests/performance/employment_separation_acceptance_timestamp.test.mjs @@ -0,0 +1,189 @@ +import assert from "node:assert/strict"; +import { createHash } from "node:crypto"; +import test from "node:test"; + +import { validateEmploymentSeparationAcceptance } from "./employment_separation_acceptance_contract.mjs"; +import { + ACCEPTANCE_CANDIDATE_SHA, + ACCEPTANCE_PROFILE_PRECONDITIONS, + acceptanceFixtureBytes, + acceptanceFixtureSha256, + acceptanceLoadModel, +} from "./employment_separation_acceptance_fixture_test_support.mjs"; + +const FIXTURE_BYTES = acceptanceFixtureBytes(); +const FIXTURE_SHA256 = acceptanceFixtureSha256(FIXTURE_BYTES); + +function render(value) { + return Buffer.from(`${JSON.stringify(value, null, 2)}\n`, "utf8"); +} + +function performanceResult() { + return { + schema_version: "orgmetra.employment_separation.performance_result.v1", + candidate_sha: ACCEPTANCE_CANDIDATE_SHA, + fixture_sha256: FIXTURE_SHA256, + selected_profile: "first_commit", + expected_iterations: 1000, + completed_iterations: 1000, + sample_complete: true, + completed_at: "2026-09-13T04:10:00Z", + load_model: acceptanceLoadModel(1000), + dataset_id: "dataset:employment-separation-perf-1", + clearance_reference: "data_clearance:perf-2026-09", + preparation_protocol_reference: "protocol:employment-separation-perf-v1", + prepared_state_evidence_reference: "evidence:prepared-state-perf-1", + resource_evidence_reference: "metrics:employment-separation-perf-1", + profile_preconditions: { ...ACCEPTANCE_PROFILE_PRECONDITIONS }, + minimum_non_contending_records: 1000, + minimum_contention_pairs: 100, + k6: { + metrics: { + iterations: { values: { count: 1000, rate: 20 } }, + checks: { values: { rate: 1, passes: 1000, fails: 0 } }, + employment_separation_unexpected_response: { values: { rate: 0, passes: 0, fails: 1000 } }, + employment_separation_latency_samples: { values: { count: 1000, rate: 20 } }, + employment_separation_first_commit_duration_ms: { + values: { "p(50)": 8.1, "p(95)": 18.4, "p(99)": 19.7, max: 22.3, count: 1000 }, + }, + }, + }, + }; +} + +function runtimeEvidence(resultArtifact) { + return { + schema_version: "orgmetra.employment_separation.runtime_evidence.v1", + candidate_sha: ACCEPTANCE_CANDIDATE_SHA, + observed_service_sha: ACCEPTANCE_CANDIDATE_SHA, + selected_profile: "first_commit", + performance_result_sha256: createHash("sha256").update(resultArtifact).digest("hex"), + fixture_sha256: FIXTURE_SHA256, + environment_reference: "environment:perf-staging-1", + deployment_reference: "deployment:orgmetra-people-a1", + observer_reference: "observer:perf-runtime-1", + load_observation_reference: "evidence:perf-load-observation-1", + observed_load_model: acceptanceLoadModel(1000), + resource_evidence_reference: "metrics:employment-separation-perf-1", + observed_at: "2026-09-13T04:10:01Z", + host_cpu_percent_p95: 42.5, + host_rss_bytes_max: 536870912, + db_pool_acquire_p95_ms: 1.2, + db_pool_in_use_max: 18, + db_pool_waiters_max: 2, + db_connections_max: 20, + residual_http_tasks: 0, + residual_db_sessions: 0, + residual_open_transactions: 0, + residual_sockets: 0, + residual_background_workers: 0, + residual_pool_checkouts: 0, + residual_pool_waiters: 0, + }; +} + +test("rejects an impossible result completion calendar date instead of accepting Date.parse normalization", () => { + const performance = performanceResult(); + performance.completed_at = "2026-02-30T04:10:00Z"; + const artifact = render(performance); + + assert.throws( + () => validateEmploymentSeparationAcceptance(artifact, runtimeEvidence(artifact), FIXTURE_BYTES), + /result.completed_at must be an RFC 3339 UTC timestamp/, + ); +}); + +test("rejects an impossible runtime observation calendar date instead of accepting Date.parse normalization", () => { + const performance = performanceResult(); + const artifact = render(performance); + const runtime = runtimeEvidence(artifact); + runtime.observed_at = "2026-09-31T04:10:01Z"; + + assert.throws( + () => validateEmploymentSeparationAcceptance(artifact, runtime, FIXTURE_BYTES), + /runtime.observed_at must be an RFC 3339 UTC timestamp/, + ); +}); + +test("rejects year zero in result completion evidence before chronology comparison", () => { + const performance = performanceResult(); + performance.completed_at = "0000-01-01T00:00:00Z"; + const artifact = render(performance); + + assert.throws( + () => validateEmploymentSeparationAcceptance(artifact, runtimeEvidence(artifact), FIXTURE_BYTES), + /result.completed_at must be an RFC 3339 UTC timestamp/, + ); +}); + +test("rejects year zero in runtime observation evidence before chronology comparison", () => { + const performance = performanceResult(); + const artifact = render(performance); + const runtime = runtimeEvidence(artifact); + runtime.observed_at = "0000-01-01T00:00:00Z"; + + assert.throws( + () => validateEmploymentSeparationAcceptance(artifact, runtime, FIXTURE_BYTES), + /runtime.observed_at must be an RFC 3339 UTC timestamp/, + ); +}); + +test("rejects a sub-millisecond runtime observation that precedes completion", () => { + const performance = performanceResult(); + performance.completed_at = "2026-09-13T04:10:00.0009Z"; + const artifact = render(performance); + const runtime = runtimeEvidence(artifact); + runtime.observed_at = "2026-09-13T04:10:00.0001Z"; + + assert.throws( + () => validateEmploymentSeparationAcceptance(artifact, runtime, FIXTURE_BYTES), + /runtime.observed_at must not precede result.completed_at/, + ); +}); + +test("accepts equivalent fractional instants with trailing-zero spelling differences", () => { + const performance = performanceResult(); + performance.completed_at = "2026-09-13T04:10:00.100Z"; + const artifact = render(performance); + const runtime = runtimeEvidence(artifact); + runtime.observed_at = "2026-09-13T04:10:00.1000Z"; + + const evidence = validateEmploymentSeparationAcceptance(artifact, runtime, FIXTURE_BYTES); + assert.equal(evidence.structurally_valid, true); +}); + +test("rejects a fixture prepared after the measured run completed", () => { + const performance = performanceResult(); + performance.completed_at = "2026-09-13T04:10:00.0001Z"; + const fixture = JSON.parse(FIXTURE_BYTES.toString("utf8")); + fixture.prepared_at = "2026-09-13T04:10:00.0009Z"; + const fixtureArtifact = render(fixture); + const fixtureSha256 = createHash("sha256").update(fixtureArtifact).digest("hex"); + performance.fixture_sha256 = fixtureSha256; + const artifact = render(performance); + const runtime = runtimeEvidence(artifact); + runtime.fixture_sha256 = fixtureSha256; + runtime.observed_at = "2026-09-13T04:10:00.001Z"; + + assert.throws( + () => validateEmploymentSeparationAcceptance(artifact, runtime, fixtureArtifact), + /fixture.prepared_at must not follow result.completed_at/, + ); +}); + +test("accepts an equivalent fixture preparation instant with trailing-zero spelling differences", () => { + const performance = performanceResult(); + performance.completed_at = "2026-09-13T04:10:00.100Z"; + const fixture = JSON.parse(FIXTURE_BYTES.toString("utf8")); + fixture.prepared_at = "2026-09-13T04:10:00.1000Z"; + const fixtureArtifact = render(fixture); + const fixtureSha256 = createHash("sha256").update(fixtureArtifact).digest("hex"); + performance.fixture_sha256 = fixtureSha256; + const artifact = render(performance); + const runtime = runtimeEvidence(artifact); + runtime.fixture_sha256 = fixtureSha256; + runtime.observed_at = "2026-09-13T04:10:00.101Z"; + + const evidence = validateEmploymentSeparationAcceptance(artifact, runtime, fixtureArtifact); + assert.equal(evidence.structurally_valid, true); +}); diff --git a/tests/performance/employment_separation_artifact_byte_budget.test.mjs b/tests/performance/employment_separation_artifact_byte_budget.test.mjs new file mode 100644 index 000000000..358352966 --- /dev/null +++ b/tests/performance/employment_separation_artifact_byte_budget.test.mjs @@ -0,0 +1,132 @@ +import assert from "node:assert/strict"; +import { createHash } from "node:crypto"; +import test from "node:test"; + +import { validateEmploymentSeparationAcceptance } from "./employment_separation_acceptance_contract.mjs"; +import { + ACCEPTANCE_CANDIDATE_SHA, + ACCEPTANCE_PROFILE_PRECONDITIONS, + acceptanceFixtureBytes, + acceptanceFixtureSha256, + acceptanceLoadModel, +} from "./employment_separation_acceptance_fixture_test_support.mjs"; +import { parseRuntimeEvidenceArtifact } from "./employment_separation_runtime_evidence_artifact.mjs"; + +const MAXIMUM_RESULT_ARTIFACT_BYTES = 1024 * 1024; +const MAXIMUM_RUNTIME_EVIDENCE_BYTES = 1024 * 1024; +const MAXIMUM_FIXTURE_ARTIFACT_BYTES = 8 * 1024 * 1024; +const ITERATIONS = 1000; + +function render(value) { + return Buffer.from(`${JSON.stringify(value, null, 2)}\n`, "utf8"); +} + +function padWithJsonWhitespace(bytes, maximumBytes) { + assert.ok(bytes.length <= maximumBytes, "test fixture must fit within the declared byte budget"); + return Buffer.concat([ + bytes, + Buffer.alloc(maximumBytes + 1 - bytes.length, 0x20), + ]); +} + +function result(fixtureSha256) { + return { + schema_version: "orgmetra.employment_separation.performance_result.v1", + candidate_sha: ACCEPTANCE_CANDIDATE_SHA, + fixture_sha256: fixtureSha256, + selected_profile: "first_commit", + expected_iterations: ITERATIONS, + completed_iterations: ITERATIONS, + sample_complete: true, + completed_at: "2026-09-13T04:10:00Z", + load_model: acceptanceLoadModel(ITERATIONS), + dataset_id: "dataset:employment-separation-perf-1", + clearance_reference: "data_clearance:perf-2026-09", + preparation_protocol_reference: "protocol:employment-separation-perf-v1", + prepared_state_evidence_reference: "evidence:prepared-state-perf-1", + resource_evidence_reference: "metrics:employment-separation-perf-1", + profile_preconditions: { ...ACCEPTANCE_PROFILE_PRECONDITIONS }, + minimum_non_contending_records: 1000, + minimum_contention_pairs: 100, + k6: { + metrics: { + iterations: { values: { count: ITERATIONS } }, + checks: { values: { rate: 1, passes: ITERATIONS, fails: 0 } }, + employment_separation_unexpected_response: { values: { rate: 0, passes: 0, fails: ITERATIONS } }, + employment_separation_latency_samples: { values: { count: ITERATIONS } }, + employment_separation_first_commit_duration_ms: { + values: { "p(50)": 8, "p(95)": 18, "p(99)": 19, max: 22, count: ITERATIONS }, + }, + }, + }, + }; +} + +function runtime(resultArtifact, fixtureSha256) { + return { + schema_version: "orgmetra.employment_separation.runtime_evidence.v1", + candidate_sha: ACCEPTANCE_CANDIDATE_SHA, + observed_service_sha: ACCEPTANCE_CANDIDATE_SHA, + selected_profile: "first_commit", + performance_result_sha256: createHash("sha256").update(resultArtifact).digest("hex"), + fixture_sha256: fixtureSha256, + environment_reference: "environment:perf-staging-1", + deployment_reference: "deployment:orgmetra-people-a1", + observer_reference: "observer:perf-runtime-1", + load_observation_reference: "evidence:perf-load-observation-1", + observed_load_model: acceptanceLoadModel(ITERATIONS), + resource_evidence_reference: "metrics:employment-separation-perf-1", + observed_at: "2026-09-13T04:10:01Z", + host_cpu_percent_p95: 42, + host_rss_bytes_max: 536870912, + db_pool_acquire_p95_ms: 1, + db_pool_in_use_max: 18, + db_pool_waiters_max: 2, + db_connections_max: 20, + residual_http_tasks: 0, + residual_db_sessions: 0, + residual_open_transactions: 0, + residual_sockets: 0, + residual_background_workers: 0, + residual_pool_checkouts: 0, + residual_pool_waiters: 0, + }; +} + +test("rejects oversized runtime evidence before UTF-8 decode or JSON scanning", () => { + const oversized = padWithJsonWhitespace(Buffer.from("{}", "utf8"), MAXIMUM_RUNTIME_EVIDENCE_BYTES); + assert.throws( + () => parseRuntimeEvidenceArtifact(oversized), + /runtime evidence must not exceed 1048576 bytes/, + ); +}); + +test("rejects oversized performance-result evidence before UTF-8 decode or JSON scanning", () => { + const fixtureBytes = acceptanceFixtureBytes(); + const fixtureSha256 = acceptanceFixtureSha256(fixtureBytes); + const resultBytes = padWithJsonWhitespace(render(result(fixtureSha256)), MAXIMUM_RESULT_ARTIFACT_BYTES); + + assert.throws( + () => validateEmploymentSeparationAcceptance( + resultBytes, + runtime(resultBytes, fixtureSha256), + fixtureBytes, + ), + /performance result must not exceed 1048576 bytes/, + ); +}); + +test("rejects oversized performance-fixture evidence before UTF-8 decode or JSON scanning", () => { + const fixtureBytes = padWithJsonWhitespace(acceptanceFixtureBytes(), MAXIMUM_FIXTURE_ARTIFACT_BYTES); + const fixtureSha256 = acceptanceFixtureSha256(fixtureBytes); + const resultBytes = render(result(fixtureSha256)); + + assert.throws( + () => validateEmploymentSeparationAcceptance( + resultBytes, + runtime(resultBytes, fixtureSha256), + fixtureBytes, + ), + /performance fixture must not exceed 8388608 bytes/, + ); +}); diff --git a/tests/performance/employment_separation_authenticated_evidence_gate.mjs b/tests/performance/employment_separation_authenticated_evidence_gate.mjs new file mode 100644 index 000000000..3ea4393b2 --- /dev/null +++ b/tests/performance/employment_separation_authenticated_evidence_gate.mjs @@ -0,0 +1,17 @@ +const AUTHENTICATED_EVIDENCE_OWNER = "ContextualWisdomLab/.github#2162"; + +/** + * Block commercial acceptance while exact performance evidence has no + * organization-owned authenticated attestation path. Local byte/digest + * consistency is useful structural evidence, but it is not an authority + * boundary because the same caller can replace both bytes and self-asserted + * digests. The gate is removed only after the released central owner contract + * is consumed and verified at the acceptance entry point. + */ +export function requireAuthenticatedPerformanceEvidence() { + throw new Error( + `commercial acceptance requires authenticated performance-evidence attestation from ${AUTHENTICATED_EVIDENCE_OWNER}; local result/runtime/fixture bytes and caller-supplied digests are structural evidence only`, + ); +} + +export { AUTHENTICATED_EVIDENCE_OWNER }; diff --git a/tests/performance/employment_separation_buyer_path.js b/tests/performance/employment_separation_buyer_path.js new file mode 100644 index 000000000..41ef8cc65 --- /dev/null +++ b/tests/performance/employment_separation_buyer_path.js @@ -0,0 +1,201 @@ +import http from "k6/http"; +import crypto from "k6/crypto"; +import { check, fail } from "k6"; +import exec from "k6/execution"; +import { Counter, Rate, Trend } from "k6/metrics"; + +import { + parsePerformanceFixtureArtifact, + requirePerformanceFixtureByteBudget, +} from "./employment_separation_fixture_artifact.mjs"; +import { + requestBody, + requestHeaders, + validatePerformanceFixture, +} from "./employment_separation_fixture_contract.mjs"; +import { + governedSeparationRequestParams, + requireGovernedSeparationHttpsOrigin, +} from "./employment_separation_request_contract.mjs"; +import { requirePinnedK6Runtime } from "./employment_separation_k6_runtime_contract.mjs"; +import { normalizeEmploymentSeparationK6Summary } from "./employment_separation_k6_summary_contract.mjs"; +import { + hasGovernedSeparationJsonMediaType, + hasGovernedSeparationNoStorePolicy, + isGovernedSeparationConflict, + isGovernedSeparationSuccess, + parseGovernedSeparationResponseBody, +} from "./employment_separation_response_contract.mjs"; +import { + PERFORMANCE_SUMMARY_TREND_STATS, + approvedPerformanceLoadModel, + arrivalRateScenarioForPerformanceProfile, + requireDirectPerformanceClientNetwork, + requirePerformanceProfile, + requireVerifiedTlsTransport, + thresholdsForPerformanceProfile, +} from "./employment_separation_run_contract.mjs"; +import { buyerPathElapsedMs } from "./employment_separation_timing_contract.mjs"; + +const ROUTE = "/v1/employment-separations"; +const MINIMUM_NON_CONTENDING_RECORDS = 1000; +const MINIMUM_CONTENTION_PAIRS = 100; +const TREND_BY_PROFILE = Object.freeze({ + first_commit: "employment_separation_first_commit_duration_ms", + replay: "employment_separation_replay_duration_ms", + rejection: "employment_separation_rejection_duration_ms", + contention: "employment_separation_contention_duration_ms", +}); +const fixturePath = __ENV.ORGMETRA_PERFORMANCE_DATA_FILE; +const baseUrl = requireGovernedSeparationHttpsOrigin(__ENV.ORGMETRA_PERFORMANCE_BASE_URL || ""); +const bearerToken = __ENV.ORGMETRA_PERFORMANCE_BEARER_TOKEN || ""; +const targetSha = (__ENV.ORGMETRA_PERFORMANCE_TARGET_SHA || "").toLowerCase(); +const selectedProfile = requirePerformanceProfile(__ENV.ORGMETRA_PERFORMANCE_PROFILE || ""); +const clientNetworkTopology = requireDirectPerformanceClientNetwork(__ENV); +const k6Runtime = requirePinnedK6Runtime({ + version: __ENV.ORGMETRA_PERFORMANCE_K6_VERSION || "", + image: __ENV.ORGMETRA_PERFORMANCE_K6_IMAGE || "", + imageDigest: __ENV.ORGMETRA_PERFORMANCE_K6_IMAGE_DIGEST || "", + runnerIdentity: __ENV.ORGMETRA_PERFORMANCE_K6_RUNNER_IDENTITY || "", +}); + +if (!fixturePath) fail("ORGMETRA_PERFORMANCE_DATA_FILE is required"); +if (!bearerToken) fail("ORGMETRA_PERFORMANCE_BEARER_TOKEN is required and must not be stored in the fixture"); +if (!/^[0-9a-f]{40}$/.test(targetSha)) fail("ORGMETRA_PERFORMANCE_TARGET_SHA must be a full Git commit SHA"); + +const fixtureBytes = open(fixturePath, "b"); +requirePerformanceFixtureByteBudget(fixtureBytes); +const fixtureSha256 = crypto.sha256(fixtureBytes, "hex"); +let fixtureDocument; +try { fixtureDocument = parsePerformanceFixtureArtifact(fixtureBytes); } +catch (error) { fail(error.message); } +const fixture = validatePerformanceFixture(fixtureDocument, { + minimumNonContendingRecords: MINIMUM_NON_CONTENDING_RECORDS, + minimumContentionPairs: MINIMUM_CONTENTION_PAIRS, +}); +if (fixture.candidate_sha.toLowerCase() !== targetSha) fail("performance fixture candidate_sha does not match ORGMETRA_PERFORMANCE_TARGET_SHA"); + +const selectedRecords = fixture.profiles[selectedProfile]; +const approvedLoadModel = approvedPerformanceLoadModel(selectedProfile); +const selectedScenario = arrivalRateScenarioForPerformanceProfile(selectedProfile, { + expectedIterations: selectedRecords.length, +}); + +const firstCommitDuration = new Trend("employment_separation_first_commit_duration_ms", true); +const replayDuration = new Trend("employment_separation_replay_duration_ms", true); +const rejectionDuration = new Trend("employment_separation_rejection_duration_ms", true); +const contentionDuration = new Trend("employment_separation_contention_duration_ms", true); +const latencySamples = new Counter("employment_separation_latency_samples"); +const unexpectedResponse = new Rate("employment_separation_unexpected_response"); + +export const options = { + discardResponseBodies: false, + scenarios: { [selectedProfile]: selectedScenario }, + thresholds: thresholdsForPerformanceProfile(selectedProfile, selectedRecords.length), + summaryTrendStats: PERFORMANCE_SUMMARY_TREND_STATS, +}; + +function recordAt(profile) { + const records = fixture.profiles[profile]; + const index = exec.scenario.iterationInTest; + if (index < 0 || index >= records.length) fail(`${profile} iteration ${index} is outside the fixture`); + return records[index]; +} +function requireCommercialTls() { + requireVerifiedTlsTransport(exec.test.options.insecureSkipTLSVerify); +} +function parseJson(response) { + if (!hasGovernedSeparationJsonMediaType(response.headers)) return null; + if (!hasGovernedSeparationNoStorePolicy(response.headers)) return null; + try { return parseGovernedSeparationResponseBody(response.body); } + catch (_) { return null; } +} +function post(command, profile) { + requireCommercialTls(); + const headers = requestHeaders(command, bearerToken); + return http.post( + `${baseUrl}${ROUTE}`, + requestBody(command), + governedSeparationRequestParams(headers, profile), + ); +} +function observe(response, trend, profile, predicate) { + trend.add(buyerPathElapsedMs(response.timings), { profile }); + latencySamples.add(1, { profile }); + const passed = check(response, { [`${profile} returned the governed result`]: predicate }); + unexpectedResponse.add(!passed, { profile }); +} + +export function firstCommit() { + const command = recordAt("first_commit"); + const response = post(command, "first_commit"); + observe(response, firstCommitDuration, "first_commit", (result) => isGovernedSeparationSuccess(result.status, parseJson(result), { employmentRecordId: command.payload.employment_record_id, replayed: false })); +} +export function replay() { + const command = recordAt("replay"); + const response = post(command, "replay"); + observe(response, replayDuration, "replay", (result) => isGovernedSeparationSuccess(result.status, parseJson(result), { employmentRecordId: command.payload.employment_record_id, replayed: true })); +} +export function rejection() { + const response = post(recordAt("rejection"), "rejection"); + observe(response, rejectionDuration, "rejection", (result) => isGovernedSeparationConflict(result.status, parseJson(result))); +} +export function contention() { + requireCommercialTls(); + const pair = recordAt("contention"); + const responses = http.batch([ + [ + "POST", + `${baseUrl}${ROUTE}`, + requestBody(pair.left), + governedSeparationRequestParams(requestHeaders(pair.left, bearerToken), "contention"), + ], + [ + "POST", + `${baseUrl}${ROUTE}`, + requestBody(pair.right), + governedSeparationRequestParams(requestHeaders(pair.right, bearerToken), "contention"), + ], + ]); + for (const response of responses) { contentionDuration.add(buyerPathElapsedMs(response.timings), { profile: "contention" }); latencySamples.add(1, { profile: "contention" }); } + const parsed = responses.map((response) => ({ status: response.status, body: parseJson(response) })); + const successes = parsed.filter(({ status, body }) => isGovernedSeparationSuccess(status, body, { employmentRecordId: pair.left.payload.employment_record_id, replayed: false })); + const conflicts = parsed.filter(({ status, body }) => isGovernedSeparationConflict(status, body)); + const passed = check(parsed, { "contention serializes one governed commit and one governed conflict": () => successes.length === 1 && conflicts.length === 1 }); + unexpectedResponse.add(!passed, { profile: "contention" }); +} + +export function handleSummary(data) { + const normalizedK6 = normalizeEmploymentSeparationK6Summary(data, { + expectedK6Version: k6Runtime.version, + trendName: TREND_BY_PROFILE[selectedProfile], + }); + const completedIterations = normalizedK6.metrics.iterations.values.count; + const payload = { + schema_version: "orgmetra.employment_separation.performance_result.v1", + candidate_sha: targetSha, + fixture_sha256: fixtureSha256, + k6_version: k6Runtime.version, + k6_image: k6Runtime.image, + k6_image_digest: k6Runtime.image_digest, + k6_runner_identity: k6Runtime.runner_identity, + selected_profile: selectedProfile, + expected_iterations: selectedRecords.length, + completed_iterations: completedIterations, + sample_complete: completedIterations === selectedRecords.length, + completed_at: new Date().toISOString(), + load_model: { ...approvedLoadModel, client_network_topology: clientNetworkTopology }, + dataset_id: fixture.dataset_id, + clearance_reference: fixture.clearance_reference, + preparation_protocol_reference: fixture.preparation_protocol_reference, + prepared_state_evidence_reference: fixture.prepared_state_evidence_reference, + resource_evidence_reference: fixture.resource_evidence_reference, + profile_preconditions: fixture.profile_preconditions, + minimum_non_contending_records: MINIMUM_NON_CONTENDING_RECORDS, + minimum_contention_pairs: MINIMUM_CONTENTION_PAIRS, + k6: normalizedK6, + }; + const rendered = `${JSON.stringify(payload, null, 2)}\n`; + const path = __ENV.ORGMETRA_PERFORMANCE_SUMMARY_FILE || `employment-separation-performance-${selectedProfile}.json`; + return { [path]: rendered, stdout: rendered }; +} diff --git a/tests/performance/employment_separation_commercial_owner_gate.mjs b/tests/performance/employment_separation_commercial_owner_gate.mjs new file mode 100644 index 000000000..16d7bbc41 --- /dev/null +++ b/tests/performance/employment_separation_commercial_owner_gate.mjs @@ -0,0 +1,19 @@ +import { requireAuthenticatedDeploymentEvidence } from "./employment_separation_deployment_evidence_gate.mjs"; +import { requireAuthenticatedPerformanceEvidence } from "./employment_separation_authenticated_evidence_gate.mjs"; + +export function requireCommercialPerformanceAuthorities() { + const failures = []; + for (const gate of [ + requireAuthenticatedPerformanceEvidence, + requireAuthenticatedDeploymentEvidence, + ]) { + try { + gate(); + } catch (error) { + failures.push(error instanceof Error ? error.message : String(error)); + } + } + if (failures.length !== 0) { + throw new Error(failures.join("\n")); + } +} diff --git a/tests/performance/employment_separation_commercial_owner_gate.test.mjs b/tests/performance/employment_separation_commercial_owner_gate.test.mjs new file mode 100644 index 000000000..cea0ac4bb --- /dev/null +++ b/tests/performance/employment_separation_commercial_owner_gate.test.mjs @@ -0,0 +1,18 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import { requireCommercialPerformanceAuthorities } from "./employment_separation_commercial_owner_gate.mjs"; + +const PERFORMANCE_ATTESTATION_GAP_PATTERN = /authenticated performance-evidence attestation.*ContextualWisdomLab\/.github#2162/; +const DEPLOYMENT_IDENTITY_GAP_PATTERN = /authenticated deployed-candidate evidence.*ContextualWisdomLab\/Orgmetra#395/; + +test("reports every unresolved commercial evidence authority in one fail-closed result", () => { + assert.throws( + () => requireCommercialPerformanceAuthorities(), + (error) => { + assert.match(error.message, PERFORMANCE_ATTESTATION_GAP_PATTERN); + assert.match(error.message, DEPLOYMENT_IDENTITY_GAP_PATTERN); + return true; + }, + ); +}); diff --git a/tests/performance/employment_separation_composed_evidence_contract.test.mjs b/tests/performance/employment_separation_composed_evidence_contract.test.mjs new file mode 100644 index 000000000..ef64860b4 --- /dev/null +++ b/tests/performance/employment_separation_composed_evidence_contract.test.mjs @@ -0,0 +1,156 @@ +import assert from "node:assert/strict"; +import { createHash } from "node:crypto"; +import test from "node:test"; + +import { validateEmploymentSeparationAcceptance } from "./employment_separation_acceptance_contract.mjs"; +import { + ACCEPTANCE_CANDIDATE_SHA, + ACCEPTANCE_PROFILE_PRECONDITIONS, + acceptanceFixtureBytes, + acceptanceFixtureSha256, + acceptanceLoadModel, +} from "./employment_separation_acceptance_fixture_test_support.mjs"; +import { validatePinnedK6AcceptanceEvidence } from "./employment_separation_k6_evidence_contract.mjs"; +import { + PINNED_K6_IMAGE, + PINNED_K6_IMAGE_DIGEST, + PINNED_K6_RUNNER_IDENTITY, + PINNED_K6_VERSION, +} from "./employment_separation_k6_runtime_contract.mjs"; + +const FIXTURE_BYTES = acceptanceFixtureBytes(); +const FIXTURE_SHA256 = acceptanceFixtureSha256(FIXTURE_BYTES); +const ITERATIONS = 1000; + +function render(value) { + return Buffer.from(`${JSON.stringify(value, null, 2)}\n`, "utf8"); +} + +function performanceResult() { + return { + schema_version: "orgmetra.employment_separation.performance_result.v1", + candidate_sha: ACCEPTANCE_CANDIDATE_SHA, + fixture_sha256: FIXTURE_SHA256, + k6_version: PINNED_K6_VERSION, + k6_image: PINNED_K6_IMAGE, + k6_image_digest: PINNED_K6_IMAGE_DIGEST, + k6_runner_identity: PINNED_K6_RUNNER_IDENTITY, + selected_profile: "first_commit", + expected_iterations: ITERATIONS, + completed_iterations: ITERATIONS, + sample_complete: true, + completed_at: "2026-09-13T04:10:00Z", + load_model: acceptanceLoadModel(ITERATIONS), + dataset_id: "dataset:employment-separation-perf-1", + clearance_reference: "data_clearance:perf-2026-09", + preparation_protocol_reference: "protocol:employment-separation-perf-v1", + prepared_state_evidence_reference: "evidence:prepared-state-perf-1", + resource_evidence_reference: "metrics:employment-separation-perf-1", + profile_preconditions: { ...ACCEPTANCE_PROFILE_PRECONDITIONS }, + minimum_non_contending_records: 1000, + minimum_contention_pairs: 100, + k6: { + metrics: { + iterations: { values: { count: ITERATIONS } }, + checks: { values: { rate: 1, passes: ITERATIONS, fails: 0 } }, + employment_separation_unexpected_response: { values: { rate: 0, passes: 0, fails: ITERATIONS } }, + employment_separation_latency_samples: { values: { count: ITERATIONS } }, + employment_separation_first_commit_duration_ms: { + values: { "p(50)": 8, "p(95)": 18, "p(99)": 19, max: 22, count: ITERATIONS }, + }, + }, + }, + }; +} + +function runtimeEvidence(resultArtifact) { + return { + schema_version: "orgmetra.employment_separation.runtime_evidence.v1", + candidate_sha: ACCEPTANCE_CANDIDATE_SHA, + observed_service_sha: ACCEPTANCE_CANDIDATE_SHA, + observed_k6_version: PINNED_K6_VERSION, + observed_k6_image: PINNED_K6_IMAGE, + observed_k6_image_digest: PINNED_K6_IMAGE_DIGEST, + observed_k6_runner_identity: PINNED_K6_RUNNER_IDENTITY, + selected_profile: "first_commit", + performance_result_sha256: createHash("sha256").update(resultArtifact).digest("hex"), + fixture_sha256: FIXTURE_SHA256, + environment_reference: "environment:perf-staging-1", + deployment_reference: "deployment:orgmetra-people-a1", + observer_reference: "observer:perf-runtime-1", + load_observation_reference: "evidence:perf-load-observation-1", + observed_load_model: acceptanceLoadModel(ITERATIONS), + resource_evidence_reference: "metrics:employment-separation-perf-1", + observed_at: "2026-09-13T04:10:01Z", + host_cpu_percent_p95: 42, + host_rss_bytes_max: 536870912, + db_pool_acquire_p95_ms: 1, + db_pool_in_use_max: 18, + db_pool_waiters_max: 2, + db_connections_max: 20, + residual_http_tasks: 0, + residual_db_sessions: 0, + residual_open_transactions: 0, + residual_sockets: 0, + residual_background_workers: 0, + residual_pool_checkouts: 0, + residual_pool_waiters: 0, + }; +} + +test("one evidence pair satisfies both structural and pinned-k6 contracts", () => { + const resultArtifact = render(performanceResult()); + const runtime = runtimeEvidence(resultArtifact); + + const structural = validateEmploymentSeparationAcceptance(resultArtifact, runtime, FIXTURE_BYTES); + const pinned = validatePinnedK6AcceptanceEvidence(resultArtifact, runtime); + + assert.equal(structural.structurally_valid, true); + assert.deepEqual(pinned, { + k6_version: PINNED_K6_VERSION, + k6_image: PINNED_K6_IMAGE, + k6_image_digest: PINNED_K6_IMAGE_DIGEST, + k6_runner_identity: PINNED_K6_RUNNER_IDENTITY, + }); +}); + +test("structural schema rejects a partial result k6 identity group", () => { + const result = performanceResult(); + delete result.k6_runner_identity; + const resultArtifact = render(result); + + assert.throws( + () => validateEmploymentSeparationAcceptance( + resultArtifact, + runtimeEvidence(resultArtifact), + FIXTURE_BYTES, + ), + /either all or none of k6_version, k6_image, k6_image_digest, k6_runner_identity/, + ); +}); + +test("structural schema rejects a partial runtime k6 identity group", () => { + const resultArtifact = render(performanceResult()); + const runtime = runtimeEvidence(resultArtifact); + delete runtime.observed_k6_runner_identity; + + assert.throws( + () => validateEmploymentSeparationAcceptance(resultArtifact, runtime, FIXTURE_BYTES), + /either all or none of observed_k6_version, observed_k6_image, observed_k6_image_digest, observed_k6_runner_identity/, + ); +}); + +test("structural schema rejects non-string declared k6 identity evidence", () => { + const result = performanceResult(); + result.k6_version = 220; + const resultArtifact = render(result); + + assert.throws( + () => validateEmploymentSeparationAcceptance( + resultArtifact, + runtimeEvidence(resultArtifact), + FIXTURE_BYTES, + ), + /result.k6_version must be a non-empty string/, + ); +}); diff --git a/tests/performance/employment_separation_deployment_evidence_gate.mjs b/tests/performance/employment_separation_deployment_evidence_gate.mjs new file mode 100644 index 000000000..1271c4720 --- /dev/null +++ b/tests/performance/employment_separation_deployment_evidence_gate.mjs @@ -0,0 +1,14 @@ +export const AUTHENTICATED_DEPLOYMENT_EVIDENCE_OWNER = "ContextualWisdomLab/Orgmetra#395"; + +/** + * Keep commercial performance acceptance fail closed until the service that + * actually answered the timed requests is independently bound to the exact + * source candidate. A caller-authored observed_service_sha or deployment + * reference can be made internally consistent and later attested as bytes; + * neither proves which deployed workload served the HTTPS origin. + */ +export function requireAuthenticatedDeploymentEvidence() { + throw new Error( + `commercial acceptance requires authenticated deployed-candidate evidence from ${AUTHENTICATED_DEPLOYMENT_EVIDENCE_OWNER}; caller-supplied observed_service_sha and deployment references are structural evidence only`, + ); +} diff --git a/tests/performance/employment_separation_duplicate_json_members.test.mjs b/tests/performance/employment_separation_duplicate_json_members.test.mjs new file mode 100644 index 000000000..a90dc69e0 --- /dev/null +++ b/tests/performance/employment_separation_duplicate_json_members.test.mjs @@ -0,0 +1,143 @@ +import assert from "node:assert/strict"; +import { createHash } from "node:crypto"; +import test from "node:test"; + +import { validateEmploymentSeparationAcceptance } from "./employment_separation_acceptance_contract.mjs"; +import { + acceptanceFixtureBytes, + acceptanceFixtureSha256, + acceptanceLoadModel, +} from "./employment_separation_acceptance_fixture_test_support.mjs"; +import { parseRuntimeEvidenceArtifact } from "./employment_separation_runtime_evidence_artifact.mjs"; + +const CANDIDATE_SHA = "a".repeat(40); +const FIXTURE_BYTES = acceptanceFixtureBytes(); + +function performanceResult(fixtureSha256 = acceptanceFixtureSha256(FIXTURE_BYTES)) { + return { + schema_version: "orgmetra.employment_separation.performance_result.v1", + candidate_sha: CANDIDATE_SHA, + fixture_sha256: fixtureSha256, + selected_profile: "first_commit", + expected_iterations: 1000, + completed_iterations: 1000, + sample_complete: true, + completed_at: "2026-09-13T04:10:00Z", + load_model: acceptanceLoadModel(1000), + dataset_id: "dataset:employment-separation-perf-1", + clearance_reference: "data_clearance:perf-2026-09", + preparation_protocol_reference: "protocol:employment-separation-perf-v1", + prepared_state_evidence_reference: "evidence:prepared-state-perf-1", + resource_evidence_reference: "metrics:employment-separation-perf-1", + profile_preconditions: { + first_commit: "active_current_expected_version", + replay: "same_key_same_semantics_already_committed", + rejection: "expected_version_stale_or_semantic_conflict", + contention: "active_current_expected_version", + }, + minimum_non_contending_records: 1000, + minimum_contention_pairs: 100, + k6: { + metrics: { + iterations: { values: { count: 1000, rate: 40 } }, + checks: { values: { rate: 1, passes: 1000, fails: 0 } }, + employment_separation_unexpected_response: { values: { rate: 0, passes: 0, fails: 1000 } }, + employment_separation_latency_samples: { values: { count: 1000, rate: 40 } }, + employment_separation_first_commit_duration_ms: { + values: { "p(50)": 8.1, "p(95)": 18.4, "p(99)": 19.7, max: 22.3, count: 1000 }, + }, + }, + }, + }; +} + +function render(value) { + return Buffer.from(`${JSON.stringify(value, null, 2)}\n`, "utf8"); +} + +function runtimeEvidence(resultArtifact, fixtureSha256 = acceptanceFixtureSha256(FIXTURE_BYTES)) { + return { + schema_version: "orgmetra.employment_separation.runtime_evidence.v1", + candidate_sha: CANDIDATE_SHA, + observed_service_sha: CANDIDATE_SHA, + selected_profile: "first_commit", + performance_result_sha256: createHash("sha256").update(resultArtifact).digest("hex"), + fixture_sha256: fixtureSha256, + environment_reference: "environment:perf-staging-1", + deployment_reference: "deployment:orgmetra-people-a1", + observer_reference: "observer:perf-runtime-1", + load_observation_reference: "evidence:perf-load-observation-1", + observed_load_model: acceptanceLoadModel(1000), + resource_evidence_reference: "metrics:employment-separation-perf-1", + observed_at: "2026-09-13T04:10:01Z", + host_cpu_percent_p95: 42.5, + host_rss_bytes_max: 536870912, + db_pool_acquire_p95_ms: 1.2, + db_pool_in_use_max: 18, + db_pool_waiters_max: 2, + db_connections_max: 20, + residual_http_tasks: 0, + residual_db_sessions: 0, + residual_open_transactions: 0, + residual_sockets: 0, + residual_background_workers: 0, + residual_pool_checkouts: 0, + residual_pool_waiters: 0, + }; +} + +function duplicateCandidateSha(bytes) { + const text = bytes.toString("utf8"); + const needle = `"candidate_sha": "${CANDIDATE_SHA}"`; + const replacement = `"candidate_sha": "${"b".repeat(40)}",\n "candidate_\\u0073ha": "${CANDIDATE_SHA}"`; + assert.notEqual(text.indexOf(needle), -1); + return Buffer.from(text.replace(needle, replacement), "utf8"); +} + +test("rejects escaped-equivalent duplicate member names in result bytes", () => { + const resultArtifact = duplicateCandidateSha(render(performanceResult())); + assert.throws( + () => validateEmploymentSeparationAcceptance( + resultArtifact, + runtimeEvidence(resultArtifact), + FIXTURE_BYTES, + ), + /duplicate JSON object member name/, + ); +}); + +test("rejects escaped-equivalent duplicate member names in fixture bytes", () => { + const fixtureArtifact = duplicateCandidateSha(FIXTURE_BYTES); + const fixtureSha256 = acceptanceFixtureSha256(fixtureArtifact); + const resultArtifact = render(performanceResult(fixtureSha256)); + assert.throws( + () => validateEmploymentSeparationAcceptance( + resultArtifact, + runtimeEvidence(resultArtifact, fixtureSha256), + fixtureArtifact, + ), + /duplicate JSON object member name/, + ); +}); + +test("rejects duplicate runtime evidence member names before JSON last-value-wins collapse", () => { + const runtimeArtifact = Buffer.from( + `{"candidate_sha":"${"b".repeat(40)}","candidate_\\u0073ha":"${CANDIDATE_SHA}"}`, + "utf8", + ); + assert.throws(() => parseRuntimeEvidenceArtifact(runtimeArtifact), /duplicate JSON object member name/); +}); + +test("rejects duplicate member names recursively in nested result objects", () => { + const text = render(performanceResult()).toString("utf8"); + const needle = '"checks": {'; + const resultArtifact = Buffer.from(text.replace(needle, '"checks": {"values":{"rate":0}}, "ch\\u0065cks": {'), "utf8"); + assert.throws( + () => validateEmploymentSeparationAcceptance( + resultArtifact, + runtimeEvidence(resultArtifact), + FIXTURE_BYTES, + ), + /duplicate JSON object member name/, + ); +}); diff --git a/tests/performance/employment_separation_fixture_artifact.mjs b/tests/performance/employment_separation_fixture_artifact.mjs new file mode 100644 index 000000000..64ebb9d5b --- /dev/null +++ b/tests/performance/employment_separation_fixture_artifact.mjs @@ -0,0 +1,27 @@ +import { parseStrictJsonText } from "./strict_json_artifact.mjs"; + +export const MAXIMUM_PERFORMANCE_FIXTURE_BYTES = 8 * 1024 * 1024; + +export function requirePerformanceFixtureByteBudget(value) { + if (!(value instanceof Uint8Array) && !(value instanceof ArrayBuffer)) { + throw new Error("performance fixture must be supplied as raw bytes"); + } + if (value.byteLength > MAXIMUM_PERFORMANCE_FIXTURE_BYTES) { + throw new Error(`performance fixture must not exceed ${MAXIMUM_PERFORMANCE_FIXTURE_BYTES} bytes`); + } + return value; +} + +export function parsePerformanceFixtureArtifact(value) { + const bytes = requirePerformanceFixtureByteBudget(value); + let text; + try { + text = new TextDecoder("utf-8", { fatal: true }).decode(bytes); + } catch (error) { + throw new Error("performance fixture must be valid UTF-8", { cause: error }); + } + if (text.trim() === "") { + throw new Error("performance fixture must be non-empty JSON text"); + } + return parseStrictJsonText(text, "performance fixture"); +} diff --git a/tests/performance/employment_separation_fixture_artifact.test.mjs b/tests/performance/employment_separation_fixture_artifact.test.mjs new file mode 100644 index 000000000..182f6d630 --- /dev/null +++ b/tests/performance/employment_separation_fixture_artifact.test.mjs @@ -0,0 +1,41 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import { + MAXIMUM_PERFORMANCE_FIXTURE_BYTES, + parsePerformanceFixtureArtifact, + requirePerformanceFixtureByteBudget, +} from "./employment_separation_fixture_artifact.mjs"; + +const MAXIMUM_BYTES = 8 * 1024 * 1024; + +test("fixture byte budget is the governed 8 MiB ceiling", () => { + assert.equal(MAXIMUM_PERFORMANCE_FIXTURE_BYTES, MAXIMUM_BYTES); +}); + +test("direct workload boundary rejects oversized fixture bytes before parsing", () => { + const oversized = Buffer.alloc(MAXIMUM_BYTES + 1, 0x20); + assert.throws( + () => requirePerformanceFixtureByteBudget(oversized), + /performance fixture must not exceed 8388608 bytes/, + ); +}); + +test("direct workload boundary rejects escaped-equivalent duplicate JSON member names", () => { + const bytes = Buffer.from( + '{"schema_version":"one","schema_\\u0076ersion":"two"}\n', + "utf8", + ); + assert.doesNotThrow(() => JSON.parse(bytes.toString("utf8"))); + assert.throws( + () => parsePerformanceFixtureArtifact(bytes), + /duplicate JSON object member name "schema_version"/, + ); +}); + +test("direct workload boundary preserves ordinary valid JSON objects", () => { + assert.deepEqual( + parsePerformanceFixtureArtifact(Buffer.from('{"profiles":{}}\n', "utf8")), + { profiles: {} }, + ); +}); diff --git a/tests/performance/employment_separation_fixture_contract.mjs b/tests/performance/employment_separation_fixture_contract.mjs new file mode 100644 index 000000000..cf8ff2130 --- /dev/null +++ b/tests/performance/employment_separation_fixture_contract.mjs @@ -0,0 +1,269 @@ +const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; +const ACTOR_PATTERN = /^[a-z][a-z0-9_]*:[A-Za-z0-9][A-Za-z0-9._~-]*$/; +const DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/; +const UTC_TIMESTAMP_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/; +const VERSION_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]*$/; +const SHA_PATTERN = /^[0-9a-f]{40}$/; +const BODY_KEYS = Object.freeze([ + "confirmation_reference", + "employment_record_id", + "evidence_reference", + "evidence_version_code", + "expected_employment_record_version_id", + "person_record_id", + "separation_effective_on", + "separation_reason_code", +]); +const REASON_CODES = new Set([ + "voluntary_resignation", + "retirement_transition", + "fixed_term_completion", + "position_elimination", + "employer_initiated_separation", +]); +const PROFILE_NAMES = Object.freeze(["first_commit", "replay", "rejection", "contention"]); +const PROFILE_PRECONDITIONS = Object.freeze({ + first_commit: "active_current_expected_version", + replay: "same_key_same_semantics_already_committed", + rejection: "expected_version_stale_or_semantic_conflict", + contention: "active_current_expected_version", +}); +const MAXIMUM_NON_CONTENDING_RECORDS = 1000; +const MAXIMUM_CONTENTION_PAIRS = 100; + +function fail(message) { + throw new Error(message); +} + +function isPlainObject(value) { + return value !== null && typeof value === "object" && !Array.isArray(value); +} + +function requirePlainObject(value, label) { + if (!isPlainObject(value)) fail(`${label} must be an object`); + return value; +} + +function requireExactKeys(value, expected, label) { + const actual = Object.keys(value).sort(); + const wanted = [...expected].sort(); + if (actual.length !== wanted.length || actual.some((key, index) => key !== wanted[index])) { + fail(`${label} must contain exactly: ${wanted.join(", ")}`); + } +} + +function requireString(value, label) { + if (typeof value !== "string" || value.trim() === "") fail(`${label} must be a non-empty string`); + return value; +} + +function requireNamespacedReference(value, label) { + const text = requireString(value, label); + if (text.length > 200 || !ACTOR_PATTERN.test(text)) fail(`${label} must be a namespaced opaque reference`); + return text; +} + +function daysInGregorianMonth(year, month) { + if (year < 1 || year > 9999 || month < 1 || month > 12) return 0; + const leapYear = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0); + return [31, leapYear ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month - 1]; +} + +function requireFullDate(value, label) { + const text = requireString(value, label); + if (!DATE_PATTERN.test(text)) fail(`${label} must be an RFC 3339 full-date`); + const [year, month, day] = text.split("-").map(Number); + const maximumDay = daysInGregorianMonth(year, month); + if (maximumDay === 0 || day < 1 || day > maximumDay) fail(`${label} must be an RFC 3339 full-date`); + return text; +} + +function requireUtcTimestamp(value, label) { + const text = requireString(value, label); + if (!UTC_TIMESTAMP_PATTERN.test(text)) fail(`${label} must be an RFC 3339 UTC timestamp`); + const year = Number(text.slice(0, 4)); + const month = Number(text.slice(5, 7)); + const day = Number(text.slice(8, 10)); + const hour = Number(text.slice(11, 13)); + const minute = Number(text.slice(14, 16)); + const second = Number(text.slice(17, 19)); + const maximumDay = daysInGregorianMonth(year, month); + if ( + maximumDay === 0 + || day < 1 + || day > maximumDay + || hour > 23 + || minute > 59 + || second > 59 + ) fail(`${label} must be an RFC 3339 UTC timestamp`); + return text; +} + +function requireUuid(value, label) { + const text = requireString(value, label); + if (!UUID_PATTERN.test(text)) fail(`${label} must be a canonical UUID string`); + const compact = text.split("-").join("").toLowerCase(); + if (compact === "0".repeat(32) || compact === "f".repeat(32)) fail(`${label} must be an operational UUID`); + return text.toLowerCase(); +} + +function requireCommand(command, label) { + const value = requirePlainObject(command, label); + requireExactKeys(value, ["actor_reference", "idempotency_key", "payload", "tenant_record_id"], label); + requireUuid(value.tenant_record_id, `${label}.tenant_record_id`); + requireNamespacedReference(value.actor_reference, `${label}.actor_reference`); + const key = requireString(value.idempotency_key, `${label}.idempotency_key`); + if (key.length < 16 || key.length > 200 || [...key].some((character) => { + const code = character.charCodeAt(0); + return code < 0x21 || code > 0x7e; + })) { + fail(`${label}.idempotency_key must be 16 to 200 visible ASCII characters`); + } + + const payload = requirePlainObject(value.payload, `${label}.payload`); + requireExactKeys(payload, BODY_KEYS, `${label}.payload`); + requireUuid(payload.person_record_id, `${label}.payload.person_record_id`); + requireUuid(payload.employment_record_id, `${label}.payload.employment_record_id`); + requireUuid(payload.expected_employment_record_version_id, `${label}.payload.expected_employment_record_version_id`); + requireFullDate(payload.separation_effective_on, `${label}.payload.separation_effective_on`); + if (!REASON_CODES.has(payload.separation_reason_code)) { + fail(`${label}.payload.separation_reason_code must use the governed vocabulary`); + } + requireNamespacedReference(payload.evidence_reference, `${label}.payload.evidence_reference`); + const evidenceVersion = requireString(payload.evidence_version_code, `${label}.payload.evidence_version_code`); + if (!VERSION_PATTERN.test(evidenceVersion)) fail(`${label}.payload.evidence_version_code must be a whitespace-free version token`); + requireNamespacedReference(payload.confirmation_reference, `${label}.payload.confirmation_reference`); + return value; +} + +function semanticCommand(command) { + return JSON.stringify({ + actor_reference: command.actor_reference, + payload: command.payload, + tenant_record_id: command.tenant_record_id, + }); +} + +function recordIdentity(command) { + return command.payload.employment_record_id.toLowerCase(); +} + +function reserveIdentity(seen, command, label) { + const identity = recordIdentity(command); + if (seen.has(identity)) fail(`${label} reuses an Employment assigned to another performance profile`); + seen.add(identity); +} + +export const PERFORMANCE_FIXTURE_SCHEMA = "orgmetra.employment_separation.performance_fixture.v1"; + +export function validatePerformanceFixture( + document, + { minimumNonContendingRecords = 1000, minimumContentionPairs = 100 } = {}, +) { + const fixture = requirePlainObject(document, "fixture"); + requireExactKeys( + fixture, + [ + "candidate_sha", + "clearance_reference", + "dataset_id", + "prepared_at", + "prepared_state_evidence_reference", + "preparation_protocol_reference", + "profile_preconditions", + "profiles", + "resource_evidence_reference", + "right_cleared", + "schema_version", + "synthetic", + ], + "fixture", + ); + if (fixture.schema_version !== PERFORMANCE_FIXTURE_SCHEMA) fail("fixture.schema_version is unsupported"); + if (fixture.right_cleared !== true) fail("fixture.right_cleared must be true for commercial acceptance"); + if (fixture.synthetic !== false) fail("fixture.synthetic must be false for commercial acceptance"); + requireNamespacedReference(fixture.clearance_reference, "fixture.clearance_reference"); + requireNamespacedReference(fixture.dataset_id, "fixture.dataset_id"); + requireNamespacedReference(fixture.preparation_protocol_reference, "fixture.preparation_protocol_reference"); + requireNamespacedReference(fixture.prepared_state_evidence_reference, "fixture.prepared_state_evidence_reference"); + requireNamespacedReference(fixture.resource_evidence_reference, "fixture.resource_evidence_reference"); + requireUtcTimestamp(fixture.prepared_at, "fixture.prepared_at"); + const candidateSha = requireString(fixture.candidate_sha, "fixture.candidate_sha").toLowerCase(); + if (!SHA_PATTERN.test(candidateSha)) fail("fixture.candidate_sha must be a full Git commit SHA"); + + const preconditions = requirePlainObject(fixture.profile_preconditions, "fixture.profile_preconditions"); + requireExactKeys(preconditions, PROFILE_NAMES, "fixture.profile_preconditions"); + for (const profile of PROFILE_NAMES) { + if (preconditions[profile] !== PROFILE_PRECONDITIONS[profile]) { + fail(`fixture.profile_preconditions.${profile} must be ${PROFILE_PRECONDITIONS[profile]}`); + } + } + + const profiles = requirePlainObject(fixture.profiles, "fixture.profiles"); + requireExactKeys(profiles, PROFILE_NAMES, "fixture.profiles"); + for (const profile of ["first_commit", "replay", "rejection"]) { + if (!Array.isArray(profiles[profile]) || profiles[profile].length < minimumNonContendingRecords) { + fail(`fixture.profiles.${profile} must contain at least ${minimumNonContendingRecords} records`); + } + if (profiles[profile].length > MAXIMUM_NON_CONTENDING_RECORDS) { + fail(`fixture.profiles.${profile} must contain at most ${MAXIMUM_NON_CONTENDING_RECORDS} records`); + } + } + if (!Array.isArray(profiles.contention) || profiles.contention.length < minimumContentionPairs) { + fail(`fixture.profiles.contention must contain at least ${minimumContentionPairs} pairs`); + } + if (profiles.contention.length > MAXIMUM_CONTENTION_PAIRS) { + fail(`fixture.profiles.contention must contain at most ${MAXIMUM_CONTENTION_PAIRS} pairs`); + } + + const seenEmployment = new Set(); + const seenKeys = new Set(); + for (const profile of ["first_commit", "replay", "rejection"]) { + profiles[profile].forEach((raw, index) => { + const label = `fixture.profiles.${profile}[${index}]`; + const command = requireCommand(raw, label); + reserveIdentity(seenEmployment, command, label); + if (seenKeys.has(command.idempotency_key)) fail(`${label}.idempotency_key must be unique across fixture records`); + seenKeys.add(command.idempotency_key); + }); + } + + profiles.contention.forEach((raw, index) => { + const label = `fixture.profiles.contention[${index}]`; + const pair = requirePlainObject(raw, label); + requireExactKeys(pair, ["left", "right"], label); + const left = requireCommand(pair.left, `${label}.left`); + const right = requireCommand(pair.right, `${label}.right`); + if (left.idempotency_key === right.idempotency_key) fail(`${label} must use distinct idempotency keys`); + if (semanticCommand(left) !== semanticCommand(right)) fail(`${label} commands must differ only by idempotency key`); + reserveIdentity(seenEmployment, left, label); + for (const command of [left, right]) { + if (seenKeys.has(command.idempotency_key)) fail(`${label} idempotency keys must be unique across fixture records`); + seenKeys.add(command.idempotency_key); + } + }); + + return fixture; +} + +export function requestHeaders(command, bearerToken) { + requireCommand(command, "command"); + const token = requireString(bearerToken, "bearer token"); + if (token.length > 8192 || [...token].some((character) => { + const code = character.charCodeAt(0); + return code < 0x21 || code > 0x7e; + })) fail("bearer token must be 1 to 8192 visible ASCII characters"); + return { + Authorization: `Bearer ${token}`, + "Content-Type": "application/json", + "Idempotency-Key": command.idempotency_key, + "X-Actor-Reference": command.actor_reference, + "X-Purpose-Code": "workforce_admin", + "X-Tenant-Reference": command.tenant_record_id, + }; +} + +export function requestBody(command) { + requireCommand(command, "command"); + return JSON.stringify(command.payload); +} diff --git a/tests/performance/employment_separation_fixture_contract.test.mjs b/tests/performance/employment_separation_fixture_contract.test.mjs new file mode 100644 index 000000000..fee18a34c --- /dev/null +++ b/tests/performance/employment_separation_fixture_contract.test.mjs @@ -0,0 +1,192 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import { + PERFORMANCE_FIXTURE_SCHEMA, + requestBody, + requestHeaders, + validatePerformanceFixture, +} from "./employment_separation_fixture_contract.mjs"; + +const ZERO = "00000000-0000-0000-0000-000000000000"; + +function command(seed, key = `separation-key-${seed.toString().padStart(4, "0")}`) { + const suffix = seed.toString(16).padStart(12, "0"); + return { + tenant_record_id: `10000000-0000-4000-8000-${suffix}`, + actor_reference: `worker_admin:perf_${seed}`, + idempotency_key: key, + payload: { + person_record_id: `20000000-0000-4000-8000-${suffix}`, + employment_record_id: `30000000-0000-4000-8000-${suffix}`, + expected_employment_record_version_id: `40000000-0000-4000-8000-${suffix}`, + separation_effective_on: "2026-09-13", + separation_reason_code: "voluntary_resignation", + evidence_reference: `evidence:perf_${seed}`, + evidence_version_code: "v1", + confirmation_reference: `confirmation:perf_${seed}`, + }, + }; +} + +function fixture() { + const left = command(4, "contention-key-left-0001"); + const right = structuredClone(left); + right.idempotency_key = "contention-key-right-0001"; + return { + schema_version: PERFORMANCE_FIXTURE_SCHEMA, + candidate_sha: "a".repeat(40), + right_cleared: true, + synthetic: false, + clearance_reference: "data_clearance:perf-2026-09", + dataset_id: "dataset:employment-separation-perf-1", + prepared_at: "2026-09-13T02:00:00Z", + preparation_protocol_reference: "protocol:employment-separation-perf-v1", + prepared_state_evidence_reference: "evidence:prepared-state-perf-1", + resource_evidence_reference: "metrics:employment-separation-perf-1", + profile_preconditions: { + first_commit: "active_current_expected_version", + replay: "same_key_same_semantics_already_committed", + rejection: "expected_version_stale_or_semantic_conflict", + contention: "active_current_expected_version", + }, + profiles: { + first_commit: [command(1)], + replay: [command(2)], + rejection: [command(3)], + contention: [{ left, right }], + }, + }; +} + +const smallAcceptance = { minimumNonContendingRecords: 1, minimumContentionPairs: 1 }; + +test("accepts a right-cleared fixture with explicit prepared-state provenance", () => { + const value = fixture(); + assert.equal(validatePerformanceFixture(value, smallAcceptance), value); +}); + +test("rejects an impossible fixture preparation calendar timestamp", () => { + const value = fixture(); + value.prepared_at = "2026-02-30T02:00:00Z"; + assert.throws( + () => validatePerformanceFixture(value, smallAcceptance), + /fixture.prepared_at must be an RFC 3339 UTC timestamp/, + ); +}); + +test("rejects synthetic or uncleared commercial fixtures", () => { + const synthetic = fixture(); + synthetic.synthetic = true; + assert.throws(() => validatePerformanceFixture(synthetic, smallAcceptance), /synthetic must be false/); + + const uncleared = fixture(); + uncleared.right_cleared = false; + assert.throws(() => validatePerformanceFixture(uncleared, smallAcceptance), /right_cleared must be true/); +}); + +test("requires explicit preparation protocol and prepared-state evidence", () => { + const badProtocol = fixture(); + badProtocol.preparation_protocol_reference = "not namespaced"; + assert.throws(() => validatePerformanceFixture(badProtocol, smallAcceptance), /preparation_protocol_reference must be a namespaced opaque reference/); + + const badEvidence = fixture(); + badEvidence.prepared_state_evidence_reference = "not namespaced"; + assert.throws(() => validatePerformanceFixture(badEvidence, smallAcceptance), /prepared_state_evidence_reference must be a namespaced opaque reference/); +}); + +test("pins the pre-state semantics of every measured profile", () => { + const value = fixture(); + value.profile_preconditions.replay = "active_current_expected_version"; + assert.throws(() => validatePerformanceFixture(value, smallAcceptance), /profile_preconditions.replay/); +}); + +test("rejects sentinel identities and profile cross-contamination", () => { + const sentinel = fixture(); + sentinel.profiles.first_commit[0].payload.person_record_id = ZERO; + assert.throws(() => validatePerformanceFixture(sentinel, smallAcceptance), /operational UUID/); + + const overlap = fixture(); + overlap.profiles.replay[0].payload.employment_record_id = overlap.profiles.first_commit[0].payload.employment_record_id; + assert.throws(() => validatePerformanceFixture(overlap, smallAcceptance), /reuses an Employment/); +}); + +test("requires contention commands to differ only by idempotency key", () => { + const value = fixture(); + value.profiles.contention[0].right.payload.separation_reason_code = "retirement_transition"; + assert.throws(() => validatePerformanceFixture(value, smallAcceptance), /differ only by idempotency key/); +}); + +test("enforces minimum sample cardinality rather than silently shrinking the run", () => { + assert.throws( + () => validatePerformanceFixture(fixture(), { minimumNonContendingRecords: 2, minimumContentionPairs: 1 }), + /at least 2 records/, + ); +}); + +test("rejects surplus non-contending records before walking an unbounded profile", () => { + const value = fixture(); + value.profiles.replay = Array.from({ length: 1001 }, (_, index) => command(10000 + index)); + assert.throws( + () => validatePerformanceFixture(value, smallAcceptance), + /at most 1000 records/, + ); +}); + +test("rejects surplus contention pairs before walking an unbounded profile", () => { + const value = fixture(); + value.profiles.contention = Array.from({ length: 101 }, (_, index) => { + const left = command(20000 + index, `contention-left-${index.toString().padStart(4, "0")}`); + const right = structuredClone(left); + right.idempotency_key = `contention-right-${index.toString().padStart(4, "0")}`; + return { left, right }; + }); + assert.throws( + () => validatePerformanceFixture(value, smallAcceptance), + /at most 100 pairs/, + ); +}); + +test("matches the production Idempotency-Key length and visible-ASCII contract", () => { + const shortKey = fixture(); + shortKey.profiles.first_commit[0].idempotency_key = "too-short"; + assert.throws(() => validatePerformanceFixture(shortKey, smallAcceptance), /16 to 200 visible ASCII/); + + const hiddenByte = fixture(); + hiddenByte.profiles.first_commit[0].idempotency_key = "valid-prefix-0001\n"; + assert.throws(() => validatePerformanceFixture(hiddenByte, smallAcceptance), /16 to 200 visible ASCII/); +}); + +test("rejects fixture values the HTTP and application boundaries would reject", () => { + const badDate = fixture(); + badDate.profiles.first_commit[0].payload.separation_effective_on = "2026-02-30"; + assert.throws(() => validatePerformanceFixture(badDate, smallAcceptance), /RFC 3339 full-date/); + + const badEvidence = fixture(); + badEvidence.profiles.first_commit[0].payload.evidence_reference = "not namespaced"; + assert.throws(() => validatePerformanceFixture(badEvidence, smallAcceptance), /namespaced opaque reference/); + + const badVersion = fixture(); + badVersion.profiles.first_commit[0].payload.evidence_version_code = "version with spaces"; + assert.throws(() => validatePerformanceFixture(badVersion, smallAcceptance), /whitespace-free version token/); +}); + +test("rejects bearer values that the production authentication boundary would reject", () => { + const value = command(10); + assert.throws(() => requestHeaders(value, "has space"), /visible ASCII/); + assert.throws(() => requestHeaders(value, `token${"x".repeat(8192)}`), /visible ASCII/); +}); + +test("builds the exact published separation request without storing bearer credentials in fixtures", () => { + const value = command(9, "exact-key-00000001"); + const headers = requestHeaders(value, "opaque-token"); + assert.deepEqual(headers, { + Authorization: "Bearer opaque-token", + "Content-Type": "application/json", + "Idempotency-Key": "exact-key-00000001", + "X-Actor-Reference": "worker_admin:perf_9", + "X-Purpose-Code": "workforce_admin", + "X-Tenant-Reference": "10000000-0000-4000-8000-000000000009", + }); + assert.deepEqual(JSON.parse(requestBody(value)), value.payload); +}); \ No newline at end of file diff --git a/tests/performance/employment_separation_gregorian_year_bounds.test.mjs b/tests/performance/employment_separation_gregorian_year_bounds.test.mjs new file mode 100644 index 000000000..7cc9e71d7 --- /dev/null +++ b/tests/performance/employment_separation_gregorian_year_bounds.test.mjs @@ -0,0 +1,54 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import { acceptanceFixture } from "./employment_separation_acceptance_fixture_test_support.mjs"; +import { validatePerformanceFixture } from "./employment_separation_fixture_contract.mjs"; + +const smallAcceptance = { minimumNonContendingRecords: 1, minimumContentionPairs: 1 }; + +function smallFixture() { + const value = acceptanceFixture(); + value.profiles.first_commit = value.profiles.first_commit.slice(0, 1); + value.profiles.replay = value.profiles.replay.slice(0, 1); + value.profiles.rejection = value.profiles.rejection.slice(0, 1); + value.profiles.contention = value.profiles.contention.slice(0, 1); + return value; +} + +test("accepts the earliest People business date without ECMAScript year normalization", () => { + const value = smallFixture(); + value.profiles.first_commit[0].payload.separation_effective_on = "0001-01-01"; + assert.doesNotThrow(() => validatePerformanceFixture(value, smallAcceptance)); +}); + +test("accepts the latest People business date in the four-digit contract", () => { + const value = smallFixture(); + value.profiles.first_commit[0].payload.separation_effective_on = "9999-12-31"; + assert.doesNotThrow(() => validatePerformanceFixture(value, smallAcceptance)); +}); + +test("rejects year zero in People business dates", () => { + const value = smallFixture(); + value.profiles.first_commit[0].payload.separation_effective_on = "0000-01-01"; + assert.throws( + () => validatePerformanceFixture(value, smallAcceptance), + /separation_effective_on must be an RFC 3339 full-date/, + ); +}); + +test("accepts the governed UTC timestamp year boundaries", () => { + for (const preparedAt of ["0001-01-01T00:00:00Z", "9999-12-31T23:59:59.999999Z"]) { + const value = smallFixture(); + value.prepared_at = preparedAt; + assert.doesNotThrow(() => validatePerformanceFixture(value, smallAcceptance)); + } +}); + +test("rejects year zero in governed UTC evidence timestamps", () => { + const value = smallFixture(); + value.prepared_at = "0000-01-01T00:00:00Z"; + assert.throws( + () => validatePerformanceFixture(value, smallAcceptance), + /fixture.prepared_at must be an RFC 3339 UTC timestamp/, + ); +}); diff --git a/tests/performance/employment_separation_k6_evidence_contract.mjs b/tests/performance/employment_separation_k6_evidence_contract.mjs new file mode 100644 index 000000000..46579a725 --- /dev/null +++ b/tests/performance/employment_separation_k6_evidence_contract.mjs @@ -0,0 +1,61 @@ +import { TextDecoder } from "node:util"; + +import { + PINNED_K6_IMAGE, + PINNED_K6_IMAGE_DIGEST, + PINNED_K6_RUNNER_IDENTITY, + PINNED_K6_VERSION, + requirePinnedK6Runtime, +} from "./employment_separation_k6_runtime_contract.mjs"; +import { parseStrictJsonText } from "./strict_json_artifact.mjs"; + +const MAXIMUM_RESULT_ARTIFACT_BYTES = 1024 * 1024; + +function fail(message) { throw new Error(message); } + +function parseResultBytes(value) { + if (!(value instanceof Uint8Array) && !(value instanceof ArrayBuffer)) fail("performance result must be supplied as raw bytes"); + const bytes = value instanceof Uint8Array ? value : new Uint8Array(value); + if (bytes.byteLength > MAXIMUM_RESULT_ARTIFACT_BYTES) { + fail(`performance result must not exceed ${MAXIMUM_RESULT_ARTIFACT_BYTES} bytes`); + } + + let text; + try { text = new TextDecoder("utf-8", { fatal: true }).decode(bytes); } + catch (error) { throw new Error("performance result must be valid UTF-8", { cause: error }); } + const result = parseStrictJsonText(text, "performance result"); + if (result === null || typeof result !== "object" || Array.isArray(result)) fail("performance result must be an object"); + return result; +} + +function runtimeField(runtime, name) { + if (runtime === null || typeof runtime !== "object" || Array.isArray(runtime)) fail("runtime evidence must be an object"); + const value = runtime[name]; + if (typeof value !== "string" || value === "") fail(`runtime.${name} must be a non-empty string`); + return value; +} + +export function validatePinnedK6AcceptanceEvidence(resultArtifact, runtimeEvidence) { + const result = parseResultBytes(resultArtifact); + const resultRuntime = requirePinnedK6Runtime({ + version: result.k6_version, + image: result.k6_image, + imageDigest: result.k6_image_digest, + runnerIdentity: result.k6_runner_identity, + }); + const observedRuntime = requirePinnedK6Runtime({ + version: runtimeField(runtimeEvidence, "observed_k6_version"), + image: runtimeField(runtimeEvidence, "observed_k6_image"), + imageDigest: runtimeField(runtimeEvidence, "observed_k6_image_digest"), + runnerIdentity: runtimeField(runtimeEvidence, "observed_k6_runner_identity"), + }); + for (const field of ["version", "image", "image_digest", "runner_identity"]) { + if (resultRuntime[field] !== observedRuntime[field]) fail(`runtime observed k6 ${field} must match the performance result`); + } + return Object.freeze({ + k6_version: PINNED_K6_VERSION, + k6_image: PINNED_K6_IMAGE, + k6_image_digest: PINNED_K6_IMAGE_DIGEST, + k6_runner_identity: PINNED_K6_RUNNER_IDENTITY, + }); +} diff --git a/tests/performance/employment_separation_k6_evidence_contract.test.mjs b/tests/performance/employment_separation_k6_evidence_contract.test.mjs new file mode 100644 index 000000000..14c297f8e --- /dev/null +++ b/tests/performance/employment_separation_k6_evidence_contract.test.mjs @@ -0,0 +1,76 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import { + PINNED_K6_IMAGE, + PINNED_K6_IMAGE_DIGEST, + PINNED_K6_RUNNER_IDENTITY, + PINNED_K6_VERSION, +} from "./employment_separation_k6_runtime_contract.mjs"; +import { validatePinnedK6AcceptanceEvidence } from "./employment_separation_k6_evidence_contract.mjs"; + +function resultBytes(overrides = {}) { + return Buffer.from(`${JSON.stringify({ + k6_version: PINNED_K6_VERSION, + k6_image: PINNED_K6_IMAGE, + k6_image_digest: PINNED_K6_IMAGE_DIGEST, + k6_runner_identity: PINNED_K6_RUNNER_IDENTITY, + ...overrides, + })}\n`, "utf8"); +} +function runtime(overrides = {}) { + return { + observed_k6_version: PINNED_K6_VERSION, + observed_k6_image: PINNED_K6_IMAGE, + observed_k6_image_digest: PINNED_K6_IMAGE_DIGEST, + observed_k6_runner_identity: PINNED_K6_RUNNER_IDENTITY, + ...overrides, + }; +} + +test("binds result identity to independently observed pinned upstream k6 OCI evidence", () => { + assert.deepEqual(validatePinnedK6AcceptanceEvidence(resultBytes(), runtime()), { + k6_version: PINNED_K6_VERSION, + k6_image: PINNED_K6_IMAGE, + k6_image_digest: PINNED_K6_IMAGE_DIGEST, + k6_runner_identity: PINNED_K6_RUNNER_IDENTITY, + }); +}); + +test("rejects matching-but-unpinned image digests in result evidence", () => { + const fake = `sha256:${"0".repeat(64)}`; + assert.throws(() => validatePinnedK6AcceptanceEvidence(resultBytes({ k6_image_digest: fake }), runtime({ observed_k6_image_digest: fake })), /OCI image digest/); +}); + +test("rejects runtime observation that does not identify the exact pinned image", () => { + assert.throws(() => validatePinnedK6AcceptanceEvidence(resultBytes(), runtime({ observed_k6_runner_identity: "ghcr.io/grafana/k6@sha256:substitute" })), /runner identity/); +}); + +test("rejects oversized result evidence before UTF-8 decode or JSON scanning", () => { + const ordinary = resultBytes(); + const oversized = Buffer.concat([ + ordinary, + Buffer.alloc((1024 * 1024) + 1 - ordinary.length, 0x20), + ]); + assert.throws( + () => validatePinnedK6AcceptanceEvidence(oversized, runtime()), + /performance result must not exceed 1048576 bytes/, + ); +}); + +test("rejects escaped-equivalent duplicate k6 identity members before JSON collapse", () => { + const duplicate = Buffer.from( + `{\"k6_version\":${JSON.stringify(PINNED_K6_VERSION)},` + + `\"k6_image\":${JSON.stringify(PINNED_K6_IMAGE)},` + + `\"k6_image_digest\":\"sha256:${"0".repeat(64)}\",` + + `\"k6_image_\\u0064igest\":${JSON.stringify(PINNED_K6_IMAGE_DIGEST)},` + + `\"k6_runner_identity\":${JSON.stringify(PINNED_K6_RUNNER_IDENTITY)}}\n`, + "utf8", + ); + + assert.doesNotThrow(() => JSON.parse(duplicate.toString("utf8"))); + assert.throws( + () => validatePinnedK6AcceptanceEvidence(duplicate, runtime()), + /duplicate JSON object member name "k6_image_digest"/, + ); +}); diff --git a/tests/performance/employment_separation_k6_runtime_contract.mjs b/tests/performance/employment_separation_k6_runtime_contract.mjs new file mode 100644 index 000000000..058917c29 --- /dev/null +++ b/tests/performance/employment_separation_k6_runtime_contract.mjs @@ -0,0 +1,30 @@ +export const PINNED_K6_VERSION = "2.2.0"; +export const PINNED_K6_IMAGE = "ghcr.io/grafana/k6"; +export const PINNED_K6_IMAGE_DIGEST = "sha256:9bd01d6941fca969cb61bb57d2da5ee9b385fe2aa8881df3798c196564d6ace6"; +export const PINNED_K6_RUNNER_IDENTITY = `${PINNED_K6_IMAGE}@${PINNED_K6_IMAGE_DIGEST}`; + +export function requirePinnedK6Version(value) { + if (value !== PINNED_K6_VERSION) { + throw new Error(`commercial Employment separation performance runs require k6 ${PINNED_K6_VERSION}`); + } + return value; +} + +export function requirePinnedK6Runtime({ version, image, imageDigest, runnerIdentity }) { + requirePinnedK6Version(version); + if (image !== PINNED_K6_IMAGE) { + throw new Error(`commercial Employment separation performance runs require ${PINNED_K6_IMAGE}`); + } + if (imageDigest !== PINNED_K6_IMAGE_DIGEST) { + throw new Error("commercial Employment separation performance runs require the pinned upstream k6 OCI image digest"); + } + if (runnerIdentity !== PINNED_K6_RUNNER_IDENTITY) { + throw new Error("commercial Employment separation performance runs require the pinned upstream k6 OCI runner identity"); + } + return Object.freeze({ + version, + image, + image_digest: imageDigest, + runner_identity: runnerIdentity, + }); +} diff --git a/tests/performance/employment_separation_k6_runtime_contract.test.mjs b/tests/performance/employment_separation_k6_runtime_contract.test.mjs new file mode 100644 index 000000000..ab6a7f366 --- /dev/null +++ b/tests/performance/employment_separation_k6_runtime_contract.test.mjs @@ -0,0 +1,292 @@ +import assert from "node:assert/strict"; +import { spawnSync } from "node:child_process"; +import { + chmodSync, + existsSync, + mkdtempSync, + mkdirSync, + readFileSync, + rmSync, + writeFileSync, +} from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { fileURLToPath } from "node:url"; +import test from "node:test"; + +import { + PINNED_K6_IMAGE, + PINNED_K6_IMAGE_DIGEST, + PINNED_K6_RUNNER_IDENTITY, + PINNED_K6_VERSION, + requirePinnedK6Runtime, + requirePinnedK6Version, +} from "./employment_separation_k6_runtime_contract.mjs"; + +function validRuntime() { + return { + version: PINNED_K6_VERSION, + image: PINNED_K6_IMAGE, + imageDigest: PINNED_K6_IMAGE_DIGEST, + runnerIdentity: PINNED_K6_RUNNER_IDENTITY, + }; +} + +test("accepts only the repository-pinned upstream k6 OCI image", () => { + assert.equal(PINNED_K6_VERSION, "2.2.0"); + assert.equal(requirePinnedK6Version("2.2.0"), "2.2.0"); + assert.deepEqual(requirePinnedK6Runtime(validRuntime()), { + version: PINNED_K6_VERSION, + image: PINNED_K6_IMAGE, + image_digest: PINNED_K6_IMAGE_DIGEST, + runner_identity: PINNED_K6_RUNNER_IDENTITY, + }); +}); + +test("rejects substituted versions, images, digests, and runner identities", () => { + for (const value of ["", "2.1.0", "2.2.1", "2.3.0", "v2.2.0", "2.2.0-dev"]) { + assert.throws(() => requirePinnedK6Version(value), /require k6 2\.2\.0/); + } + assert.throws(() => requirePinnedK6Runtime({ ...validRuntime(), image: "example.invalid/k6" }), /require ghcr\.io\/grafana\/k6/); + assert.throws(() => requirePinnedK6Runtime({ ...validRuntime(), imageDigest: `sha256:${"0".repeat(64)}` }), /OCI image digest/); + assert.throws(() => requirePinnedK6Runtime({ ...validRuntime(), runnerIdentity: "ghcr.io/grafana/k6@sha256:substitute" }), /runner identity/); +}); + +test("canonical benchmark runner does not accept ungoverned k6 CLI overrides", () => { + const runner = readFileSync(new URL("./run_employment_separation_benchmark.sh", import.meta.url), "utf8"); + assert.doesNotMatch( + runner, + /"\$@"/, + "arbitrary k6 CLI flags can override version-controlled script options and __ENV inputs", + ); + assert.match( + runner, + /"\$\{PINNED_K6_RUNNER_IDENTITY\}" run "\$\{WORKLOAD\}"\s*$/m, + "commercial measurement must end at the version-controlled workload without caller-supplied k6 flags", + ); +}); + +test("canonical benchmark runner does not forward caller-controlled load-model environment", () => { + const runner = readFileSync(new URL("./run_employment_separation_benchmark.sh", import.meta.url), "utf8"); + for (const name of [ + "ORGMETRA_PERFORMANCE_TARGET_RPS", + "ORGMETRA_PERFORMANCE_DURATION_SECONDS", + "ORGMETRA_PERFORMANCE_PREALLOCATED_VUS", + "ORGMETRA_PERFORMANCE_MAX_VUS", + ]) { + assert.doesNotMatch(runner, new RegExp(`--env ${name}(?:\\s|$)`), `${name} must be version-controlled by the workload`); + } +}); + +test("canonical benchmark runner binds the mounted workload to an immutable exact-candidate image", () => { + const runner = readFileSync(new URL("./run_employment_separation_benchmark.sh", import.meta.url), "utf8"); + assert.match( + runner, + /git -C "\$\{repo_root\}" rev-parse --verify HEAD/, + "the source repository must be checked against an exact HEAD before materialization", + ); + assert.match( + runner, + /repository_head.*ORGMETRA_PERFORMANCE_TARGET_SHA|ORGMETRA_PERFORMANCE_TARGET_SHA.*repository_head/s, + "the exact source checkout must match the measured candidate SHA", + ); + assert.match( + runner, + /git -C "\$\{repo_root\}" status --porcelain=v1 --untracked-files=all/, + "commercial evidence must reject modified, staged, or untracked source bytes", + ); + assert.match( + runner, + /git -C "\$\{repo_root\}" archive --format=tar "\$\{target_sha\}"[\s\S]*podman import/, + "the executed workload must be imported from the verified immutable candidate commit", + ); + assert.doesNotMatch( + runner, + /--volume "\$\{repo_root\}:\/workspace:ro"/, + "the live host working tree must never be mounted as the executable workload", + ); + assert.match( + runner, + /--mount "type=image,source=\$\{workload_image_id\},destination=\/workspace"/, + "Podman must mount only the imported candidate image at /workspace", + ); + assert.match( + runner, + /podman image rm --force "\$\{workload_image_id\}"/, + "the ephemeral candidate image must be removed after the run", + ); +}); + +test("canonical benchmark runner maps host evidence ownership to the pinned non-root k6 user", () => { + const runner = readFileSync(new URL("./run_employment_separation_benchmark.sh", import.meta.url), "utf8"); + assert.match(runner, /readonly PINNED_K6_CONTAINER_UID="12345"/); + assert.match(runner, /readonly PINNED_K6_CONTAINER_GID="12345"/); + assert.match( + runner, + /podman image inspect --format '\{\{\.Config\.User\}\}' "\$\{PINNED_K6_RUNNER_IDENTITY\}"/, + "the pinned OCI image's configured non-root user must be verified before measurement", + ); + assert.match( + runner, + /--userns="keep-id:uid=\$\{PINNED_K6_CONTAINER_UID\},gid=\$\{PINNED_K6_CONTAINER_GID\}"/, + "the invoking host owner must map to k6 UID/GID so private fixture and staging paths remain usable without world-writable permissions", + ); +}); + +test("canonical benchmark runner cannot publish stale or failed-run summary evidence", () => { + const runner = readFileSync(new URL("./run_employment_separation_benchmark.sh", import.meta.url), "utf8"); + assert.match( + runner, + /summary_target="\$\{summary_dir\}\/\$\{summary_name\}"/, + "the requested result path must be treated as a final publication target", + ); + assert.match( + runner, + /if \[\[ -e "\$\{summary_target\}" \|\| -L "\$\{summary_target\}" \]\]/, + "a pre-existing result artifact must fail closed instead of surviving a failed rerun", + ); + assert.match( + runner, + /mktemp -d .*orgmetra-employment-separation-performance/, + "k6 must write into a private per-run staging directory", + ); + assert.doesNotMatch( + runner, + /--volume "\$\{summary_dir\}:\/output:rw"/, + "k6 must not write directly into the caller-visible result directory", + ); + assert.match( + runner, + /\[\[ ! -f "\$\{summary_run_file\}" \|\| -L "\$\{summary_run_file\}" \|\| ! -s "\$\{summary_run_file\}" \]\]/, + "the staged result must be a non-empty regular file", + ); + assert.match( + runner, + /summary_source_identity=.*stat --printf='%d:%i:%s'/, + "publication must bind the validated source device, inode, and size before linking", + ); + assert.match( + runner, + /summary_source_digest=.*sha256sum/, + "publication must bind the exact validated source bytes before linking", + ); + assert.match( + runner, + /ln "\$\{summary_run_file\}" "\$\{summary_target\}"/, + "publication must use a no-clobber atomic link so a concurrent stale artifact cannot win", + ); + assert.match( + runner, + /summary_target_identity=.*stat --printf='%d:%i:%s'/, + "the published link must be re-identified after link creation", + ); + assert.match( + runner, + /summary_target_digest=.*sha256sum/, + "the published link bytes must be re-hashed after link creation", + ); + assert.match( + runner, + /summary_source_identity[\s\S]*summary_source_identity_after[\s\S]*summary_target_identity[\s\S]*summary_source_digest[\s\S]*summary_source_digest_after[\s\S]*summary_target_digest/, + "source identity and bytes must remain unchanged across publication and equal the published artifact", + ); + assert.match( + runner, + /rm -f -- "\$\{summary_target\}"/, + "a publication-integrity mismatch must remove the untrusted caller-visible artifact before failing", + ); +}); + +test("canonical benchmark runner rejects a staged-path replacement during publication", () => { + const repoRoot = fileURLToPath(new URL("../..", import.meta.url)); + const runnerPath = fileURLToPath(new URL("./run_employment_separation_benchmark.sh", import.meta.url)); + const temporaryRoot = mkdtempSync(join(tmpdir(), "orgmetra-summary-publication-race-")); + try { + const fakeBin = join(temporaryRoot, "bin"); + mkdirSync(fakeBin); + const podmanPath = join(fakeBin, "podman"); + writeFileSync( + podmanPath, + `#!/usr/bin/env bash +set -euo pipefail +if [[ "$1" == "image" && "$2" == "exists" ]]; then exit 0; fi +if [[ "$1" == "image" && "$2" == "inspect" ]]; then printf '12345\\n'; exit 0; fi +if [[ "$1" == "image" && "$2" == "rm" ]]; then exit 0; fi +if [[ "$1" == "import" ]]; then cat >/dev/null; printf 'sha256:fake-workload\\n'; exit 0; fi +if [[ "$1" == "run" ]]; then + if [[ "\${!#}" == "version" ]]; then printf 'k6 v2.2.0\\n'; exit 0; fi + output_dir='' + summary_name='' + args=("$@") + for ((i=0; i<\${#args[@]}; i++)); do + if [[ "\${args[$i]}" == "--volume" ]]; then + value="\${args[$((i+1))]}" + if [[ "$value" == *":/output:rw" ]]; then output_dir="\${value%:/output:rw}"; fi + fi + if [[ "\${args[$i]}" == "--env" ]]; then + value="\${args[$((i+1))]}" + if [[ "$value" == ORGMETRA_PERFORMANCE_SUMMARY_FILE=/output/* ]]; then + summary_name="\${value#ORGMETRA_PERFORMANCE_SUMMARY_FILE=/output/}" + fi + fi + done + [[ -n "$output_dir" && -n "$summary_name" ]] + printf '{"schema_version":"orgmetra.race_probe.original"}\\n' > "$output_dir/$summary_name" + exit 0 +fi +exit 99 +`, + { mode: 0o700 }, + ); + chmodSync(podmanPath, 0o700); + + const lnPath = join(fakeBin, "ln"); + writeFileSync( + lnPath, + `#!/usr/bin/env bash +set -euo pipefail +printf '{"schema_version":"orgmetra.race_probe.replaced"}\\n' > "$1" +exec /bin/ln "$@" +`, + { mode: 0o700 }, + ); + chmodSync(lnPath, 0o700); + + const fixturePath = join(temporaryRoot, "fixture.json"); + const summaryPath = join(temporaryRoot, "result.json"); + writeFileSync(fixturePath, "{}\n", { mode: 0o600 }); + + const head = spawnSync("git", ["-C", repoRoot, "rev-parse", "HEAD"], { encoding: "utf8" }); + assert.equal(head.status, 0, head.stderr); + const targetSha = head.stdout.trim(); + const result = spawnSync("bash", [runnerPath], { + cwd: repoRoot, + encoding: "utf8", + env: { + ...process.env, + PATH: `${fakeBin}:${process.env.PATH ?? ""}`, + ORGMETRA_PERFORMANCE_BASE_URL: "http://127.0.0.1:18080", + ORGMETRA_PERFORMANCE_BEARER_TOKEN: "test-only-token", + ORGMETRA_PERFORMANCE_TARGET_SHA: targetSha, + ORGMETRA_PERFORMANCE_PROFILE: "first_commit", + ORGMETRA_PERFORMANCE_DATA_FILE: fixturePath, + ORGMETRA_PERFORMANCE_SUMMARY_FILE: summaryPath, + }, + }); + assert.notEqual(result.status, 0); + assert.match(result.stderr, /summary changed during publication/); + assert.equal(existsSync(summaryPath), false, "a replaced staged artifact must never remain published"); + } finally { + rmSync(temporaryRoot, { recursive: true, force: true }); + } +}); + +test("canonical benchmark runner rejects CLI overrides before any Podman dependency is needed", () => { + const runnerPath = fileURLToPath(new URL("./run_employment_separation_benchmark.sh", import.meta.url)); + const result = spawnSync("bash", [runnerPath, "--duration", "1s"], { + encoding: "utf8", + env: { PATH: process.env.PATH ?? "" }, + }); + assert.equal(result.status, 64); + assert.match(result.stderr, /does not accept caller-supplied k6 CLI options/); +}); diff --git a/tests/performance/employment_separation_k6_summary_contract.mjs b/tests/performance/employment_separation_k6_summary_contract.mjs new file mode 100644 index 000000000..bb720b2f3 --- /dev/null +++ b/tests/performance/employment_separation_k6_summary_contract.mjs @@ -0,0 +1,103 @@ +const SUPPORTED_TREND_STATS = Object.freeze(["p(50)", "p(95)", "p(99)", "max", "count"]); + +function fail(message) { + throw new Error(message); +} + +function plainObject(value, label) { + if (value === null || typeof value !== "object" || Array.isArray(value)) { + fail(`${label} must be an object`); + } + return value; +} + +function nonEmptyString(value, label) { + if (typeof value !== "string" || value.trim() === "") fail(`${label} must be a non-empty string`); + return value; +} + +function nonNegativeInteger(value, label) { + if (!Number.isSafeInteger(value) || value < 0) fail(`${label} must be a non-negative safe integer`); + return value; +} + +function finiteNumber(value, label, { minimum = 0, maximum = Number.POSITIVE_INFINITY } = {}) { + if (typeof value !== "number" || !Number.isFinite(value) || value < minimum || value > maximum) { + fail(`${label} must be a finite number between ${minimum} and ${maximum}`); + } + return value; +} + +function exactStringArray(value, expected, label) { + if (!Array.isArray(value) || value.length !== expected.length) { + fail(`${label} must equal ${expected.join(", ")}`); + } + for (let index = 0; index < expected.length; index += 1) { + if (value[index] !== expected[index]) fail(`${label} must equal ${expected.join(", ")}`); + } +} + +function requireMetric(metrics, name, type) { + const metric = plainObject(metrics[name], `k6 summary.metrics.${name}`); + if (metric.type !== type) fail(`k6 summary.metrics.${name}.type must be ${type}`); + return plainObject(metric.values, `k6 summary.metrics.${name}.values`); +} + +function counterValue(metrics, name) { + return nonNegativeInteger(requireMetric(metrics, name, "counter").count, `k6 summary.metrics.${name}.values.count`); +} + +function rateValue(metrics, name) { + const values = requireMetric(metrics, name, "rate"); + const passes = nonNegativeInteger(values.passes, `k6 summary.metrics.${name}.values.passes`); + const fails = nonNegativeInteger(values.fails, `k6 summary.metrics.${name}.values.fails`); + const total = passes + fails; + if (!Number.isSafeInteger(total)) fail(`k6 summary.metrics.${name} sample total must be a safe integer`); + const rate = finiteNumber(values.rate, `k6 summary.metrics.${name}.values.rate`, { maximum: 1 }); + const expectedRate = total === 0 ? 0 : passes / total; + if (Math.abs(rate - expectedRate) > Number.EPSILON * 8) { + fail(`k6 summary.metrics.${name}.values.rate must equal passes / total`); + } + return Object.freeze({ passes, fails, rate }); +} + +function trendValue(metrics, name) { + const values = requireMetric(metrics, name, "trend"); + return Object.freeze({ + "p(50)": finiteNumber(values["p(50)"], `k6 summary.metrics.${name}.values.p(50)`), + "p(95)": finiteNumber(values["p(95)"], `k6 summary.metrics.${name}.values.p(95)`), + "p(99)": finiteNumber(values["p(99)"], `k6 summary.metrics.${name}.values.p(99)`), + max: finiteNumber(values.max, `k6 summary.metrics.${name}.values.max`), + count: nonNegativeInteger(values.count, `k6 summary.metrics.${name}.values.count`), + }); +} + +export function normalizeEmploymentSeparationK6Summary(summary, { expectedK6Version, trendName }) { + const document = plainObject(summary, "k6 summary"); + if (Object.prototype.hasOwnProperty.call(document, "version")) { + fail("pinned k6 2.2 machine-readable summary cannot supply required p(99) and trend count evidence"); + } + const options = plainObject(document.options, "k6 summary.options"); + exactStringArray(options.summaryTrendStats, SUPPORTED_TREND_STATS, "k6 summary.options.summaryTrendStats"); + const requiredK6Version = nonEmptyString(expectedK6Version, "expectedK6Version"); + const requiredTrendName = nonEmptyString(trendName, "trendName"); + const metrics = plainObject(document.metrics, "k6 summary.metrics"); + + const iterations = counterValue(metrics, "iterations"); + const latencySamples = counterValue(metrics, "employment_separation_latency_samples"); + const unexpected = rateValue(metrics, "employment_separation_unexpected_response"); + const checks = rateValue(metrics, "checks"); + const trend = trendValue(metrics, requiredTrendName); + + return Object.freeze({ + summary_contract: "k6-legacy-handle-summary", + summary_k6_version: requiredK6Version, + metrics: Object.freeze({ + iterations: Object.freeze({ values: Object.freeze({ count: iterations }) }), + checks: Object.freeze({ values: checks }), + employment_separation_unexpected_response: Object.freeze({ values: unexpected }), + employment_separation_latency_samples: Object.freeze({ values: Object.freeze({ count: latencySamples }) }), + [requiredTrendName]: Object.freeze({ values: trend }), + }), + }); +} diff --git a/tests/performance/employment_separation_k6_summary_contract.test.mjs b/tests/performance/employment_separation_k6_summary_contract.test.mjs new file mode 100644 index 000000000..2ea57f32d --- /dev/null +++ b/tests/performance/employment_separation_k6_summary_contract.test.mjs @@ -0,0 +1,90 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import { normalizeEmploymentSeparationK6Summary } from "./employment_separation_k6_summary_contract.mjs"; + +const TREND = "employment_separation_first_commit_duration_ms"; +const STATS = ["p(50)", "p(95)", "p(99)", "max", "count"]; + +function legacySummary({ iterations = 1000, latencySamples = iterations, trendName = TREND } = {}) { + return { + root_group: { name: "", path: "", id: "", groups: [], checks: [] }, + options: { summaryTrendStats: [...STATS], summaryTimeUnit: "", noColor: true }, + state: { isStdOutTTY: false, isStdErrTTY: false, testRunDurationMs: 50000 }, + setup_data: null, + metrics: { + iterations: { type: "counter", contains: "default", values: { count: iterations, rate: 20 } }, + checks: { type: "rate", contains: "default", values: { rate: 1, passes: iterations, fails: 0 } }, + employment_separation_unexpected_response: { + type: "rate", contains: "default", values: { rate: 0, passes: 0, fails: iterations }, + }, + employment_separation_latency_samples: { + type: "counter", contains: "default", values: { count: latencySamples, rate: 20 }, + }, + [trendName]: { + type: "trend", + contains: "time", + values: { "p(50)": 8.1, "p(95)": 18.4, "p(99)": 19.7, max: 22.3, count: latencySamples }, + }, + }, + }; +} + +function machineReadableSummary() { + return { + version: "1.0.0", + metadata: { generatedAt: "2026-09-15T11:00:00Z", k6Version: "2.2.0" }, + config: { execution: "local", script: "employment_separation_buyer_path.js", duration: 50 }, + results: { + metrics: [{ + name: TREND, + type: "trend", + contains: "time", + values: { avg: 10, max: 22.3, med: 8.1, min: 2.2, "p(90)": 16.2, "p(95)": 18.4 }, + }], + checks: { metrics: [], results: [] }, + }, + }; +} + +test("normalizes the pinned k6 2.2 default handleSummary contract with configured commercial percentiles", () => { + const normalized = normalizeEmploymentSeparationK6Summary(legacySummary(), { + expectedK6Version: "2.2.0", + trendName: TREND, + }); + assert.deepEqual(normalized.metrics.iterations.values, { count: 1000 }); + assert.deepEqual(normalized.metrics.checks.values, { rate: 1, passes: 1000, fails: 0 }); + assert.deepEqual(normalized.metrics.employment_separation_unexpected_response.values, { rate: 0, passes: 0, fails: 1000 }); + assert.equal(normalized.metrics.employment_separation_latency_samples.values.count, 1000); + assert.deepEqual(normalized.metrics[TREND].values, { "p(50)": 8.1, "p(95)": 18.4, "p(99)": 19.7, max: 22.3, count: 1000 }); + assert.equal(normalized.summary_contract, "k6-legacy-handle-summary"); + assert.equal(normalized.summary_k6_version, "2.2.0"); +}); + +test("rejects the real k6 2.2 machine-readable trend shape instead of fabricating p99 or count", () => { + assert.throws( + () => normalizeEmploymentSeparationK6Summary(machineReadableSummary(), { expectedK6Version: "2.2.0", trendName: TREND }), + /cannot supply required p\(99\) and trend count evidence/i, + ); +}); + +test("rejects a legacy summary whose configured trend statistics do not match commercial evidence", () => { + const summary = legacySummary(); + summary.options.summaryTrendStats = ["med", "p(95)", "max"]; + assert.throws( + () => normalizeEmploymentSeparationK6Summary(summary, { expectedK6Version: "2.2.0", trendName: TREND }), + /summaryTrendStats/i, + ); +}); + +test("preserves one contention verdict while retaining two latency samples", () => { + const contentionTrend = "employment_separation_contention_duration_ms"; + const normalized = normalizeEmploymentSeparationK6Summary( + legacySummary({ iterations: 100, latencySamples: 200, trendName: contentionTrend }), + { expectedK6Version: "2.2.0", trendName: contentionTrend }, + ); + assert.deepEqual(normalized.metrics.checks.values, { rate: 1, passes: 100, fails: 0 }); + assert.deepEqual(normalized.metrics.employment_separation_unexpected_response.values, { rate: 0, passes: 0, fails: 100 }); + assert.equal(normalized.metrics.employment_separation_latency_samples.values.count, 200); + assert.equal(normalized.metrics[contentionTrend].values.count, 200); +}); diff --git a/tests/performance/employment_separation_request_contract.mjs b/tests/performance/employment_separation_request_contract.mjs new file mode 100644 index 000000000..ebdcfb66f --- /dev/null +++ b/tests/performance/employment_separation_request_contract.mjs @@ -0,0 +1,71 @@ +const PERFORMANCE_PROFILES = new Set(["first_commit", "replay", "rejection", "contention"]); +const HTTPS_ORIGIN_PREFIX = "https://"; +const DNS_OR_IPV4_HOST_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9.-]*[A-Za-z0-9])?$/; +const BRACKETED_IP_LITERAL_PATTERN = /^\[[0-9A-Fa-f:.]+\]$/; + +function isPlainObject(value) { + return value !== null && typeof value === "object" && !Array.isArray(value); +} + +function invalidHttpsOrigin() { + throw new Error("ORGMETRA_PERFORMANCE_BASE_URL must be an authenticated HTTPS origin"); +} + +function requireValidPort(value) { + if (!/^\d{1,5}$/.test(value)) invalidHttpsOrigin(); + const port = Number(value); + if (!Number.isSafeInteger(port) || port < 1 || port > 65535) invalidHttpsOrigin(); +} + +export function requireGovernedSeparationHttpsOrigin(value) { + if (typeof value !== "string" || value === "" || value !== value.trim()) invalidHttpsOrigin(); + if (!value.startsWith(HTTPS_ORIGIN_PREFIX)) invalidHttpsOrigin(); + + let authority = value.slice(HTTPS_ORIGIN_PREFIX.length); + const slash = authority.indexOf("/"); + if (slash !== -1) { + if (slash !== authority.length - 1 || authority.lastIndexOf("/") !== slash) invalidHttpsOrigin(); + authority = authority.slice(0, -1); + } + if ( + authority === "" + || authority.includes("@") + || authority.includes("?") + || authority.includes("#") + || /[\u0000-\u0020\u007f]/.test(authority) + ) invalidHttpsOrigin(); + + if (authority.startsWith("[")) { + const closingBracket = authority.indexOf("]"); + if (closingBracket < 2) invalidHttpsOrigin(); + const host = authority.slice(0, closingBracket + 1); + if (!BRACKETED_IP_LITERAL_PATTERN.test(host) || !host.includes(":")) invalidHttpsOrigin(); + const suffix = authority.slice(closingBracket + 1); + if (suffix !== "") { + if (!suffix.startsWith(":")) invalidHttpsOrigin(); + requireValidPort(suffix.slice(1)); + } + } else { + const colon = authority.lastIndexOf(":"); + if (colon !== authority.indexOf(":")) invalidHttpsOrigin(); + const host = colon === -1 ? authority : authority.slice(0, colon); + if (!DNS_OR_IPV4_HOST_PATTERN.test(host) || host.includes("..")) invalidHttpsOrigin(); + if (colon !== -1) requireValidPort(authority.slice(colon + 1)); + } + + return `${HTTPS_ORIGIN_PREFIX}${authority}`; +} + +export function governedSeparationRequestParams(headers, profile) { + if (!isPlainObject(headers)) { + throw new Error("Employment-separation request headers must be a plain object"); + } + if (!PERFORMANCE_PROFILES.has(profile)) { + throw new Error(`unsupported Employment-separation performance profile: ${profile}`); + } + return { + headers, + redirects: 0, + tags: { profile }, + }; +} diff --git a/tests/performance/employment_separation_request_contract.test.mjs b/tests/performance/employment_separation_request_contract.test.mjs new file mode 100644 index 000000000..f1c8e68d8 --- /dev/null +++ b/tests/performance/employment_separation_request_contract.test.mjs @@ -0,0 +1,83 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import { + governedSeparationRequestParams, + requireGovernedSeparationHttpsOrigin, +} from "./employment_separation_request_contract.mjs"; + +const headers = Object.freeze({ + Authorization: "Bearer performance-token", + "Content-Type": "application/json", + "Idempotency-Key": "employment-separation-0001", + "X-Actor-Reference": "worker:performance-operator", + "X-Purpose-Code": "workforce_admin", + "X-Tenant-Reference": "10000000-0000-4000-8000-000000000001", +}); + +test("commercial Employment-separation origin requires authenticated HTTPS", () => { + assert.equal( + requireGovernedSeparationHttpsOrigin("https://people.example.com"), + "https://people.example.com", + ); + assert.equal( + requireGovernedSeparationHttpsOrigin("https://people.example.com:8443/"), + "https://people.example.com:8443", + ); + assert.equal( + requireGovernedSeparationHttpsOrigin("https://127.0.0.1:9443"), + "https://127.0.0.1:9443", + ); +}); + +test("commercial Employment-separation origin rejects plaintext and caller-controlled URL components", () => { + for (const invalid of [ + "http://people.example.com", + "https://user:password@people.example.com", + "https://people.example.com/v1", + "https://people.example.com?tenant=other", + "https://people.example.com#fragment", + " https://people.example.com", + "https://people.example.com:0", + "https://people.example.com:65536", + ]) { + assert.throws( + () => requireGovernedSeparationHttpsOrigin(invalid), + /authenticated HTTPS origin/, + invalid, + ); + } +}); + +test("governed request params disable redirects without changing the request headers", () => { + const params = governedSeparationRequestParams(headers, "first_commit"); + + assert.equal(params.redirects, 0); + assert.strictEqual(params.headers, headers); + assert.deepEqual(params.tags, { profile: "first_commit" }); + assert.deepEqual(Object.keys(params).sort(), ["headers", "redirects", "tags"]); +}); + +test("governed request params apply the same no-redirect policy to every buyer profile", () => { + for (const profile of ["first_commit", "replay", "rejection", "contention"]) { + const params = governedSeparationRequestParams(headers, profile); + assert.equal(params.redirects, 0, `${profile} must not follow redirects`); + assert.deepEqual(params.tags, { profile }); + } +}); + +test("governed request params reject unknown profiles", () => { + assert.throws( + () => governedSeparationRequestParams(headers, "redirected_success"), + /unsupported Employment-separation performance profile/, + ); +}); + +test("governed request params reject non-object headers", () => { + for (const invalid of [null, [], "Authorization: Bearer token"] ) { + assert.throws( + () => governedSeparationRequestParams(invalid, "first_commit"), + /request headers must be a plain object/, + ); + } +}); diff --git a/tests/performance/employment_separation_response_contract.mjs b/tests/performance/employment_separation_response_contract.mjs new file mode 100644 index 000000000..ff9c4682b --- /dev/null +++ b/tests/performance/employment_separation_response_contract.mjs @@ -0,0 +1,215 @@ +import { parseStrictJsonText } from "./strict_json_artifact.mjs"; + +const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; +const UTC_TIMESTAMP_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,6})?Z$/; +const SUPPORT_REFERENCE_PATTERN = /^err_[A-Za-z0-9_-]{32}$/; +const HTTP_TOKEN_PATTERN = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/u; +const MAXIMUM_GOVERNED_RESPONSE_BODY_BYTES = 16 * 1024; +const SUCCESS_RESPONSE_KEYS = Object.freeze([ + "employment_record_id", + "separated_employment_record_version_id", + "recorded_at", + "replayed", +]); +const ERROR_RESPONSE_KEYS = Object.freeze([ + "error_code", + "message", + "next_action", + "support_reference", +]); + +function isPlainObject(value) { + return value !== null && typeof value === "object" && !Array.isArray(value); +} + +function hasExactKeys(value, expectedKeys) { + const actual = Object.keys(value).sort(); + const expected = [...expectedKeys].sort(); + return actual.length === expected.length && actual.every((key, index) => key === expected[index]); +} + +function isOperationalUuid(value) { + if (typeof value !== "string" || !UUID_PATTERN.test(value)) return false; + const compact = value.replaceAll("-", "").toLowerCase(); + return compact !== "0".repeat(32) && compact !== "f".repeat(32); +} + +function isValidUtcTimestamp(value) { + if (typeof value !== "string" || !UTC_TIMESTAMP_PATTERN.test(value)) return false; + const year = Number(value.slice(0, 4)); + const month = Number(value.slice(5, 7)); + const day = Number(value.slice(8, 10)); + const hour = Number(value.slice(11, 13)); + const minute = Number(value.slice(14, 16)); + const second = Number(value.slice(17, 19)); + const leapYear = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0); + const daysInMonth = [31, leapYear ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + return ( + year >= 1 + && year <= 9999 + && month >= 1 + && month <= 12 + && day >= 1 + && day <= daysInMonth[month - 1] + && hour <= 23 + && minute <= 59 + && second <= 59 + ); +} + +function splitContentTypeSegments(value) { + const segments = []; + let start = 0; + let quoted = false; + let escaped = false; + for (let index = 0; index < value.length; index += 1) { + const character = value[index]; + if (escaped) { + const codePoint = character.codePointAt(0); + if (codePoint !== 0x09 && (codePoint < 0x20 || codePoint === 0x7f)) return null; + escaped = false; + continue; + } + if (quoted) { + if (character === "\\") { + escaped = true; + } else if (character === '"') { + quoted = false; + } else { + const codePoint = character.codePointAt(0); + if (codePoint !== 0x09 && (codePoint < 0x20 || codePoint === 0x7f)) return null; + } + continue; + } + if (character === '"') { + quoted = true; + continue; + } + if (character === ",") return null; + if (character === ";") { + segments.push(value.slice(start, index)); + start = index + 1; + } + } + if (quoted || escaped) return null; + segments.push(value.slice(start)); + return segments; +} + +function isValidQuotedParameterValue(value) { + if (value.length < 2 || value[0] !== '"' || value[value.length - 1] !== '"') return false; + let escaped = false; + for (let index = 1; index < value.length - 1; index += 1) { + const character = value[index]; + const codePoint = character.codePointAt(0); + if (escaped) { + if (codePoint !== 0x09 && (codePoint < 0x20 || codePoint === 0x7f)) return false; + escaped = false; + continue; + } + if (character === "\\") { + escaped = true; + continue; + } + if (character === '"') return false; + if (codePoint !== 0x09 && (codePoint < 0x20 || codePoint === 0x7f)) return false; + } + return !escaped; +} + +function parseContentTypeParameter(segment) { + const text = segment.trim(); + const separator = text.indexOf("="); + if (separator <= 0) return null; + const rawName = text.slice(0, separator); + const rawValue = text.slice(separator + 1); + if (rawName !== rawName.trim() || rawValue !== rawValue.trim()) return null; + if (!HTTP_TOKEN_PATTERN.test(rawName) || rawValue === "") return null; + if (!HTTP_TOKEN_PATTERN.test(rawValue) && !isValidQuotedParameterValue(rawValue)) return null; + return rawName.toLowerCase(); +} + +function singleResponseHeaderValue(headers, fieldName) { + if (!isPlainObject(headers)) return null; + const entries = Object.entries(headers).filter(([name]) => name.toLowerCase() === fieldName); + if (entries.length !== 1 || typeof entries[0][1] !== "string") return null; + return entries[0][1]; +} + +function commaSeparatedHttpTokens(value) { + const tokens = value.split(",").map((token) => token.trim()); + if (tokens.length < 1 || tokens.some((token) => !HTTP_TOKEN_PATTERN.test(token))) return null; + const normalized = tokens.map((token) => token.toLowerCase()); + if (new Set(normalized).size !== normalized.length) return null; + return normalized; +} + +function exceedsUtf8ByteBudget(value, maximumBytes) { + let bytes = 0; + for (const character of value) { + const codePoint = character.codePointAt(0); + if (codePoint <= 0x7f) bytes += 1; + else if (codePoint <= 0x7ff) bytes += 2; + else if (codePoint <= 0xffff) bytes += 3; + else bytes += 4; + if (bytes > maximumBytes) return true; + } + return false; +} + +export function hasGovernedSeparationJsonMediaType(headers) { + const value = singleResponseHeaderValue(headers, "content-type"); + if (value === null) return false; + const segments = splitContentTypeSegments(value); + if (segments === null || segments.length < 1) return false; + const mediaType = segments[0].trim().toLowerCase(); + if (mediaType !== "application/json") return false; + const parameterNames = new Set(); + for (const segment of segments.slice(1)) { + const parameterName = parseContentTypeParameter(segment); + if (parameterName === null || parameterNames.has(parameterName)) return false; + parameterNames.add(parameterName); + } + return true; +} + +export function hasGovernedSeparationNoStorePolicy(headers) { + const cacheControl = singleResponseHeaderValue(headers, "cache-control"); + const vary = singleResponseHeaderValue(headers, "vary"); + if (cacheControl === null || vary === null) return false; + if (cacheControl.trim().toLowerCase() !== "no-store") return false; + const varyTokens = commaSeparatedHttpTokens(vary); + return varyTokens !== null && varyTokens.includes("authorization"); +} + +export function parseGovernedSeparationResponseBody(value) { + if (typeof value !== "string") { + throw new Error("governed separation response body must be JSON text"); + } + if (exceedsUtf8ByteBudget(value, MAXIMUM_GOVERNED_RESPONSE_BODY_BYTES)) { + throw new Error( + `governed separation response body must not exceed ${MAXIMUM_GOVERNED_RESPONSE_BODY_BYTES} UTF-8 bytes`, + ); + } + return parseStrictJsonText(value, "governed separation response body"); +} + +export function isGovernedSeparationSuccess(status, body, { employmentRecordId, replayed }) { + if (status !== 200 || !isPlainObject(body) || !hasExactKeys(body, SUCCESS_RESPONSE_KEYS)) return false; + if (!isOperationalUuid(employmentRecordId)) return false; + if (typeof replayed !== "boolean") return false; + if (!isOperationalUuid(body.employment_record_id) || body.employment_record_id.toLowerCase() !== employmentRecordId.toLowerCase()) { + return false; + } + if (!isOperationalUuid(body.separated_employment_record_version_id)) return false; + if (!isValidUtcTimestamp(body.recorded_at)) return false; + return body.replayed === replayed; +} + +export function isGovernedSeparationConflict(status, body) { + if (status !== 409 || !isPlainObject(body) || !hasExactKeys(body, ERROR_RESPONSE_KEYS)) return false; + if (body.error_code !== "separation_conflict") return false; + if (typeof body.message !== "string" || body.message.length < 1 || body.message.length > 1000) return false; + if (typeof body.next_action !== "string" || body.next_action.length < 1 || body.next_action.length > 1000) return false; + return typeof body.support_reference === "string" && SUPPORT_REFERENCE_PATTERN.test(body.support_reference); +} \ No newline at end of file diff --git a/tests/performance/employment_separation_response_contract.test.mjs b/tests/performance/employment_separation_response_contract.test.mjs new file mode 100644 index 000000000..325b08f47 --- /dev/null +++ b/tests/performance/employment_separation_response_contract.test.mjs @@ -0,0 +1,256 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import { + hasGovernedSeparationJsonMediaType, + hasGovernedSeparationNoStorePolicy, + isGovernedSeparationConflict, + isGovernedSeparationSuccess, + parseGovernedSeparationResponseBody, +} from "./employment_separation_response_contract.mjs"; + +const EMPLOYMENT = "30000000-0000-4000-8000-000000000001"; +const VERSION = "50000000-0000-4000-8000-000000000001"; + +function successBody(replayed = false) { + return { + employment_record_id: EMPLOYMENT, + separated_employment_record_version_id: VERSION, + recorded_at: "2026-09-13T02:00:00Z", + replayed, + }; +} + +function conflictBody(overrides = {}) { + return { + error_code: "separation_conflict", + message: "Refresh Employment and Assignment state, then retry.", + next_action: "Refresh Employment and Assignment state, then retry.", + support_reference: "err_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef", + ...overrides, + }; +} + +test("accepts the published first-commit and replay response shape", () => { + assert.equal( + isGovernedSeparationSuccess(200, successBody(false), { employmentRecordId: EMPLOYMENT, replayed: false }), + true, + ); + assert.equal( + isGovernedSeparationSuccess(200, successBody(true), { employmentRecordId: EMPLOYMENT, replayed: true }), + true, + ); +}); + +test("rejects undeclared fields in the published success response envelope", () => { + assert.equal( + isGovernedSeparationSuccess( + 200, + { ...successBody(false), extra: "undeclared" }, + { employmentRecordId: EMPLOYMENT, replayed: false }, + ), + false, + ); +}); + +test("rejects sentinel separated-version identities in governed success evidence", () => { + for (const separatedVersionId of [ + "00000000-0000-0000-0000-000000000000", + "ffffffff-ffff-ffff-ffff-ffffffffffff", + ]) { + assert.equal( + isGovernedSeparationSuccess( + 200, + { ...successBody(false), separated_employment_record_version_id: separatedVersionId }, + { employmentRecordId: EMPLOYMENT, replayed: false }, + ), + false, + ); + } +}); + +test("rejects impossible success-response calendar timestamps", () => { + const body = successBody(false); + body.recorded_at = "2026-02-30T02:00:00Z"; + assert.equal( + isGovernedSeparationSuccess(200, body, { employmentRecordId: EMPLOYMENT, replayed: false }), + false, + ); +}); + +test("keeps success-response recorded_at inside the People datetime year domain", () => { + for (const recordedAt of ["0001-01-01T00:00:00Z", "9999-12-31T23:59:59.999999Z"]) { + assert.equal( + isGovernedSeparationSuccess( + 200, + { ...successBody(false), recorded_at: recordedAt }, + { employmentRecordId: EMPLOYMENT, replayed: false }, + ), + true, + ); + } + assert.equal( + isGovernedSeparationSuccess( + 200, + { ...successBody(false), recorded_at: "0000-01-01T00:00:00Z" }, + { employmentRecordId: EMPLOYMENT, replayed: false }, + ), + false, + ); +}); + +test("rejects success responses that are replay- or target-inconsistent", () => { + assert.equal( + isGovernedSeparationSuccess(200, successBody(true), { employmentRecordId: EMPLOYMENT, replayed: false }), + false, + ); + assert.equal( + isGovernedSeparationSuccess(200, successBody(false), { + employmentRecordId: "30000000-0000-4000-8000-000000000002", + replayed: false, + }), + false, + ); +}); + +test("accepts only one governed JSON response media type", () => { + assert.equal(hasGovernedSeparationJsonMediaType({ "Content-Type": "application/json" }), true); + assert.equal(hasGovernedSeparationJsonMediaType({ "content-type": "Application/JSON; charset=utf-8" }), true); + assert.equal(hasGovernedSeparationJsonMediaType({}), false); + assert.equal(hasGovernedSeparationJsonMediaType({ "Content-Type": "text/plain" }), false); + assert.equal( + hasGovernedSeparationJsonMediaType({ + "Content-Type": "application/json", + "content-type": "application/json", + }), + false, + ); + assert.equal(hasGovernedSeparationJsonMediaType({ "Content-Type": ["application/json"] }), false); +}); + +test("requires the canonical no-store and Authorization-vary response policy", () => { + assert.equal( + hasGovernedSeparationNoStorePolicy({ "Cache-Control": "no-store", Vary: "Authorization" }), + true, + ); + assert.equal( + hasGovernedSeparationNoStorePolicy({ "cache-control": "No-Store", vary: "Accept-Encoding, authorization" }), + true, + ); + assert.equal(hasGovernedSeparationNoStorePolicy({ Vary: "Authorization" }), false); + assert.equal( + hasGovernedSeparationNoStorePolicy({ "Cache-Control": "public, no-store", Vary: "Authorization" }), + false, + ); + assert.equal( + hasGovernedSeparationNoStorePolicy({ "Cache-Control": "no-store, s-maxage=60", Vary: "Authorization" }), + false, + ); + assert.equal( + hasGovernedSeparationNoStorePolicy({ "Cache-Control": "no-store", Vary: "Accept-Encoding" }), + false, + ); + assert.equal( + hasGovernedSeparationNoStorePolicy({ "Cache-Control": "no-store", Vary: "*" }), + false, + ); + assert.equal( + hasGovernedSeparationNoStorePolicy({ + "Cache-Control": "no-store", + "cache-control": "no-store", + Vary: "Authorization", + }), + false, + ); + assert.equal( + hasGovernedSeparationNoStorePolicy({ "Cache-Control": ["no-store"], Vary: "Authorization" }), + false, + ); +}); + +test("rejects k6-collapsed duplicate Content-Type values without rejecting quoted parameter commas", () => { + assert.equal( + hasGovernedSeparationJsonMediaType({ "Content-Type": "application/json; charset=utf-8, text/plain" }), + false, + ); + assert.equal( + hasGovernedSeparationJsonMediaType({ "Content-Type": "application/json; profile=\"a,b\"" }), + true, + ); + assert.equal( + hasGovernedSeparationJsonMediaType({ "Content-Type": "application/json; profile=\"a\\\",b\"" }), + true, + ); + assert.equal( + hasGovernedSeparationJsonMediaType({ "Content-Type": "application/json; profile=\"unterminated, text/plain" }), + false, + ); + assert.equal( + hasGovernedSeparationJsonMediaType({ "Content-Type": "application/json; profile=a\"b,c\"" }), + false, + ); + assert.equal(hasGovernedSeparationJsonMediaType({ "Content-Type": "application/json;" }), false); +}); + +test("rejects invalid or duplicate media-type parameters", () => { + assert.equal(hasGovernedSeparationJsonMediaType({ "Content-Type": "application/json; charset =utf-8" }), false); + assert.equal(hasGovernedSeparationJsonMediaType({ "Content-Type": "application/json; charset= utf-8" }), false); + assert.equal( + hasGovernedSeparationJsonMediaType({ "Content-Type": "application/json; charset=utf-8; Charset=latin1" }), + false, + ); +}); + +test("accepts only the published closed ErrorResponse shape for separation conflicts", () => { + assert.equal(isGovernedSeparationConflict(409, conflictBody()), true); + assert.equal(isGovernedSeparationConflict(409, { error: "separation_conflict" }), false); + assert.equal(isGovernedSeparationConflict(409, { error_code: "separation_conflict" }), false); + assert.equal(isGovernedSeparationConflict(409, conflictBody({ support_reference: "trace-123" })), false); + assert.equal(isGovernedSeparationConflict(409, { ...conflictBody(), extra: "undeclared" }), false); + assert.equal(isGovernedSeparationConflict(404, conflictBody()), false); +}); + +test("binds conflict support references to the canonical People token_urlsafe length", () => { + assert.equal(isGovernedSeparationConflict(409, conflictBody()), true); + for (const suffixLength of [24, 31, 33, 80]) { + assert.equal( + isGovernedSeparationConflict(409, conflictBody({ support_reference: `err_${"A".repeat(suffixLength)}` })), + false, + ); + } +}); + +test("bounds governed response JSON before strict parsing while preserving the closed error envelope", () => { + const escapedBoundaryText = "\u0001".repeat(1000); + const largestSemanticEnvelope = JSON.stringify(conflictBody({ + message: escapedBoundaryText, + next_action: escapedBoundaryText, + })); + assert.ok(largestSemanticEnvelope.length < 16 * 1024); + assert.deepEqual( + parseGovernedSeparationResponseBody(largestSemanticEnvelope), + JSON.parse(largestSemanticEnvelope), + ); + + const oversized = `${" ".repeat(16 * 1024)}${JSON.stringify(successBody(false))}`; + assert.throws( + () => parseGovernedSeparationResponseBody(oversized), + /governed separation response body must not exceed 16384 UTF-8 bytes/, + ); +}); + +test("strict response parsing rejects duplicate trust-bearing JSON members before semantic validation", () => { + assert.throws( + () => parseGovernedSeparationResponseBody(`{"employment_record_id":"${EMPLOYMENT}","separated_employment_record_version_id":"${VERSION}","recorded_at":"2026-09-13T02:00:00Z","replayed":true,"replayed":false}`), + /duplicate JSON object member name "replayed"/, + ); + assert.throws( + () => parseGovernedSeparationResponseBody('{"error_code":"separation_conflict","\\u0065rror_code":"separation_conflict"}'), + /duplicate JSON object member name "error_code"/, + ); +}); + +test("strict response parsing preserves the published response object", () => { + const body = successBody(false); + assert.deepEqual(parseGovernedSeparationResponseBody(JSON.stringify(body)), body); +}); \ No newline at end of file diff --git a/tests/performance/employment_separation_response_timestamp_precision.test.mjs b/tests/performance/employment_separation_response_timestamp_precision.test.mjs new file mode 100644 index 000000000..f75b8a1db --- /dev/null +++ b/tests/performance/employment_separation_response_timestamp_precision.test.mjs @@ -0,0 +1,36 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import { isGovernedSeparationSuccess } from "./employment_separation_response_contract.mjs"; + +const EMPLOYMENT = "30000000-0000-4000-8000-000000000001"; +const VERSION = "50000000-0000-4000-8000-000000000001"; + +function successBody(recordedAt) { + return { + employment_record_id: EMPLOYMENT, + separated_employment_record_version_id: VERSION, + recorded_at: recordedAt, + replayed: false, + }; +} + +function accepted(recordedAt) { + return isGovernedSeparationSuccess(200, successBody(recordedAt), { + employmentRecordId: EMPLOYMENT, + replayed: false, + }); +} + +test("accepts only recorded_at precision emitted by canonical People datetime serialization", () => { + for (const recordedAt of [ + "2026-09-13T02:00:00Z", + "2026-09-13T02:00:00.1Z", + "2026-09-13T02:00:00.123456Z", + ]) { + assert.equal(accepted(recordedAt), true, recordedAt); + } + + assert.equal(accepted("2026-09-13T02:00:00.1234567Z"), false); + assert.equal(accepted(`2026-09-13T02:00:00.${"1".repeat(17000)}Z`), false); +}); diff --git a/tests/performance/employment_separation_run_contract.mjs b/tests/performance/employment_separation_run_contract.mjs new file mode 100644 index 000000000..69bf08d0f --- /dev/null +++ b/tests/performance/employment_separation_run_contract.mjs @@ -0,0 +1,165 @@ +export const PERFORMANCE_PROFILES = Object.freeze([ + "first_commit", + "replay", + "rejection", + "contention", +]); + +export const PERFORMANCE_SUMMARY_TREND_STATS = Object.freeze([ + "p(50)", + "p(95)", + "p(99)", + "max", + "count", +]); + +export const PERFORMANCE_CLIENT_NETWORK_TOPOLOGY = "direct_no_client_mitm_proxy"; + +const EXEC_BY_PROFILE = Object.freeze({ + first_commit: "firstCommit", + replay: "replay", + rejection: "rejection", + contention: "contention", +}); +const APPROVED_LOAD_BY_PROFILE = Object.freeze({ + first_commit: Object.freeze({ target_rps: 20, duration_seconds: 50, preallocated_vus: 20, max_vus: 80 }), + replay: Object.freeze({ target_rps: 20, duration_seconds: 50, preallocated_vus: 20, max_vus: 80 }), + rejection: Object.freeze({ target_rps: 20, duration_seconds: 50, preallocated_vus: 20, max_vus: 80 }), + contention: Object.freeze({ target_rps: 10, duration_seconds: 10, preallocated_vus: 20, max_vus: 80 }), +}); +const PROXY_ENVIRONMENT_KEYS = Object.freeze([ + "HTTP_PROXY", + "HTTPS_PROXY", + "ALL_PROXY", + "http_proxy", + "https_proxy", + "all_proxy", +]); + +export function requirePerformanceProfile(value) { + if (typeof value !== "string" || !PERFORMANCE_PROFILES.includes(value)) { + throw new Error(`ORGMETRA_PERFORMANCE_PROFILE must be exactly one of: ${PERFORMANCE_PROFILES.join(", ")}`); + } + return value; +} + +function positiveInteger(value, label) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error(`${label} must be a positive safe integer`); + } + return value; +} + +export function approvedPerformanceLoadModel(profile) { + const selectedProfile = requirePerformanceProfile(profile); + const approved = APPROVED_LOAD_BY_PROFILE[selectedProfile]; + return Object.freeze({ + executor: "constant-arrival-rate", + target_rps: approved.target_rps, + duration_seconds: approved.duration_seconds, + preallocated_vus: approved.preallocated_vus, + max_vus: approved.max_vus, + client_network_topology: PERFORMANCE_CLIENT_NETWORK_TOPOLOGY, + }); +} + +export function requireDirectPerformanceClientNetwork(environment) { + if (environment === null || typeof environment !== "object" || Array.isArray(environment)) { + throw new Error("performance client environment must be an object"); + } + const configuredProxy = PROXY_ENVIRONMENT_KEYS.find((key) => ( + typeof environment[key] === "string" && environment[key].trim() !== "" + )); + if (configuredProxy) { + throw new Error(`${configuredProxy} must be unset for commercial timing acceptance`); + } + return PERFORMANCE_CLIENT_NETWORK_TOPOLOGY; +} + +export function requireVerifiedTlsTransport(insecureSkipTlsVerify) { + if (![false, null, undefined].includes(insecureSkipTlsVerify)) { + throw new Error("TLS certificate verification must remain enabled for commercial timing acceptance"); + } + return false; +} + +export function validatePerformanceLoadModel(value, expectedIterations, profile) { + if (value === null || typeof value !== "object" || Array.isArray(value)) { + throw new Error("load_model must be an object"); + } + const expectedKeys = [ + "executor", + "target_rps", + "duration_seconds", + "preallocated_vus", + "max_vus", + "client_network_topology", + ].sort(); + const actualKeys = Object.keys(value).sort(); + if (actualKeys.length !== expectedKeys.length || actualKeys.some((key, index) => key !== expectedKeys[index])) { + throw new Error(`load_model must contain exactly ${expectedKeys.join(", ")}`); + } + if (value.executor !== "constant-arrival-rate") { + throw new Error("load_model.executor must be constant-arrival-rate"); + } + if (value.client_network_topology !== PERFORMANCE_CLIENT_NETWORK_TOPOLOGY) { + throw new Error(`load_model.client_network_topology must be ${PERFORMANCE_CLIENT_NETWORK_TOPOLOGY}`); + } + const iterations = positiveInteger(expectedIterations, "expectedIterations"); + const selectedProfile = requirePerformanceProfile(profile); + const rate = positiveInteger(value.target_rps, "load_model.target_rps"); + const duration = positiveInteger(value.duration_seconds, "load_model.duration_seconds"); + const preAllocated = positiveInteger(value.preallocated_vus, "load_model.preallocated_vus"); + const maximum = positiveInteger(value.max_vus, "load_model.max_vus"); + if (maximum < preAllocated) { + throw new Error("load_model.max_vus must be greater than or equal to load_model.preallocated_vus"); + } + const approved = approvedPerformanceLoadModel(selectedProfile); + for (const field of ["target_rps", "duration_seconds", "preallocated_vus", "max_vus"]) { + if (value[field] !== approved[field]) { + throw new Error(`load_model.${field} must match the approved ${selectedProfile} load model`); + } + } + const scheduledIterations = rate * duration; + if (!Number.isSafeInteger(scheduledIterations) || scheduledIterations !== iterations) { + throw new Error("load_model target_rps * duration_seconds must equal expectedIterations exactly"); + } + return approved; +} + +export function arrivalRateScenarioForPerformanceProfile(profile, { expectedIterations }) { + const selectedProfile = requirePerformanceProfile(profile); + const approved = approvedPerformanceLoadModel(selectedProfile); + const loadModel = validatePerformanceLoadModel(approved, expectedIterations, selectedProfile); + return { + executor: loadModel.executor, + exec: EXEC_BY_PROFILE[selectedProfile], + rate: loadModel.target_rps, + timeUnit: "1s", + duration: `${loadModel.duration_seconds}s`, + preAllocatedVUs: loadModel.preallocated_vus, + maxVUs: loadModel.max_vus, + gracefulStop: "30s", + }; +} + +export function thresholdsForPerformanceProfile(profile, expectedIterations) { + requirePerformanceProfile(profile); + if (!Number.isSafeInteger(expectedIterations) || expectedIterations < 1) { + throw new Error("expectedIterations must be a positive safe integer"); + } + const expectedLatencySamples = profile === "contention" ? expectedIterations * 2 : expectedIterations; + if (!Number.isSafeInteger(expectedLatencySamples)) { + throw new Error("expected latency sample count must be a positive safe integer"); + } + const thresholds = { + employment_separation_unexpected_response: ["rate==0"], + employment_separation_latency_samples: [`count>=${expectedLatencySamples}`], + checks: ["rate==1"], + iterations: [`count>=${expectedIterations}`], + }; + if (profile === "first_commit") { + thresholds.employment_separation_first_commit_duration_ms = ["p(95)<=20"]; + } + return thresholds; +} diff --git a/tests/performance/employment_separation_run_contract.test.mjs b/tests/performance/employment_separation_run_contract.test.mjs new file mode 100644 index 000000000..34a2d8c86 --- /dev/null +++ b/tests/performance/employment_separation_run_contract.test.mjs @@ -0,0 +1,142 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import { + PERFORMANCE_CLIENT_NETWORK_TOPOLOGY, + PERFORMANCE_SUMMARY_TREND_STATS, + approvedPerformanceLoadModel, + arrivalRateScenarioForPerformanceProfile, + requireDirectPerformanceClientNetwork, + requirePerformanceProfile, + requireVerifiedTlsTransport, + thresholdsForPerformanceProfile, + validatePerformanceLoadModel, +} from "./employment_separation_run_contract.mjs"; + +test("requires one explicit performance profile per run", () => { + for (const profile of ["first_commit", "replay", "rejection", "contention"]) { + assert.equal(requirePerformanceProfile(profile), profile); + } + assert.throws(() => requirePerformanceProfile(""), /must be exactly one of/); + assert.throws(() => requirePerformanceProfile("all"), /must be exactly one of/); +}); + +test("uses one version-controlled open arrival-rate model per profile", () => { + assert.deepEqual(approvedPerformanceLoadModel("first_commit"), { + executor: "constant-arrival-rate", + target_rps: 20, + duration_seconds: 50, + preallocated_vus: 20, + max_vus: 80, + client_network_topology: PERFORMANCE_CLIENT_NETWORK_TOPOLOGY, + }); + assert.deepEqual(approvedPerformanceLoadModel("contention"), { + executor: "constant-arrival-rate", + target_rps: 10, + duration_seconds: 10, + preallocated_vus: 20, + max_vus: 80, + client_network_topology: PERFORMANCE_CLIENT_NETWORK_TOPOLOGY, + }); + assert.deepEqual(arrivalRateScenarioForPerformanceProfile("first_commit", { + expectedIterations: 1000, + }), { + executor: "constant-arrival-rate", + exec: "firstCommit", + rate: 20, + timeUnit: "1s", + duration: "50s", + preAllocatedVUs: 20, + maxVUs: 80, + gracefulStop: "30s", + }); +}); + +test("refuses fixture cardinality that does not exactly fit the approved schedule", () => { + assert.throws(() => arrivalRateScenarioForPerformanceProfile("first_commit", { + expectedIterations: 999, + }), /must equal expectedIterations exactly/); + assert.throws(() => arrivalRateScenarioForPerformanceProfile("contention", { + expectedIterations: 99, + }), /must equal expectedIterations exactly/); +}); + +test("binds result evidence to the exact approved profile load model", () => { + const approved = approvedPerformanceLoadModel("first_commit"); + assert.deepEqual(validatePerformanceLoadModel(approved, 1000, "first_commit"), approved); + assert.deepEqual( + validatePerformanceLoadModel(approvedPerformanceLoadModel("replay"), 1000, "replay"), + approvedPerformanceLoadModel("replay"), + ); + assert.deepEqual( + validatePerformanceLoadModel(approvedPerformanceLoadModel("rejection"), 1000, "rejection"), + approvedPerformanceLoadModel("rejection"), + ); + assert.throws(() => validatePerformanceLoadModel({ + ...approved, + target_rps: 1, + duration_seconds: 1000, + preallocated_vus: 1, + max_vus: 1, + }, 1000, "first_commit"), /approved first_commit load model/); + assert.throws(() => validatePerformanceLoadModel({ + ...approved, + executor: "shared-iterations", + }, 1000, "first_commit"), /constant-arrival-rate/); +}); + +test("refuses load-model validation without explicit profile identity", () => { + assert.throws( + () => validatePerformanceLoadModel(approvedPerformanceLoadModel("replay"), 1000), + /profile/i, + ); +}); + +test("fails closed when the k6 client is routed through an ambient proxy", () => { + assert.equal(requireDirectPerformanceClientNetwork({}), PERFORMANCE_CLIENT_NETWORK_TOPOLOGY); + assert.throws( + () => requireDirectPerformanceClientNetwork({ HTTPS_PROXY: "https://proxy.example" }), + /HTTPS_PROXY must be unset/, + ); + assert.throws( + () => requireDirectPerformanceClientNetwork({ all_proxy: "socks5://proxy.example" }), + /all_proxy must be unset/, + ); +}); + +test("fails closed when resolved k6 options disable TLS certificate verification", () => { + for (const safeDefault of [false, null, undefined]) { + assert.equal(requireVerifiedTlsTransport(safeDefault), false); + } + for (const unsafe of [true, "true", 1]) { + assert.throws( + () => requireVerifiedTlsTransport(unsafe), + /TLS certificate verification must remain enabled/, + ); + } +}); + +test("uses exact scheduled completion rather than a zero-sample dropped-iteration threshold", () => { + assert.deepEqual(thresholdsForPerformanceProfile("first_commit", 1000), { + employment_separation_unexpected_response: ["rate==0"], + employment_separation_latency_samples: ["count>=1000"], + checks: ["rate==1"], + iterations: ["count>=1000"], + employment_separation_first_commit_duration_ms: ["p(95)<=20"], + }); + assert.deepEqual(thresholdsForPerformanceProfile("contention", 100), { + employment_separation_unexpected_response: ["rate==0"], + employment_separation_latency_samples: ["count>=200"], + checks: ["rate==1"], + iterations: ["count>=100"], + }); +}); + +test("refuses acceptance thresholds without an exact positive iteration requirement", () => { + assert.throws(() => thresholdsForPerformanceProfile("first_commit", 0), /positive safe integer/); + assert.throws(() => thresholdsForPerformanceProfile("first_commit", 1.5), /positive safe integer/); +}); + +test("requires buyer percentiles plus the exact Trend sample count", () => { + assert.deepEqual(PERFORMANCE_SUMMARY_TREND_STATS, ["p(50)", "p(95)", "p(99)", "max", "count"]); +}); diff --git a/tests/performance/employment_separation_runner_result_digest.mjs b/tests/performance/employment_separation_runner_result_digest.mjs new file mode 100644 index 000000000..cdb85c8b2 --- /dev/null +++ b/tests/performance/employment_separation_runner_result_digest.mjs @@ -0,0 +1,21 @@ +import { createHash } from "node:crypto"; + +const SHA256_PATTERN = /^[0-9a-f]{64}$/; + +function rawBytes(value) { + if (value instanceof Uint8Array) return value; + if (value instanceof ArrayBuffer) return new Uint8Array(value); + throw new Error("performance result must be supplied as raw bytes"); +} + +export function validateRunnerResultDigest(resultArtifact, expectedDigest) { + if (typeof expectedDigest !== "string" || !SHA256_PATTERN.test(expectedDigest)) { + throw new Error("runner result digest must be a SHA-256 digest"); + } + const bytes = rawBytes(resultArtifact); + const actualDigest = createHash("sha256").update(bytes).digest("hex"); + if (actualDigest !== expectedDigest) { + throw new Error("runner result digest does not bind the supplied performance result"); + } + return actualDigest; +} diff --git a/tests/performance/employment_separation_runtime_evidence_artifact.mjs b/tests/performance/employment_separation_runtime_evidence_artifact.mjs new file mode 100644 index 000000000..db565a292 --- /dev/null +++ b/tests/performance/employment_separation_runtime_evidence_artifact.mjs @@ -0,0 +1,40 @@ +import { createHash } from "node:crypto"; +import { TextDecoder } from "node:util"; + +import { parseStrictJsonText } from "./strict_json_artifact.mjs"; + +const MAXIMUM_RUNTIME_EVIDENCE_BYTES = 1024 * 1024; + +function rawBytes(value) { + if (value instanceof Uint8Array) return value; + if (value instanceof ArrayBuffer) return new Uint8Array(value); + throw new Error("runtime evidence must be supplied as raw bytes"); +} + +export function parseRuntimeEvidenceArtifact(value) { + const bytes = rawBytes(value); + if (bytes.byteLength > MAXIMUM_RUNTIME_EVIDENCE_BYTES) { + throw new Error(`runtime evidence must not exceed ${MAXIMUM_RUNTIME_EVIDENCE_BYTES} bytes`); + } + + let text; + try { + text = new TextDecoder("utf-8", { fatal: true }).decode(bytes); + } catch (error) { + throw new Error("runtime evidence must be valid UTF-8", { cause: error }); + } + + if (text.trim() === "") { + throw new Error("runtime evidence must be non-empty JSON text"); + } + + const parsed = parseStrictJsonText(text, "runtime evidence"); + if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) { + throw new Error("runtime evidence must be a JSON object"); + } + + return { + parsed, + sha256: createHash("sha256").update(bytes).digest("hex"), + }; +} diff --git a/tests/performance/employment_separation_runtime_evidence_artifact.test.mjs b/tests/performance/employment_separation_runtime_evidence_artifact.test.mjs new file mode 100644 index 000000000..5cac4eae7 --- /dev/null +++ b/tests/performance/employment_separation_runtime_evidence_artifact.test.mjs @@ -0,0 +1,48 @@ +import assert from "node:assert/strict"; +import { createHash } from "node:crypto"; +import test from "node:test"; + +import { parseRuntimeEvidenceArtifact } from "./employment_separation_runtime_evidence_artifact.mjs"; + +function sha256(bytes) { + return createHash("sha256").update(bytes).digest("hex"); +} + +test("binds acceptance runtime evidence to the exact raw artifact bytes", () => { + const bytes = Buffer.from('{"schema_version":"orgmetra.employment_separation.runtime_evidence.v1"}\n', "utf8"); + const document = parseRuntimeEvidenceArtifact(bytes); + + assert.deepEqual(document.parsed, { + schema_version: "orgmetra.employment_separation.runtime_evidence.v1", + }); + assert.equal(document.sha256, sha256(bytes)); + assert.throws( + () => parseRuntimeEvidenceArtifact(bytes.toString("utf8")), + /runtime evidence must be supplied as raw bytes/, + ); +}); + +test("rejects byte-distinct malformed UTF-8 before lossy JSON interpretation can collapse it", () => { + const prefix = Buffer.from('{"observer_reference":"observer:', "utf8"); + const suffix = Buffer.from('"}', "utf8"); + const first = Buffer.concat([prefix, Buffer.from([0x80]), suffix]); + const second = Buffer.concat([prefix, Buffer.from([0x81]), suffix]); + + assert.equal(first.toString("utf8"), second.toString("utf8")); + assert.notEqual(sha256(first), sha256(second)); + assert.doesNotThrow(() => JSON.parse(first.toString("utf8"))); + assert.doesNotThrow(() => JSON.parse(second.toString("utf8"))); + assert.throws(() => parseRuntimeEvidenceArtifact(first), /runtime evidence must be valid UTF-8/); + assert.throws(() => parseRuntimeEvidenceArtifact(second), /runtime evidence must be valid UTF-8/); +}); + +test("rejects JSON values that are not runtime-evidence objects", () => { + assert.throws( + () => parseRuntimeEvidenceArtifact(Buffer.from("[]\n", "utf8")), + /runtime evidence must be a JSON object/, + ); + assert.throws( + () => parseRuntimeEvidenceArtifact(Buffer.from("null\n", "utf8")), + /runtime evidence must be a JSON object/, + ); +}); diff --git a/tests/performance/employment_separation_timing_contract.mjs b/tests/performance/employment_separation_timing_contract.mjs new file mode 100644 index 000000000..aab93b444 --- /dev/null +++ b/tests/performance/employment_separation_timing_contract.mjs @@ -0,0 +1,27 @@ +function fail(message) { + throw new Error(message); +} + +function finiteNonNegative(value, label) { + if (typeof value !== "number" || !Number.isFinite(value) || value < 0) { + fail(`${label} must be a finite non-negative number`); + } + return value; +} + +export function buyerPathElapsedMs(timings) { + if (timings === null || typeof timings !== "object" || Array.isArray(timings)) { + fail("response.timings must be an object"); + } + + const blocked = finiteNonNegative(timings.blocked, "response.timings.blocked"); + finiteNonNegative(timings.connecting, "response.timings.connecting"); + finiteNonNegative(timings.tls_handshaking, "response.timings.tls_handshaking"); + const duration = finiteNonNegative(timings.duration, "response.timings.duration"); + + // k6 v2.2 computes blocked from GetConn to GotConn, so TCP connect and TLS + // handshake are nested inside blocked for a new direct connection. Duration is + // the later sending + waiting + receiving interval. Adding connecting/TLS again + // would double-count cold-connection latency and could create a false RED. + return blocked + duration; +} diff --git a/tests/performance/employment_separation_timing_contract.test.mjs b/tests/performance/employment_separation_timing_contract.test.mjs new file mode 100644 index 000000000..c548d6b16 --- /dev/null +++ b/tests/performance/employment_separation_timing_contract.test.mjs @@ -0,0 +1,48 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import { buyerPathElapsedMs } from "./employment_separation_timing_contract.mjs"; + +test("counts k6 blocked once because it already spans TCP and TLS acquisition", () => { + assert.equal(buyerPathElapsedMs({ + blocked: 7, + connecting: 2.5, + tls_handshaking: 3, + duration: 14, + }), 21); +}); + +test("preserves keep-alive requests when connection phases are zero", () => { + assert.equal(buyerPathElapsedMs({ + blocked: 0.4, + connecting: 0, + tls_handshaking: 0, + duration: 9.6, + }), 10); +}); + +test("includes cold connection acquisition without double-counting nested TCP and TLS phases", () => { + assert.equal(buyerPathElapsedMs({ + blocked: 10, + connecting: 4, + tls_handshaking: 5, + duration: 10, + }), 20); +}); + +test("rejects missing, negative, or non-finite timing evidence", () => { + for (const timings of [ + null, + [], + {}, + { blocked: 1, connecting: 2, tls_handshaking: 3 }, + { blocked: -1, connecting: 2, tls_handshaking: 3, duration: 10 }, + { blocked: 1, connecting: -1, tls_handshaking: 3, duration: 10 }, + { blocked: 1, connecting: 2, tls_handshaking: -1, duration: 10 }, + { blocked: 1, connecting: 2, tls_handshaking: 3, duration: -1 }, + { blocked: Number.NaN, connecting: 2, tls_handshaking: 3, duration: 10 }, + { blocked: 1, connecting: 2, tls_handshaking: 3, duration: Number.POSITIVE_INFINITY }, + ]) { + assert.throws(() => buyerPathElapsedMs(timings), /timings|finite non-negative/); + } +}); diff --git a/tests/performance/run_employment_separation_benchmark.sh b/tests/performance/run_employment_separation_benchmark.sh new file mode 100755 index 000000000..27630b7cb --- /dev/null +++ b/tests/performance/run_employment_separation_benchmark.sh @@ -0,0 +1,158 @@ +#!/usr/bin/env bash +set -euo pipefail + +readonly PINNED_K6_VERSION="2.2.0" +readonly PINNED_K6_IMAGE="ghcr.io/grafana/k6" +readonly PINNED_K6_IMAGE_DIGEST="sha256:9bd01d6941fca969cb61bb57d2da5ee9b385fe2aa8881df3798c196564d6ace6" +readonly PINNED_K6_RUNNER_IDENTITY="${PINNED_K6_IMAGE}@${PINNED_K6_IMAGE_DIGEST}" +readonly PINNED_K6_CONTAINER_UID="12345" +readonly PINNED_K6_CONTAINER_GID="12345" +readonly MAXIMUM_FIXTURE_ARTIFACT_BYTES="8388608" +readonly WORKLOAD="/workspace/tests/performance/employment_separation_buyer_path.js" + +if (( $# != 0 )); then + printf 'commercial Employment separation benchmark does not accept caller-supplied k6 CLI options\n' >&2 + exit 64 +fi + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd -P)" +target_sha="${ORGMETRA_PERFORMANCE_TARGET_SHA:-}" +if [[ ! "${target_sha}" =~ ^[0-9a-f]{40}$ ]]; then + printf 'ORGMETRA_PERFORMANCE_TARGET_SHA must be the exact lowercase 40-character candidate SHA\n' >&2 + exit 1 +fi +repository_head="$(git -C "${repo_root}" rev-parse --verify HEAD)" +if [[ "${repository_head}" != "${ORGMETRA_PERFORMANCE_TARGET_SHA}" ]]; then + printf 'benchmark checkout HEAD must equal ORGMETRA_PERFORMANCE_TARGET_SHA; head=%s target=%s\n' "${repository_head}" "${ORGMETRA_PERFORMANCE_TARGET_SHA}" >&2 + exit 1 +fi +repository_status="$(git -C "${repo_root}" status --porcelain=v1 --untracked-files=all)" +if [[ -n "${repository_status}" ]]; then + printf 'commercial performance evidence requires an exact clean checkout; modified, staged, or untracked files are present\n' >&2 + exit 1 +fi + +fixture_path="${ORGMETRA_PERFORMANCE_DATA_FILE:-}" +summary_path="${ORGMETRA_PERFORMANCE_SUMMARY_FILE:-}" +if [[ -z "${fixture_path}" || ! -f "${fixture_path}" ]]; then + printf 'ORGMETRA_PERFORMANCE_DATA_FILE must point to the right-cleared fixture\n' >&2 + exit 1 +fi +fixture_path="$(realpath "${fixture_path}")" +fixture_bytes="$(stat --printf='%s' -- "${fixture_path}")" +if [[ ! "${fixture_bytes}" =~ ^[0-9]+$ ]] || (( fixture_bytes > MAXIMUM_FIXTURE_ARTIFACT_BYTES )); then + printf 'performance fixture must not exceed %s bytes\n' "${MAXIMUM_FIXTURE_ARTIFACT_BYTES}" >&2 + exit 1 +fi + +if ! command -v podman >/dev/null 2>&1; then + printf 'podman is required for the pinned commercial k6 runner\n' >&2 + exit 1 +fi +if ! podman image exists "${PINNED_K6_RUNNER_IDENTITY}"; then + printf 'preload the exact pinned k6 image before measurement: %s\n' "${PINNED_K6_RUNNER_IDENTITY}" >&2 + exit 1 +fi +image_user="$(podman image inspect --format '{{.Config.User}}' "${PINNED_K6_RUNNER_IDENTITY}")" +if [[ "${image_user}" != "${PINNED_K6_CONTAINER_UID}" ]]; then + printf 'pinned k6 image must declare container UID %s; observed: %s\n' "${PINNED_K6_CONTAINER_UID}" "${image_user}" >&2 + exit 1 +fi +version_line="$(podman run --rm --pull=never "${PINNED_K6_RUNNER_IDENTITY}" version 2>&1 | head -n 1)" +version_token="$(printf '%s\n' "${version_line}" | awk '{print $2}')" +if [[ "${version_token}" != "v${PINNED_K6_VERSION}" ]]; then + printf 'pinned k6 image must report v%s; observed: %s\n' "${PINNED_K6_VERSION}" "${version_line}" >&2 + exit 1 +fi + +workload_image_id="" +summary_run_dir="" +cleanup() { + if [[ -n "${workload_image_id}" ]]; then + podman image rm --force "${workload_image_id}" >/dev/null 2>&1 || true + fi + if [[ -n "${summary_run_dir}" ]]; then + rm -rf -- "${summary_run_dir}" + fi +} +trap cleanup EXIT +workload_image_id="$( + git -C "${repo_root}" archive --format=tar "${target_sha}" \ + | podman import --quiet --message "Orgmetra Employment separation benchmark ${target_sha}" - +)" +if [[ -z "${workload_image_id}" ]] || ! podman image exists "${workload_image_id}"; then + printf 'failed to materialize immutable benchmark workload image for %s\n' "${target_sha}" >&2 + exit 1 +fi + +if [[ -z "${summary_path}" ]]; then + printf 'ORGMETRA_PERFORMANCE_SUMMARY_FILE is required\n' >&2 + exit 1 +fi +summary_dir="$(realpath -m "$(dirname "${summary_path}")")" +summary_name="$(basename "${summary_path}")" +mkdir -p "${summary_dir}" +summary_target="${summary_dir}/${summary_name}" +if [[ -e "${summary_target}" || -L "${summary_target}" ]]; then + printf 'ORGMETRA_PERFORMANCE_SUMMARY_FILE must not already exist; refusing stale-result reuse: %s\n' "${summary_target}" >&2 + exit 1 +fi +summary_run_dir="$(mktemp -d "${summary_dir}/.orgmetra-employment-separation-performance.XXXXXX")" +summary_run_file="${summary_run_dir}/${summary_name}" + +export ORGMETRA_PERFORMANCE_K6_VERSION="${PINNED_K6_VERSION}" +export ORGMETRA_PERFORMANCE_K6_IMAGE="${PINNED_K6_IMAGE}" +export ORGMETRA_PERFORMANCE_K6_IMAGE_DIGEST="${PINNED_K6_IMAGE_DIGEST}" +export ORGMETRA_PERFORMANCE_K6_RUNNER_IDENTITY="${PINNED_K6_RUNNER_IDENTITY}" + +podman run --rm --pull=never --network=host --read-only \ + --user="${PINNED_K6_CONTAINER_UID}:${PINNED_K6_CONTAINER_GID}" \ + --userns="keep-id:uid=${PINNED_K6_CONTAINER_UID},gid=${PINNED_K6_CONTAINER_GID}" \ + --cap-drop=ALL --security-opt=no-new-privileges --pids-limit=256 \ + --tmpfs /tmp:rw,nosuid,nodev,noexec \ + --mount "type=image,source=${workload_image_id},destination=/workspace" \ + --volume "${fixture_path}:/evidence/fixture.json:ro" \ + --volume "${summary_run_dir}:/output:rw" \ + --workdir /workspace \ + --env ORGMETRA_PERFORMANCE_BASE_URL \ + --env ORGMETRA_PERFORMANCE_BEARER_TOKEN \ + --env ORGMETRA_PERFORMANCE_TARGET_SHA \ + --env ORGMETRA_PERFORMANCE_PROFILE \ + --env ORGMETRA_PERFORMANCE_K6_VERSION \ + --env ORGMETRA_PERFORMANCE_K6_IMAGE \ + --env ORGMETRA_PERFORMANCE_K6_IMAGE_DIGEST \ + --env ORGMETRA_PERFORMANCE_K6_RUNNER_IDENTITY \ + --env ORGMETRA_PERFORMANCE_DATA_FILE=/evidence/fixture.json \ + --env "ORGMETRA_PERFORMANCE_SUMMARY_FILE=/output/${summary_name}" \ + "${PINNED_K6_RUNNER_IDENTITY}" run "${WORKLOAD}" + +if [[ ! -f "${summary_run_file}" || -L "${summary_run_file}" || ! -s "${summary_run_file}" ]]; then + printf 'successful k6 execution did not produce one non-empty regular summary artifact\n' >&2 + exit 1 +fi +summary_source_identity="$(stat --printf='%d:%i:%s' -- "${summary_run_file}")" +summary_source_digest="$(sha256sum -- "${summary_run_file}" | awk '{print $1}')" +if ! ln "${summary_run_file}" "${summary_target}"; then + printf 'failed to publish benchmark summary without clobbering an existing artifact: %s\n' "${summary_target}" >&2 + exit 1 +fi +summary_source_identity_after="$(stat --printf='%d:%i:%s' -- "${summary_run_file}" 2>/dev/null || true)" +summary_source_digest_after="$(sha256sum -- "${summary_run_file}" 2>/dev/null | awk '{print $1}' || true)" +summary_target_identity="$(stat --printf='%d:%i:%s' -- "${summary_target}" 2>/dev/null || true)" +summary_target_digest="$(sha256sum -- "${summary_target}" 2>/dev/null | awk '{print $1}' || true)" +if [[ -z "${summary_source_identity_after}" || -z "${summary_source_digest_after}" || -z "${summary_target_identity}" || -z "${summary_target_digest}" \ + || "${summary_source_identity}" != "${summary_source_identity_after}" \ + || "${summary_source_identity}" != "${summary_target_identity}" \ + || "${summary_source_digest}" != "${summary_source_digest_after}" \ + || "${summary_source_digest}" != "${summary_target_digest}" ]]; then + rm -f -- "${summary_target}" + printf 'benchmark summary changed during publication; refusing unbound result evidence\n' >&2 + exit 1 +fi + +# This digest is structural evidence only. The caller-visible pathname and any +# value a caller can copy from stdout remain under the same authority. Commercial +# acceptance stays fail closed until the organization-owned authenticated +# attestation boundary tracked by ContextualWisdomLab/.github#2162 binds these +# exact result bytes independently. +printf 'ORGMETRA_PERFORMANCE_RESULT_SHA256=%s\n' "${summary_source_digest}" diff --git a/tests/performance/run_employment_separation_benchmark_fixture_budget.test.mjs b/tests/performance/run_employment_separation_benchmark_fixture_budget.test.mjs new file mode 100644 index 000000000..6edd5126b --- /dev/null +++ b/tests/performance/run_employment_separation_benchmark_fixture_budget.test.mjs @@ -0,0 +1,51 @@ +import assert from "node:assert/strict"; +import { mkdtempSync, rmSync, truncateSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { spawnSync } from "node:child_process"; +import { fileURLToPath } from "node:url"; +import test from "node:test"; + +const runner = fileURLToPath(new URL("./run_employment_separation_benchmark.sh", import.meta.url)); +const MAXIMUM_FIXTURE_ARTIFACT_BYTES = 8 * 1024 * 1024; + +function repositoryHead() { + const result = spawnSync("git", ["rev-parse", "--verify", "HEAD"], { + cwd: fileURLToPath(new URL("../..", import.meta.url)), + encoding: "utf8", + }); + assert.equal(result.status, 0, result.stderr); + return result.stdout.trim(); +} + +test("runner rejects an oversized fixture before Podman availability or image checks", () => { + const directory = mkdtempSync(join(tmpdir(), "orgmetra-perf-fixture-budget-")); + try { + const fixture = join(directory, "fixture.json"); + const summary = join(directory, "summary.json"); + truncateSync(fixture, MAXIMUM_FIXTURE_ARTIFACT_BYTES + 1); + + const result = spawnSync("bash", [runner], { + cwd: fileURLToPath(new URL("../..", import.meta.url)), + encoding: "utf8", + env: { + ...process.env, + ORGMETRA_PERFORMANCE_TARGET_SHA: repositoryHead(), + ORGMETRA_PERFORMANCE_DATA_FILE: fixture, + ORGMETRA_PERFORMANCE_SUMMARY_FILE: summary, + ORGMETRA_PERFORMANCE_BASE_URL: "http://127.0.0.1:1", + ORGMETRA_PERFORMANCE_BEARER_TOKEN: "test-only-not-a-real-secret", + ORGMETRA_PERFORMANCE_PROFILE: "first_commit", + }, + }); + + assert.notEqual(result.status, 0); + assert.match( + result.stderr, + /performance fixture must not exceed 8388608 bytes/, + ); + assert.doesNotMatch(result.stderr, /podman is required|preload the exact pinned k6 image/); + } finally { + rmSync(directory, { recursive: true, force: true }); + } +}); diff --git a/tests/performance/strict_json_artifact.mjs b/tests/performance/strict_json_artifact.mjs new file mode 100644 index 000000000..b89bfc623 --- /dev/null +++ b/tests/performance/strict_json_artifact.mjs @@ -0,0 +1,112 @@ +const MAX_JSON_NESTING_DEPTH = 64; + +function invalidJson(label, cause) { + if (cause === undefined) return new Error(`${label} must be valid JSON`); + return new Error(`${label} must be valid JSON`, { cause }); +} + +function skipWhitespace(text, start) { + let index = start; + while (index < text.length && /[\u0009\u000a\u000d\u0020]/u.test(text[index])) index += 1; + return index; +} + +function scanString(text, start, label) { + if (text[start] !== '"') throw invalidJson(label); + let index = start + 1; + while (index < text.length) { + const character = text[index]; + if (character === '"') { + const raw = text.slice(start, index + 1); + try { + return { end: index + 1, value: JSON.parse(raw) }; + } catch (error) { + throw invalidJson(label, error); + } + } + if (character === "\\") { + index += 1; + if (index >= text.length) throw invalidJson(label); + if (text[index] === "u") { + const escape = text.slice(index + 1, index + 5); + if (!/^[0-9a-fA-F]{4}$/u.test(escape)) throw invalidJson(label); + index += 5; + continue; + } + if (!['"', "\\", "/", "b", "f", "n", "r", "t"].includes(text[index])) { + throw invalidJson(label); + } + index += 1; + continue; + } + if (character.charCodeAt(0) < 0x20) throw invalidJson(label); + index += 1; + } + throw invalidJson(label); +} + +function scanPrimitive(text, start, label) { + let index = start; + while (index < text.length && !/[\u0009\u000a\u000d\u0020,\]}]/u.test(text[index])) index += 1; + if (index === start) throw invalidJson(label); + return index; +} + +function scanArray(text, start, label, depth) { + let index = skipWhitespace(text, start + 1); + if (text[index] === "]") return index + 1; + while (index < text.length) { + index = scanValue(text, index, label, depth); + index = skipWhitespace(text, index); + if (text[index] === "]") return index + 1; + if (text[index] !== ",") throw invalidJson(label); + index = skipWhitespace(text, index + 1); + } + throw invalidJson(label); +} + +function scanObject(text, start, label, depth) { + const names = new Set(); + let index = skipWhitespace(text, start + 1); + if (text[index] === "}") return index + 1; + while (index < text.length) { + const member = scanString(text, index, label); + if (names.has(member.value)) { + throw new Error(`${label} must not contain duplicate JSON object member name ${JSON.stringify(member.value)}`); + } + names.add(member.value); + index = skipWhitespace(text, member.end); + if (text[index] !== ":") throw invalidJson(label); + index = scanValue(text, skipWhitespace(text, index + 1), label, depth); + index = skipWhitespace(text, index); + if (text[index] === "}") return index + 1; + if (text[index] !== ",") throw invalidJson(label); + index = skipWhitespace(text, index + 1); + } + throw invalidJson(label); +} + +function scanValue(text, start, label, depth) { + const index = skipWhitespace(text, start); + if (index >= text.length) throw invalidJson(label); + if (text[index] === "{" || text[index] === "[") { + if (depth >= MAX_JSON_NESTING_DEPTH) { + throw new Error(`${label} exceeds maximum JSON nesting depth ${MAX_JSON_NESTING_DEPTH}`); + } + if (text[index] === "{") return scanObject(text, index, label, depth + 1); + return scanArray(text, index, label, depth + 1); + } + if (text[index] === '"') return scanString(text, index, label).end; + return scanPrimitive(text, index, label); +} + +export function parseStrictJsonText(text, label) { + if (typeof text !== "string") throw new TypeError(`${label} must be JSON text`); + const end = skipWhitespace(text, scanValue(text, 0, label, 0)); + if (end !== text.length) throw invalidJson(label); + try { + return JSON.parse(text); + } catch (error) { + throw invalidJson(label, error); + } +} diff --git a/tests/performance/strict_json_artifact.test.mjs b/tests/performance/strict_json_artifact.test.mjs new file mode 100644 index 000000000..1eff6b081 --- /dev/null +++ b/tests/performance/strict_json_artifact.test.mjs @@ -0,0 +1,19 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import { parseStrictJsonText } from "./strict_json_artifact.mjs"; + +function nestedArray(depth) { + return `${"[".repeat(depth)}0${"]".repeat(depth)}`; +} + +test("accepts JSON evidence at the declared nesting boundary", () => { + assert.doesNotThrow(() => parseStrictJsonText(nestedArray(64), "evidence")); +}); + +test("rejects JSON evidence beyond the declared nesting boundary", () => { + assert.throws( + () => parseStrictJsonText(nestedArray(65), "evidence"), + /maximum JSON nesting depth 64/, + ); +});