feat: add CI deployment and live progress

This commit is contained in:
2026-09-05 12:09:16 +00:00 Unverified
parent 321f4e807a
commit aa02826dbb
27 changed files with 2711 additions and 180 deletions
+53
View File
@@ -2,6 +2,7 @@ import { describe, expect, test } from "bun:test";
import {
MemoryOperationStore,
MemoryWorkspaceLeaseProvider,
MAX_OPERATION_EVENTS,
OperationConflictError,
OperationValidationError,
recoverStaleOperations,
@@ -153,6 +154,58 @@ describe("operation store", () => {
).rejects.toBeInstanceOf(OperationValidationError);
});
test("persists monotonic bounded progress events with cursors", async () => {
const store = new MemoryOperationStore(undefined, () => "event-operation");
const operation = await store.create({
workspaceId: "demo",
action: "resources.apply",
idempotencyKey: "events",
});
await store.emit(operation.metadata.name, {
resource: { apiVersion: "v1", kind: "Secret", name: "credentials" },
phase: "apply",
state: "started",
});
const complete = await store.emit(operation.metadata.name, {
resource: { apiVersion: "v1", kind: "Secret", name: "credentials" },
phase: "apply",
state: "succeeded",
});
expect(complete.sequence).toBe(2);
expect(await store.events(operation.metadata.name, 1)).toMatchObject({
items: [expect.objectContaining({ sequence: 2 })],
retainedFirstSequence: 1,
cursorGap: false,
});
await store.transition(operation.metadata.name, "running");
await store.transition(operation.metadata.name, "succeeded");
expect(await store.events(operation.metadata.name, 1)).toMatchObject({
items: [expect.objectContaining({ sequence: 2 })],
retainedFirstSequence: 1,
cursorGap: false,
});
await expect(
store.events(operation.metadata.name, -1),
).rejects.toBeInstanceOf(OperationValidationError);
for (let index = 0; index < MAX_OPERATION_EVENTS; index++) {
await store.emit(operation.metadata.name, {
resource: { apiVersion: "v1", kind: "Service", name: `web-${index}` },
phase: "apply",
state: "succeeded",
});
}
const bounded = await store.events(operation.metadata.name);
expect(bounded.items).toHaveLength(MAX_OPERATION_EVENTS);
expect(bounded.items[0]?.sequence).toBe(3);
expect(bounded).toMatchObject({
retainedFirstSequence: 3,
cursorGap: true,
});
expect(await store.events(operation.metadata.name, 2)).toMatchObject({
cursorGap: false,
});
});
test("provides exclusive, renewable per-workspace leases", async () => {
let now = 0;
const leases = new MemoryWorkspaceLeaseProvider(() => now);