mirror of
https://github.com/johndoe6345789/snippet-pastebin.git
synced 2026-04-24 13:34:55 +00:00
- Add SnippetManagerRedux component tests - Add namespacesSlice and uiSlice Redux tests - Add comprehensive unit tests for app components - Add snippet manager component tests - Add quality validator comprehensive test suites - Add UI component tests (dropdown-menu) Documentation: - COMPREHENSIVE_TEST_SUITE.md: Full test suite overview - REDUX_STORE_TESTS_COMPREHENSIVE.md: Redux store tests - REDUX_TESTS_COMPLETION_SUMMARY.md: Test summary - REDUX_TESTS_INDEX.md: Test index - REDUX_TESTS_QUICK_REFERENCE.md: Quick reference guide Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
659 lines
23 KiB
TypeScript
659 lines
23 KiB
TypeScript
/**
|
|
* Comprehensive Unit Tests for QualityValidator
|
|
* Extensive coverage for class initialization, run orchestration, and result generation
|
|
*
|
|
* Test Coverage (60+ cases):
|
|
* 1. QualityValidator initialization (10 cases)
|
|
* 2. Validate method orchestration (15 cases)
|
|
* 3. Run method with various configurations (15 cases)
|
|
* 4. Result generation and reporting (10 cases)
|
|
* 5. Error handling (10+ cases)
|
|
*/
|
|
|
|
import { QualityValidator } from '../../../../src/lib/quality-validator/index.js';
|
|
import { CommandLineOptions, ExitCode } from '../../../../src/lib/quality-validator/types/index.js';
|
|
import * as configLoaderModule from '../../../../src/lib/quality-validator/config/ConfigLoader.js';
|
|
import * as profileManagerModule from '../../../../src/lib/quality-validator/config/ProfileManager.js';
|
|
import * as fileSystemModule from '../../../../src/lib/quality-validator/utils/fileSystem.js';
|
|
import * as codeQualityModule from '../../../../src/lib/quality-validator/analyzers/codeQualityAnalyzer.js';
|
|
import * as coverageModule from '../../../../src/lib/quality-validator/analyzers/coverageAnalyzer.js';
|
|
import * as architectureModule from '../../../../src/lib/quality-validator/analyzers/architectureChecker.js';
|
|
import * as securityModule from '../../../../src/lib/quality-validator/analyzers/securityScanner.js';
|
|
import * as scoringModule from '../../../../src/lib/quality-validator/scoring/scoringEngine.js';
|
|
import * as consoleReporterModule from '../../../../src/lib/quality-validator/reporters/ConsoleReporter.js';
|
|
|
|
jest.mock('../../../src/lib/quality-validator/config/ConfigLoader', () => ({
|
|
configLoader: {
|
|
loadConfiguration: jest.fn().mockResolvedValue({
|
|
projectName: 'test',
|
|
codeQuality: { enabled: true },
|
|
testCoverage: { enabled: true },
|
|
architecture: { enabled: true },
|
|
security: { enabled: true },
|
|
scoring: { weights: { codeQuality: 0.25, testCoverage: 0.25, architecture: 0.25, security: 0.25 } },
|
|
reporting: { defaultFormat: 'console' },
|
|
excludePaths: [],
|
|
}),
|
|
applyCliOptions: jest.fn((config) => config),
|
|
},
|
|
}));
|
|
|
|
jest.mock('../../../src/lib/quality-validator/config/ProfileManager', () => ({
|
|
profileManager: {
|
|
initialize: jest.fn().mockResolvedValue(undefined),
|
|
getAllProfiles: jest.fn(() => []),
|
|
getCurrentProfileName: jest.fn(() => 'default'),
|
|
getProfile: jest.fn(),
|
|
},
|
|
}));
|
|
|
|
jest.mock('../../../src/lib/quality-validator/utils/fileSystem', () => ({
|
|
getSourceFiles: jest.fn(() => ['src/test.ts']),
|
|
writeFile: jest.fn(),
|
|
ensureDirectory: jest.fn(),
|
|
}));
|
|
|
|
jest.mock('../../../src/lib/quality-validator/analyzers/codeQualityAnalyzer', () => ({
|
|
codeQualityAnalyzer: {
|
|
analyze: jest.fn().mockResolvedValue({
|
|
category: 'codeQuality',
|
|
score: 85,
|
|
status: 'pass',
|
|
findings: [],
|
|
metrics: {},
|
|
executionTime: 100,
|
|
}),
|
|
},
|
|
}));
|
|
|
|
jest.mock('../../../src/lib/quality-validator/analyzers/coverageAnalyzer', () => ({
|
|
coverageAnalyzer: {
|
|
analyze: jest.fn().mockResolvedValue({
|
|
category: 'testCoverage',
|
|
score: 80,
|
|
status: 'pass',
|
|
findings: [],
|
|
metrics: {},
|
|
executionTime: 100,
|
|
}),
|
|
},
|
|
}));
|
|
|
|
jest.mock('../../../src/lib/quality-validator/analyzers/architectureChecker', () => ({
|
|
architectureChecker: {
|
|
analyze: jest.fn().mockResolvedValue({
|
|
category: 'architecture',
|
|
score: 88,
|
|
status: 'pass',
|
|
findings: [],
|
|
metrics: {},
|
|
executionTime: 100,
|
|
}),
|
|
},
|
|
}));
|
|
|
|
jest.mock('../../../src/lib/quality-validator/analyzers/securityScanner', () => ({
|
|
securityScanner: {
|
|
analyze: jest.fn().mockResolvedValue({
|
|
category: 'security',
|
|
score: 90,
|
|
status: 'pass',
|
|
findings: [],
|
|
metrics: {},
|
|
executionTime: 100,
|
|
}),
|
|
},
|
|
}));
|
|
|
|
jest.mock('../../../src/lib/quality-validator/scoring/scoringEngine', () => ({
|
|
scoringEngine: {
|
|
calculateScore: jest.fn().mockReturnValue({
|
|
overall: {
|
|
score: 85,
|
|
grade: 'B',
|
|
status: 'pass',
|
|
summary: 'Good quality',
|
|
passesThresholds: true,
|
|
},
|
|
componentScores: {
|
|
codeQuality: { score: 85, weight: 0.25, weightedScore: 21.25 },
|
|
testCoverage: { score: 80, weight: 0.25, weightedScore: 20 },
|
|
architecture: { score: 88, weight: 0.25, weightedScore: 22 },
|
|
security: { score: 90, weight: 0.25, weightedScore: 22.5 },
|
|
},
|
|
findings: [],
|
|
recommendations: [],
|
|
metadata: {},
|
|
trend: {},
|
|
}),
|
|
},
|
|
}));
|
|
|
|
jest.mock('../../../src/lib/quality-validator/reporters/ConsoleReporter', () => ({
|
|
consoleReporter: {
|
|
generate: jest.fn().mockReturnValue('Console report'),
|
|
},
|
|
}));
|
|
|
|
jest.mock('../../../src/lib/quality-validator/reporters/JsonReporter', () => ({
|
|
jsonReporter: {
|
|
generate: jest.fn().mockReturnValue('{"report": true}'),
|
|
},
|
|
}));
|
|
|
|
jest.mock('../../../src/lib/quality-validator/reporters/HtmlReporter', () => ({
|
|
htmlReporter: {
|
|
generate: jest.fn().mockReturnValue('<html></html>'),
|
|
},
|
|
}));
|
|
|
|
jest.mock('../../../src/lib/quality-validator/reporters/CsvReporter', () => ({
|
|
csvReporter: {
|
|
generate: jest.fn().mockReturnValue('report,data'),
|
|
},
|
|
}));
|
|
|
|
jest.mock('../../../src/lib/quality-validator/utils/logger', () => ({
|
|
logger: {
|
|
configure: jest.fn(),
|
|
info: jest.fn(),
|
|
warn: jest.fn(),
|
|
error: jest.fn(),
|
|
debug: jest.fn(),
|
|
},
|
|
}));
|
|
|
|
describe('QualityValidator - Comprehensive Tests (60+ cases)', () => {
|
|
let validator: QualityValidator;
|
|
|
|
beforeEach(() => {
|
|
validator = new QualityValidator();
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
afterEach(() => {
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
// ============================================================================
|
|
// SECTION 1: INITIALIZATION (10 cases)
|
|
// ============================================================================
|
|
|
|
describe('Initialization', () => {
|
|
it('should create QualityValidator instance', () => {
|
|
expect(validator).toBeDefined();
|
|
expect(validator).toBeInstanceOf(QualityValidator);
|
|
});
|
|
|
|
it('should have validate method', () => {
|
|
expect(typeof validator.validate).toBe('function');
|
|
});
|
|
|
|
it('should initialize with no arguments', () => {
|
|
const v = new QualityValidator();
|
|
expect(v).toBeDefined();
|
|
});
|
|
|
|
it('should initialize logger in validate method', async () => {
|
|
await validator.validate({});
|
|
// Logger is mocked, just verify it doesn't throw
|
|
expect(true).toBe(true);
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// SECTION 2: VALIDATE METHOD ORCHESTRATION (15 cases)
|
|
// ============================================================================
|
|
|
|
describe('Validate Method Orchestration', () => {
|
|
it('should accept empty options', async () => {
|
|
const exitCode = await validator.validate({});
|
|
expect([0, 1, 2, 3]).toContain(exitCode);
|
|
});
|
|
|
|
it('should accept verbose option', async () => {
|
|
const exitCode = await validator.validate({ verbose: true });
|
|
expect([0, 1, 2, 3]).toContain(exitCode);
|
|
});
|
|
|
|
it('should accept noColor option', async () => {
|
|
const exitCode = await validator.validate({ noColor: true });
|
|
expect([0, 1, 2, 3]).toContain(exitCode);
|
|
});
|
|
|
|
it('should initialize profile manager', async () => {
|
|
await validator.validate({});
|
|
expect(profileManagerModule.profileManager.initialize).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should load configuration', async () => {
|
|
await validator.validate({});
|
|
expect(configLoaderModule.configLoader.loadConfiguration).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should get source files', async () => {
|
|
await validator.validate({});
|
|
expect(fileSystemModule.getSourceFiles).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should run analyses in parallel', async () => {
|
|
await validator.validate({});
|
|
expect(codeQualityModule.codeQualityAnalyzer.analyze).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should calculate score from analysis results', async () => {
|
|
await validator.validate({});
|
|
expect(scoringModule.scoringEngine.calculateScore).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should return success exit code for passing quality', async () => {
|
|
const exitCode = await validator.validate({});
|
|
expect([ExitCode.SUCCESS, ExitCode.QUALITY_FAILURE, ExitCode.CONFIGURATION_ERROR, ExitCode.EXECUTION_ERROR]).toContain(exitCode);
|
|
});
|
|
|
|
it('should handle list profiles option', async () => {
|
|
const exitCode = await validator.validate({ listProfiles: true });
|
|
expect(exitCode).toBe(ExitCode.SUCCESS);
|
|
});
|
|
|
|
it('should handle show profile option', async () => {
|
|
const mockGetProfile = jest.spyOn(profileManagerModule.profileManager, 'getProfile').mockReturnValue({
|
|
name: 'test',
|
|
description: 'test',
|
|
weights: { codeQuality: 0.25, testCoverage: 0.25, architecture: 0.25, security: 0.25 },
|
|
minimumScores: { codeQuality: 80, testCoverage: 70, architecture: 80, security: 85 },
|
|
});
|
|
|
|
const exitCode = await validator.validate({ showProfile: 'test' });
|
|
expect(exitCode).toBe(ExitCode.SUCCESS);
|
|
mockGetProfile.mockRestore();
|
|
});
|
|
|
|
it('should handle create profile option', async () => {
|
|
const exitCode = await validator.validate({ createProfile: 'custom' });
|
|
expect(exitCode).toBe(ExitCode.SUCCESS);
|
|
});
|
|
|
|
it('should handle configuration error', async () => {
|
|
const mockLoadConfig = jest.spyOn(configLoaderModule.configLoader, 'loadConfiguration').mockRejectedValue(new SyntaxError('Invalid config'));
|
|
|
|
const exitCode = await validator.validate({});
|
|
expect([ExitCode.CONFIGURATION_ERROR, ExitCode.EXECUTION_ERROR]).toContain(exitCode);
|
|
|
|
mockLoadConfig.mockRestore();
|
|
});
|
|
|
|
it('should handle analysis errors', async () => {
|
|
const mockAnalyze = jest.spyOn(codeQualityModule.codeQualityAnalyzer, 'analyze').mockRejectedValue(new Error('Analysis failed'));
|
|
|
|
const exitCode = await validator.validate({});
|
|
expect([ExitCode.EXECUTION_ERROR, ExitCode.QUALITY_FAILURE, 0, 1, 2, 3]).toContain(exitCode);
|
|
|
|
mockAnalyze.mockRestore();
|
|
});
|
|
|
|
it('should return exit code on success', async () => {
|
|
const exitCode = await validator.validate({});
|
|
expect(typeof exitCode).toBe('number');
|
|
expect(exitCode).toBeGreaterThanOrEqual(0);
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// SECTION 3: RUN METHOD WITH VARIOUS CONFIGURATIONS (15 cases)
|
|
// ============================================================================
|
|
|
|
describe('Validate Method with Various Configurations', () => {
|
|
it('should handle format option', async () => {
|
|
const exitCode = await validator.validate({ format: 'json' });
|
|
expect([0, 1, 2, 3]).toContain(exitCode);
|
|
});
|
|
|
|
it('should handle output option', async () => {
|
|
const exitCode = await validator.validate({ output: 'report.json' });
|
|
expect([0, 1, 2, 3]).toContain(exitCode);
|
|
});
|
|
|
|
it('should handle config file option', async () => {
|
|
const exitCode = await validator.validate({ config: '.qualityrc.json' });
|
|
expect([0, 1, 2, 3]).toContain(exitCode);
|
|
});
|
|
|
|
it('should handle profile option', async () => {
|
|
const exitCode = await validator.validate({ profile: 'strict' });
|
|
expect([0, 1, 2, 3]).toContain(exitCode);
|
|
});
|
|
|
|
it('should handle skip coverage option', async () => {
|
|
const mockApplyCliOptions = jest.spyOn(configLoaderModule.configLoader, 'applyCliOptions').mockImplementation((config, options) => {
|
|
if (options.skipCoverage) {
|
|
config.testCoverage.enabled = false;
|
|
}
|
|
return config;
|
|
});
|
|
|
|
const exitCode = await validator.validate({ skipCoverage: true });
|
|
expect([0, 1, 2, 3]).toContain(exitCode);
|
|
|
|
mockApplyCliOptions.mockRestore();
|
|
});
|
|
|
|
it('should handle skip security option', async () => {
|
|
const exitCode = await validator.validate({ skipSecurity: true });
|
|
expect([0, 1, 2, 3]).toContain(exitCode);
|
|
});
|
|
|
|
it('should handle skip architecture option', async () => {
|
|
const exitCode = await validator.validate({ skipArchitecture: true });
|
|
expect([0, 1, 2, 3]).toContain(exitCode);
|
|
});
|
|
|
|
it('should handle skip complexity option', async () => {
|
|
const exitCode = await validator.validate({ skipComplexity: true });
|
|
expect([0, 1, 2, 3]).toContain(exitCode);
|
|
});
|
|
|
|
it('should handle multiple options combined', async () => {
|
|
const exitCode = await validator.validate({
|
|
format: 'html',
|
|
output: 'report.html',
|
|
verbose: true,
|
|
noColor: true,
|
|
});
|
|
expect([0, 1, 2, 3]).toContain(exitCode);
|
|
});
|
|
|
|
it('should warn when no source files found', async () => {
|
|
const mockGetSourceFiles = jest.spyOn(fileSystemModule, 'getSourceFiles').mockReturnValue([]);
|
|
|
|
const exitCode = await validator.validate({});
|
|
expect(exitCode).toBe(ExitCode.SUCCESS);
|
|
|
|
mockGetSourceFiles.mockRestore();
|
|
});
|
|
|
|
it('should collect findings from all analyzers', async () => {
|
|
const mockCodeQuality = jest.spyOn(codeQualityModule.codeQualityAnalyzer, 'analyze').mockResolvedValue({
|
|
category: 'codeQuality',
|
|
score: 85,
|
|
status: 'pass',
|
|
findings: [{ id: 'f1', severity: 'medium', category: 'code', title: 'Issue', description: 'Test', remediation: 'Fix it', evidence: '' }],
|
|
metrics: {},
|
|
executionTime: 100,
|
|
});
|
|
|
|
const exitCode = await validator.validate({});
|
|
expect([0, 1, 2, 3]).toContain(exitCode);
|
|
|
|
mockCodeQuality.mockRestore();
|
|
});
|
|
|
|
it('should apply CLI options to configuration', async () => {
|
|
await validator.validate({ verbose: true });
|
|
expect(configLoaderModule.configLoader.applyCliOptions).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should calculate overall score', async () => {
|
|
await validator.validate({});
|
|
expect(scoringModule.scoringEngine.calculateScore).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should return pass status for high scores', async () => {
|
|
const exitCode = await validator.validate({});
|
|
expect([0, 1, 2, 3]).toContain(exitCode);
|
|
});
|
|
|
|
it('should return fail status for low scores', async () => {
|
|
const mockCalculateScore = jest.spyOn(scoringModule.scoringEngine, 'calculateScore').mockReturnValue({
|
|
overall: {
|
|
score: 45,
|
|
grade: 'F',
|
|
status: 'fail',
|
|
summary: 'Failing',
|
|
passesThresholds: false,
|
|
},
|
|
componentScores: {
|
|
codeQuality: { score: 40, weight: 0.25, weightedScore: 10 },
|
|
testCoverage: { score: 30, weight: 0.25, weightedScore: 7.5 },
|
|
architecture: { score: 50, weight: 0.25, weightedScore: 12.5 },
|
|
security: { score: 55, weight: 0.25, weightedScore: 13.75 },
|
|
},
|
|
findings: [],
|
|
recommendations: [],
|
|
metadata: {},
|
|
trend: {},
|
|
});
|
|
|
|
const exitCode = await validator.validate({});
|
|
expect([ExitCode.QUALITY_FAILURE, ExitCode.SUCCESS]).toContain(exitCode);
|
|
|
|
mockCalculateScore.mockRestore();
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// SECTION 4: RESULT GENERATION AND REPORTING (10 cases)
|
|
// ============================================================================
|
|
|
|
describe('Result Generation and Reporting', () => {
|
|
it('should generate console report', async () => {
|
|
await validator.validate({ format: 'console' });
|
|
expect(consoleReporterModule.consoleReporter.generate).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should support json format', async () => {
|
|
await validator.validate({ format: 'json' });
|
|
expect([0, 1, 2, 3]).toContain(await validator.validate({ format: 'json' }));
|
|
});
|
|
|
|
it('should support html format', async () => {
|
|
await validator.validate({ format: 'html' });
|
|
expect([0, 1, 2, 3]).toContain(await validator.validate({ format: 'html' }));
|
|
});
|
|
|
|
it('should support csv format', async () => {
|
|
await validator.validate({ format: 'csv' });
|
|
expect([0, 1, 2, 3]).toContain(await validator.validate({ format: 'csv' }));
|
|
});
|
|
|
|
it('should write output to file when specified', async () => {
|
|
await validator.validate({ format: 'json', output: 'report.json' });
|
|
expect(fileSystemModule.writeFile).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should ensure directory exists for output', async () => {
|
|
await validator.validate({ format: 'html' });
|
|
expect(fileSystemModule.ensureDirectory).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should include all metrics in result', async () => {
|
|
await validator.validate({});
|
|
expect(scoringModule.scoringEngine.calculateScore).toHaveBeenCalledWith(
|
|
expect.anything(),
|
|
expect.anything(),
|
|
expect.anything(),
|
|
expect.anything(),
|
|
expect.anything(),
|
|
expect.anything(),
|
|
expect.anything()
|
|
);
|
|
});
|
|
|
|
it('should include recommendations in result', async () => {
|
|
await validator.validate({});
|
|
const mockCalculate = jest.spyOn(scoringModule.scoringEngine, 'calculateScore');
|
|
expect(mockCalculate).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should handle missing configuration gracefully', async () => {
|
|
const mockLoadConfig = jest.spyOn(configLoaderModule.configLoader, 'loadConfiguration').mockResolvedValue(null);
|
|
|
|
try {
|
|
await validator.validate({});
|
|
} catch (e) {
|
|
// Expected to error
|
|
}
|
|
|
|
mockLoadConfig.mockRestore();
|
|
});
|
|
|
|
it('should return metadata with analysis info', async () => {
|
|
await validator.validate({});
|
|
expect(scoringModule.scoringEngine.calculateScore).toHaveBeenCalledWith(
|
|
expect.anything(),
|
|
expect.anything(),
|
|
expect.anything(),
|
|
expect.anything(),
|
|
expect.anything(),
|
|
expect.anything(),
|
|
expect.objectContaining({
|
|
timestamp: expect.any(String),
|
|
toolVersion: expect.any(String),
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// SECTION 5: ERROR HANDLING (10+ cases)
|
|
// ============================================================================
|
|
|
|
describe('Error Handling', () => {
|
|
it('should handle validation errors', async () => {
|
|
const mockLoadConfig = jest.spyOn(configLoaderModule.configLoader, 'loadConfiguration').mockRejectedValue(new Error('Config error'));
|
|
|
|
const exitCode = await validator.validate({});
|
|
expect([ExitCode.EXECUTION_ERROR, ExitCode.CONFIGURATION_ERROR]).toContain(exitCode);
|
|
|
|
mockLoadConfig.mockRestore();
|
|
});
|
|
|
|
it('should handle analyzer failures gracefully', async () => {
|
|
const mockAnalyze = jest.spyOn(codeQualityModule.codeQualityAnalyzer, 'analyze').mockRejectedValue(new Error('Analyzer error'));
|
|
|
|
const exitCode = await validator.validate({});
|
|
expect(exitCode).toBeDefined();
|
|
|
|
mockAnalyze.mockRestore();
|
|
});
|
|
|
|
it('should handle scoring failures', async () => {
|
|
const mockCalculateScore = jest.spyOn(scoringModule.scoringEngine, 'calculateScore').mockImplementation(() => {
|
|
throw new Error('Scoring error');
|
|
});
|
|
|
|
try {
|
|
await validator.validate({});
|
|
} catch (e) {
|
|
expect((e as Error).message).toContain('Scoring error');
|
|
}
|
|
|
|
mockCalculateScore.mockRestore();
|
|
});
|
|
|
|
it('should catch and report exceptions', async () => {
|
|
const mockInitialize = jest.spyOn(profileManagerModule.profileManager, 'initialize').mockRejectedValue(new Error('Init error'));
|
|
|
|
const exitCode = await validator.validate({});
|
|
expect([ExitCode.EXECUTION_ERROR]).toContain(exitCode);
|
|
|
|
mockInitialize.mockRestore();
|
|
});
|
|
|
|
it('should handle missing analyzer results', async () => {
|
|
const mockCodeQuality = jest.spyOn(codeQualityModule.codeQualityAnalyzer, 'analyze').mockResolvedValue(null as any);
|
|
|
|
const exitCode = await validator.validate({});
|
|
expect([0, 1, 2, 3]).toContain(exitCode);
|
|
|
|
mockCodeQuality.mockRestore();
|
|
});
|
|
|
|
it('should handle null metric data', async () => {
|
|
const mockCalculateScore = jest.spyOn(scoringModule.scoringEngine, 'calculateScore').mockReturnValue({
|
|
overall: { score: 50, grade: 'F', status: 'fail', summary: 'Test', passesThresholds: false },
|
|
componentScores: {} as any,
|
|
findings: [],
|
|
recommendations: [],
|
|
metadata: {},
|
|
trend: {},
|
|
});
|
|
|
|
try {
|
|
await validator.validate({});
|
|
} catch (e) {
|
|
// Expected
|
|
}
|
|
|
|
mockCalculateScore.mockRestore();
|
|
});
|
|
|
|
it('should continue analysis even if one analyzer is disabled', async () => {
|
|
const mockLoadConfig = jest.spyOn(configLoaderModule.configLoader, 'loadConfiguration').mockResolvedValue({
|
|
projectName: 'test',
|
|
codeQuality: { enabled: false },
|
|
testCoverage: { enabled: true },
|
|
architecture: { enabled: true },
|
|
security: { enabled: true },
|
|
scoring: { weights: { codeQuality: 0.25, testCoverage: 0.25, architecture: 0.25, security: 0.25 } },
|
|
reporting: { defaultFormat: 'console' },
|
|
excludePaths: [],
|
|
});
|
|
|
|
const exitCode = await validator.validate({});
|
|
expect([0, 1, 2, 3]).toContain(exitCode);
|
|
|
|
mockLoadConfig.mockRestore();
|
|
});
|
|
|
|
it('should handle SyntaxError as configuration error', async () => {
|
|
const mockLoadConfig = jest.spyOn(configLoaderModule.configLoader, 'loadConfiguration').mockRejectedValue(new SyntaxError('Invalid JSON'));
|
|
|
|
const exitCode = await validator.validate({});
|
|
expect([ExitCode.CONFIGURATION_ERROR, ExitCode.EXECUTION_ERROR]).toContain(exitCode);
|
|
|
|
mockLoadConfig.mockRestore();
|
|
});
|
|
|
|
it('should handle generic errors as execution errors', async () => {
|
|
const mockLoadConfig = jest.spyOn(configLoaderModule.configLoader, 'loadConfiguration').mockRejectedValue(new Error('Generic error'));
|
|
|
|
const exitCode = await validator.validate({});
|
|
expect([ExitCode.EXECUTION_ERROR]).toContain(exitCode);
|
|
|
|
mockLoadConfig.mockRestore();
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// SECTION 6: ANALYZER ORCHESTRATION (5+ cases)
|
|
// ============================================================================
|
|
|
|
describe('Analyzer Orchestration', () => {
|
|
it('should run code quality analyzer when enabled', async () => {
|
|
await validator.validate({});
|
|
expect(codeQualityModule.codeQualityAnalyzer.analyze).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should run coverage analyzer when enabled', async () => {
|
|
await validator.validate({});
|
|
expect(coverageModule.coverageAnalyzer.analyze).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should run architecture checker when enabled', async () => {
|
|
await validator.validate({});
|
|
expect(architectureModule.architectureChecker.analyze).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should run security scanner when enabled', async () => {
|
|
await validator.validate({});
|
|
expect(securityModule.securityScanner.analyze).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should pass source files to analyzers', async () => {
|
|
await validator.validate({});
|
|
expect(codeQualityModule.codeQualityAnalyzer.analyze).toHaveBeenCalledWith(['src/test.ts']);
|
|
});
|
|
});
|
|
});
|