import test from 'node:test' import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' import { dirname, resolve } from 'node:path' import { fileURLToPath } from 'node:url' const root = resolve(dirname(fileURLToPath(import.meta.url)), '..') const read = path => readFileSync(resolve(root, path), 'utf8') test('plugin registry api exposes status and immutable publication operations', () => { const api = read('src/api/generator/plugin.js') for (const name of [ 'getPluginRegistryStatus', 'probePluginSqlTarget', 'getPluginDeliveryVerification', 'createPluginDeliveryRehearsal', 'listPluginDeliveryRehearsals', 'getPluginDeliveryRehearsal', 'startPluginDeliveryRehearsal', 'listPluginDeliveryRehearsalAttempts', 'getPluginDeliveryRehearsalAttempt', 'abortPluginDeliveryRehearsalAttempt', 'reconcilePluginDeliveryRehearsalAttempt', 'comparePluginDeliveryRehearsalAttempts', 'getPluginDeliveryReleaseGate', 'getPluginRegistryReadiness', 'bootstrapPluginRegistry', 'listPlugins', 'getPlugin', 'addPlugin', 'updatePlugin', 'createPluginVersion', 'publishPluginVersion', 'rollbackPluginVersion', 'preflightPluginTransition', 'executePluginTransition', 'retryPluginTransition', 'compensatePluginTransition', 'requestPluginTransitionApproval', 'listPluginTransitionApprovals', 'approvePluginTransitionApproval', 'revokePluginTransitionApproval', 'getPluginTransitionExecutions', 'reconcilePluginTransitionExecution' ]) { assert.equal(api.includes(`export function ${name}`), true, `${name} should be exported`) } assert.match(api, /plugin\/registry-status/) assert.match(api, /plugin\/sql-target\/probe/) assert.match(api, /plugin\/delivery-verification/) assert.match(api, /plugin\/delivery-rehearsals/) assert.match(api, /delivery-rehearsals\/\$\{rehearsalId\}/) assert.match(api, /delivery-rehearsals\/\$\{rehearsalId\}\/attempts/) assert.match(api, /attempts\/\$\{attemptId\}\/abort/) assert.match(api, /attempts\/\$\{attemptId\}\/reconcile/) assert.match(api, /attempt-comparison/) assert.match(api, /release-gate/) assert.match(api, /baselineAttemptId, candidateAttemptId/) assert.match(api, /sql-target\/probe', method: 'post'/) assert.match(api, /plugin\/readiness/) assert.match(api, /plugin\/bootstrap/) assert.match(api, /versions\/\$\{pluginVersionId\}\/publish/) assert.match(api, /versions\/\$\{pluginVersionId\}\/rollback/) assert.match(api, /transitions\/\$\{transitionId\}\/preflight/) assert.match(api, /params: \{ executionMode \}/) assert.match(api, /transitions\/\$\{transitionId\}\/execute/) assert.match(api, /transitions\/\$\{transitionId\}\/retry/) assert.match(api, /transitions\/\$\{transitionId\}\/compensate/) assert.match(api, /executePluginTransition\(pluginId, transitionId, data\)/) assert.match(api, /retryPluginTransition\(pluginId, transitionId, data\)/) assert.match(api, /compensatePluginTransition\(pluginId, transitionId, data\)/) assert.match(api, /transitions\/\$\{transitionId\}\/approvals`/) assert.match(api, /approvals\/\$\{approvalId\}\/approve/) assert.match(api, /approvals\/\$\{approvalId\}\/revoke/) assert.match(api, /executions\/\$\{executionId\}\/reconcile/) }) test('plugin registry view manages installed definitions and release lifecycle', () => { const view = read('src/views/generator/plugin/index.vue') assert.match(view, /name: 'PluginRegistry'/) assert.match(view, /registryStatus\.databaseEnabled/) assert.match(view, /registryReadiness\.readyReleaseCount/) assert.match(view, /bootstrapPluginRegistry/) assert.match(view, /readinessStateLabel/) assert.match(view, /contributionReversible/) assert.match(view, /IRREVERSIBLE_CONTRIBUTION/) assert.match(view, /installedManifests/) assert.match(view, /createPluginVersion/) assert.match(view, /publishPluginVersion/) assert.match(view, /rollbackPluginVersion/) assert.match(view, /detail\.transitions/) assert.match(view, /transitionOperationLabel/) assert.match(view, /inspectTransitionPlan/) assert.match(view, /transitionFingerprint/) assert.match(view, /latestExecutionStatus/) assert.match(view, /runTransition/) assert.match(view, /openExecutionHistory/) assert.match(view, /executionDialogOpen/) assert.match(view, /reconcilePluginTransitionExecution/) assert.match(view, /reconcileExecution/) assert.match(view, /outbox\.status/) assert.match(view, /deliveryCount/) assert.match(view, /leaseUntil/) assert.match(view, /QUEUED/) assert.match(view, /PROCESSING/) assert.match(view, /DELIVERED/) assert.match(view, /已入队/) assert.match(view, /dry-run:v1/) assert.match(view, /sql-jdbc:v1/) assert.match(view, /permission-jdbc:v1/) assert.match(view, /menu-jdbc:v1/) assert.match(view, /executorCode/) assert.match(view, /routed:v1/) assert.match(view, /executionEnvironmentCode/) assert.match(view, /routingFingerprint/) assert.match(view, /approvalRequiredForNonDryRun/) assert.match(view, /approvalSeparationEnabled/) assert.match(view, /approvalRequestTtlMinutes/) assert.match(view, /approvalValidityMinutes/) assert.match(view, /realExecutorConfigured/) assert.match(view, /executorRoutes/) assert.match(view, /expectedTransitionFingerprint/) assert.match(view, /expectedRoutingFingerprint/) assert.match(view, /environmentCode/) assert.match(view, /approvalId/) assert.match(view, /requestPluginTransitionApproval/) assert.match(view, /listPluginTransitionApprovals/) assert.match(view, /approvePluginTransitionApproval/) assert.match(view, /revokePluginTransitionApproval/) assert.match(view, /preflightPluginTransition/) assert.match(view, /approvalPreflight/) assert.match(view, /executableApprovals/) assert.match(view, /preflightStateLabel/) assert.match(view, /approvalPreflight\.routes/) assert.match(view, /requestReason/) assert.match(view, /requesterRoles/) assert.match(view, /approverRoles/) assert.match(view, /approvalReason/) assert.match(view, /\$prompt/) assert.match(view, /approvedBy/) assert.match(view, /approvedAt/) assert.match(view, /step\.row\.executorCode/) assert.match(view, /sqlTargetReady/) assert.match(view, /sqlTargetEnvironmentCode/) assert.match(view, /sqlTargetCatalog/) assert.match(view, /probePluginSqlTarget/) assert.match(view, /sqlTargetProbeLoading/) assert.match(view, /sqlTargetProbe/) assert.match(view, /probeSqlTarget/) assert.match(view, /NOT_CHECKED/) assert.match(view, /LEDGER_UNAVAILABLE/) assert.match(view, /PERMISSION_LEDGER_UNAVAILABLE/) assert.match(view, /permissionLedgerRequired/) assert.match(view, /permissionLedgerReady/) assert.match(view, /MENU_LEDGER_UNAVAILABLE/) assert.match(view, /menuLedgerRequired/) assert.match(view, /menuLedgerReady/) assert.match(view, /INVALID_CONFIGURATION/) assert.doesNotMatch(view, /registryStatus\.jdbcUrl/) assert.doesNotMatch(view, /registryStatus\.username/) assert.doesNotMatch(view, /registryStatus\.password/) assert.match(view, /class="manifest-view"/) assert.match(view, /generator:plugin:publish/) assert.match(view, /generator:plugin:rollback/) assert.match(view, /Conflicting|Manifest/) assert.doesNotMatch(view, /