MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 2m19s
MindSift now speaks MCP (Streamable HTTP, stateless JSON-RPC) at POST /api/v1/mcp so Claude Code, Codex, Claude Desktop, Cursor and custom agents can read, create and complete tasks and keep shared lists. API keys get the scopes tasks:read/write and lists:read/write; tools/list only shows what the key may call and every tool checks its scope. Task and list write paths are shared helpers (updateTask, createTask, addListItem, setListItemDone) used by REST and MCP alike. Settings: scope selection and the SectionPicker (import, restore) use ToggleSwitch rows instead of checkbox-like buttons; new MCP section with connection snippets for Claude Code, Codex and JSON configs. Docs, README, help and changelog updated; v0.28.0. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
51 lines
2.3 KiB
TypeScript
51 lines
2.3 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import { render, screen, fireEvent, act } from '@testing-library/react';
|
|
|
|
const createAPIKey = vi.fn(async () => ({ id: 'k1', userId: 'u', name: 'HA', keyPrefix: 'mindsift_sec_ab', scopes: 'webhook:triage,webhook:list', createdAt: '' }));
|
|
vi.mock('../../../lib/api', () => ({
|
|
api: {
|
|
getAPIKeys: vi.fn(async () => [
|
|
{ id: 'k0', userId: 'u', name: 'Rauchmelder', keyPrefix: 'mindsift_sec_xy', scopes: 'webhook:triage,webhook:task', createdAt: '' },
|
|
]),
|
|
getAllLists: vi.fn(async () => []),
|
|
getTaskLists: vi.fn(async () => []),
|
|
createAPIKey: (...args: unknown[]) => createAPIKey(...(args as [])),
|
|
deleteAPIKey: vi.fn(),
|
|
testWebhookIngest: vi.fn(),
|
|
},
|
|
triggerHaptic: vi.fn(),
|
|
}));
|
|
|
|
import { ApiKeysPanel } from '../ApiKeysPanel';
|
|
|
|
describe('ApiKeysPanel', () => {
|
|
beforeEach(() => createAPIKey.mockClear());
|
|
|
|
it('shows the scopes of existing keys as badges', async () => {
|
|
render(<ApiKeysPanel />);
|
|
expect(await screen.findByText('Rauchmelder')).toBeTruthy();
|
|
expect(screen.getAllByText('📋 Aufgaben anlegen').length).toBeGreaterThan(1); // switch label + badge
|
|
});
|
|
|
|
it('creates a key with the chosen scopes and never without one', async () => {
|
|
render(<ApiKeysPanel />);
|
|
const triage = screen.getByRole('switch', { name: /Triage/ });
|
|
fireEvent.click(triage); // last remaining scope cannot be switched off
|
|
expect(triage.getAttribute('aria-checked')).toBe('true');
|
|
fireEvent.click(screen.getByRole('switch', { name: /Listeneinträge anlegen/ }));
|
|
await act(async () => {
|
|
fireEvent.click(screen.getByRole('button', { name: /API-Key erzeugen/ }));
|
|
});
|
|
expect(createAPIKey).toHaveBeenCalledWith('Home Assistant', ['webhook:triage', 'webhook:list']);
|
|
});
|
|
|
|
it('offers agent scopes and shows how to connect an MCP client', async () => {
|
|
render(<ApiKeysPanel />);
|
|
fireEvent.click(screen.getByRole('switch', { name: /Aufgaben ändern/ }));
|
|
expect(screen.getByRole('switch', { name: /Aufgaben ändern/ }).getAttribute('aria-checked')).toBe('true');
|
|
expect(screen.getByText(/claude mcp add --transport http mindsift/)).toBeTruthy();
|
|
fireEvent.click(screen.getByRole('radio', { name: 'OpenAI Codex' }));
|
|
expect(screen.getByText(/\[mcp_servers\.mindsift\]/)).toBeTruthy();
|
|
});
|
|
});
|