mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-30 13:13:25 +00:00
* fix(creation): prevent duplicate workspaces and agents on retries * test(creation): cover plugin hooks across receipt replays * fix(creation): recover draft retries without accepting partial workspaces * refactor(creation): name receipt inputs and tag compatibility gate * fix(file-observer): track files announced by native change events * fix(creation): keep initial prompts in daemon-owned creation requests * fix(creation): reuse connection-owned progress and recovery * test(creation): adapt handoff gates to combined requests * ci(desktop): avoid xz compression for smoke packages * ci(desktop): pass valid RPM compression through FPM config * refactor(creation): give legacy and modern requests one identity owner * fix(creation): log observer failures without interrupting creation
155 lines
4.8 KiB
TypeScript
155 lines
4.8 KiB
TypeScript
import { beforeEach, describe, expect, it } from "vitest";
|
|
import { isActiveCreateFlowForDraft, useCreateFlowStore } from "./create-flow-store";
|
|
|
|
describe("create-flow-store", () => {
|
|
beforeEach(() => {
|
|
useCreateFlowStore.setState({ pendingByDraftId: {} });
|
|
});
|
|
|
|
it("accepts a retry after failure while rejecting repeated active or completed submissions", () => {
|
|
const store = useCreateFlowStore.getState();
|
|
const submission = {
|
|
draftId: "draft-retry",
|
|
serverId: "server-1",
|
|
workspaceId: "workspace-1",
|
|
agentId: null,
|
|
clientMessageId: "draft-retry:initial-message",
|
|
text: "hello",
|
|
timestamp: 1,
|
|
};
|
|
expect(store.trySetPending(submission)).toBe(true);
|
|
expect(store.trySetPending(submission)).toBe(false);
|
|
store.markLifecycle({
|
|
draftId: submission.draftId,
|
|
lifecycle: "abandoned",
|
|
errorMessage: "Disconnected",
|
|
});
|
|
expect(store.trySetPending(submission)).toBe(true);
|
|
expect(useCreateFlowStore.getState().pendingByDraftId[submission.draftId]).toEqual({
|
|
...submission,
|
|
lifecycle: "active",
|
|
});
|
|
store.markLifecycle({ draftId: submission.draftId, lifecycle: "sent" });
|
|
expect(store.trySetPending(submission)).toBe(false);
|
|
});
|
|
|
|
it("tracks lifecycle transitions explicitly", () => {
|
|
const store = useCreateFlowStore.getState();
|
|
store.setPending({
|
|
draftId: "draft-1",
|
|
serverId: "server-1",
|
|
agentId: null,
|
|
clientMessageId: "msg-1",
|
|
text: "hello",
|
|
timestamp: Date.now(),
|
|
images: [],
|
|
});
|
|
|
|
expect(useCreateFlowStore.getState().pendingByDraftId["draft-1"]?.lifecycle).toBe("active");
|
|
|
|
useCreateFlowStore.getState().markLifecycle({ draftId: "draft-1", lifecycle: "abandoned" });
|
|
expect(useCreateFlowStore.getState().pendingByDraftId["draft-1"]?.lifecycle).toBe("abandoned");
|
|
});
|
|
|
|
it("rekeys draft id idempotently", () => {
|
|
useCreateFlowStore.getState().setPending({
|
|
draftId: "draft-a",
|
|
serverId: "server-1",
|
|
agentId: null,
|
|
clientMessageId: "msg-1",
|
|
text: "hello",
|
|
timestamp: Date.now(),
|
|
images: [],
|
|
});
|
|
|
|
useCreateFlowStore.getState().rekeyDraft({
|
|
fromDraftId: "draft-a",
|
|
toDraftId: "draft-b",
|
|
});
|
|
useCreateFlowStore.getState().rekeyDraft({
|
|
fromDraftId: "draft-a",
|
|
toDraftId: "draft-b",
|
|
});
|
|
|
|
expect(useCreateFlowStore.getState().pendingByDraftId["draft-b"]?.draftId).toBe("draft-b");
|
|
});
|
|
|
|
it("supports multiple pending attempts", () => {
|
|
useCreateFlowStore.getState().setPending({
|
|
draftId: "draft-1",
|
|
serverId: "server-1",
|
|
agentId: null,
|
|
clientMessageId: "msg-1",
|
|
text: "one",
|
|
timestamp: Date.now(),
|
|
});
|
|
useCreateFlowStore.getState().setPending({
|
|
draftId: "draft-2",
|
|
serverId: "server-1",
|
|
agentId: null,
|
|
clientMessageId: "msg-2",
|
|
text: "two",
|
|
timestamp: Date.now(),
|
|
});
|
|
|
|
const state = useCreateFlowStore.getState();
|
|
expect(Object.keys(state.pendingByDraftId).sort()).toEqual(["draft-1", "draft-2"]);
|
|
|
|
useCreateFlowStore.getState().clear({ draftId: "draft-1" });
|
|
expect(useCreateFlowStore.getState().pendingByDraftId["draft-1"]).toBeUndefined();
|
|
});
|
|
|
|
it("clears pending handoff state by agent", () => {
|
|
useCreateFlowStore.getState().setPending({
|
|
draftId: "draft-1",
|
|
serverId: "server-1",
|
|
agentId: null,
|
|
clientMessageId: "msg-1",
|
|
text: "hello",
|
|
timestamp: Date.now(),
|
|
});
|
|
useCreateFlowStore.getState().updateAgentId({ draftId: "draft-1", agentId: "agent-1" });
|
|
useCreateFlowStore.getState().markLifecycle({ draftId: "draft-1", lifecycle: "sent" });
|
|
|
|
useCreateFlowStore.getState().clearByAgent({ serverId: "server-1", agentId: "agent-1" });
|
|
|
|
expect(useCreateFlowStore.getState().pendingByDraftId["draft-1"]).toBeUndefined();
|
|
});
|
|
|
|
it("matches only active pending create flows for a draft and server", () => {
|
|
useCreateFlowStore.getState().setPending({
|
|
draftId: "draft-1",
|
|
serverId: "server-1",
|
|
agentId: null,
|
|
clientMessageId: "msg-1",
|
|
text: "hello",
|
|
timestamp: Date.now(),
|
|
});
|
|
const pending = useCreateFlowStore.getState().pendingByDraftId["draft-1"];
|
|
|
|
expect(
|
|
isActiveCreateFlowForDraft({
|
|
pending,
|
|
serverId: "server-1",
|
|
draftId: " draft-1 ",
|
|
}),
|
|
).toBe(true);
|
|
expect(
|
|
isActiveCreateFlowForDraft({
|
|
pending,
|
|
serverId: "server-2",
|
|
draftId: "draft-1",
|
|
}),
|
|
).toBe(false);
|
|
|
|
useCreateFlowStore.getState().markLifecycle({ draftId: "draft-1", lifecycle: "sent" });
|
|
expect(
|
|
isActiveCreateFlowForDraft({
|
|
pending: useCreateFlowStore.getState().pendingByDraftId["draft-1"],
|
|
serverId: "server-1",
|
|
draftId: "draft-1",
|
|
}),
|
|
).toBe(false);
|
|
});
|
|
});
|