feat(plugins): add batch install update and uninstall actions
This commit is contained in:
13 files changed
+988
-138
No files matched your search
@@ -0,0 +1,50 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { isBatchSelectableListing, runBatch } from "@/lib/plugins/pluginBatch";
|
||||
|
||||
describe("runBatch", () => {
|
||||
it("runs items sequentially and records all successes", async () => {
|
||||
const order: string[] = [];
|
||||
const outcome = await runBatch(
|
||||
[{ id: "a" }, { id: "b" }, { id: "c" }],
|
||||
(item) => item.id,
|
||||
async (item) => {
|
||||
order.push(item.id);
|
||||
},
|
||||
);
|
||||
expect(order).toEqual(["a", "b", "c"]);
|
||||
expect(outcome.succeeded).toEqual(["a", "b", "c"]);
|
||||
expect(outcome.failed).toEqual([]);
|
||||
});
|
||||
|
||||
it("captures a failing item, continues, and does not roll back", async () => {
|
||||
const ran: string[] = [];
|
||||
const outcome = await runBatch(
|
||||
[{ id: "a" }, { id: "b" }, { id: "c" }],
|
||||
(item) => item.id,
|
||||
async (item) => {
|
||||
ran.push(item.id);
|
||||
if (item.id === "b") throw new Error("boom");
|
||||
},
|
||||
);
|
||||
expect(ran).toEqual(["a", "b", "c"]);
|
||||
expect(outcome.succeeded).toEqual(["a", "c"]);
|
||||
expect(outcome.failed).toEqual([{ name: "b", error: "boom" }]);
|
||||
});
|
||||
|
||||
it("returns an empty outcome for empty input", async () => {
|
||||
const action = vi.fn();
|
||||
const outcome = await runBatch([], (item: { id: string }) => item.id, action);
|
||||
expect(action).not.toHaveBeenCalled();
|
||||
expect(outcome.succeeded).toEqual([]);
|
||||
expect(outcome.failed).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isBatchSelectableListing", () => {
|
||||
it("allows install and update, blocks installed and unsupported", () => {
|
||||
expect(isBatchSelectableListing("install")).toBe(true);
|
||||
expect(isBatchSelectableListing("update")).toBe(true);
|
||||
expect(isBatchSelectableListing("installed")).toBe(false);
|
||||
expect(isBatchSelectableListing("unsupported")).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
// Pure, testable helpers for batch plugin operations (install / update / uninstall).
|
||||
// The component owns selection state and UI; this module owns the sequential execution
|
||||
// and result aggregation so partial failures can be reported without aborting the batch.
|
||||
|
||||
export interface BatchItemOutcome {
|
||||
name: string;
|
||||
ok: boolean;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export interface BatchOutcome {
|
||||
results: BatchItemOutcome[];
|
||||
succeeded: string[];
|
||||
failed: { name: string; error: string }[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Run `action` over `items` sequentially, recording per-item success/failure. A failing item is
|
||||
* captured and the batch continues; nothing is rolled back. `nameOf` supplies a human label used
|
||||
* in the summary. Empty input yields an empty outcome.
|
||||
*/
|
||||
export async function runBatch<T>(items: readonly T[], nameOf: (item: T) => string, action: (item: T) => Promise<void>): Promise<BatchOutcome> {
|
||||
const results: BatchItemOutcome[] = [];
|
||||
for (const item of items) {
|
||||
const name = nameOf(item);
|
||||
try {
|
||||
await action(item);
|
||||
results.push({ name, ok: true });
|
||||
} catch (cause) {
|
||||
results.push({ name, ok: false, error: cause instanceof Error ? cause.message : String(cause) });
|
||||
}
|
||||
}
|
||||
return {
|
||||
results,
|
||||
succeeded: results.filter((result) => result.ok).map((result) => result.name),
|
||||
failed: results.filter((result) => !result.ok).map((result) => ({ name: result.name, error: result.error ?? "" })),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Which marketplace listing statuses are actionable in a batch (a selectable plugin that is not yet
|
||||
* installed, or has an update). Installed / unsupported listings are not batch-selectable.
|
||||
*/
|
||||
export function isBatchSelectableListing(status: string): boolean {
|
||||
return status === "install" || status === "update";
|
||||
}
|
||||
Reference in new issue
Block a user