142 lines
4.6 KiB
TypeScript
142 lines
4.6 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
||
import { ArchitectureGuardrails } from './ArchitectureGuardrails';
|
||
|
||
/**
|
||
* Architecture Guardrail Tests
|
||
*
|
||
* These tests enforce the architectural contract for the website.
|
||
* They use an allowlist to permit existing violations while preventing new ones.
|
||
*
|
||
* The goal is to shrink the allowlist slice-by-slice until zero violations remain.
|
||
*/
|
||
describe('Architecture Guardrails', () => {
|
||
const guardrails = new ArchitectureGuardrails();
|
||
|
||
it('should detect all violations in the codebase', () => {
|
||
const allViolations = guardrails.scan();
|
||
|
||
// This test documents the current state
|
||
// It will always pass but shows what violations exist
|
||
console.log(`\n📊 Total violations found: ${allViolations.length}`);
|
||
|
||
if (allViolations.length > 0) {
|
||
console.log('\n📋 Violations by rule:');
|
||
const byRule = allViolations.reduce((acc, v) => {
|
||
acc[v.ruleName] = (acc[v.ruleName] || 0) + 1;
|
||
return acc;
|
||
}, {} as Record<string, number>);
|
||
|
||
Object.entries(byRule).forEach(([rule, count]) => {
|
||
console.log(` - ${rule}: ${count}`);
|
||
});
|
||
}
|
||
|
||
// We expect violations to exist initially
|
||
expect(allViolations.length).toBeGreaterThanOrEqual(0);
|
||
});
|
||
|
||
it('should have no violations after filtering by allowlist', () => {
|
||
const filteredViolations = guardrails.getFilteredViolations();
|
||
|
||
console.log(`\n🔍 Filtered violations (after allowlist): ${filteredViolations.length}`);
|
||
|
||
if (filteredViolations.length > 0) {
|
||
console.log('\n❌ New violations not in allowlist:');
|
||
filteredViolations.forEach(v => {
|
||
console.log(` - ${v.toString()}`);
|
||
});
|
||
}
|
||
|
||
// This is the main assertion - no new violations allowed
|
||
expect(filteredViolations.length).toBe(0);
|
||
});
|
||
|
||
it('should not have stale allowlist entries', () => {
|
||
const staleEntries = guardrails.findStaleAllowlistEntries();
|
||
|
||
console.log(`\n🧹 Stale allowlist entries: ${staleEntries.length}`);
|
||
|
||
if (staleEntries.length > 0) {
|
||
console.log('\n⚠️ These allowlist entries no longer match any violations:');
|
||
staleEntries.forEach(entry => {
|
||
console.log(` - ${entry}`);
|
||
});
|
||
console.log('\n💡 Consider removing them from allowed-violations.ts');
|
||
}
|
||
|
||
// Stale entries should be removed to keep allowlist clean
|
||
expect(staleEntries.length).toBe(0);
|
||
});
|
||
|
||
it('should enforce: no ContainerManager in server page queries', () => {
|
||
const violations = guardrails.getFilteredViolations().filter(
|
||
v => v.ruleName === 'no-container-manager-in-server'
|
||
);
|
||
|
||
expect(violations.length).toBe(0);
|
||
});
|
||
|
||
it('should enforce: no PageDataFetcher.fetch() in server page queries', () => {
|
||
const violations = guardrails.getFilteredViolations().filter(
|
||
v => v.ruleName === 'no-page-data-fetcher-fetch-in-server'
|
||
);
|
||
|
||
expect(violations.length).toBe(0);
|
||
});
|
||
|
||
it('should enforce: no view-models imports in server code', () => {
|
||
const violations = guardrails.getFilteredViolations().filter(
|
||
v => v.ruleName === 'no-view-models-in-server'
|
||
);
|
||
|
||
expect(violations.length).toBe(0);
|
||
});
|
||
|
||
it('should enforce: no view-models/display-objects in templates', () => {
|
||
const violations = guardrails.getFilteredViolations().filter(
|
||
v => v.ruleName === 'no-view-models-in-templates'
|
||
);
|
||
|
||
expect(violations.length).toBe(0);
|
||
});
|
||
|
||
it('should enforce: no Intl.* or toLocale* in presentation paths', () => {
|
||
const violations = guardrails.getFilteredViolations().filter(
|
||
v => v.ruleName === 'no-intl-in-presentation'
|
||
);
|
||
|
||
expect(violations.length).toBe(0);
|
||
});
|
||
|
||
it('should enforce: no client-side write fetch', () => {
|
||
const violations = guardrails.getFilteredViolations().filter(
|
||
v => v.ruleName === 'no-client-write-fetch'
|
||
);
|
||
|
||
expect(violations.length).toBe(0);
|
||
});
|
||
|
||
it('should enforce: no *Template.tsx under app/', () => {
|
||
const violations = guardrails.getFilteredViolations().filter(
|
||
v => v.ruleName === 'no-templates-in-app'
|
||
);
|
||
|
||
expect(violations.length).toBe(0);
|
||
});
|
||
|
||
it('should enforce: no hooks directory in apps/website/', () => {
|
||
const violations = guardrails.getFilteredViolations().filter(
|
||
v => v.ruleName === 'no-hooks-directory'
|
||
);
|
||
|
||
expect(violations.length).toBe(0);
|
||
});
|
||
|
||
it('should enforce: no as any usage', () => {
|
||
const violations = guardrails.getFilteredViolations().filter(
|
||
v => v.ruleName === 'no-as-any'
|
||
);
|
||
|
||
expect(violations.length).toBe(0);
|
||
});
|
||
}); |