This commit fixes compiler bug where it fails when optional values are compiled into absent values in nested calls. - Throw exception with more context for easier future debugging. - Add better validation of argument values for nested calls. - Refactor `FunctionCallCompiler` for better clarity and modularize it to make it more maintainable and testable. - Refactor related interface to not have `I` prefix, and function/variable names for better clarity. Context: Discovered this issue while attempting to call `RunInlineCodeAsTrustedInstaller` which in turn invokes `RunPowerShell` for issue #246. This led to the realization that despite parameters flagged as optional, the nested argument compilation didn't support them.
88 lines
2.6 KiB
TypeScript
88 lines
2.6 KiB
TypeScript
import { describe, it } from 'vitest';
|
|
import { NodeDataError, INodeDataErrorContext } from '@/application/Parser/NodeValidation/NodeDataError';
|
|
import { NodeData } from '@/application/Parser/NodeValidation/NodeData';
|
|
import { getAbsentObjectTestCases, getAbsentStringTestCases, itEachAbsentTestCase } from '@tests/unit/shared/TestCases/AbsentTests';
|
|
import { expectDeepThrowsError } from '@tests/unit/shared/Assertions/ExpectDeepThrowsError';
|
|
|
|
export interface ITestScenario {
|
|
readonly act: () => void;
|
|
readonly expectedContext: INodeDataErrorContext;
|
|
}
|
|
|
|
export class NodeValidationTestRunner {
|
|
public testInvalidNodeName(
|
|
testBuildPredicate: (invalidName: string) => ITestScenario,
|
|
) {
|
|
describe('throws given invalid names', () => {
|
|
// arrange
|
|
const testCases = [
|
|
...getAbsentStringTestCases().map((testCase) => ({
|
|
testName: `missing name (${testCase.valueName})`,
|
|
nameValue: testCase.absentValue,
|
|
expectedMessage: 'missing name',
|
|
})),
|
|
{
|
|
testName: 'invalid type',
|
|
nameValue: 33,
|
|
expectedMessage: 'Name (33) is not a string but number.',
|
|
},
|
|
];
|
|
for (const testCase of testCases) {
|
|
it(`given "${testCase.testName}"`, () => {
|
|
const test = testBuildPredicate(testCase.nameValue as never);
|
|
expectThrowsNodeError(test, testCase.expectedMessage);
|
|
});
|
|
}
|
|
});
|
|
return this;
|
|
}
|
|
|
|
public testMissingNodeData(
|
|
testBuildPredicate: (missingNode: NodeData) => ITestScenario,
|
|
) {
|
|
describe('throws given missing node data', () => {
|
|
itEachAbsentTestCase([
|
|
...getAbsentObjectTestCases(),
|
|
{
|
|
valueName: 'empty object',
|
|
absentValue: {},
|
|
},
|
|
], (absentValue) => {
|
|
// arrange
|
|
const expectedError = 'missing node data';
|
|
// act
|
|
const test = testBuildPredicate(absentValue as NodeData);
|
|
// assert
|
|
expectThrowsNodeError(test, expectedError);
|
|
});
|
|
});
|
|
return this;
|
|
}
|
|
|
|
public runThrowingCase(
|
|
testCase: {
|
|
readonly name: string,
|
|
readonly scenario: ITestScenario,
|
|
readonly expectedMessage: string
|
|
},
|
|
) {
|
|
it(testCase.name, () => {
|
|
expectThrowsNodeError(testCase.scenario, testCase.expectedMessage);
|
|
});
|
|
return this;
|
|
}
|
|
}
|
|
|
|
export function expectThrowsNodeError(
|
|
test: ITestScenario,
|
|
expectedMessage: string,
|
|
) {
|
|
// arrange
|
|
const expected = new NodeDataError(expectedMessage, test.expectedContext);
|
|
// act
|
|
const act = () => test.act();
|
|
// assert
|
|
expectDeepThrowsError(act, expected);
|
|
return this;
|
|
}
|