Add validation for max line length in compiler
This commit adds validation logic in compiler to check for max allowed characters per line for scripts. This allows preventing bugs caused by limitation of terminal emulators. Other supporting changes: - Rename/refactor related code for clarity and better maintainability. - Drop `I` prefix from interfaces to align with latest convention. - Refactor CodeValidator to be functional rather than object-oriented for simplicity. - Refactor syntax definition construction to be functional and be part of rule for better separation of concerns. - Refactored validation logic to use an enum-based factory pattern for improved maintainability and scalability.
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { CodeValidationRule } from '@/application/Parser/Executable/Script/Validation/CodeValidationRule';
|
||||
import { analyzeEmptyLines } from '@/application/Parser/Executable/Script/Validation/Analyzers/AnalyzeEmptyLines';
|
||||
import { analyzeDuplicateLines } from '@/application/Parser/Executable/Script/Validation/Analyzers/AnalyzeDuplicateLines';
|
||||
import { analyzeTooLongLines } from '@/application/Parser/Executable/Script/Validation/Analyzers/AnalyzeTooLongLines';
|
||||
import { createValidationAnalyzers } from '@/application/Parser/Executable/Script/Validation/ValidationRuleAnalyzerFactory';
|
||||
import type { CodeValidationAnalyzer } from '@/application/Parser/Executable/Script/Validation/Analyzers/CodeValidationAnalyzer';
|
||||
import { formatAssertionMessage } from '@tests/shared/FormatAssertionMessage';
|
||||
|
||||
describe('ValidationRuleAnalyzerFactory', () => {
|
||||
describe('createValidationAnalyzers', () => {
|
||||
it('throws error when no rules are provided', () => {
|
||||
// arrange
|
||||
const expectedErrorMessage = 'missing rules';
|
||||
const rules: readonly CodeValidationRule[] = [];
|
||||
const context = new TestContext()
|
||||
.withRules(rules);
|
||||
// act
|
||||
const act = () => context.create();
|
||||
// assert
|
||||
expect(act).to.throw(expectedErrorMessage);
|
||||
});
|
||||
|
||||
it('creates correct analyzers for all valid rules', () => {
|
||||
// arrange
|
||||
const expectedAnalyzersForRules: Record<CodeValidationRule, CodeValidationAnalyzer> = {
|
||||
[CodeValidationRule.NoEmptyLines]: analyzeEmptyLines,
|
||||
[CodeValidationRule.NoDuplicatedLines]: analyzeDuplicateLines,
|
||||
[CodeValidationRule.NoTooLongLines]: analyzeTooLongLines,
|
||||
};
|
||||
const givenRules: CodeValidationRule[] = Object
|
||||
.keys(expectedAnalyzersForRules)
|
||||
.map((r) => Number(r) as CodeValidationRule);
|
||||
const context = new TestContext()
|
||||
.withRules(givenRules);
|
||||
// act
|
||||
const actualAnalyzers = context.create();
|
||||
// assert
|
||||
expect(actualAnalyzers).to.have.lengthOf(Object.entries(expectedAnalyzersForRules).length);
|
||||
const expectedAnalyzers = Object.values(expectedAnalyzersForRules);
|
||||
expect(actualAnalyzers).to.deep.equal(expectedAnalyzers);
|
||||
});
|
||||
|
||||
it('throws error for unknown rule', () => {
|
||||
// arrange
|
||||
const unknownRule = 9999 as CodeValidationRule;
|
||||
const expectedErrorMessage = `Unknown rule: ${unknownRule}`;
|
||||
const context = new TestContext()
|
||||
.withRules([unknownRule]);
|
||||
// arrange
|
||||
const act = () => context.create();
|
||||
// assert
|
||||
expect(act).to.throw(expectedErrorMessage);
|
||||
});
|
||||
|
||||
it('throws error for duplicate rules', () => {
|
||||
// arrange
|
||||
const duplicate1 = CodeValidationRule.NoEmptyLines;
|
||||
const duplicate2 = CodeValidationRule.NoDuplicatedLines;
|
||||
const rules: CodeValidationRule[] = [
|
||||
duplicate1, duplicate1,
|
||||
duplicate2, duplicate2,
|
||||
];
|
||||
const expectedErrorMessage: string = [
|
||||
'Duplicate rules are not allowed.',
|
||||
`Duplicates found: ${CodeValidationRule[duplicate1]} (2 times), ${CodeValidationRule[duplicate2]} (2 times)`,
|
||||
].join(' ');
|
||||
const context = new TestContext()
|
||||
.withRules(rules);
|
||||
// act
|
||||
const act = () => context.create();
|
||||
// assert
|
||||
expect(act).to.throw(expectedErrorMessage);
|
||||
});
|
||||
|
||||
it('handles single rule correctly', () => {
|
||||
// arrange
|
||||
const givenRule = CodeValidationRule.NoEmptyLines;
|
||||
const expectedAnalyzer = analyzeEmptyLines;
|
||||
const context = new TestContext()
|
||||
.withRules([givenRule]);
|
||||
// act
|
||||
const analyzers = context.create();
|
||||
// assert
|
||||
expect(analyzers).to.have.lengthOf(1);
|
||||
expect(analyzers[0]).toBe(expectedAnalyzer);
|
||||
});
|
||||
|
||||
it('handles multiple unique rules correctly', () => {
|
||||
// arrange
|
||||
const expectedRuleAnalyzerPairs = new Map<CodeValidationRule, CodeValidationAnalyzer>([
|
||||
[CodeValidationRule.NoEmptyLines, analyzeEmptyLines],
|
||||
[CodeValidationRule.NoDuplicatedLines, analyzeDuplicateLines],
|
||||
]);
|
||||
const rules = Array.from(expectedRuleAnalyzerPairs.keys());
|
||||
const context = new TestContext()
|
||||
.withRules(rules);
|
||||
// act
|
||||
const actualAnalyzers = context.create();
|
||||
// assert
|
||||
expect(actualAnalyzers).to.have.lengthOf(expectedRuleAnalyzerPairs.size);
|
||||
actualAnalyzers.forEach((analyzer, index) => {
|
||||
const rule = rules[index];
|
||||
const expectedAnalyzer = expectedRuleAnalyzerPairs.get(rule);
|
||||
expect(analyzer).to.equal(expectedAnalyzer, formatAssertionMessage([
|
||||
`Analyzer for rule ${CodeValidationRule[rule]} does not match the expected analyzer`,
|
||||
]));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
class TestContext {
|
||||
private rules: readonly CodeValidationRule[] = [CodeValidationRule.NoDuplicatedLines];
|
||||
|
||||
public withRules(rules: readonly CodeValidationRule[]): this {
|
||||
this.rules = rules;
|
||||
return this;
|
||||
}
|
||||
|
||||
public create(): ReturnType<typeof createValidationAnalyzers> {
|
||||
return createValidationAnalyzers(this.rules);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user