diff --git a/docs/eve-migration-roadmap.md b/docs/eve-migration-roadmap.md index 54e1037..f133941 100644 --- a/docs/eve-migration-roadmap.md +++ b/docs/eve-migration-roadmap.md @@ -280,6 +280,13 @@ Exit criteria: Recently cut: +- Provider owner conformance consumption now verifies the interactive-world + contract instead of only provider presence. The generic conformance consumer + supports `--expect-provider-surface`, `--expect-provider-command`, and + `--expect-provider-receipt-state`; the export now carries provider receipt + states; and Aetheria's consumer smoke asserts the daemon game/editor surfaces, + daemon command boundary, and accepted/reconciled receipts from the exported + provider pack. - Plugin owner conformance consumption now has a smoke runner at `scripts/run-plugin-owner-conformance-consumer-smoke.ps1`. The generic conformance consumer can assert plugin ABI operations and capabilities with diff --git a/scripts/run-aetheria-conformance-consumer-smoke.ps1 b/scripts/run-aetheria-conformance-consumer-smoke.ps1 index c971589..1a98e2d 100644 --- a/scripts/run-aetheria-conformance-consumer-smoke.ps1 +++ b/scripts/run-aetheria-conformance-consumer-smoke.ps1 @@ -40,7 +40,15 @@ try { --expect-pack provider ` --expect-fixture aetheria-world ` --expect-provider aetheria ` - --expect-scenario aetheria-world-command-replay + --expect-scenario aetheria-world-command-replay ` + --expect-provider-surface aetheria:aetheria.daemon.game ` + --expect-provider-surface aetheria:aetheria.daemon.editor ` + --expect-provider-command aetheria:aetheria.daemon.commands ` + --expect-provider-receipt-state aetheria:accepted ` + --expect-provider-receipt-state aetheria:reconciled + if ($LASTEXITCODE -ne 0) { + throw "Aetheria conformance consumer smoke failed with exit code $LASTEXITCODE" + } } finally { Pop-Location } diff --git a/tools/conformance/consume-export.mjs b/tools/conformance/consume-export.mjs index f4c2ef1..836b47c 100644 --- a/tools/conformance/consume-export.mjs +++ b/tools/conformance/consume-export.mjs @@ -18,6 +18,9 @@ if (!exportDirectory) { " --expect-split-target ", " --expect-plugin-operation ", " --expect-plugin-capability ", + " --expect-provider-surface ", + " --expect-provider-command ", + " --expect-provider-receipt-state ", ].join("\n")); process.exit(2); } @@ -120,6 +123,36 @@ function validateIndex(index, directory, expectations, errors) { for (const expectedProvider of expectations.providers) { if (!providers.some(provider => provider.providerId === expectedProvider)) errors.push(`providers:${expectedProvider}:missing`); } + for (const expectation of expectations.providerSurfaces) { + const provider = providers.find(candidate => candidate.providerId === expectation.providerId); + if (!provider) { + errors.push(`providers:${expectation.providerId}:missing`); + continue; + } + if (!Array.isArray(provider.surfaces) || !provider.surfaces.includes(expectation.surfaceId)) { + errors.push(`providers:${expectation.providerId}:surface:${expectation.surfaceId}:missing`); + } + } + for (const expectation of expectations.providerCommands) { + const provider = providers.find(candidate => candidate.providerId === expectation.providerId); + if (!provider) { + errors.push(`providers:${expectation.providerId}:missing`); + continue; + } + if (!Array.isArray(provider.commands) || !provider.commands.includes(expectation.command)) { + errors.push(`providers:${expectation.providerId}:command:${expectation.command}:missing`); + } + } + for (const expectation of expectations.providerReceiptStates) { + const provider = providers.find(candidate => candidate.providerId === expectation.providerId); + if (!provider) { + errors.push(`providers:${expectation.providerId}:missing`); + continue; + } + if (!Array.isArray(provider.receiptStates) || !provider.receiptStates.includes(expectation.state)) { + errors.push(`providers:${expectation.providerId}:receiptState:${expectation.state}:missing`); + } + } for (const expectedRuntime of expectations.runtimes) { if (!runtimes.some(runtime => runtime.runtimeId === expectedRuntime)) errors.push(`runtimes:${expectedRuntime}:missing`); } @@ -157,6 +190,9 @@ function parseArguments(args) { pluginOperations: [], pluginCapabilities: [], providers: [], + providerSurfaces: [], + providerCommands: [], + providerReceiptStates: [], runtimes: [], scenarios: [], splitTargets: [], @@ -171,6 +207,9 @@ function parseArguments(args) { ["--expect-split-target", expectations.splitTargets], ["--expect-plugin-operation", expectations.pluginOperations], ["--expect-plugin-capability", expectations.pluginCapabilities], + ["--expect-provider-surface", expectations.providerSurfaces], + ["--expect-provider-command", expectations.providerCommands], + ["--expect-provider-receipt-state", expectations.providerReceiptStates], ]); for (let index = 1; index < args.length; index += 1) { @@ -189,6 +228,12 @@ function parseArguments(args) { target.push(parsePluginExpectation(value, "operation")); } else if (option === "--expect-plugin-capability") { target.push(parsePluginExpectation(value, "capability")); + } else if (option === "--expect-provider-surface") { + target.push(parseProviderExpectation(value, "surfaceId")); + } else if (option === "--expect-provider-command") { + target.push(parseProviderExpectation(value, "command")); + } else if (option === "--expect-provider-receipt-state") { + target.push(parseProviderExpectation(value, "state")); } else { target.push(value); } @@ -209,3 +254,15 @@ function parsePluginExpectation(value, field) { [field]: value.slice(separator + 1), }; } + +function parseProviderExpectation(value, field) { + const separator = value.indexOf(":"); + if (separator <= 0 || separator === value.length - 1) { + console.error(`Expected provider ${field} in form, got: ${value}`); + process.exit(2); + } + return { + providerId: value.slice(0, separator), + [field]: value.slice(separator + 1), + }; +} diff --git a/tools/parity/run-parity.mjs b/tools/parity/run-parity.mjs index 01244e0..0630c9f 100644 --- a/tools/parity/run-parity.mjs +++ b/tools/parity/run-parity.mjs @@ -1357,6 +1357,7 @@ function buildConformanceExport(report) { advertisementPath: provider.advertisementPath, scenarioPath: provider.scenarioPath, scenarioId: provider.scenarioId, + receiptStates: provider.scenarioReceiptStates || [], surfaces: provider.surfaceIds, commands: provider.commandIds, pluginRequirements: provider.pluginRequirements,