REQ-MOB-010: BarcodeScreen.tsx — barcode scanner via react-native-camera REQ-VIZ-001: WeeklyCalorieChart.tsx — 7-day bar chart on History screen REQ-VIZ-002: Streak tracker — GET /meals/streak + HomeScreen badge REQ-UX-001: Quick-add calories — POST /meals/quick-add + QuickAddScreen REQ-UX-002: Food favourites — UserFoodMemory.favourite + toggle endpoint + FoodRow star REQ-UX-003: GoalBanner.tsx — in-app slide-in when daily target hit REQ-EXP-001: ExportController — GET /export/meals CSV download REQ-WTR-001: Water tracking — WaterEntry entity + POST/GET /water + DailyDetails widget REQ-UX-004: Daily logging reminder — HomeScreen after-18:00 banner Also: Flyway V2 (favourite), V3 (water_entries), V4 (source constraints) Traceability, CHANGELOG, PLAN updated after each feature
15 KiB
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:
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:
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:
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:
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
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
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
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)
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)
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)
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?
# 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:
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
# .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...')?
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:
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:
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