feat(team-mode): add team mailbox ack with tests
This commit is contained in:
@@ -0,0 +1,45 @@
|
|||||||
|
/// <reference types="bun-types" />
|
||||||
|
|
||||||
|
import { describe, expect, test } from "bun:test"
|
||||||
|
import { mkdtemp, readdir } from "node:fs/promises"
|
||||||
|
import { randomUUID } from "node:crypto"
|
||||||
|
import { tmpdir } from "node:os"
|
||||||
|
import path from "node:path"
|
||||||
|
|
||||||
|
import { TeamModeConfigSchema } from "../../../config/schema/team-mode"
|
||||||
|
import { getInboxDir, resolveBaseDir } from "../team-registry/paths"
|
||||||
|
import { ackMessages } from "./ack"
|
||||||
|
import { sendMessage } from "./send"
|
||||||
|
|
||||||
|
async function createBaseDirectory(): Promise<string> {
|
||||||
|
return await mkdtemp(path.join(tmpdir(), "team-mailbox-ack-"))
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("ackMessages", () => {
|
||||||
|
test("moves inbox files into processed and stays idempotent", async () => {
|
||||||
|
// given
|
||||||
|
const config = TeamModeConfigSchema.parse({ base_dir: await createBaseDirectory() })
|
||||||
|
const teamRunId = randomUUID()
|
||||||
|
const messageId = randomUUID()
|
||||||
|
await sendMessage({
|
||||||
|
version: 1,
|
||||||
|
messageId,
|
||||||
|
from: "lead",
|
||||||
|
to: "m1",
|
||||||
|
kind: "message",
|
||||||
|
body: "hello",
|
||||||
|
timestamp: 100,
|
||||||
|
}, teamRunId, config, { isLead: true, activeMembers: ["m1"] })
|
||||||
|
|
||||||
|
// when
|
||||||
|
await ackMessages(teamRunId, "m1", [messageId], config)
|
||||||
|
await ackMessages(teamRunId, "m1", [messageId], config)
|
||||||
|
|
||||||
|
// then
|
||||||
|
const inboxDir = getInboxDir(resolveBaseDir(config), teamRunId, "m1")
|
||||||
|
const inboxEntries = await readdir(inboxDir)
|
||||||
|
const processedEntries = await readdir(path.join(inboxDir, "processed"))
|
||||||
|
expect(inboxEntries).not.toContain(`${messageId}.json`)
|
||||||
|
expect(processedEntries).toContain(`${messageId}.json`)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { mkdir, rename } from "node:fs/promises"
|
||||||
|
import path from "node:path"
|
||||||
|
|
||||||
|
import type { TeamModeConfig } from "../../../config/schema/team-mode"
|
||||||
|
import { getInboxDir, resolveBaseDir } from "../team-registry/paths"
|
||||||
|
|
||||||
|
export async function ackMessages(
|
||||||
|
teamRunId: string,
|
||||||
|
memberName: string,
|
||||||
|
messageIds: string[],
|
||||||
|
config: TeamModeConfig,
|
||||||
|
): Promise<void> {
|
||||||
|
const baseDir = resolveBaseDir(config)
|
||||||
|
const inboxDir = getInboxDir(baseDir, teamRunId, memberName)
|
||||||
|
const processedDir = path.join(inboxDir, "processed")
|
||||||
|
await mkdir(processedDir, { recursive: true, mode: 0o700 })
|
||||||
|
|
||||||
|
for (const messageId of messageIds) {
|
||||||
|
const messageFileName = `${messageId}.json`
|
||||||
|
const sourcePath = path.join(inboxDir, messageFileName)
|
||||||
|
const targetPath = path.join(processedDir, messageFileName)
|
||||||
|
|
||||||
|
try {
|
||||||
|
await rename(sourcePath, targetPath)
|
||||||
|
} catch (error) {
|
||||||
|
const err = error as NodeJS.ErrnoException
|
||||||
|
if (err.code === "ENOENT") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user