From cc0b7d3b64a1d5127dbe1f6641cc9bf5470ef470 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sat, 5 Sep 2026 19:10:58 +0300 Subject: [PATCH] =?UTF-8?q?test(runner):=20expectError=20=D0=B1=D0=BE?= =?UTF-8?q?=D0=BB=D1=8C=D1=88=D0=B5=20=D0=BD=D0=B5=20=D0=BE=D1=82=D0=BA?= =?UTF-8?q?=D0=BB=D1=8E=D1=87=D0=B0=D0=B5=D1=82=20=D1=84=D0=B0=D0=B9=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=D1=8B=D0=B5=20=D0=BE=D0=B6=D0=B8=D0=B4=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Кейс с expectError и fileContains/fileNotContains/filesEqual/preserves проходил при любом содержимом файла — проверки лежали под !expectError вместе со снэпшотом. Под !expectError остались только снэпшот и идемпотентность: эталон с аварийного состояния снимать нельзя, а файлы, написанные до отказа, проверять нужно. Заодно во втором кодовом пути раннера не было filesEqual — пути выровнены. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_013MQkqXxErBepYmoUycfkbn --- tests/skills/runner.mjs | 85 ++++++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 35 deletions(-) diff --git a/tests/skills/runner.mjs b/tests/skills/runner.mjs index c566300d3..594837b18 100644 --- a/tests/skills/runner.mjs +++ b/tests/skills/runner.mjs @@ -958,27 +958,32 @@ async function runCaseAsync(testCase, opts) { } } } + // Файловые ожидания проверяются И У НЕГАТИВНОГО кейса: навык мог отказать, но до + // отказа что-то написать — именно это и проверяется. Под `!expectError` остаются только + // снэпшот и идемпотентность: эталон с аварийного состояния снимать нельзя. Раньше + // всё лежало под `!expectError`, и кейс с expectError + fileContains молча проходил + // при любом содержимом файла (тот же класс, что когда-то был со stdout). + if (caseData.expect?.preserves) { + const specs = Array.isArray(caseData.expect.preserves) + ? caseData.expect.preserves : [caseData.expect.preserves]; + for (const spec of specs) errors.push(...checkPreserves(workDir, spec)); + } + if (caseData.expect?.filesEqual) { + const specs = Array.isArray(caseData.expect.filesEqual) + ? caseData.expect.filesEqual : [caseData.expect.filesEqual]; + for (const spec of specs) errors.push(...checkFilesEqual(workDir, spec)); + } + if (caseData.expect?.fileContains) { + const specs = Array.isArray(caseData.expect.fileContains) + ? caseData.expect.fileContains : [caseData.expect.fileContains]; + for (const spec of specs) errors.push(...checkFileContains(workDir, spec, true)); + } + if (caseData.expect?.fileNotContains) { + const specs = Array.isArray(caseData.expect.fileNotContains) + ? caseData.expect.fileNotContains : [caseData.expect.fileNotContains]; + for (const spec of specs) errors.push(...checkFileContains(workDir, spec, false)); + } if (!caseData.expectError) { - if (caseData.expect?.preserves) { - const specs = Array.isArray(caseData.expect.preserves) - ? caseData.expect.preserves : [caseData.expect.preserves]; - for (const spec of specs) errors.push(...checkPreserves(workDir, spec)); - } - if (caseData.expect?.filesEqual) { - const specs = Array.isArray(caseData.expect.filesEqual) - ? caseData.expect.filesEqual : [caseData.expect.filesEqual]; - for (const spec of specs) errors.push(...checkFilesEqual(workDir, spec)); - } - if (caseData.expect?.fileContains) { - const specs = Array.isArray(caseData.expect.fileContains) - ? caseData.expect.fileContains : [caseData.expect.fileContains]; - for (const spec of specs) errors.push(...checkFileContains(workDir, spec, true)); - } - if (caseData.expect?.fileNotContains) { - const specs = Array.isArray(caseData.expect.fileNotContains) - ? caseData.expect.fileNotContains : [caseData.expect.fileNotContains]; - for (const spec of specs) errors.push(...checkFileContains(workDir, spec, false)); - } if (errors.length === 0 && !caseData.expectError && !workspace.readOnly) { const snapshotConfig = { ...skillConfig.snapshot, runtime: opts.runtime }; if (opts.updateSnapshots) { @@ -1197,22 +1202,32 @@ function runCase(testCase, opts) { } } + // Файловые ожидания проверяются И У НЕГАТИВНОГО кейса: навык мог отказать, но до + // отказа что-то написать — именно это и проверяется. Под `!expectError` остаются только + // снэпшот и идемпотентность: эталон с аварийного состояния снимать нельзя. Раньше + // всё лежало под `!expectError`, и кейс с expectError + fileContains молча проходил + // при любом содержимом файла (тот же класс, что когда-то был со stdout). + if (caseData.expect?.preserves) { + const specs = Array.isArray(caseData.expect.preserves) + ? caseData.expect.preserves : [caseData.expect.preserves]; + for (const spec of specs) errors.push(...checkPreserves(workDir, spec)); + } + if (caseData.expect?.filesEqual) { + const specs = Array.isArray(caseData.expect.filesEqual) + ? caseData.expect.filesEqual : [caseData.expect.filesEqual]; + for (const spec of specs) errors.push(...checkFilesEqual(workDir, spec)); + } + if (caseData.expect?.fileContains) { + const specs = Array.isArray(caseData.expect.fileContains) + ? caseData.expect.fileContains : [caseData.expect.fileContains]; + for (const spec of specs) errors.push(...checkFileContains(workDir, spec, true)); + } + if (caseData.expect?.fileNotContains) { + const specs = Array.isArray(caseData.expect.fileNotContains) + ? caseData.expect.fileNotContains : [caseData.expect.fileNotContains]; + for (const spec of specs) errors.push(...checkFileContains(workDir, spec, false)); + } if (!caseData.expectError) { - if (caseData.expect?.preserves) { - const specs = Array.isArray(caseData.expect.preserves) - ? caseData.expect.preserves : [caseData.expect.preserves]; - for (const spec of specs) errors.push(...checkPreserves(workDir, spec)); - } - if (caseData.expect?.fileContains) { - const specs = Array.isArray(caseData.expect.fileContains) - ? caseData.expect.fileContains : [caseData.expect.fileContains]; - for (const spec of specs) errors.push(...checkFileContains(workDir, spec, true)); - } - if (caseData.expect?.fileNotContains) { - const specs = Array.isArray(caseData.expect.fileNotContains) - ? caseData.expect.fileNotContains : [caseData.expect.fileNotContains]; - for (const spec of specs) errors.push(...checkFileContains(workDir, spec, false)); - } // Snapshot comparison (skip for external/read-only workspaces) if (errors.length === 0 && !caseData.expectError && !workspace.readOnly) {