mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-03 01:29:56 +00:00
* test(core): split oversized subprocess consumer suites Separate CLI lifecycle, Gateway startup, command dispatch, and bare-root flows; media reads and storage; catalog metadata and hosted persistence. Prepare workers during collection only for the six observed consumers. Keep all 221 cases, assertions, and fixture initialization order; the exit-only and catalog metadata files remain declaration-free. Prune the two split baselines and one already-stale baseline entry. Validation: 221 passing cases; six collection loads and two none results. Changed-file formatting/lint, affected tsgo graphs, both line-cap ratchets, and diff checks pass. The global changed-path typecheck remains blocked by the inherited agents-other root budget (724/720); affected graphs pass. The review's baseline concern was rejected against the native stale-entry check and direct lint of the unchanged target file. * fix(test): collect extension subprocess dependencies Move Anthropic's real process helper acquisition to collection while retaining vi.importActual and its mock reset behavior. Move DeepInfra and xAI's one-time subject imports and mock setup to collection, keeping their real HTTP transport and negative SSRF assertions intact. Validation: all 20 cases pass and first declaration loads are collection; all case names/counts are unchanged. Formatting and extension test types pass. The normal extension lint lane was blocked while preparing Plugin SDK declarations by its existing 300000 ms timeout; no timeout or guard was changed. The final candidate received the scoped P2 review recorded with the preceding core split commit. * test(plugin-sdk): preload compiled subprocesses for extension tests Move worker preparation into collection through a repo-local, non-production SDK subpath. Let Codex app-server setup preload declarations while keeping worker-cpu imports after file mocks; preserve per-test imports and assertions. Register private source and declaration aliases without adding package exports or production artifacts. Remove unused CLI fixture exports and reuse the alias normalization owner so the existing dead-export and line-cap gates pass. * fix(test): preload compiled subprocesses for newly screened suites * fix(test): preload remaining screened subprocess declarations Move compiled subprocess preparation out of test and hook deadlines in six confirmed declaration consumers. Preserve lazy imports, mock ordering, and assertions; keep the shared meeting plugin helper unchanged so platform-scoped loading retains its existing order. * style(test): keep Parallel suite separator * fix(ci): keep protected selection for split CLI and catalog suites The owner splits moved protected run-main and official-catalog cases into four new suites. Add them to PR_PROTECTED_RUNTIME_TEST_FILES so global inputs such as pnpm-lock.yaml still select them, and assert that in the existing global-input planner test.
129 lines
4.5 KiB
TypeScript
129 lines
4.5 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
import { r2StorageProvider } from "../extensions/cloudflare/api.js";
|
|
import { openStorageLocation } from "../src/storage/locations.js";
|
|
import "../src/test-utils/prepare-compiled-subprocesses.js";
|
|
|
|
const { s3ModulePath, send } = await vi.hoisted(async () => {
|
|
const { createRequire } = await import("node:module");
|
|
const require = createRequire(new URL("../extensions/cloudflare/package.json", import.meta.url));
|
|
return {
|
|
s3ModulePath: require.resolve("@aws-sdk/client-s3"),
|
|
send: vi.fn<
|
|
(command: {
|
|
constructor: { name: string };
|
|
input: { Key?: string; UploadId?: string };
|
|
}) => Promise<unknown>
|
|
>(),
|
|
};
|
|
});
|
|
|
|
vi.mock(s3ModulePath, async (importOriginal) => ({
|
|
...(await importOriginal<Record<string, unknown>>()),
|
|
S3Client: class {
|
|
send = send;
|
|
destroy() {}
|
|
},
|
|
}));
|
|
|
|
describe("R2 storage publication", () => {
|
|
it.each([false, true])(
|
|
"fences multipart publication when authority is lost (cleanup fails: %s)",
|
|
async (cleanupFails) => {
|
|
const marker = JSON.stringify({
|
|
version: 1,
|
|
locationId: "11111111-1111-4111-8111-111111111111",
|
|
createdAt: 0,
|
|
encryption: "none",
|
|
});
|
|
const commands: string[] = [];
|
|
const activeUploads = new Set<string>();
|
|
send.mockImplementation(async (command) => {
|
|
const name = command.constructor.name;
|
|
commands.push(name);
|
|
if (name === "GetObjectCommand") {
|
|
return {
|
|
Body: {
|
|
transformToWebStream: () =>
|
|
new ReadableStream<Uint8Array>({
|
|
start(controller) {
|
|
controller.enqueue(Buffer.from(marker));
|
|
controller.close();
|
|
},
|
|
}),
|
|
},
|
|
};
|
|
}
|
|
if (name === "CreateMultipartUploadCommand") {
|
|
activeUploads.add("staged-backup");
|
|
return { UploadId: "staged-backup" };
|
|
}
|
|
if (name === "UploadPartCommand") {
|
|
return { ETag: "uploaded-part" };
|
|
}
|
|
if (name === "AbortMultipartUploadCommand") {
|
|
if (cleanupFails) {
|
|
throw new Error("synthetic-private-cleanup-detail");
|
|
}
|
|
activeUploads.delete(command.input.UploadId ?? "");
|
|
}
|
|
return {};
|
|
});
|
|
const location = await openStorageLocation({
|
|
name: "archive",
|
|
config: {
|
|
storage: {
|
|
locations: {
|
|
archive: {
|
|
provider: "r2",
|
|
settings: {
|
|
accountId: "0".repeat(32),
|
|
bucket: "test-bucket",
|
|
accessKeyId: { source: "env", provider: "default", id: "R2_KEY" },
|
|
secretAccessKey: { source: "env", provider: "default", id: "R2_SECRET" },
|
|
},
|
|
encryption: "none",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
env: { R2_KEY: "synthetic-test-key", R2_SECRET: "synthetic-test-secret" },
|
|
registry: {
|
|
storageProviders: new Map([
|
|
["r2", { pluginId: "cloudflare", source: "test", provider: r2StorageProvider }],
|
|
]),
|
|
},
|
|
});
|
|
let owner = "installation-a";
|
|
async function* archive() {
|
|
yield Buffer.alloc(64 * 1024 * 1024);
|
|
owner = "installation-b";
|
|
}
|
|
try {
|
|
const failure = await location
|
|
.putObject("backup.tar.gz", archive(), {
|
|
precondition: async () => {
|
|
if (owner !== "installation-a") {
|
|
throw new Error("Backup namespace belongs to another OpenClaw installation.");
|
|
}
|
|
},
|
|
})
|
|
.catch((error: unknown) => error);
|
|
expect(failure).toBeInstanceOf(Error);
|
|
expect(String(failure)).toContain("belongs to another OpenClaw installation");
|
|
if (cleanupFails) {
|
|
expect(String(failure)).toContain(
|
|
"Multipart cleanup also failed; remove incomplete uploads in bucket test-bucket.",
|
|
);
|
|
}
|
|
expect(String(failure)).not.toContain("synthetic-private-cleanup-detail");
|
|
expect(commands).toContain("UploadPartCommand");
|
|
expect(commands).toContain("AbortMultipartUploadCommand");
|
|
expect(commands).not.toContain("CompleteMultipartUploadCommand");
|
|
expect(commands).not.toContain("PutObjectCommand");
|
|
expect(activeUploads.size).toBe(cleanupFails ? 1 : 0);
|
|
} finally {
|
|
await location.close();
|
|
}
|
|
},
|
|
);
|
|
});
|