Files
kandev/apps/web/components/settings/agents/agent-admin-gating.test.tsx
T
João Salavisa 481359ddb2 feat(auth): organization units, tenancy, roles and scopes for team access (#2919)
Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-05 13:40:43 +00:00

116 lines
4.1 KiB
TypeScript

import { cleanup, render, screen } from "@testing-library/react";
import type { ReactNode } from "react";
import { afterEach, describe, expect, it, vi } from "vitest";
import { TooltipProvider } from "@kandev/ui/tooltip";
import type { Agent, AgentProfile } from "@/lib/types/http";
// Agents and agent profiles are org configuration: every mutating route behind
// these components requires org.config.manage, which only an administrator
// holds. isAdmin is the frontend's mirror of that scope, so it is the one knob
// these cases turn.
let isAdmin = true;
vi.mock("@/hooks/domains/auth/use-is-admin", () => ({ useIsAdmin: () => isAdmin }));
const storeState = {
settingsAgents: { items: [] as Agent[] },
agentProfiles: { items: [] as Array<{ id: string }> },
};
vi.mock("@/components/state-provider", () => ({
useAppStore: (selector: (s: unknown) => unknown) => selector(storeState),
useAppStoreApi: () => ({ getState: () => storeState, setState: vi.fn() }),
}));
vi.mock("@/app/actions/agents", () => ({
deleteAgentProfileAction: vi.fn(),
duplicateAgentProfileAction: vi.fn(),
}));
vi.mock("@/components/toast-provider", () => ({ useToast: () => ({ toast: vi.fn() }) }));
vi.mock("@/lib/routing/client-router", () => ({
useRouter: () => ({ push: vi.fn() }),
usePathname: () => "/settings/agents",
}));
vi.mock("@/hooks/use-responsive-breakpoint", () => ({
useResponsiveBreakpoint: () => ({ isFullDesktop: true, isFinePointer: true }),
}));
vi.mock("@/components/routing/app-link", () => ({
default: ({ children, ...props }: { children?: ReactNode }) => <a {...props}>{children}</a>,
}));
vi.mock("@kandev/ui/dropdown-menu", () => ({
DropdownMenu: ({ children }: { children?: ReactNode }) => <div>{children}</div>,
DropdownMenuTrigger: ({ children }: { children?: ReactNode }) => <>{children}</>,
DropdownMenuContent: ({ children }: { children?: ReactNode }) => <div>{children}</div>,
DropdownMenuItem: ({ children }: { children?: ReactNode }) => <button>{children}</button>,
}));
import { ProfileRow } from "./agent-profiles-section";
import { InstallAgentCard } from "@/components/settings/install-agent-card";
import type { AvailableAgent } from "@/lib/types/http";
const AGENT = {
id: "agent-1",
name: "claude",
profiles: [{ id: "p-1", name: "Alpha", agentDisplayName: "Claude Code" } as AgentProfile],
} as unknown as Agent;
const AVAILABLE = {
name: "claude",
display_name: "Claude Code",
install_script: "npm i -g claude",
} as unknown as AvailableAgent;
afterEach(() => {
cleanup();
isAdmin = true;
});
describe("agent settings write controls follow org.config.manage", () => {
it("offers a profile's duplicate and delete actions to an administrator", () => {
render(
<TooltipProvider>
<ProfileRow agent={AGENT} profile={AGENT.profiles[0]} />
</TooltipProvider>,
);
expect(screen.queryByTestId("duplicate-profile-inline-p-1")).not.toBeNull();
expect(screen.queryByTestId("delete-profile-inline-p-1")).not.toBeNull();
});
// The row itself stays: seeing which profiles exist is a read, and the task
// dialog offers the same profiles. Only the writes go.
it("withholds them from a member, keeping the row readable", () => {
isAdmin = false;
render(
<TooltipProvider>
<ProfileRow agent={AGENT} profile={AGENT.profiles[0]} />
</TooltipProvider>,
);
expect(screen.getByText("Alpha")).toBeTruthy();
expect(screen.queryByTestId("duplicate-profile-inline-p-1")).toBeNull();
expect(screen.queryByTestId("delete-profile-inline-p-1")).toBeNull();
expect(screen.queryByTestId("profile-actions-menu-p-1")).toBeNull();
});
it("renders the install button only when the caller may install", () => {
const { rerender } = render(
<TooltipProvider>
<InstallAgentCard agent={AVAILABLE} job={undefined} onInstall={vi.fn()} />
</TooltipProvider>,
);
expect(screen.queryByTestId("install-button-claude")).not.toBeNull();
rerender(
<TooltipProvider>
<InstallAgentCard agent={AVAILABLE} job={undefined} />
</TooltipProvider>,
);
expect(screen.queryByTestId("install-button-claude")).toBeNull();
});
});