agent-ecosystem/mcp-server/test/schemas.test.ts
iliya 161c675aaa feat: update pnpm lockfile and workspace configuration, enhance tool usage tracking and UI components
- Added new dependencies for mcp-server in pnpm-lock.yaml, including fastmcp and zod.
- Updated pnpm-workspace.yaml to include mcp-server in the workspace packages.
- Modified TeamDataService and TeamProvisioningService to exclude 'SendMessage' from tool usage counts, improving accuracy in tool tracking.
- Enhanced ChatHistory component to display context injection percentages, improving user feedback on context usage.
- Updated TeamDetailView to ensure session details are fetched periodically for active tabs, enhancing data freshness.
- Improved ActivityTimeline styling for better visual separation of sessions and messages.
2026-03-06 15:54:55 +02:00

220 lines
7.4 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import {
teamNameSchema,
taskIdSchema,
memberNameSchema,
taskStatusSchema,
kanbanColumnSchema,
clarificationSchema,
linkTypeSchema,
linkOperationSchema,
reviewDecisionSchema,
reviewerOperationSchema,
taskIdsArraySchema,
filePathSchema,
safeFilenameSchema,
mimeTypeSchema,
} from '../src/schemas.js';
describe('teamNameSchema', () => {
it('accepts valid team names', () => {
expect(teamNameSchema.parse('acme')).toBe('acme');
expect(teamNameSchema.parse('my-team')).toBe('my-team');
expect(teamNameSchema.parse('My_Team')).toBe('My_Team');
expect(teamNameSchema.parse('team123')).toBe('team123');
});
it('rejects empty', () => {
expect(() => teamNameSchema.parse('')).toThrow();
});
it('rejects path traversal', () => {
expect(() => teamNameSchema.parse('..')).toThrow();
expect(() => teamNameSchema.parse('.')).toThrow();
expect(() => teamNameSchema.parse('a/b')).toThrow();
expect(() => teamNameSchema.parse('a\\b')).toThrow();
expect(() => teamNameSchema.parse('a..b')).toThrow();
});
it('rejects null bytes', () => {
expect(() => teamNameSchema.parse('a\0b')).toThrow();
});
it('rejects too long', () => {
expect(() => teamNameSchema.parse('a'.repeat(129))).toThrow();
});
});
describe('taskIdSchema', () => {
it('accepts numeric IDs', () => {
expect(taskIdSchema.parse('1')).toBe('1');
expect(taskIdSchema.parse('42')).toBe('42');
expect(taskIdSchema.parse('1234567890')).toBe('1234567890');
});
it('accepts zero', () => {
expect(taskIdSchema.parse('0')).toBe('0');
});
it('accepts leading zeros', () => {
expect(taskIdSchema.parse('007')).toBe('007');
});
it('rejects non-numeric', () => {
expect(() => taskIdSchema.parse('abc')).toThrow();
expect(() => taskIdSchema.parse('')).toThrow();
expect(() => taskIdSchema.parse('1.5')).toThrow();
expect(() => taskIdSchema.parse('-1')).toThrow();
});
it('rejects too long', () => {
expect(() => taskIdSchema.parse('12345678901')).toThrow();
});
});
describe('memberNameSchema', () => {
it('accepts valid member names', () => {
expect(memberNameSchema.parse('alice')).toBe('alice');
expect(memberNameSchema.parse('bob-smith')).toBe('bob-smith');
expect(memberNameSchema.parse('user_1')).toBe('user_1');
});
it('rejects path traversal', () => {
expect(() => memberNameSchema.parse('..')).toThrow();
expect(() => memberNameSchema.parse('a/b')).toThrow();
expect(() => memberNameSchema.parse('a\\b')).toThrow();
expect(() => memberNameSchema.parse('a..b')).toThrow();
});
it('rejects null bytes', () => {
expect(() => memberNameSchema.parse('a\0b')).toThrow();
});
it('rejects too long', () => {
expect(() => memberNameSchema.parse('a'.repeat(129))).toThrow();
});
it('rejects empty', () => {
expect(() => memberNameSchema.parse('')).toThrow();
});
});
describe('enum schemas', () => {
it('taskStatusSchema accepts valid values', () => {
expect(taskStatusSchema.parse('pending')).toBe('pending');
expect(taskStatusSchema.parse('in_progress')).toBe('in_progress');
expect(taskStatusSchema.parse('completed')).toBe('completed');
expect(taskStatusSchema.parse('deleted')).toBe('deleted');
expect(() => taskStatusSchema.parse('invalid')).toThrow();
});
it('kanbanColumnSchema accepts valid values', () => {
expect(kanbanColumnSchema.parse('review')).toBe('review');
expect(kanbanColumnSchema.parse('approved')).toBe('approved');
expect(() => kanbanColumnSchema.parse('todo')).toThrow();
expect(() => kanbanColumnSchema.parse('')).toThrow();
});
it('clarificationSchema accepts valid values', () => {
expect(clarificationSchema.parse('lead')).toBe('lead');
expect(clarificationSchema.parse('user')).toBe('user');
expect(clarificationSchema.parse('clear')).toBe('clear');
expect(() => clarificationSchema.parse('nobody')).toThrow();
});
it('linkTypeSchema accepts valid values', () => {
expect(linkTypeSchema.parse('blocked-by')).toBe('blocked-by');
expect(linkTypeSchema.parse('blocks')).toBe('blocks');
expect(linkTypeSchema.parse('related')).toBe('related');
});
it('linkOperationSchema accepts valid values', () => {
expect(linkOperationSchema.parse('link')).toBe('link');
expect(linkOperationSchema.parse('unlink')).toBe('unlink');
});
it('reviewDecisionSchema accepts valid values', () => {
expect(reviewDecisionSchema.parse('approve')).toBe('approve');
expect(reviewDecisionSchema.parse('request-changes')).toBe('request-changes');
});
it('reviewerOperationSchema accepts valid values', () => {
expect(reviewerOperationSchema.parse('list')).toBe('list');
expect(reviewerOperationSchema.parse('add')).toBe('add');
expect(reviewerOperationSchema.parse('remove')).toBe('remove');
});
});
describe('taskIdsArraySchema', () => {
it('accepts valid arrays', () => {
expect(taskIdsArraySchema.parse(['1', '2', '3'])).toEqual(['1', '2', '3']);
expect(taskIdsArraySchema.parse([])).toEqual([]);
});
it('rejects arrays with invalid IDs', () => {
expect(() => taskIdsArraySchema.parse(['abc'])).toThrow();
expect(() => taskIdsArraySchema.parse(['1', 'bad'])).toThrow();
});
});
describe('filePathSchema', () => {
it('accepts absolute paths', () => {
expect(filePathSchema.parse('/home/user/file.txt')).toBe('/home/user/file.txt');
expect(filePathSchema.parse('/tmp/attachment.pdf')).toBe('/tmp/attachment.pdf');
});
it('rejects relative paths', () => {
expect(() => filePathSchema.parse('relative/path.txt')).toThrow();
expect(() => filePathSchema.parse('./file.txt')).toThrow();
});
it('rejects path traversal', () => {
expect(() => filePathSchema.parse('/home/user/../etc/passwd')).toThrow();
expect(() => filePathSchema.parse('/home/../../../etc/shadow')).toThrow();
});
it('rejects null bytes', () => {
expect(() => filePathSchema.parse('/home/user/\0evil')).toThrow();
});
it('rejects empty', () => {
expect(() => filePathSchema.parse('')).toThrow();
});
});
describe('safeFilenameSchema', () => {
it('accepts valid filenames', () => {
expect(safeFilenameSchema.parse('report.pdf')).toBe('report.pdf');
expect(safeFilenameSchema.parse('my-file_v2.tar.gz')).toBe('my-file_v2.tar.gz');
});
it('rejects path separators', () => {
expect(() => safeFilenameSchema.parse('../../evil')).toThrow();
expect(() => safeFilenameSchema.parse('dir/file')).toThrow();
expect(() => safeFilenameSchema.parse('dir\\file')).toThrow();
});
it('rejects null bytes', () => {
expect(() => safeFilenameSchema.parse('file\0name')).toThrow();
});
it('rejects too long', () => {
expect(() => safeFilenameSchema.parse('a'.repeat(256))).toThrow();
});
});
describe('mimeTypeSchema', () => {
it('accepts valid MIME types', () => {
expect(mimeTypeSchema.parse('application/pdf')).toBe('application/pdf');
expect(mimeTypeSchema.parse('image/png')).toBe('image/png');
expect(mimeTypeSchema.parse('text/plain')).toBe('text/plain');
expect(mimeTypeSchema.parse('application/octet-stream')).toBe('application/octet-stream');
});
it('rejects invalid formats', () => {
expect(() => mimeTypeSchema.parse('invalid')).toThrow();
expect(() => mimeTypeSchema.parse('/pdf')).toThrow();
expect(() => mimeTypeSchema.parse('application/')).toThrow();
expect(() => mimeTypeSchema.parse('')).toThrow();
});
});