mirror of
https://github.com/openclaw/openclaw.git
synced 2026-11-03 22:24:47 +00:00
* test(scripts): consolidate q0113 performance command fixtures Share command dispatch and capture across node, cp, curl, and rg fixtures. Keep old/current candidate branches and existing config, environment, auth, process cleanup, and event-order assertions. * test(qa-lab): deslop q0114 tests Retire stale menu and managed-session product proofs whose fixture exports no longer exist. Keep menu scope/localization/retry coverage in bot-native-command-menu-sync.test.ts and priority/account-scoped skills in bot-native-commands.registry.test.ts and bot-native-command-dispatch.routing.test.ts. Keep managed-session creation and reuse in server.sessions.create.worktrees.test.ts, snapshot and deletion in server.sessions.delete-worktree-lifecycle.test.ts, active-owner GC in owner-protection.test.ts, and foreign Git locks in service.remove-lease.test.ts. The removed useWorktreeGateway fixture was retired bya743b8a267. Remove repeated HTTP/RPC missing-tool translation already covered by tools-invoke-http.test.ts, and cron CRUD already covered by server.cron.test.ts. Preserve the real provider cancellation-to-wake regression from48406f2a41and all live authorization boundaries. CI-only campaign: oxfmt, plain oxlint and diff checks pass; no tests or coverage run. Shard LOC 2288 -> 1798 (-21.4%), declarations 10 -> 8. Below 40% because remaining Docker isolation, paired-node routing, native slot settlement, admitted CLI reload and Telegram conversation-binding scenarios protect distinct behavior. * test(qa-lab): consolidate q0114 boundary proof Retire the emoji-list Gateway replay. extensions/telegram/src/action-runtime.test.ts, discovers only the delegated current chat and bounds provider reaction lists, already invokes the registered action against real HTTP fixtures and rejects foreign chat/account/provider before I/O. src/agents/tools/message-tool.test.ts, trusts only capability-bound current-turn identity, owns trusted context propagation. Keep direct invocation focused on the real HTTP/RPC-to-plugin bridge, generated and repeated call IDs, and exact approval/block envelopes. Auth rejection remains in auth.test.ts and http-utils.authorize-request.test.ts; JSON parsing in http-common.test.ts; scope rejection in server-plugin-in-process-dispatch.scopes.test.ts and server-methods.dispatch-authorization.test.ts; shared-secret owner behavior and tool results in tools-invoke-http.test.ts and agents-list-tool.test.ts. Replace reload log wording and timestamp parsing with public plugin generation/active state before CLI completion. Keep config settlement before completion and the actual delivered reply for #144809. Admitted-generation retention remains in prepared-model-runtime.plugin-drain.test.ts and server-plugin-reload.prepared-borrow.test.ts; deferred channel work and receipt settlement remain in server-reload-handlers.test.ts. Remove node capability inventories and prepare/run call-shape replays; keep physical execution effects, selected destination, reconnect and refusal paths. bash-tools.exec-host-node-phases.test.ts covers ambiguous binding before executable filtering, and bash-tools.exec-host-node.integration.test.ts exercises prepare/run policy. Remove one identical sandbox attachment read. CI-only: formatting, plain oxlint and diff checks pass. No test, build or coverage runs. This commit removes 426 further lines: original shard 2288 -> 1372 (-40.03%); declarations 10 -> 7. No production, helper or runner changes. * test(ui): deslop q0112 tests Remove duplicate gallery controls, block-art style probes, header truncation checks, sidebar presentation replays, and non-asserting proof-capture plumbing. Keep browser-only geometry, focus, migration, security, and recovery paths. Remaining proof: chat-responsive.browser.test.ts caps a nested session trail while ellipsizing both titles; chat-user-bubble-disclosure.e2e.test.ts keeps dense block art within five preview rows; chat-code-block-fences.e2e.test.ts checks rendered block-art rows. WebsiteWidget always renders its escape link independently of iframe load, and website.test.ts plus the retained dashboard scenario cover its URL, target, and opener isolation. Presentation owners: solid/settings-ui.test.tsx, settings-ui.browser.test.tsx, solid/icon.test.tsx, solid/kbd.test.tsx, solid/presentation-primitives.test.tsx, and secret-reveal-focus.e2e.test.ts. app-sidebar.sessions.test.ts imports app-sidebar-cases/narration.ts, including roster subscription release; the retained split-pane E2E covers streaming and observer headlines after reconnect. CI-only validation: plain oxlint, oxfmt, and git diff --check passed. No tests, coverage, Testbox, builds, or typechecks run. Lines 2378 -> 2152 (9.5%); below the 40% target because remaining browser contracts are distinct. * test(ui): consolidate q0112 duplicate e2e layers Retire v3 migration replay in favor of outbox-blob-recovery.test.ts: migrates landed v3 %s without retiring its exact Blob or attempt, and chat-outbox-payloads.e2e.test.ts: sends and explicitly retries an attachment after a non-local plain HTTP reload. Keep the v2 migration with independent durable draft and queue-edit proof. History refill and cursor owners remain in chat-pane-history-autofill.test.ts, chat-pane-history.test.ts, chat-pane-history-settlement.browser.test.tsx, and chat-pane-warm-reload.test.ts. Keep browser focus retention and hidden-activity refill. Empty welcome behavior remains in chat-pane-identity.test.ts. Header retry remains in chat-pane-header-workspace-icon.test.tsx: recovers the workspace icon after a transient 503 without remounting. Mobile controls and Details remain in mobile-chat-header.e2e.test.ts; retain the additional non-overlap geometry here. Remove sidebar narration and mocked event filtering from split-delivery proof: app-sidebar-cases/narration.ts covers leases and previews; app-sidebar-session-narration.test.ts covers observer ownership; gateway-misc.test.ts and session-message-events.test.ts test actual gateway filtering. Keep two-pane stream/reconnect and first-token-before-ack browser cases. Dashboard panel replay remains in board-split-transcript.e2e.test.ts and mobile-chat-header.e2e.test.ts; retain website frame identity and native navigation. Drop fixture echo assertions and redundant theme/layout combinations. Preserve both shadow-parent style checks. No dead subjects or source-grep inventories found. CI-only: oxfmt, plain oxlint, and diff-check pass; no test/build/typecheck/coverage runs. Additional 516 lines removed; original shard 2378 -> 1636 lines (31.2%). The 40% target remains unmet; do not remove unique auth, layout, migration, or ordering proof to claim it. * test(ui): remove duplicated q0108 e2e layers Port q0108 onto the campaign lane. The first shard commit is already present in HEAD; apply the remaining cleanup while retaining all newer lane inventory changes. Resolve the UI-path inventory conflict by removing the retired real-Gateway partial-refresh suite without restoring the already-retired catalog-bootstrap entry. Affected test files match the final shard commit. Validation: oxlint exit 0; oxfmt --check exit 0; working-tree and staged diff checks pass; isolated autoreview scoped-clean. Tests were not run under the requested CI-only policy. * test(ui): deslop q0115 tests Remove duplicated preview and cancellation scenarios: chat-video-attachment-preview.e2e.test.ts retains frame gating, decoding, URL cleanup and unsupported media; chat-video-compression.e2e.test.ts retains New Session cancellation. Keep all cron outcome branches while dropping redundant selection combinations and overview no-op replays. ui/src/lib/cron/action-feedback.test.ts retains reason mapping; retained E2E rows cover same-job, changed-job, overview status and error presentation. Reuse the standard browser harness and compact fixtures; remove non-asserting screenshot and request-dump plumbing. Retain distinct recovery, accessibility, layout and media lifecycle assertions. CI-only shard: formatting, plain oxlint and diff checks only; no runtime or coverage claim. * test(ui): consolidate q0115 e2e owner coverage Reduce the shard from 2913 base lines to 1747 without changing production or shared helpers. Keep browser-only geometry, downloads, loading dismissal, image handoff ordering, and failure/retry proof. Keepers for removed layers: sidebar-show-all.e2e.test.ts, sidebar-interactions.e2e.test.ts, session-agent-column.e2e.test.ts and app-sidebar.people.test.ts own roster navigation and metadata. chat-session-workspace.test.ts owns closed/inactive reads; chat-session-workspace-rail.test.tsx owns filtering. Merge the remaining Files scrolling and reveal scenario on one fixture. logs-page.test.tsx and src/logging/log-tail.test.ts own reconnect, stale reads and missing-file replacement; retain real Gateway download/source proof. session-progress-card-refresh.test.ts owns saved timestamps; share refresh setup while retaining both layouts and retry. new-session-page.catalog-reconnect.e2e.test.ts, draft-native-terminal-start.test.ts and terminal-panel.test.ts own native startup and independent dock lifecycle. chat-video-compression.e2e.test.ts owns encoder consent, IME and playback; retain the differing filename and error-presentation assertions for coordinator diagnosis. chat-details-mobile-content.test.tsx and chat-details.browser.test.ts own local environment exclusion and session retirement. CI-only: plain oxlint, oxfmt and git diff --check pass. No tests, coverage, builds, Testbox, or check-changed runs. * test(scripts): deslop q0116 tests Remove repeated Control UI config-shape checks covered by the native inventory and resource ownership cases in test/vitest-ui-e2e-config.test.ts. Keep private-server classification, frozen routing and include-file selection. Keep shared ratchet fork-point proof in check-env-var-count.test.ts (compares against the fork budget when the base branch later shrinks). Retain max-lines error propagation, staged content and rename contracts. Trim repeated CPU argument guards, sparse missing-object proof, and planner overflow checks; reuse tracked temp cleanup. CI-only audit: formatting, plain oxlint and diff checks passed; tests and coverage were not run. Net reduction: 136 test/support lines (6.5%), below the 40% target to preserve distinct behavior. * test(scripts): remove q0116 source and inventory replays Remove the Control UI source-name classifier and duplicate filesystem census; native project and resource ownership remain covered by test/vitest-ui-e2e-config.test.ts. Retain the generic serial invariant, include-file checks, packaged planner routing and frozen fallback contract. Replace QA diagnostic-name inventories with sanitized outcome assertions. Trim the fingerprint install-input replay to one representative while preserving hook and sparse-index boundaries. Seed only the Bun archive selected by the CPU fixture; keep archive integrity, symlink, layout and fallback rejection cases. Reduce planner fixture cohorts and copied group shapes while retaining lane ordering, partition coverage, scenario eligibility and overflow proof. No production or script changes. Static-only validation: oxfmt, plain oxlint and diff check passed. Cumulative shard LOC 2095 -> 1813 (13.5%); 40% target remains unmet rather than dropping distinct recovery and security behavior. * test(ui): deslop q0117 tests Reuse the real-Gateway fixture and consolidate reduced-motion proof into the existing mobile Inbox flow. Remove redundant capture plumbing while retaining layout readiness and all real-Gateway lifecycle assertions. Automation retry and pagination remain covered by session-hovercard-automations.async.test.tsx (first-read readiness and next owner page). Wildcard switch and override behavior remain covered by panels-tools-skills.browser.test.ts; the profile case retains config.set validation proof. * test(qa-lab): deslop q0118 tests Reuse the publishable-plugin fixture, remove duplicate diagnostic projections, and simplify single-use Discord wrappers while retaining every test case and behavioral assertion. Preserve candidate identity checks, raw Slack recorder retention, cleanup ordering, and authority boundaries. CI-only shard: local format, oxlint, and diff checks; runtime proof deferred to hosted CI. Reduction is below the requested target because the remaining scenarios exercise distinct transport, lifecycle, security, and release-check paths. * test(qa-lab): remove duplicated q0118 proof layers Drop Discord multipart and poll replay: retained owners are send.components.multipart.test.ts, outbound-payload.contract.test.ts, send.creates-thread.test.ts (registerSendAssetsAndRetriesTests), actions/message-bodies.test.ts, and src/infra/outbound/message-action-execution.accepted-results.test.ts. Drop workspace attachment and binary-output replays covered by extensions/file-transfer/src/workspace-service.test.ts and src/media/read-capability.workspace-access.test.ts. Retain real node enrollment, restart, document editing, denied access, and exact-file replacement proof. Remove Slack's separate evidence-copy/receipt framework and its associated destination guards. Retain the original transport recorder, archive/package identity checks, native plugin entry observation, public callbacks, and ordered idempotent cleanup. CI-only: format, plain oxlint, and diff checks pass; no test runs. The shard remains below the requested 40 percent reduction because its remaining scenarios exercise distinct boundaries. * test(qa-lab): consolidate node file boundary proof Remove the repeated enrollment/restart/document-access scenario. Retained owner proof: src/node-host/runner.test.ts (pairing candidate persistence, paired restart, expired fallback tokens), src/cli/node-cli/register.test.ts (pair-if-needed), extensions/file-transfer/src/workspace-service.test.ts (remote documents and no local fallback), src/gateway/server-methods/agents-files.test.ts (read/write preconditions), and extensions/file-transfer/src/node-host/file-write.test.ts (parent creation). Preserve read-only document-write and direct node-fetch denials in the real Gateway/node security fixture alongside the existing exact-file replacement race. No production changes or test runs; plain oxlint, format, and diff checks passed. Additional shard-only commit; prior commits unchanged. * test(scripts): deslop q0119 tests Keep manifest recovery ownership proof while removing repeated inputs to the same current-byte mismatch guard. The retained runtime recovery test and preserves-edits test cover refusal and unchanged source bytes; the original runtime/public export contracts remain. Keep mixed excluded-file coverage in docs-mirror-freshness and the ordinary-source/non-PR cases in ci-hourly; remove equivalent input rows. Minimize SQLite reader fixtures to queried columns while retaining writer/schema and byte-cap assertions. Share native CLI option literals without deleting its scenarios. CI-only shard: plain oxlint, oxfmt, and diff-check passed. No tests or coverage run. Lines 2517 -> 2436; target 40% not reached because remaining child-process, protocol, schema, and admission cases cover distinct behaviors. * test(scripts): trim q0119 inventories and duplicate branches Replace the copied CodeQL category list with an independent policy-file inventory, retain main-ref/credential guards, and remove cron, runner metadata, and mock call-shape restatements. Keep docs freshness outcomes and main-ref/network boundaries without per-request paging assertions. Retained runtime recovery ownership covers post-preparation edits. Native CLI cases retain direct/delegated admission, explicit policy, scalar errors, metadata exits, selection kinds, and parallel empty ownership; run-vitest.test.ts also retains enforces direct empty-file policy. SQLite authoritative schema selection covers unversioned dispatch; summary projections retain current and pre-v13 cases, and existing-schema writes retain both decision outcomes. No whole E2E scenario had equivalent sibling proof and no subject was missing. Preserve Doctor join/cleanup, native interruption/closure ownership, and all distinct byte-cap/error paths. CI-only static checks passed; no tests run. Cumulative lines 2517 -> 2383, below the requested 40% reduction. * test(scripts): consolidate q0119 artifact and policy cases Share malformed/oversized fixtures across legacy JSON and both SQLite schemas without deleting their outcomes. Replace duplicate i18n write recording with the existing mock history; retain byte-exact write and no-write assertions. Check CodeQL category completeness separately from a generic per-row language/config invariant. Remove null/missing patch and nonmatching query variants through the same jq predicates. Native CLI aliases and duplicate scalar variants remain covered by run-vitest.test.ts (enforces direct empty-file policy; round-trips repeated native options through delegation) and retained compound-help and exact-file cases. No whole survivor scenario is duplicated: backup-rollback stubs Doctor calls, while native-assignments tests publication rather than app-server isolation. No missing subjects found. CI-only: oxfmt, plain oxlint, diff-check passed; no tests or coverage run. Total 2517 -> 2338 lines (7.1%); requested 40% remains unmet without dropping unique contracts. * test(ui): deslop q0122 tests Remove the obsolete DOM-host render probe. ChatPaneView no longer exposes the probed state/render/presence API; chat-pane-session-presentation.test.ts and chat-pane-presence.test.ts retain owner-level invalidation coverage, and sidebar-complete-roster.e2e.test.ts retains browser roster updates. Remove the terminal echo replay covered by terminal-repaint.e2e.test.ts and terminal-docking.e2e.test.ts. Consolidate activity media setup while retaining CSP/referrer, keyboard, layout, refresh and enrichment-failure assertions. Remove optional companion screenshot plumbing without removing its behavior assertions. CI-only campaign: oxfmt, plain oxlint and diff checks pass; tests and coverage intentionally not run. Shard LOC 2067 -> 1789; declarations 19 -> 16; expanded cases 31 -> 27. Short of 40% because retained lifecycle, security and interaction paths are distinct. No production changes. * test(ui): remove redundant q0122 e2e layers PASS 15 cumulative shard LOC: 2067 -> 1239 (-40.1%). Static declarations 19 -> 10; expanded cases 31 -> 16. CI-only: oxfmt, plain oxlint and diff checks pass; no tests, coverage or build commands run. Delete terminal-open E2E: terminal-panel-restore.test.ts covers normal exit retirement/no close RPC, terminal-panel.test.ts covers theme rendering, terminal-connection.test.ts covers malformed opens, terminal-panel-readiness.test.ts covers retries, and terminal-docking.e2e.test.ts plus terminal-repaint.e2e.test.ts retain browser transport/docking/repaint proof. Remove its exact inventory/timing entries. Reduce upload replays to browser selection/drop, partial-result insertion and redaction. terminal-file-upload.test.ts retains native title/path quoting and input safety; terminal-panel-upload.test.ts retains retry/progress lifecycle. Use the existing E2E suite owner directly instead of the removed terminalSuite/pageFixture imports. session-display.test.ts retains title-pending and fallback decisions; chat-pane-session-presentation.test.ts, chat-pane-session-menu.test.ts and sidebar-complete-roster.e2e.test.ts retain owner/browser title updates. Keep pending sidebar placement/admission/rejection and reduced motion, dropping copied CSS values. chat-session-activity.test.ts retains exact disclosure markup, attribution and separate automation grouping; keep browser keyboard and navigation flows. Consolidate Side chat preview/upload/recovery; chat-attachment-remove.e2e.test.ts retains main composer preview, and chat-session-companion-history.test.ts retains image retirement/draft recovery. Remove nonasserting gallery captures; keep media security/layout/refresh checks. * test(ui): deslop q0120 tests Remove optional capture-only setup and reuse local attachment and session fixtures. Keep distinct browser, lifecycle, authority, and error paths. Empty export aliases remain covered by ui/src/pages/chat/chat-send-submit.test.ts (corrects a rejected command path and handles an export) and ui/src/pages/chat/export.test.ts (does not download empty). The populated browser download E2E remains. Repeated chrome CSS assertions remain in ui/src/components/form-controls.browser.test.ts (uses one canonical scrollbar width while preserving normal content scroll and text entry). Exact device-folder field exclusion remains in ui/src/pages/new-session/create-params.test.ts (creates a device folder without leaking another workspace source). CI-only: oxfmt, plain oxlint, and git diff --check passed; no tests or coverage executed. LOC 2106 to 1910; static cases 39 to 37. Below the 40 percent target to preserve distinct behavior. * test(scripts): deslop q0121 tests Remove redundant runtime inventories, native-import argument/format replays, and locale transport cases already exercised by control-ui-locale-finalizer.test.ts (restore CLI, canonical corrections, glossary changes, fallback rejection). Keep plugin byte restoration in the retry case. Retain lifecycle error paths with representative exit 23; ci-platform-checkout.test.ts owns actual cleanup/timeout exit 125 proof. Keep cancelled waiver rejection for the identical non-failure branch. Replace Periphery's source-derived prose inventory with an independent fixture and factor repetitive release fixture expectations. CI-only: oxfmt, plain oxlint, and git diff --check passed. No tests or coverage run. Shard LOC 2532 -> 2385 (5.8%); honest shortfall from 40% because remaining proof covers distinct safety, lifecycle, and parser contracts. No production or support changes. * test(scripts): consolidate q0121 owner-boundary coverage Remove generic runtime policy replays covered by ci-run-node-test-shard, Periphery draft/manual and source-inventory checks covered by ancillary-workflow-concurrency, and basic waiver matching covered by release-ci-summary.qualification's npm/Docker admission test. Keep locale run/path rejection in control-ui-locale-finalizer and host-directory refusal in src/plugins/plugin-peer-link. Remove redundant sampler/parser and exported exemption subset checks; preserve name-and-path exception constraints and branch-identifying release failure assertions. CI-only static gates: oxfmt, plain oxlint, and git diff --check pass. No tests or coverage run. Additional 158 lines removed; original shard 2532 -> 2227 (-12.0%), short of 40% because remaining owner-boundary behavior has no equivalent proof. No production changes or deleted files. * test(scripts): shrink q0121 fixture scaffolding Reuse existing filesystem and Git fixtures, minimize synthetic AST/metrics inputs, remove unused observer options, and share runtime include and release admission assertion scaffolding. Retain distinct parser, authority, artifact, and cleanup behavior; collapse the duplicate empty Gateway inventory row onto gateway-core. CI-only static proof: oxfmt, plain oxlint and git diff --check pass. No tests/builds/coverage run. Shard 2227 -> 2088 this pass; original 2532 -> 2088 (-17.5%), still below the requested 40% reduction. No production or shared helper edits, no deleted files. * test(cli): deslop q0123 tests Remove the duplicate hooks JSON process suite. The hooks describe block in test/cli-json-stdout.agents.e2e.test.ts retains all 22 failure rows and the offline success case, including parent/leaf JSON, TTY finalization, unknown and blank agents, explicit Gateway refusal, local report failure, missing hooks, and config non-mutation. * test(agents): deslop q0125 tests Reuse the existing visible-child fixture and terminal assertion, consolidate repeated delivery assertions and expiry fixtures, and remove the redundant CLI/no-attachment serial continuation row. The native/no-attachment and CLI/attachment rows retain both decision paths. Keep the distinct lifecycle, persistence, cancellation, and interleaving scenarios; the safe reduction remains below the 40% target. Static lint, formatting, and diff checks only under campaign CI-only mode. * test(agents): consolidate q0125 lifecycle coverage Share requester and lifecycle setup while retaining restart injection, terminal error grace, frozen-result refresh, timeout recovery, raw-child identity and serial continuation proof. Preserve delayed retention in the cron-child scenario. Remove the retired lifecycle path from the protected-test inventory. Removed native projection replay is covered by run.shared-integration.test.ts / sessions-yield.orchestration: preserves child ownership through transient retries; backend.test.ts: rejects forged settlement; requester-run-settlement.test.ts: settles requester continuation only when permitted. Binding and cohort matrices remain covered by subagent-gateway-context-binding.test.ts and subagent-registry-requester-yield.test.ts: settles requester batches according to the receipt state. Archive overlap remains covered by subagent-registry-sweeper-lifecycle.test.ts: settles overlapping sweeps; subagent-control.retirement.test.ts: preserves captured cancellation ownership through retirement write rollback / normal retirement; cancellation-reconciliation: retires superseded tombstones without mutating the successor; context-cleanup: handles plugin runtime loader failure. Pending delivery remains covered by registry tests retaining completion through its deadline and retrying completion delete runs. Other removed replays remain covered by queued-registration: keeps lifecycle error grace on its admitted attempt; registry: publishes aborted lifecycle end events only after killed reconciliation; helpers: preserves a valid UTF-8 prefix within the frozen-result byte budget; lifecycle-cleanup: retains a child result and retries parent restart admission without a sweep. Existing receipt boundary scenarios retain before-yield delivery and the retained frozen-output case covers late traffic. Removed dead requester-history gate and no-op legacy attachment fixture. CI-only campaign: no test, coverage, build or typecheck runs. Plain oxlint, oxfmt and git diff --check pass. Shard test LOC is 2377 -> 1425 (40.05% reduction); no new helper files. * test(scripts): consolidate q0116 owner-boundary proof Port q0116 commits ddd95d5d03f09c00c960eecc727cc09817ca89e1, 7f35460a32828511ea7c9087d7c1fab6258b7ecf, and daa4b4d9e7c8b13b6ddf5a1c8881a0af4efddf59 onto the campaign lane. The first two changes are already present; retain the lane's removal of model-catalog-partial-refresh from the frozen fallback inventory while applying the final owner-boundary consolidations. CI-only validation: plain oxlint exit 0, oxfmt --check passed for all seven touched files, git diff --check clean. Isolated review found no actionable findings. No tests, coverage, Testbox, or check-changed run. * test(scripts): deslop q0124 tests Keep one artifact-identity rejection at the deep-equality boundary; retain digest rejection, both restore profiles, and producer-provenance cases in repo-e2e-artifacts.test.ts. Remove Markdown heading inventories while retaining grouped manifest/dependency evidence and release-age exclusions in transitive-manifest-risk-report.test.ts. Share iOS metadata rejection setup and use tracked tool cleanup without dropping metadata or provenance cases. CI-only campaign: oxfmt, plain oxlint and git diff --check passed. No tests, coverage or typechecks run. Conservative reduction: 2457 to 2372 lines; distinct signing, audit, causal transcript, ratchet and recovery boundaries retained. * test(cli): preserve current hooks boundary coverage * test(agents): retain one owner for lifecycle proof * test(qa-lab): preserve evidence and integration boundaries * test: preserve current fixtures after CI feedback * test: retain candidate and release safety contracts * test(scripts): retain release recovery and admission proof
210 lines
7.9 KiB
TypeScript
210 lines
7.9 KiB
TypeScript
import { mkdirSync, writeFileSync } from "node:fs";
|
|
import path from "node:path";
|
|
import { DatabaseSync } from "node:sqlite";
|
|
import { pathToFileURL } from "node:url";
|
|
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
import { withEnvAsync } from "../../src/test-utils/env.js";
|
|
import { useAutoCleanupTempDirTracker } from "../helpers/temp-dir.js";
|
|
|
|
const MODULE_URL = pathToFileURL(path.resolve("scripts/e2e/lib/plugin-index-sqlite.mjs")).href;
|
|
let importCounter = 0;
|
|
const tempRoots = useAutoCleanupTempDirTracker(afterEach);
|
|
|
|
async function loadPluginIndex(env: Record<string, string> = {}) {
|
|
return withEnvAsync(env, () => import(`${MODULE_URL}?case=${importCounter++}`));
|
|
}
|
|
|
|
function sqlitePath(root: string) {
|
|
return path.join(root, "state", "openclaw.sqlite");
|
|
}
|
|
|
|
function withSqlite<T>(root: string, action: (db: DatabaseSync) => T): T {
|
|
const dbPath = sqlitePath(root);
|
|
mkdirSync(path.dirname(dbPath), { recursive: true });
|
|
const db = new DatabaseSync(dbPath);
|
|
try {
|
|
return action(db);
|
|
} finally {
|
|
db.close();
|
|
}
|
|
}
|
|
|
|
function writeLegacyIndex(root: string, text: string) {
|
|
const file = path.join(root, "plugins", "installs.json");
|
|
mkdirSync(path.dirname(file), { recursive: true });
|
|
writeFileSync(file, text, "utf8");
|
|
}
|
|
|
|
function currentValueJson(installRecords: unknown) {
|
|
return JSON.stringify({ revision: Date.now(), index: { installRecords } });
|
|
}
|
|
|
|
function writeCurrentSqliteIndex(root: string, valueJson: string, schemaVersion = 13) {
|
|
withSqlite(root, (db) => {
|
|
db.exec(`PRAGMA user_version = ${schemaVersion}`);
|
|
db.prepare(
|
|
`CREATE TABLE config_machine_state AS
|
|
SELECT 'plugins.installedIndex' AS state_key, ? AS value_json`,
|
|
).run(valueJson);
|
|
});
|
|
}
|
|
|
|
function writePreV13SqliteIndex(root: string, installRecordsJson: string, schemaVersion = 12) {
|
|
withSqlite(root, (db) => {
|
|
db.exec(`PRAGMA user_version = ${schemaVersion}`);
|
|
db.prepare(
|
|
`CREATE TABLE installed_plugin_index AS SELECT
|
|
'installed-plugin-index' AS index_key, 1 AS version, '1' AS host_contract_version,
|
|
'1' AS compat_registry_version, 1 AS migration_version, 'hash' AS policy_hash,
|
|
0 AS generated_at_ms, NULL AS refresh_reason, ? AS install_records_json,
|
|
'[]' AS plugins_json, '[]' AS diagnostics_json, NULL AS warning`,
|
|
).run(installRecordsJson);
|
|
});
|
|
}
|
|
|
|
function readTableNames(root: string) {
|
|
const db = new DatabaseSync(sqlitePath(root), { readOnly: true });
|
|
try {
|
|
return db
|
|
.prepare("SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name")
|
|
.all()
|
|
.map((row) => row.name);
|
|
} finally {
|
|
db.close();
|
|
}
|
|
}
|
|
|
|
describe("plugin index SQLite E2E helpers", () => {
|
|
let root: string;
|
|
let options: { stateDir: string; configPath: string };
|
|
const readRecords = async () => (await loadPluginIndex()).readPluginInstallRecords(options);
|
|
beforeEach(() => {
|
|
root = tempRoots.make("openclaw-plugin-index-");
|
|
options = { stateDir: root, configPath: path.join(root, "openclaw.json") };
|
|
});
|
|
it("reads legacy install records when SQLite index state is absent", async () => {
|
|
writeLegacyIndex(
|
|
root,
|
|
JSON.stringify({ records: { demo: { installPath: "/tmp/demo", source: "npm" } } }),
|
|
);
|
|
|
|
expect(await readRecords()).toEqual({
|
|
demo: { installPath: "/tmp/demo", source: "npm" },
|
|
});
|
|
});
|
|
|
|
const sources = [
|
|
["legacy JSON", writeLegacyIndex, "JSON artifact"],
|
|
["current SQLite", writeCurrentSqliteIndex, "value_json"],
|
|
["pre-v13 SQLite", writePreV13SqliteIndex, "install_records_json"],
|
|
] as const;
|
|
it.each(sources)("keeps malformed %s as an empty fallback", async (_name, write) => {
|
|
write(root, "{not-json");
|
|
expect(await readRecords()).toEqual({});
|
|
});
|
|
it.each(sources)("rejects oversized %s before parsing it", async (_name, write, column) => {
|
|
write(root, JSON.stringify({ filler: "x".repeat(128) }));
|
|
const index = await loadPluginIndex({ OPENCLAW_PLUGIN_INDEX_JSON_MAX_BYTES: "64" });
|
|
expect(() => index.readPluginInstallIndex(options)).toThrow(
|
|
`plugin index ${column} exceeded 64 bytes`,
|
|
);
|
|
});
|
|
|
|
it.each([
|
|
[13, "current"],
|
|
[12, "legacy"],
|
|
[0, "current"],
|
|
] as const)("selects the authoritative %s schema index", async (schema, selected) => {
|
|
writePreV13SqliteIndex(root, JSON.stringify({ legacy: { source: "npm" } }), schema);
|
|
writeCurrentSqliteIndex(root, currentValueJson({ current: { source: "npm" } }), schema);
|
|
expect(await readRecords()).toEqual({ [selected]: { source: "npm" } });
|
|
});
|
|
|
|
it.each([
|
|
[13, writePreV13SqliteIndex, JSON.stringify({ stale: { source: "npm" } })],
|
|
[12, writeCurrentSqliteIndex, currentValueJson({ future: { source: "npm" } })],
|
|
] as const)("never cross-reads another schema's table for v%s", async (schema, write, value) => {
|
|
write(root, value, schema);
|
|
expect(await readRecords()).toEqual({});
|
|
});
|
|
|
|
it.each([12, 13])("bounds only required summary fields in schema v%s", async (schema) => {
|
|
const index = await loadPluginIndex();
|
|
const installRecords = { demo: { source: "npm", future: { values: [false, null, "keep"] } } };
|
|
const plugins = [
|
|
{ pluginId: "disabled", enabled: false, rootDir: "/disabled" },
|
|
{ pluginId: "enabled", enabled: true, rootDir: "/enabled" },
|
|
{ pluginId: "missing" },
|
|
{ pluginId: "null", enabled: null, rootDir: null },
|
|
];
|
|
index.writePluginInstallIndexForE2E(
|
|
{
|
|
installRecords,
|
|
plugins: plugins.map((plugin) => ({ ...plugin, description: "x".repeat(300_000) })),
|
|
diagnostics: ["y".repeat(1024 * 1024)],
|
|
},
|
|
{ ...options, storageMode: schema === 12 ? "pre-v13" : "current" },
|
|
);
|
|
withSqlite(root, (db) => db.exec(`PRAGMA user_version = ${schema}`));
|
|
|
|
expect(index.readPluginInstallIndex({ ...options, summary: true })).toEqual({
|
|
installRecords,
|
|
plugins,
|
|
});
|
|
expect(() => index.readPluginInstallIndex(options)).toThrow(/exceeded 1048576 bytes/u);
|
|
});
|
|
|
|
it.each(["receipts", "roots"])("retains the summary byte cap for required %s", async (field) => {
|
|
const index = await loadPluginIndex();
|
|
index.writePluginInstallIndexForE2E(
|
|
{
|
|
installRecords: { demo: { future: field === "receipts" ? "é".repeat(600_000) : "kept" } },
|
|
plugins: [
|
|
{ pluginId: "demo", rootDir: field === "roots" ? "x".repeat(1024 * 1024) : "/demo" },
|
|
],
|
|
},
|
|
options,
|
|
);
|
|
expect(() => index.readPluginInstallIndex({ ...options, summary: true })).toThrow(
|
|
"plugin index summary exceeded 1048576 bytes",
|
|
);
|
|
});
|
|
|
|
it.each([
|
|
{ mode: "current", schemaVersion: 0, options: {}, current: true },
|
|
{ mode: "pre-v13", schemaVersion: 0, options: { storageMode: "pre-v13" }, current: false },
|
|
...[0, 13].map((schemaVersion) => ({
|
|
mode: `existing schema v${schemaVersion}`,
|
|
schemaVersion,
|
|
options: { storageMode: "existing-schema" },
|
|
current: schemaVersion >= 13,
|
|
})),
|
|
])(
|
|
"writes only the $mode storage contract",
|
|
async ({ current, options: writerOptions, schemaVersion }) => {
|
|
withSqlite(root, (db) => db.exec(`PRAGMA user_version = ${schemaVersion}`));
|
|
const index = await loadPluginIndex();
|
|
index.writePluginInstallIndexForE2E(
|
|
{ installRecords: { demo: { source: "npm" } } },
|
|
{ stateDir: root, ...writerOptions },
|
|
);
|
|
|
|
const tables = readTableNames(root);
|
|
expect(tables).toContain(current ? "config_machine_state" : "installed_plugin_index");
|
|
expect(tables).not.toContain(current ? "installed_plugin_index" : "config_machine_state");
|
|
expect(index.readPluginInstallRecords({ stateDir: root })).toEqual({
|
|
demo: { source: "npm" },
|
|
});
|
|
},
|
|
);
|
|
|
|
it("rejects unknown writer storage modes", async () => {
|
|
const index = await loadPluginIndex();
|
|
expect(() =>
|
|
index.writePluginInstallIndexForE2E(
|
|
{ installRecords: {} },
|
|
{ stateDir: root, storageMode: "future" },
|
|
),
|
|
).toThrow("Unknown plugin index storage mode: future");
|
|
});
|
|
});
|