import test from 'node:test' import assert from 'node:assert/strict' import { buildGrayEvidenceRemoteScript, createGrayEvidenceReaders, validateGrayEvidenceConfig } from './gray-regression-evidence.mjs' const env = { JENKINS_URL: 'https://jenkins.example.invalid/', JENKINS_USER: 'reader', JENKINS_API_TOKEN: 'private-test-token' } const probeSql = "SET NAMES utf8mb4 COLLATE utf8mb4_bin;\nSET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;\nSTART TRANSACTION WITH CONSISTENT SNAPSHOT, READ ONLY;\nSELECT JSON_OBJECT('grayProbe',1);\nCOMMIT;" test('gray reader refuses missing credentials, embedded URL credentials and SQL mutations', () => { assert.throws(() => validateGrayEvidenceConfig({}), /Missing/) assert.throws(() => validateGrayEvidenceConfig({ ...env, JENKINS_URL: 'https://secret@host.invalid' }), /Invalid/) assert.throws(() => buildGrayEvidenceRemoteScript('auth', probeSql), /target/) assert.throws(() => buildGrayEvidenceRemoteScript('order', probeSql.replace('SELECT JSON_OBJECT', 'DELETE JSON_OBJECT')), /Read-only/) }) test('gray SSH reader pins registered production host, known hosts, runtime and database credentials', () => { const script = buildGrayEvidenceRemoteScript('order', probeSql) for (const credential of ['ssh', 'host', 'known-hosts', 'env']) assert.ok(script.includes(`firstleg-production-${credential}`)) assert.ok(script.includes('StrictHostKeyChecking=yes')) assert.ok(script.includes('finally {dir.listFiles().each{it.delete()};dir.delete()}')) assert.ok(!script.includes('java-test-agent') && !script.includes('RemotingDiagnostics')) const encodedPython = [...script.matchAll(/\['([A-Za-z0-9+/=]+)'(?:,'[A-Za-z0-9+/=]+')*\]\.join\(''\)/g)] .map(match => [...match[0].matchAll(/'([A-Za-z0-9+/=]+)'/g)].map(part => part[1]).join('')) const python = encodedPython.map(value => Buffer.from(value, 'base64').toString()).find(value => value.includes('subprocess')) assert.ok(python?.includes("labels.get('com.docker.compose.project')!='firstleg-production'")) assert.ok(python.includes("e.get(key)!=expected[key]")) assert.ok(python.includes("u.path!='/'+ 'baiyue_order'")) assert.ok(python.includes('READ ONLY') === false, 'SQL is encoded independently') const lastleg = buildGrayEvidenceRemoteScript('lastleg', probeSql) const payload = [...lastleg.matchAll(/\['([A-Za-z0-9+/=]+)'(?:,'[A-Za-z0-9+/=]+')*\]\.join\(''\)/g)] .map(match => [...match[0].matchAll(/'([A-Za-z0-9+/=]+)'/g)].map(part => part[1]).join('')) .map(value => Buffer.from(value, 'base64').toString()).find(value => value.includes('subprocess')) assert.ok(payload.includes("labels.get('com.docker.compose.service')!='lastleg-service'")) }) test('preflight performs only two scoped gray probes and never exposes remote raw errors', async () => { const requests = [] const readers = createGrayEvidenceReaders(env, async (url, options) => { requests.push({ url: String(url), script: options.body.get('script') }) return { ok: true, text: async () => 'GRAY_EVIDENCE_JSON={"ok":true,"rows":[{"grayProbe":1}]}\n' } }) await readers.preflight() assert.equal(requests.length, 2) assert.ok(requests.every(request => request.url === 'https://jenkins.example.invalid/scriptText')) const failed = createGrayEvidenceReaders(env, async () => ({ ok: true, text: async () => 'MYSQL_PWD=hidden\nTraceback secret-value' })) await assert.rejects(failed.preflight(), error => error.message === 'Gray evidence JSON missing or ambiguous') }) test('preflight rejects failed probes and ambiguous results', async () => { for (const raw of ['GRAY_EVIDENCE_JSON={"ok":true,"rows":[{"grayProbe":0}]}', 'GRAY_EVIDENCE_JSON={"ok":false,"error":"MYSQL_1146"}', 'GRAY_EVIDENCE_JSON={}\nGRAY_EVIDENCE_JSON={}']) { const readers = createGrayEvidenceReaders(env, async () => ({ ok: true, text: async () => raw })) await assert.rejects(readers.preflight()) } const sourceFailure = createGrayEvidenceReaders(env, async () => ({ ok: true, text: async () => 'GRAY_EVIDENCE_JSON={"ok":false,"error":"GRAY_DATABASE_SOURCE_FAILED"}' })) await assert.rejects(sourceFailure.preflight(), { message: 'GRAY_DATABASE_SOURCE_FAILED' }) })