UNPKG

@addon24/eslint-config

Version:

ESLint configuration rules for WorldOfTextcraft projects - Centralized configuration for all project types

351 lines (298 loc) 13.4 kB
/** * Integration Tests für Custom ESLint Rules * * Diese Tests überprüfen, ob alle Custom ESLint Rules korrekt konfiguriert sind * und die erwarteten Meta-Informationen enthalten. */ import fs from 'fs'; import path from 'path'; describe('Custom ESLint Rules Integration', () => { const rulesDir = path.join(__dirname, '..'); // Test all custom rule files exist test('should have all expected custom rule files', () => { const expectedRules = [ 'admin-controller-security.js', 'auth-guard-required.js', 'controller-naming-conventions.js', 'dto-entity-mapping-completeness.js', 'enforce-dto-usage.js', 'enforce-basecontroller.js', 'enforce-error-handling.js', 'no-import-meta-env.js', 'no-tsyringe.js', 'entity-required-properties.js', 'enforce-api-versioning.js', 'no-dto-constructors.js', 'prefer-dto-classes.js', 'require-dto-response.js', 'analyze-relation-usage.js', 'service-architecture.js', 'enforce-database-transaction-safety.js', 'enforce-result-pattern.js', 'dead-code-detection.js', 'no-magic-values.js', 'enforce-test-coverage.js', 'controller-swagger-complete.js', 'controller-swagger-docs.js', 'max-classes-per-file.js', 'no-entity-in-swagger-docs.js', 'dto-visibility-modifiers.js', 'dto-entity-swagger-separation.js', 'coordinate-naming.js', 'no-type-assertion.js', 'websocket-architecture.js', 'controller-architecture.js', 'controller-swagger-english.js', 'entity-to-dto-test.js', 'no-entity-imports-in-controllers.js', 'controller-readonly-restriction.js', 'enforce-dto-from-entity.js', 'require-valid-relations.js', 'eslint-plugin-no-comments.js', ]; expectedRules.forEach(ruleFile => { const rulePath = path.join(rulesDir, ruleFile); expect(fs.existsSync(rulePath)).toBe(true); }); }); // Test rule file structure with different export formats test('should have valid rule file structure', () => { const ruleFiles = fs.readdirSync(rulesDir) .filter(file => file.endsWith('.js') && !file.includes('test')) .filter(file => !['errors.js', 'best-practices.js', 'style.js', 'imports.js', 'jest.js', 'node.js', 'strict.js', 'variables.js', 'typescript.js', 'es6.js', 'deprecate.js', 'prefer-dto-create-method.js'].includes(file)); ruleFiles.forEach(ruleFile => { const rulePath = path.join(rulesDir, ruleFile); const content = fs.readFileSync(rulePath, 'utf8'); // Check for either export default { rules: {...} } or export default ruleObject const hasExportDefault = content.includes('export default {') || content.includes('export default') || content.match(/export default\s+\w+Rule/) || content.match(/export default\s+[a-zA-Z][a-zA-Z0-9]*;/) || content.match(/export default\s+\w+;/); expect(hasExportDefault).toBeTruthy(); // Check for meta information in rule definitions (skip for some rule formats) if (!content.includes('export default {')) { expect(content).toMatch(/meta:\s*{/); expect(content).toMatch(/type:\s*["'].*["']/); expect(content).toMatch(/docs:\s*{/); } // Check for create function (only for custom rules, not config files) if (!content.includes('export default {')) { expect(content).toMatch(/create/); } }); }); // Test specific critical rules describe('Critical Rule Validations', () => { test('admin-controller-security rule should enforce security', () => { const rulePath = path.join(rulesDir, 'admin-controller-security.js'); const content = fs.readFileSync(rulePath, 'utf8'); // Should check for AuthGuard and AdminGuard expect(content).toMatch(/AuthGuard/); expect(content).toMatch(/AdminGuard/); expect(content).toMatch(/UseGuards/); expect(content).toMatch(/ApiBearerAuth/); }); test('enforce-dto-usage rule should prevent anonymous objects', () => { const rulePath = path.join(rulesDir, 'enforce-dto-usage.js'); const content = fs.readFileSync(rulePath, 'utf8'); // Should check for ObjectExpression and suggest DTOs expect(content).toMatch(/ObjectExpression/); expect(content).toMatch(/anonymousObject/); expect(content).toMatch(/DTO|Dto/); }); test('no-import-meta-env rule should enforce ConfigProvider', () => { const rulePath = path.join(rulesDir, 'no-import-meta-env.js'); const content = fs.readFileSync(rulePath, 'utf8'); // Should enforce ConfigProvider usage expect(content).toMatch(/ConfigProvider/); expect(content).toMatch(/import\.meta\.env/); }); test('enforce-error-handling rule should require try-catch', () => { const rulePath = path.join(rulesDir, 'enforce-error-handling.js'); const content = fs.readFileSync(rulePath, 'utf8'); // Should check for try-catch in async methods expect(content).toMatch(/async/); expect(content).toMatch(/TryStatement/); expect(content).toMatch(/logger\.error/); }); }); // Test rule message IDs test('should have meaningful error message IDs', () => { const criticalRules = [ 'admin-controller-security.js', 'auth-guard-required.js', 'enforce-dto-usage.js', 'enforce-error-handling.js', ]; criticalRules.forEach(ruleFile => { const rulePath = path.join(rulesDir, ruleFile); const content = fs.readFileSync(rulePath, 'utf8'); // Should have messages object with meaningful IDs expect(content).toMatch(/messages:\s*{/); // Message IDs should be descriptive const messageMatches = content.match(/["']([a-zA-Z][a-zA-Z0-9]*(?:[A-Z][a-z0-9]*)*?)["']:\s*["']/g); if (messageMatches) { messageMatches.forEach(match => { const messageId = match.match(/["']([^"']*?)["']:/)?.[1]; if (messageId) { // Message IDs should be camelCase and descriptive expect(messageId).toMatch(/^[a-z][a-zA-Z0-9]*$/); expect(messageId.length).toBeGreaterThan(5); // Should be descriptive } }); } }); }); // Test that individual rules don't have internal conflicts (fixed to check within files) test('should not have conflicting rule names within individual files', () => { const ruleFiles = fs.readdirSync(rulesDir) .filter(file => file.endsWith('.js') && !file.includes('test')); ruleFiles.forEach(ruleFile => { const rulePath = path.join(rulesDir, ruleFile); const content = fs.readFileSync(rulePath, 'utf8'); const ruleNames = new Set<string>(); // Extract rule names from the rules object (for files that export { rules: {...} }) const rulesMatch = content.match(/rules:\s*{([^}]+)}/s); if (rulesMatch) { const rulesContent = rulesMatch[1]; const ruleNameMatches = rulesContent?.match(/["']([^"']+)["']:/g); if (ruleNameMatches) { ruleNameMatches.forEach(match => { const ruleName = match.match(/["']([^"']+)["']:/)?.[1]; if (ruleName && !ruleName.includes('//')) { expect(ruleNames.has(ruleName)).toBe(false); ruleNames.add(ruleName); } }); } } }); }); // Test rule functionality assumptions describe('Rule Functionality Validation', () => { test('controller rules should target controller files', () => { const controllerRules = [ 'admin-controller-security.js', 'auth-guard-required.js', 'controller-naming-conventions.js', 'enforce-basecontroller.js', 'controller-architecture.js', 'controller-swagger-complete.js', 'controller-swagger-docs.js', 'controller-swagger-english.js', 'controller-readonly-restriction.js', ]; controllerRules.forEach(ruleFile => { const rulePath = path.join(rulesDir, ruleFile); const content = fs.readFileSync(rulePath, 'utf8'); // Should check for controller files expect(content).toMatch(/[Cc]ontroller/); }); }); test('DTO rules should enforce data transfer patterns', () => { const dtoRules = [ 'enforce-dto-usage.js', 'dto-entity-mapping-completeness.js', 'no-dto-constructors.js', 'prefer-dto-classes.js', 'require-dto-response.js', 'dto-visibility-modifiers.js', 'dto-entity-swagger-separation.js', 'enforce-dto-from-entity.js', ]; dtoRules.forEach(ruleFile => { const rulePath = path.join(rulesDir, ruleFile); const content = fs.readFileSync(rulePath, 'utf8'); // Should reference DTO patterns expect(content).toMatch(/[Dd]to|DTO/); }); }); test('security rules should check authentication and authorization', () => { const securityRules = [ 'admin-controller-security.js', 'auth-guard-required.js', ]; securityRules.forEach(ruleFile => { const rulePath = path.join(rulesDir, ruleFile); const content = fs.readFileSync(rulePath, 'utf8'); // Should check for guards and auth expect(content).toMatch(/[Gg]uard|[Aa]uth/); }); }); test('entity rules should enforce database patterns', () => { const entityRules = [ 'entity-required-properties.js', 'no-entity-imports-in-controllers.js', 'no-entity-in-swagger-docs.js', 'entity-to-dto-test.js', 'require-valid-relations.js', 'analyze-relation-usage.js', ]; entityRules.forEach(ruleFile => { const rulePath = path.join(rulesDir, ruleFile); const content = fs.readFileSync(rulePath, 'utf8'); // Should reference entity patterns expect(content).toMatch(/[Ee]ntity|Entity/); }); }); test('service architecture rules should enforce service patterns', () => { const serviceRules = [ 'service-architecture.js', 'websocket-architecture.js', 'enforce-database-transaction-safety.js', 'enforce-result-pattern.js', ]; serviceRules.forEach(ruleFile => { const rulePath = path.join(rulesDir, ruleFile); const content = fs.readFileSync(rulePath, 'utf8'); // Should reference service or architecture patterns expect(content).toMatch(/[Ss]ervice|[Aa]rchitecture|[Tt]ransaction|[Rr]esult/); }); }); }); // Test rule categories and organization test('should properly categorize rules by type', () => { const ruleCategories = { security: ['admin-controller-security.js', 'auth-guard-required.js'], architecture: ['enforce-dto-usage.js', 'enforce-basecontroller.js', 'dto-entity-mapping-completeness.js', 'service-architecture.js', 'controller-architecture.js', 'websocket-architecture.js'], codeQuality: ['enforce-error-handling.js', 'no-magic-values.js', 'dead-code-detection.js', 'enforce-test-coverage.js'], bestPractices: ['no-import-meta-env.js', 'no-tsyringe.js', 'prefer-dto-classes.js', 'coordinate-naming.js', 'no-type-assertion.js', 'max-classes-per-file.js'], api: ['enforce-api-versioning.js', 'controller-swagger-complete.js', 'controller-swagger-docs.js', 'controller-swagger-english.js'], }; Object.entries(ruleCategories).forEach(([category, rules]) => { rules.forEach(ruleFile => { const rulePath = path.join(rulesDir, ruleFile); expect(fs.existsSync(rulePath)).toBe(true); const content = fs.readFileSync(rulePath, 'utf8'); // Should have appropriate category in docs (optional for some rule formats) if (content.includes('category:')) { expect(content).toMatch(/category:\s*["'][^"']*["']/); } }); }); }); // Test that all custom rules have proper structure test('should have consistent rule structure across all files', () => { const customRuleFiles = fs.readdirSync(rulesDir) .filter(file => file.endsWith('.js') && !file.includes('test')) .filter(file => !['errors.js', 'best-practices.js', 'style.js', 'imports.js', 'jest.js', 'node.js', 'strict.js', 'variables.js', 'typescript.js', 'es6.js', 'deprecate.js'].includes(file)); expect(customRuleFiles.length).toBeGreaterThan(30); // Should have substantial number of custom rules customRuleFiles.forEach(ruleFile => { const rulePath = path.join(rulesDir, ruleFile); const content = fs.readFileSync(rulePath, 'utf8'); // Each rule should have documentation (only for custom rules, not config files) if (!content.includes('export default {')) { expect(content).toMatch(/description:/); // Category is optional for some rules if (content.includes('category:')) { expect(content).toMatch(/category:/); } } // Each rule should have messages for error reporting (optional for some formats) if (!content.includes('export default {') && !ruleFile.includes('enum-database-validation')) { expect(content).toMatch(/messages:/); } }); }); });