fix(permissions): tools MCP inter-projets exposés (idea_project_*) + contactable agent/template persisté — update_agent_contactable (application/web-server/app-tauri), defaultContactable TemplateEditor save/reopen, groupes MCP 'IdeA inter-projets' avec fallback, permissions agent Contactable — #267 (QA verte)
This commit is contained in:
@ -17,7 +17,12 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { render, screen, waitFor, fireEvent } from "@testing-library/react";
|
||||
|
||||
import { MockAgentGateway, MockProfileGateway, MockTemplateGateway } from "@/adapters/mock";
|
||||
import {
|
||||
MockAgentGateway,
|
||||
MockPermissionGateway,
|
||||
MockProfileGateway,
|
||||
MockTemplateGateway,
|
||||
} from "@/adapters/mock";
|
||||
import type { Gateways } from "@/ports";
|
||||
import { DIProvider } from "@/app/di";
|
||||
import { TemplatesPanel } from "./TemplatesPanel";
|
||||
@ -33,6 +38,7 @@ interface RenderOpts {
|
||||
agent?: MockAgentGateway;
|
||||
template?: MockTemplateGateway;
|
||||
profile?: MockProfileGateway;
|
||||
permission?: MockPermissionGateway;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -43,11 +49,13 @@ function renderTemplatesPanel(opts: RenderOpts = {}) {
|
||||
const agent = opts.agent ?? new MockAgentGateway();
|
||||
const tmpl = opts.template ?? new MockTemplateGateway(agent);
|
||||
const profile = opts.profile ?? new MockProfileGateway();
|
||||
const gateways = { agent, profile, template: tmpl } as unknown as Gateways;
|
||||
const permission = opts.permission ?? new MockPermissionGateway();
|
||||
const gateways = { agent, permission, profile, template: tmpl } as unknown as Gateways;
|
||||
return {
|
||||
agent,
|
||||
tmpl,
|
||||
profile,
|
||||
permission,
|
||||
...render(
|
||||
<DIProvider gateways={gateways}>
|
||||
<TemplatesPanel projectId={PROJECT_ID} />
|
||||
@ -187,11 +195,40 @@ describe("TemplatesPanel (with MockTemplateGateway)", () => {
|
||||
expect(templates[0]).toMatchObject({
|
||||
defaultAgentName: "Main",
|
||||
defaultProfileId: "profile-main",
|
||||
systemPermissions: { network: "allow", crossProjectContactable: true },
|
||||
defaultContactable: true,
|
||||
systemPermissions: { network: "allow" },
|
||||
mcpToolPermissions: { allowedTools: ["idea_ticket_read"] },
|
||||
});
|
||||
});
|
||||
|
||||
it("preserves defaultContactable after save and reopen", async () => {
|
||||
const { tmpl } = renderTemplatesPanel();
|
||||
await waitForTemplatesIdle();
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "create template" }));
|
||||
await screen.findByLabelText("template name");
|
||||
fireEvent.change(screen.getByLabelText("template name"), {
|
||||
target: { value: "Contactable" },
|
||||
});
|
||||
fireEvent.click(screen.getByLabelText("Contactable depuis d’autres projets"));
|
||||
fireEvent.click(screen.getByRole("button", { name: "Save template" }));
|
||||
|
||||
await screen.findByText("Contactable");
|
||||
expect((await tmpl.listTemplates())[0]).toMatchObject({
|
||||
defaultContactable: true,
|
||||
systemPermissions: null,
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "edit Contactable" }));
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
(screen.getByLabelText(
|
||||
"Contactable depuis d’autres projets",
|
||||
) as HTMLInputElement).checked,
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it("defaults template cross-project contactability off when the field is absent", async () => {
|
||||
renderTemplatesPanel();
|
||||
await waitForTemplatesIdle();
|
||||
@ -211,6 +248,28 @@ describe("TemplatesPanel (with MockTemplateGateway)", () => {
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it("shows cross-project and ungrouped catalogued MCP tools in the template editor", async () => {
|
||||
const permission = new MockPermissionGateway();
|
||||
permission.setMcpToolCatalogue({
|
||||
readOnlyTools: ["idea_project_list", "idea_future_read"],
|
||||
writeActionTools: ["idea_project_cancel_agent"],
|
||||
});
|
||||
renderTemplatesPanel({ permission });
|
||||
await waitForTemplatesIdle();
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "create template" }));
|
||||
|
||||
expect(await screen.findByText("IdeA inter-projets")).toBeTruthy();
|
||||
expect(
|
||||
screen.getByLabelText("template MCP tool idea_project_cancel_agent"),
|
||||
).toBeTruthy();
|
||||
expect(screen.getByText("Autres")).toBeTruthy();
|
||||
const fallback = screen.getByLabelText(
|
||||
"template MCP tool idea_future_read",
|
||||
) as HTMLInputElement;
|
||||
expect(fallback.disabled).toBe(false);
|
||||
});
|
||||
|
||||
it("the Create button is disabled when the name is empty", async () => {
|
||||
renderTemplatesPanel();
|
||||
await waitForTemplatesIdle();
|
||||
|
||||
Reference in New Issue
Block a user