Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions apps/loopover-miner-ui/src/chat-action-plugins-preview.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { beforeEach, describe, expect, it, vi } from "vitest";

import { chatGovernorActionsPlugin } from "../vite-chat-governor-actions";
import { chatDiscoverAttemptActionsPlugin } from "../vite-chat-discover-attempt-actions";

// The two plugins register their chat actions via a dynamic `import("./src/lib/...")` on server start; mock those
// lib modules so the registration functions are spies. vi.hoisted keeps the spies referenceable from the hoisted
// vi.mock factories. The mock paths resolve to the SAME files the plugins import (both resolve to
// apps/loopover-miner-ui/src/lib/chat-*-actions), so a single mock intercepts the plugin's own dynamic import.
const { registerGovernorChatActions, registerDiscoverAttemptChatActions } = vi.hoisted(() => ({
registerGovernorChatActions: vi.fn(),
registerDiscoverAttemptChatActions: vi.fn(),
}));
vi.mock("./lib/chat-governor-actions", () => ({ registerGovernorChatActions }));
vi.mock("./lib/chat-discover-attempt-actions", () => ({ registerDiscoverAttemptChatActions }));

type HookFn = () => void;

beforeEach(() => {
registerGovernorChatActions.mockClear();
registerDiscoverAttemptChatActions.mockClear();
});

describe("chatGovernorActionsPlugin (#7228)", () => {
it("registers the governor chat actions on configureServer", async () => {
const plugin = chatGovernorActionsPlugin();
(plugin.configureServer as HookFn)();
await vi.waitFor(() => expect(registerGovernorChatActions).toHaveBeenCalledTimes(1));
});

it("REGRESSION (#7228): also registers under configurePreviewServer (the `vite preview` deployment path)", async () => {
const plugin = chatGovernorActionsPlugin();
expect(plugin.configurePreviewServer).toBeTypeOf("function");
(plugin.configurePreviewServer as HookFn)();
await vi.waitFor(() => expect(registerGovernorChatActions).toHaveBeenCalledTimes(1));
});
});

describe("chatDiscoverAttemptActionsPlugin (#7228)", () => {
it("registers the discover/attempt chat actions on configureServer", async () => {
const plugin = chatDiscoverAttemptActionsPlugin();
(plugin.configureServer as HookFn)();
await vi.waitFor(() => expect(registerDiscoverAttemptChatActions).toHaveBeenCalledTimes(1));
});

it("REGRESSION (#7228): also registers under configurePreviewServer (the `vite preview` deployment path)", async () => {
const plugin = chatDiscoverAttemptActionsPlugin();
expect(plugin.configurePreviewServer).toBeTypeOf("function");
(plugin.configurePreviewServer as HookFn)();
await vi.waitFor(() => expect(registerDiscoverAttemptChatActions).toHaveBeenCalledTimes(1));
});
});
23 changes: 17 additions & 6 deletions apps/loopover-miner-ui/vite-chat-discover-attempt-actions.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
import type { Plugin } from "vite";

// Registers discover/attempt chat actions into the shared registry on dev-server start (#6837). Handlers call
// the existing miner-ui `requestDiscover` / `requestAttempt` clients — the same POST `/api/discover` and
// Registers discover/attempt chat actions into the shared registry on server start (#6837). Handlers call the
// existing miner-ui `requestDiscover` / `requestAttempt` clients — the same POST `/api/discover` and
// `/api/attempt` path the routes already serve. No new /api/* route is added here (mirrors
// vite-chat-governor-actions.ts).

//
// Registered from BOTH configureServer AND configurePreviewServer (#7228): `vite preview` — the mode the
// README's "persistent service" path and systemd/loopover-miner-ui.service.example actually run — only fires
// configurePreviewServer, so registering on configureServer alone left the chat-action registry empty under
// preview and every discover/attempt chat command dispatched as "unknown_action".
// registerDiscoverAttemptChatActions is already idempotent, so calling it from both hooks is safe.
export function chatDiscoverAttemptActionsPlugin(): Plugin {
const register = () => {
void import("./src/lib/chat-discover-attempt-actions").then((mod) => {
mod.registerDiscoverAttemptChatActions();
});
};
return {
name: "loopover-miner-chat-discover-attempt-actions",
configureServer() {
void import("./src/lib/chat-discover-attempt-actions").then((mod) => {
mod.registerDiscoverAttemptChatActions();
});
register();
},
configurePreviewServer() {
register();
},
};
}
25 changes: 18 additions & 7 deletions apps/loopover-miner-ui/vite-chat-governor-actions.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,27 @@
import type { Plugin } from "vite";

// Registers governor pause/resume chat actions into the shared registry on dev-server start (#6521).
// Handlers call the existing miner-ui `pauseGovernor` / `resumeGovernor` clients — same path as the Ledgers
// buttons. No new /api/governor/* route is added here.

// Registers governor pause/resume chat actions into the shared registry on server start (#6521). Handlers call
// the existing miner-ui `pauseGovernor` / `resumeGovernor` clients — same path as the Ledgers buttons. No new
// /api/governor/* route is added here.
//
// Registered from BOTH configureServer AND configurePreviewServer (#7228): `vite preview` — the mode the
// README's "persistent service" path and systemd/loopover-miner-ui.service.example actually run — only fires
// configurePreviewServer, so registering on configureServer alone left the chat-action registry empty under
// preview and every governor chat command dispatched as "unknown_action". registerGovernorChatActions is already
// idempotent, so calling it from both hooks is safe.
export function chatGovernorActionsPlugin(): Plugin {
const register = () => {
void import("./src/lib/chat-governor-actions").then((mod) => {
mod.registerGovernorChatActions();
});
};
return {
name: "loopover-miner-chat-governor-actions",
configureServer() {
void import("./src/lib/chat-governor-actions").then((mod) => {
mod.registerGovernorChatActions();
});
register();
},
configurePreviewServer() {
register();
},
};
}
Loading