diff --git a/docs/testing-strategy.md b/docs/testing-strategy.md new file mode 100644 index 00000000..f70b6867 --- /dev/null +++ b/docs/testing-strategy.md @@ -0,0 +1,52 @@ +# Multi-Agent Chat Platform: Advanced Testing Strategy + +## Comprehensive Validation Approach + +### Validation Techniques +1. **JSON Schema Validation** + - Strict type checking + - Comprehensive constraint enforcement + - Detailed error reporting + +2. **Runtime Type Validation** + - Dynamic interface verification + - Granular error handling + - Preventive error detection + +### Error Handling Strategies +- Custom error classes +- Detailed error context +- Centralized error reporting +- Graceful degradation + +### Coverage Metrics +- **Unit Test Coverage**: ≥80% +- **Branch Coverage**: ≥80% +- **Function Coverage**: ≥80% +- **Line Coverage**: ≥80% + +## Validation Scopes +1. **Data Integrity** + - Schema conformance + - Value range validation + - Mandatory field enforcement + +2. **Error Scenarios** + - Invalid input detection + - Partial data handling + - Boundary condition testing + +3. **Performance Considerations** + - Minimal validation overhead + - Efficient error generation + - Quick schema compilation + +## Reporting and Monitoring +- Comprehensive test reports +- JSON-based coverage summaries +- Detailed error trace generation + +## Continuous Improvement +- Regular schema updates +- Adaptive validation rules +- Feedback-driven refinement diff --git a/package.json b/package.json new file mode 100644 index 00000000..2b4946e7 --- /dev/null +++ b/package.json @@ -0,0 +1,56 @@ +{ + "name": "multi-agent-chat-platform", + "version": "0.1.0", + "description": "Multi-agent interactive chat platform", + "main": "src/index.ts", + "scripts": { + "test": "jest", + "test:watch": "jest --watch", + "test:coverage": "jest --coverage", + "test:verbose": "jest --verbose", + "lint": "eslint . --ext .ts", + "validate": "npm run lint && npm run test:coverage" + }, + "dependencies": { + "ajv": "^8.12.0" + }, + "devDependencies": { + "@types/jest": "^29.5.3", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "eslint": "^8.44.0", + "jest": "^29.6.2", + "ts-jest": "^29.1.1", + "typescript": "^5.1.6" + }, + "jest": { + "preset": "ts-jest", + "testEnvironment": "node", + "collectCoverage": true, + "coverageReporters": ["text", "lcov", "json-summary"], + "coverageDirectory": "coverage", + "coverageThreshold": { + "global": { + "branches": 80, + "functions": 80, + "lines": 80, + "statements": 80 + } + }, + "testMatch": [ + "**/tests/**/*.test.ts" + ] + }, + "eslintConfig": { + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended" + ], + "rules": { + "no-console": "warn", + "@typescript-eslint/explicit-function-return-type": "error" + } + } +} \ No newline at end of file diff --git a/src/component-interfaces.ts b/src/component-interfaces.ts new file mode 100644 index 00000000..2882c173 --- /dev/null +++ b/src/component-interfaces.ts @@ -0,0 +1,144 @@ +import Ajv from 'ajv'; +import componentSchemas from './schemas/component-schemas.json'; + +/** + * Comprehensive interfaces and validation for multi-agent chat platform components + * @module ComponentInterfaces + */ + +// Advanced Error Classes +export class ValidationError extends Error { + public details: unknown[]; + + constructor(message: string, details: unknown[] = []) { + super(message); + this.name = 'ValidationError'; + this.details = details; + } +} + +export class SchemaValidationError extends ValidationError { + constructor(details: unknown[]) { + super('Schema validation failed', details); + this.name = 'SchemaValidationError'; + } +} + +// JSON Schema Validator +const ajv = new Ajv({ allErrors: true }); + +// Compile schemas +const validatePersonalityProfile = ajv.compile( + componentSchemas.definitions.PersonalityProfile +); +const validateChatbotResponse = ajv.compile( + componentSchemas.definitions.ChatbotResponse +); +const validateConversationSession = ajv.compile( + componentSchemas.definitions.ConversationSession +); + +// Interfaces +export interface PersonalityProfile { + id: string; + name: string; + description: string; + tone: string; + samplePrompts: string[]; + version: number; +} + +export interface ChatbotResponse { + agentId: string; + content: string; + confidence: number; + timestamp: number; +} + +export interface ConversationSession { + sessionId: string; + participants: string[]; + startTime: number; + messages: ChatbotResponse[]; +} + +// Comprehensive Validation Functions +export function validateProfile(profile: PersonalityProfile): boolean { + const isValid = validatePersonalityProfile(profile); + + if (!isValid) { + throw new SchemaValidationError( + validatePersonalityProfile.errors || [] + ); + } + + return true; +} + +export function validateResponse(response: ChatbotResponse): boolean { + const isValid = validateChatbotResponse(response); + + if (!isValid) { + throw new SchemaValidationError( + validateChatbotResponse.errors || [] + ); + } + + return true; +} + +export function validateSession(session: ConversationSession): boolean { + const isValid = validateConversationSession(session); + + if (!isValid) { + throw new SchemaValidationError( + validateConversationSession.errors || [] + ); + } + + return true; +} + +// Factory Functions with Validation +export function createPersonalityProfile( + data: Partial +): PersonalityProfile { + const defaultProfile: PersonalityProfile = { + id: '', + name: '', + description: '', + tone: '', + samplePrompts: [], + version: 1 + }; + + const profile: PersonalityProfile = { ...defaultProfile, ...data }; + + try { + validateProfile(profile); + return profile; + } catch (error) { + if (error instanceof SchemaValidationError) { + throw new ValidationError( + 'Invalid Personality Profile', + error.details + ); + } + throw error; + } +} + +// Error Reporting Utility +export function getValidationErrorDetails( + error: SchemaValidationError +): string[] { + return error.details.map( + (err: { message?: string }) => err.message || 'Unknown validation error' + ); +} + +// Abstract Base Classes +export abstract class BaseComponent { + abstract initialize(): Promise; + abstract validate(): boolean; +} \ No newline at end of file diff --git a/src/schemas/component-schemas.json b/src/schemas/component-schemas.json new file mode 100644 index 00000000..223378fc --- /dev/null +++ b/src/schemas/component-schemas.json @@ -0,0 +1,95 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "definitions": { + "PersonalityProfile": { + "type": "object", + "required": ["id", "name", "samplePrompts", "version"], + "properties": { + "id": { + "type": "string", + "minLength": 1, + "description": "Unique identifier for the personality profile" + }, + "name": { + "type": "string", + "minLength": 1, + "description": "Name of the personality" + }, + "description": { + "type": "string", + "description": "Detailed description of the personality" + }, + "tone": { + "type": "string", + "description": "Characteristic tone of the personality" + }, + "samplePrompts": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "minItems": 1, + "description": "Sample conversation prompts" + }, + "version": { + "type": "number", + "minimum": 1, + "description": "Version of the personality profile" + } + }, + "additionalProperties": false + }, + "ChatbotResponse": { + "type": "object", + "required": ["agentId", "content", "confidence", "timestamp"], + "properties": { + "agentId": { + "type": "string", + "minLength": 1 + }, + "content": { + "type": "string", + "minLength": 1 + }, + "confidence": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "timestamp": { + "type": "number" + } + }, + "additionalProperties": false + }, + "ConversationSession": { + "type": "object", + "required": ["sessionId", "participants", "startTime", "messages"], + "properties": { + "sessionId": { + "type": "string", + "minLength": 1 + }, + "participants": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "minItems": 1 + }, + "startTime": { + "type": "number" + }, + "messages": { + "type": "array", + "items": { + "$ref": "#/definitions/ChatbotResponse" + } + } + }, + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/tests/component-interfaces.test.ts b/tests/component-interfaces.test.ts new file mode 100644 index 00000000..3d99d546 --- /dev/null +++ b/tests/component-interfaces.test.ts @@ -0,0 +1,127 @@ +import { + createPersonalityProfile, + validateProfile, + validateResponse, + validateSession, + ValidationError, + SchemaValidationError, + getValidationErrorDetails, + PersonalityProfile, + ChatbotResponse, + ConversationSession +} from '../src/component-interfaces'; + +describe('Comprehensive Component Interface Validation', () => { + describe('Personality Profile Validation', () => { + const validProfileData: PersonalityProfile = { + id: 'peter_disciple', + name: 'Peter', + description: 'Passionate follower of Jesus', + tone: 'Zealous', + samplePrompts: ['Tell me about your faith'], + version: 1 + }; + + test('creates a valid personality profile', () => { + const profile = createPersonalityProfile(validProfileData); + expect(profile).toEqual(validProfileData); + }); + + test('validates a complete personality profile', () => { + expect(() => validateProfile(validProfileData)).not.toThrow(); + }); + + test('throws SchemaValidationError for invalid profile', () => { + const invalidProfiles = [ + { ...validProfileData, id: '' }, + { ...validProfileData, name: '' }, + { ...validProfileData, samplePrompts: [] }, + { ...validProfileData, version: 0 } + ]; + + invalidProfiles.forEach(profile => { + expect(() => validateProfile(profile)).toThrow(SchemaValidationError); + }); + }); + + test('provides detailed validation error messages', () => { + try { + validateProfile({ + ...validProfileData, + id: '', + name: '' + }); + fail('Should have thrown validation error'); + } catch (error) { + if (error instanceof SchemaValidationError) { + const errorDetails = getValidationErrorDetails(error); + expect(errorDetails.length).toBeGreaterThan(0); + } + } + }); + }); + + describe('Chatbot Response Validation', () => { + const validResponse: ChatbotResponse = { + agentId: 'peter_disciple', + content: 'I believe in Jesus Christ!', + confidence: 0.95, + timestamp: Date.now() + }; + + test('validates a valid chatbot response', () => { + expect(() => validateResponse(validResponse)).not.toThrow(); + }); + + test('throws error for invalid response', () => { + const invalidResponses = [ + { ...validResponse, agentId: '' }, + { ...validResponse, content: '' }, + { ...validResponse, confidence: 1.5 }, + { ...validResponse, confidence: -0.1 } + ]; + + invalidResponses.forEach(response => { + expect(() => validateResponse(response)).toThrow(SchemaValidationError); + }); + }); + }); + + describe('Conversation Session Validation', () => { + const validSession: ConversationSession = { + sessionId: 'last_supper_session', + participants: ['peter', 'john', 'jesus'], + startTime: Date.now(), + messages: [{ + agentId: 'peter_disciple', + content: 'I will never deny you!', + confidence: 0.9, + timestamp: Date.now() + }] + }; + + test('validates a valid conversation session', () => { + expect(() => validateSession(validSession)).not.toThrow(); + }); + + test('throws error for invalid session', () => { + const invalidSessions = [ + { ...validSession, sessionId: '' }, + { ...validSession, participants: [] }, + { + ...validSession, + messages: [{ + agentId: '', + content: '', + confidence: 2, + timestamp: Date.now() + }] + } + ]; + + invalidSessions.forEach(session => { + expect(() => validateSession(session)).toThrow(SchemaValidationError); + }); + }); + }); +}); \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..92b1d94d --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "es2020", + "module": "commonjs", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "outDir": "./dist", + "rootDir": "./src" + }, + "include": ["src/**/*", "tests/**/*"], + "exclude": ["node_modules"] +} \ No newline at end of file