Advertisement

← octto

tests/tools/brainstorm.test.ts

3.4 KB Download file
// tests/tools/brainstorm.test.ts
import { afterEach, beforeEach, describe, expect, it } from "bun:test";
import { mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";

import { createSessionStore } from "../../src/session/sessions";
import { createStateStore } from "../../src/state/store";
import { createBrainstormTools } from "../../src/tools/brainstorm";
import { outputText } from "../../src/tools/output";

describe("Brainstorm Tools", () => {
  let sessions: ReturnType<typeof createSessionStore>;
  let tools: ReturnType<typeof createBrainstormTools>;
  let tempDir: string;

  beforeEach(() => {
    tempDir = mkdtempSync(join(tmpdir(), "octto-brainstorm-test-"));
    sessions = createSessionStore({ skipBrowser: true });
    tools = createBrainstormTools(sessions, undefined as any, tempDir);
  });

  afterEach(async () => {
    await sessions.cleanup();
    rmSync(tempDir, { recursive: true, force: true });
  });

  describe("create_brainstorm", () => {
    it("should create brainstorm session with branches", async () => {
      const result = await tools.create_brainstorm.execute(
        {
          request: "Add healthcheck",
          branches: [
            {
              id: "services",
              scope: "Which services to monitor",
              initial_question: {
                type: "ask_text",
                config: { question: "What services?" },
              },
            },
          ],
        },
        {} as any,
      );

      expect(result).toContain("ses_");
      expect(result).toContain("services");
    });

    it("should point the agent at await_brainstorm_complete rather than a manual answer loop", async () => {
      const result = outputText(
        await tools.create_brainstorm.execute(
          {
            request: "Add healthcheck",
            branches: [
              {
                id: "services",
                scope: "Which services to monitor",
                initial_question: {
                  type: "ask_text",
                  config: { question: "What services?" },
                },
              },
            ],
          },
          {} as any,
        ),
      );

      const nextAction = result.slice(result.indexOf("<next_action>"), result.indexOf("</next_action>"));
      expect(nextAction).toContain("await_brainstorm_complete");
      expect(nextAction).not.toContain("Call get_next_answer");
    });
  });

  describe("await_brainstorm_complete", () => {
    it("should stop collecting instead of spinning when nothing is pending", async () => {
      const stateStore = createStateStore(tempDir);
      await stateStore.createSession("ses_stalled", "req", [{ id: "b1", scope: "scope one" }]);
      const browser = await sessions.startSession({});

      const output = outputText(
        await tools.await_brainstorm_complete.execute(
          { session_id: "ses_stalled", browser_session_id: browser.session_id },
          {} as any,
        ),
      );

      // The stall is observable in the message, not the clock: the old loop spun
      // through all 50 iterations just as fast and then told the agent to retry.
      expect(output).not.toContain("Collected 50 answers");
      expect(output).toContain("cannot progress on its own");
      expect(output).toContain("end_brainstorm");
    });
  });
});