feat(09-01): define granular event types, IPC channels, entity getters, and Zustand patch methods
- Add 5 granular event interfaces (TaskUpdated, RunUpdated, ApprovalUpdated, AgentUpdated, BudgetChanged) to DesktopEvent union - Add 4 entity getter IPC channels (getTask, getAgent, getRun, getApproval) with Zod validation and company ownership checks - Add getRun method to AppDatabase following existing getTask pattern - Add 4 preload bridge methods for entity getters - Add 8 patch/remove methods and refreshBadges to CompanySlice - Add selective-ipc.test.ts with exhaustive union type tests - Extend store.test.ts with 14 patch method tests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
c1de05a7f1
commit
920a764856
8 files changed
+631
-5
No files matched your search
@@ -0,0 +1,117 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import type {
|
||||
DesktopEvent,
|
||||
TaskUpdatedEvent,
|
||||
RunUpdatedEvent,
|
||||
ApprovalUpdatedEvent,
|
||||
AgentUpdatedEvent,
|
||||
BudgetChangedEvent,
|
||||
} from "@shared/types";
|
||||
|
||||
describe("selective IPC event types", () => {
|
||||
it("TaskUpdatedEvent discriminated by type field", () => {
|
||||
const event: TaskUpdatedEvent = {
|
||||
type: "task-updated",
|
||||
taskId: "t-1",
|
||||
action: "created",
|
||||
};
|
||||
expect(event.type).toBe("task-updated");
|
||||
expect(event.taskId).toBe("t-1");
|
||||
expect(event.action).toBe("created");
|
||||
});
|
||||
|
||||
it("RunUpdatedEvent discriminated by type field", () => {
|
||||
const event: RunUpdatedEvent = {
|
||||
type: "run-updated",
|
||||
runId: "r-1",
|
||||
action: "completed",
|
||||
};
|
||||
expect(event.type).toBe("run-updated");
|
||||
expect(event.runId).toBe("r-1");
|
||||
expect(event.action).toBe("completed");
|
||||
});
|
||||
|
||||
it("ApprovalUpdatedEvent discriminated by type field", () => {
|
||||
const event: ApprovalUpdatedEvent = {
|
||||
type: "approval-updated",
|
||||
approvalId: "a-1",
|
||||
action: "decided",
|
||||
};
|
||||
expect(event.type).toBe("approval-updated");
|
||||
expect(event.approvalId).toBe("a-1");
|
||||
expect(event.action).toBe("decided");
|
||||
});
|
||||
|
||||
it("AgentUpdatedEvent discriminated by type field", () => {
|
||||
const event: AgentUpdatedEvent = {
|
||||
type: "agent-updated",
|
||||
agentId: "ag-1",
|
||||
action: "updated",
|
||||
};
|
||||
expect(event.type).toBe("agent-updated");
|
||||
expect(event.agentId).toBe("ag-1");
|
||||
expect(event.action).toBe("updated");
|
||||
});
|
||||
|
||||
it("BudgetChangedEvent discriminated by type field", () => {
|
||||
const event: BudgetChangedEvent = {
|
||||
type: "budget-changed",
|
||||
companyId: "c-1",
|
||||
};
|
||||
expect(event.type).toBe("budget-changed");
|
||||
expect(event.companyId).toBe("c-1");
|
||||
});
|
||||
|
||||
it("all five new event types are assignable to DesktopEvent union", () => {
|
||||
const events: DesktopEvent[] = [
|
||||
{ type: "task-updated", taskId: "t-1", action: "created" },
|
||||
{ type: "run-updated", runId: "r-1", action: "status_changed" },
|
||||
{ type: "approval-updated", approvalId: "a-1", action: "created" },
|
||||
{ type: "agent-updated", agentId: "ag-1", action: "paused" },
|
||||
{ type: "budget-changed", companyId: "c-1" },
|
||||
];
|
||||
expect(events).toHaveLength(5);
|
||||
});
|
||||
|
||||
it("DesktopEvent union has all 14 members (9 original + 5 new)", () => {
|
||||
// This is a compile-time check -- if any type is missing, TypeScript will error
|
||||
const exhaustiveCheck = (event: DesktopEvent): string => {
|
||||
switch (event.type) {
|
||||
case "run-log":
|
||||
return "run-log";
|
||||
case "domain-changed":
|
||||
return "domain-changed";
|
||||
case "shortcut":
|
||||
return "shortcut";
|
||||
case "run-status":
|
||||
return "run-status";
|
||||
case "heartbeat-status":
|
||||
return "heartbeat-status";
|
||||
case "agent-message":
|
||||
return "agent-message";
|
||||
case "automation-fired":
|
||||
return "automation-fired";
|
||||
case "new-message":
|
||||
return "new-message";
|
||||
case "notification-click":
|
||||
return "notification-click";
|
||||
case "task-updated":
|
||||
return "task-updated";
|
||||
case "run-updated":
|
||||
return "run-updated";
|
||||
case "approval-updated":
|
||||
return "approval-updated";
|
||||
case "agent-updated":
|
||||
return "agent-updated";
|
||||
case "budget-changed":
|
||||
return "budget-changed";
|
||||
default: {
|
||||
const _exhaustive: never = event;
|
||||
return _exhaustive;
|
||||
}
|
||||
}
|
||||
};
|
||||
// Dummy call to avoid unused-var warning
|
||||
expect(typeof exhaustiveCheck).toBe("function");
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user