Every feature needs tests. Coverage โ‰ฅ70%. Security tests 100%. No exceptions. # Testing & Quality - Virsaitis **Module**: Testing & Quality **Load**: When writing tests, checking quality gates **Version**: 3.0.0 **Updated**: 2026-02-17 --- ## ๐ŸŽฏ Purpose Defines testing standards, coverage targets, quality metrics, and validation procedures for all Virsaitis components. --- ## ๐Ÿค– Machine Policy ``` [TESTING_STANDARDS] FRAMEWORK_MCP=vitest FRAMEWORK_EXTENSION=@vscode/test-electron FRAMEWORK_AGENT=manual_review TDD=preferred [COVERAGE_TARGETS] OVERALL=70_percent_minimum SECURITY_CRITICAL=100_percent_required GOVERNANCE=100_percent_required UTILITIES=70_percent [QUALITY_GATES] BUILD=must_succeed TESTS=must_pass_all LINT=zero_errors COVERAGE=meet_targets SECURITY_TESTS=100_percent_pass ``` --- ## ๐Ÿงช Testing Frameworks ### MCP Server (TypeScript) **FRAMEWORK**: Vitest **vitest.config.ts**: ```typescript import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { coverage: { provider: 'v8', reporter: ['text', 'html', 'lcov'], lines: 70, functions: 70, branches: 70, statements: 70, exclude: [ 'node_modules/', 'build/', '**/*.test.ts', '**/*.spec.ts', ], }, globals: true, environment: 'node', }, }); ``` **RUN TESTS**: ```bash npm test # Run all tests npm run test:watch # Watch mode npm run test:coverage # Coverage report npm run test:ui # UI interface ``` ### VS Code Extension (TypeScript) **FRAMEWORK**: @vscode/test-electron **test/runTest.ts**: ```typescript import * as path from 'path'; import { runTests } from '@vscode/test-electron'; async function main() { try { const extensionDevelopmentPath = path.resolve(__dirname, '../../'); const extensionTestsPath = path.resolve(__dirname, './suite/index'); await runTests({ extensionDevelopmentPath, extensionTestsPath, }); } catch (err) { console.error('Failed to run tests'); process.exit(1); } } main(); ``` **RUN TESTS**: ```bash npm test # Run extension tests ``` Tests run in Extension Development Host (isolated VS Code instance). ### Agent (Markdown) **VALIDATION**: Manual review **CHECKLIST**: - [ ] Atomic sentence structure (one concept per sentence) - [ ] Each sentence <80 characters - [ ] No compound clauses - [ ] Clear subject-verb-object - [ ] Standalone comprehensibility **NO AUTOMATED TESTING** (atomic structure requires human judgment) --- ## ๐Ÿ“Š Coverage Targets (TIER-1) ### Minimum Coverage | Component | Overall | Security | Governance | |-----------|---------|----------|------------| | **MCP Server** | โ‰ฅ70% | 100% | 100% | | **Extension** | โ‰ฅ70% | 100% | 100% | | **Agent** | Manual | N/A | Manual | | **Skills** | Manual | N/A | Manual | ### What to Cover **MUST COVER (100%)**: - Security-critical code (secret scanning, validation) - Governance enforcement (TIER validation, file protection) - MCP tool handlers (core governance tools) - Extension interceptors (file operation blocking) **SHOULD COVER (โ‰ฅ70%)**: - Business logic - Data transformations - Error handling - Configuration management - Utility functions **CAN SKIP**: - Generated code - Third-party library wrappers (covered by library tests) - Simple getters/setters (if trivial) - Type definitions only files --- ## โœ… Test Structure ### Unit Test Pattern ```typescript import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { GovernanceValidator } from '../src/governance/validator'; describe('GovernanceValidator', () => { let validator: GovernanceValidator; beforeEach(() => { // Setup: Create fresh validator instance validator = new GovernanceValidator(); }); afterEach(() => { // Cleanup: Dispose resources validator.dispose(); }); describe('validateFileOperation', () => { describe('Protected Files', () => { it('should block modification of copilot-instructions.md', () => { // Given const operation = 'write'; const filePath = '.github/copilot-instructions.md'; // When const result = validator.validateFileOperation(operation, filePath); // Then expect(result.allowed).toBe(false); expect(result.tier).toBe('TIER-0'); expect(result.reason).toContain('protected file'); }); it('should block modification of agent files', () => { // Given const operation = 'write'; const filePath = '.github/agents/Virsaitis.agent.md'; // When const result = validator.validateFileOperation(operation, filePath); // Then expect(result.allowed).toBe(false); expect(result.tier).toBe('TIER-0'); }); }); describe('Non-Protected Files', () => { > โšก CHECKPOINT โ€” Security tests at 100% coverage? TIER-0 rules must have multiple test cases. it('should allow modification of source files', () => { // Given const operation = 'write'; const filePath = 'src/my-file.ts'; // When const result = validator.validateFileOperation(operation, filePath); // Then expect(result.allowed).toBe(true); expect(result.tier).toBeUndefined(); }); }); describe('Edge Cases', () => { it('should handle null file path', () => { // Given const operation = 'write'; const filePath = null as any; // When/Then expect(() => validator.validateFileOperation(operation, filePath)) .toThrow('File path is required'); }); it('should handle path traversal attempts', () => { // Given const operation = 'write'; const filePath = '../../../etc/passwd'; // When/Then expect(() => validator.validateFileOperation(operation, filePath)) .toThrow('Path traversal detected'); }); }); }); }); ``` ### Integration Test Pattern ```typescript describe('MCP Server Integration', () => { let server: MCPServer; let client: Client; let transport: StdioClientTransport; beforeAll(async () => { // Start MCP server via stdio transport transport = new StdioClientTransport({ command: 'node', args: ['build/index.js'] }); client = new Client({ name: 'test-client', version: '1.0.0' }); await client.connect(transport); }); afterAll(async () => { // Cleanup await server.stop(); }); it('should validate protected file operation via MCP', async () => { // Given const request = { operation: 'write', filePath: '.github/copilot-instructions.md', }; // When const response = await client.callTool('mcp_virsaitis_validate_operation', request); // Then expect(response.allowed).toBe(false); expect(response.tier).toBe('TIER-0'); }); }); ``` --- ## ๐Ÿ”’ Security Testing (TIER-1) ### Security Test Requirements **100% COVERAGE REQUIRED**: - Secret detection (all patterns) - Path traversal prevention - Command injection prevention - Input validation - Error handling (no information leaks) ### Security Test Examples ```typescript describe('Security Tests', () => { describe('Secret Detection', () => { it('should detect hardcoded API keys', () => { const code = 'const apiKey = "sk-abc123def456";'; const result = secretScanner.scan(code); expect(result.violations).toContainEqual({ type: 'API_KEY', line: 1, pattern: 'sk-abc123def456', }); }); it('should detect AWS access keys', () => { const code = 'AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE'; const result = secretScanner.scan(code); expect(result.violations).toHaveLength(1); }); it('should not flag environment variable references', () => { const code = 'const apiKey = process.env.API_KEY;'; const result = secretScanner.scan(code); expect(result.violations).toHaveLength(0); }); }); describe('Path Traversal Prevention', () => { it('should block ../ in file paths', () => { expect(() => validatePath('../../../etc/passwd')) .toThrow('Path traversal detected'); }); it('should block ~/ in file paths', () => { expect(() => validatePath('~/sensitive-file')) .toThrow('Path traversal detected'); }); }); describe('Command Injection Prevention', () => { it('should block shell metacharacters', () => { expect(() => executeCommand('npm', ['install', '; rm -rf /'])) .toThrow('Invalid argument'); }); }); }); ``` --- ## ๐ŸŽฏ Test-Driven Development (TDD) ### Red-Green-Refactor Cycle ``` 1. RED: Write failing test โ†“ 2. GREEN: Write minimum code to pass โ†“ 3. REFACTOR: Improve code quality โ†“ 4. REPEAT ``` ### TDD Example **STEP 1: Red (Write Failing Test)** ```typescript it('should block protected file modification', () => { const result = validator.validateFileOperation('write', '.github/copilot-instructions.md'); expect(result.allowed).toBe(false); }); ``` Run test: โŒ FAILS (validator not implemented) **STEP 2: Green (Minimum Implementation)** ```typescript validateFileOperation(operation: string, filePath: string): ValidationResult { if (filePath === '.github/copilot-instructions.md') { return { allowed: false, tier: 'TIER-0' }; } return { allowed: true }; } ``` Run test: โœ… PASSES **STEP 3: Refactor (Improve)** ```typescript validateFileOperation(operation: string, filePath: string): ValidationResult { const protectedPatterns = [ '.github/copilot-instructions.md', '.github/copilot-modules/', '.github/agents/', ]; const isProtected = protectedPatterns.some(pattern => filePath.includes(pattern)); if (isProtected) { return { allowed: false, tier: 'TIER-0', reason: 'Protected file modification blocked', }; } return { allowed: true }; } ``` Run test: โœ… STILL PASSES --- ## ๐Ÿ“ Quality Metrics ### Code Quality Standards (TIER-2) **LINTING**: Zero errors, warnings acceptable **COMPLEXITY**: Cyclomatic complexity <15 per function **DUPLICATION**: <5% code duplication **MAINTAINABILITY INDEX**: >70 (good), >50 (acceptable) ### Measure Quality > โšก CHECKPOINT โ€” Coverage โ‰ฅ70% overall? All tests passing? No skipped tests? ```bash # Linting npm run lint # Type checking npm run type-check # Complexity (if tool available) npx complexity-report src/ # Duplication (if tool available) npx jscpd src/ ``` --- ## ๐Ÿšฆ Quality Gates (TIER-1) ### Pre-Commit Gates **ALL MUST PASS**: ```bash npm run build # โœ… Build succeeds npm test # โœ… All tests pass npm run lint # โœ… Zero linter errors npm run type-check # โœ… TypeScript strict mode npm run test:coverage # โœ… Coverage โ‰ฅ70% npm run test:security # โœ… Security tests 100% pass ``` **IF ANY FAIL**: Must fix before commit ### Pre-Merge Gates **ALL MUST PASS**: - [ ] All pre-commit gates passed - [ ] Code review approved - [ ] Documentation updated - [ ] CHANGELOG updated - [ ] traceability.csv updated - [ ] No TIER-0 violations introduced - [ ] Performance acceptable (no regressions) ### Pre-Release Gates **ALL MUST PASS**: - [ ] All pre-merge gates passed - [ ] End-to-end tests pass - [ ] Manual testing complete (critical paths) - [ ] Distribution package built successfully - [ ] Installation instructions verified - [ ] Migration guide written (if breaking changes) --- ## ๐Ÿ”„ Continuous Integration ### CI Pipeline ```yaml # .github/workflows/test.yml name: Test on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup Node.js uses: actions/setup-node@v3 with: node-version: '18' - name: Install dependencies run: npm ci - name: Build run: npm run build - name: Lint run: npm run lint - name: Type check run: npm run type-check - name: Test run: npm test - name: Coverage run: npm run test:coverage - name: Upload coverage uses: codecov/codecov-action@v3 ``` --- ## ๐Ÿ“– Test Documentation ### Test Naming **CONVENTION**: ``` describe('[Component/Feature]', () => { describe('[Method/Function]', () => { it('should [expected behavior] when [condition]', () => { // Test implementation }); }); }); ``` **EXAMPLES**: > โšก CHECKPOINT โ€” Test names follow pattern: describe('[REQ-ID]') โ†’ describe('[AC]') โ†’ it('should...')? ```typescript describe('GovernanceValidator', () => { describe('validateFileOperation', () => { it('should block protected files when write operation', () => {}); it('should allow non-protected files when write operation', () => {}); it('should throw error when file path is null', () => {}); }); }); ``` ### Test Comments **GIVEN-WHEN-THEN**: ```typescript it('should block protected file modification', () => { // Given: Protected file and write operation const operation = 'write'; const filePath = '.github/copilot-instructions.md'; // When: Validation runs const result = validator.validateFileOperation(operation, filePath); // Then: Operation is blocked with TIER-0 expect(result.allowed).toBe(false); expect(result.tier).toBe('TIER-0'); }); ``` --- ## ๐Ÿ’ก Best Practices ### Test Independence **EACH TEST SHOULD**: - Run independently (no order dependency) - Create own test data - Clean up after itself - Not share state with other tests ### Test Data **PREFER**: - Inline test data (visible in test) - Fixtures for large data - Factories for object creation - Mocks for external dependencies **AVOID**: - Shared mutable state - Real external services (use mocks) - Hard-coded file paths (use temp directories) ### Mocking **WHEN TO MOCK**: - External services (APIs, databases) - File system operations (use in-memory) - Network requests - Time-dependent operations **EXAMPLE**: ```typescript import { vi } from 'vitest'; it('should call MCP server', async () => { // Mock fetch const fetchMock = vi.spyOn(global, 'fetch').mockResolvedValue({ ok: true, json: async () => ({ allowed: false }), } as Response); // Test const result = await mcpClient.validateOperation('write', 'file.ts'); // Verify expect(callToolMock).toHaveBeenCalledWith( 'validate_operation', expect.objectContaining({ operation: 'write' }) ); }); ``` --- ## ๐Ÿ“š Quick Reference | Aspect | Standard | Tool/Command | |--------|----------|--------------| | **Framework (MCP)** | Vitest | `npm test` | | **Framework (Extension)** | @vscode/test-electron | `npm test` | | **Coverage Target** | โ‰ฅ70% overall | `npm run test:coverage` | | **Security Coverage** | 100% required | Security test suite | | **Pre-Commit** | All tests pass | CI/git hooks | | **TDD** | Preferred | Red-Green-Refactor | --- *Testing & Quality Module v3.0.0* *Comprehensive testing standards for Virsaitis* --- ## Key Rules From This Module - Coverage target: โ‰ฅ70% overall, 100% for security-related code. - TDD preferred: Red โ†’ Green โ†’ Refactor. Write failing test first. - Every REQ-ID must have corresponding tests. Update traceability.csv. - All tests must pass before commit. No skipping, no force-push. - Definitions: `.github/virsaitis-definition-library.md` Return to hub: `.github/copilot-instructions.md`