195 lines
7.4 KiB
TypeScript
195 lines
7.4 KiB
TypeScript
/**
|
|
* Tests for quality gate override logic
|
|
*/
|
|
|
|
import { describe, it, expect } from 'vitest';
|
|
import { applyQualityGateOverrides } from '../infra/config/loaders/qualityGateOverrides.js';
|
|
import type { PieceOverrides } from '../core/models/persisted-global-config.js';
|
|
|
|
describe('applyQualityGateOverrides', () => {
|
|
it('returns undefined when no gates are defined', () => {
|
|
const result = applyQualityGateOverrides('implement', undefined, true, undefined, undefined);
|
|
expect(result).toBeUndefined();
|
|
});
|
|
|
|
it('returns YAML gates when no overrides are defined', () => {
|
|
const yamlGates = ['Test passes'];
|
|
const result = applyQualityGateOverrides('implement', yamlGates, true, undefined, undefined);
|
|
expect(result).toEqual(['Test passes']);
|
|
});
|
|
|
|
it('returns empty array when yamlGates is empty array and no overrides', () => {
|
|
const yamlGates: string[] = [];
|
|
const result = applyQualityGateOverrides('implement', yamlGates, true, undefined, undefined);
|
|
expect(result).toEqual([]);
|
|
});
|
|
|
|
it('merges global override gates with YAML gates (additive)', () => {
|
|
const yamlGates = ['Unit tests pass'];
|
|
const globalOverrides: PieceOverrides = {
|
|
qualityGates: ['E2E tests pass'],
|
|
};
|
|
const result = applyQualityGateOverrides('implement', yamlGates, true, undefined, globalOverrides);
|
|
expect(result).toEqual(['E2E tests pass', 'Unit tests pass']);
|
|
});
|
|
|
|
it('applies movement-specific override from global config', () => {
|
|
const yamlGates = ['Unit tests pass'];
|
|
const globalOverrides: PieceOverrides = {
|
|
qualityGates: ['Global gate'],
|
|
movements: {
|
|
implement: {
|
|
qualityGates: ['Movement-specific gate'],
|
|
},
|
|
},
|
|
};
|
|
const result = applyQualityGateOverrides('implement', yamlGates, true, undefined, globalOverrides);
|
|
expect(result).toEqual(['Global gate', 'Movement-specific gate', 'Unit tests pass']);
|
|
});
|
|
|
|
it('applies project overrides with higher priority than global', () => {
|
|
const yamlGates = ['YAML gate'];
|
|
const globalOverrides: PieceOverrides = {
|
|
qualityGates: ['Global gate'],
|
|
};
|
|
const projectOverrides: PieceOverrides = {
|
|
qualityGates: ['Project gate'],
|
|
};
|
|
const result = applyQualityGateOverrides('implement', yamlGates, true, projectOverrides, globalOverrides);
|
|
expect(result).toEqual(['Global gate', 'Project gate', 'YAML gate']);
|
|
});
|
|
|
|
it('applies movement-specific override from project config', () => {
|
|
const yamlGates = ['YAML gate'];
|
|
const projectOverrides: PieceOverrides = {
|
|
movements: {
|
|
implement: {
|
|
qualityGates: ['Project movement gate'],
|
|
},
|
|
},
|
|
};
|
|
const result = applyQualityGateOverrides('implement', yamlGates, true, projectOverrides, undefined);
|
|
expect(result).toEqual(['Project movement gate', 'YAML gate']);
|
|
});
|
|
|
|
it('filters global gates when qualityGatesEditOnly=true and edit=false', () => {
|
|
const yamlGates = ['YAML gate'];
|
|
const globalOverrides: PieceOverrides = {
|
|
qualityGates: ['Global gate'],
|
|
qualityGatesEditOnly: true,
|
|
};
|
|
const result = applyQualityGateOverrides('review', yamlGates, false, undefined, globalOverrides);
|
|
expect(result).toEqual(['YAML gate']); // Global gate excluded because edit=false
|
|
});
|
|
|
|
it('includes global gates when qualityGatesEditOnly=true and edit=true', () => {
|
|
const yamlGates = ['YAML gate'];
|
|
const globalOverrides: PieceOverrides = {
|
|
qualityGates: ['Global gate'],
|
|
qualityGatesEditOnly: true,
|
|
};
|
|
const result = applyQualityGateOverrides('implement', yamlGates, true, undefined, globalOverrides);
|
|
expect(result).toEqual(['Global gate', 'YAML gate']);
|
|
});
|
|
|
|
it('filters project global gates when qualityGatesEditOnly=true and edit=false', () => {
|
|
const yamlGates = ['YAML gate'];
|
|
const projectOverrides: PieceOverrides = {
|
|
qualityGates: ['Project gate'],
|
|
qualityGatesEditOnly: true,
|
|
};
|
|
const result = applyQualityGateOverrides('review', yamlGates, false, projectOverrides, undefined);
|
|
expect(result).toEqual(['YAML gate']); // Project gate excluded because edit=false
|
|
});
|
|
|
|
it('applies movement-specific gates regardless of qualityGatesEditOnly flag', () => {
|
|
const yamlGates = ['YAML gate'];
|
|
const projectOverrides: PieceOverrides = {
|
|
qualityGates: ['Project global gate'],
|
|
qualityGatesEditOnly: true,
|
|
movements: {
|
|
review: {
|
|
qualityGates: ['Review-specific gate'],
|
|
},
|
|
},
|
|
};
|
|
const result = applyQualityGateOverrides('review', yamlGates, false, projectOverrides, undefined);
|
|
// Project global gate excluded (edit=false), but movement-specific gate included
|
|
expect(result).toEqual(['Review-specific gate', 'YAML gate']);
|
|
});
|
|
|
|
it('handles complex priority scenario with all override types', () => {
|
|
const yamlGates = ['YAML gate'];
|
|
const globalOverrides: PieceOverrides = {
|
|
qualityGates: ['Global gate'],
|
|
movements: {
|
|
implement: {
|
|
qualityGates: ['Global movement gate'],
|
|
},
|
|
},
|
|
};
|
|
const projectOverrides: PieceOverrides = {
|
|
qualityGates: ['Project gate'],
|
|
movements: {
|
|
implement: {
|
|
qualityGates: ['Project movement gate'],
|
|
},
|
|
},
|
|
};
|
|
const result = applyQualityGateOverrides('implement', yamlGates, true, projectOverrides, globalOverrides);
|
|
expect(result).toEqual([
|
|
'Global gate',
|
|
'Global movement gate',
|
|
'Project gate',
|
|
'Project movement gate',
|
|
'YAML gate',
|
|
]);
|
|
});
|
|
|
|
it('returns YAML gates only when other movements are specified in overrides', () => {
|
|
const yamlGates = ['YAML gate'];
|
|
const projectOverrides: PieceOverrides = {
|
|
movements: {
|
|
review: {
|
|
qualityGates: ['Review gate'],
|
|
},
|
|
},
|
|
};
|
|
const result = applyQualityGateOverrides('implement', yamlGates, true, projectOverrides, undefined);
|
|
expect(result).toEqual(['YAML gate']); // No override for 'implement', only for 'review'
|
|
});
|
|
|
|
describe('deduplication', () => {
|
|
it('removes duplicate gates from multiple sources', () => {
|
|
const yamlGates = ['Test 1', 'Test 2'];
|
|
const globalOverrides: PieceOverrides = {
|
|
qualityGates: ['Test 2', 'Test 3'],
|
|
};
|
|
const projectOverrides: PieceOverrides = {
|
|
qualityGates: ['Test 1', 'Test 4'],
|
|
};
|
|
const result = applyQualityGateOverrides('implement', yamlGates, true, projectOverrides, globalOverrides);
|
|
// Duplicates removed: Test 1, Test 2 appear only once
|
|
expect(result).toEqual(['Test 2', 'Test 3', 'Test 1', 'Test 4']);
|
|
});
|
|
|
|
it('removes duplicate gates from single source', () => {
|
|
const projectOverrides: PieceOverrides = {
|
|
qualityGates: ['Test 1', 'Test 2', 'Test 1', 'Test 3', 'Test 2'],
|
|
};
|
|
const result = applyQualityGateOverrides('implement', undefined, true, projectOverrides, undefined);
|
|
expect(result).toEqual(['Test 1', 'Test 2', 'Test 3']);
|
|
});
|
|
|
|
it('removes duplicate gates from YAML and overrides', () => {
|
|
const yamlGates = ['npm run test', 'npm run lint'];
|
|
const projectOverrides: PieceOverrides = {
|
|
qualityGates: ['npm run test', 'npm run build'],
|
|
};
|
|
const result = applyQualityGateOverrides('implement', yamlGates, true, projectOverrides, undefined);
|
|
// 'npm run test' appears only once
|
|
expect(result).toEqual(['npm run test', 'npm run build', 'npm run lint']);
|
|
});
|
|
});
|
|
});
|