Skip to content

Commit

Permalink
Refactor csv generation to handle user entered commas.
Browse files Browse the repository at this point in the history
  • Loading branch information
brysonjbest committed Jul 23, 2024
1 parent 2decfe6 commit a14d288
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 15 deletions.
22 changes: 22 additions & 0 deletions src/api/scenarioData/scenarioData.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -461,11 +461,13 @@ describe('ScenarioDataService', () => {
inputs: { familyComposition: 'single', numberOfChildren: 2 },
outputs: { isEligible: true, baseAmount: 100 },
expectedResults: {},
resultMatch: false,
},
'Scenario 2': {
inputs: { familyComposition: 'couple', numberOfChildren: 3 },
outputs: { isEligible: false, baseAmount: 200 },
expectedResults: {},
resultMatch: false,
},
};

Expand Down Expand Up @@ -555,6 +557,26 @@ describe('ScenarioDataService', () => {

expect(csvContent.trim()).toBe(expectedCsvContent.trim());
});

it('should escape inputs and outputs containing commas or quotes', async () => {
const goRulesJSONFilename = 'test.json';
const ruleContent = { nodes: [], edges: [] };
const ruleRunResults = {
'Scenario 1': {
inputs: { input1: 'value, with, commas', input2: 'value "with" quotes' },
outputs: { output1: 'result, with, commas', output2: 'result "with" quotes' },
expectedResults: {},
},
};

jest.spyOn(service, 'runDecisionsForScenarios').mockResolvedValue(ruleRunResults);

const csvContent = await service.getCSVForRuleRun(goRulesJSONFilename, ruleContent);

const expectedCsvContent = `Scenario,Results Match Expected (Pass/Fail),Input: input1,Input: input2\nScenario 1,Fail,"value, with, commas",value "with" quotes`;

expect(csvContent.trim()).toBe(expectedCsvContent.trim());
});
});

describe('processProvidedScenarios', () => {
Expand Down
44 changes: 29 additions & 15 deletions src/api/scenarioData/scenarioData.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,29 +164,43 @@ export class ScenarioDataService {
newScenarios,
);

const inputKeys = extractUniqueKeys(ruleRunResults, 'inputs');
const outputKeys = extractUniqueKeys(ruleRunResults, 'result');
const expectedResultsKeys = extractUniqueKeys(ruleRunResults, 'expectedResults');
const keys = {
inputs: extractUniqueKeys(ruleRunResults, 'inputs'),
expectedResults: extractUniqueKeys(ruleRunResults, 'expectedResults'),
result: extractUniqueKeys(ruleRunResults, 'result'),
};

const headers = [
'Scenario',
'Results Match Expected (Pass/Fail)',
...inputKeys.map((key) => `Input: ${key}`),
...expectedResultsKeys.map((key) => `Expected Result: ${key}`),
...outputKeys.map((key) => `Result: ${key}`),
...this.prefixKeys(keys.inputs, 'Input'),
...this.prefixKeys(keys.expectedResults, 'Expected Result'),
...this.prefixKeys(keys.result, 'Result'),
];

const rows = Object.entries(ruleRunResults).map(([scenarioName, scenarioData]) => {
const resultsMatch = scenarioData.resultMatch ? 'Pass' : 'Fail';
const inputs = inputKeys.map((key) => scenarioData.inputs[key] ?? '');
const outputs = outputKeys.map((key) => scenarioData.result[key] ?? '');
const expectedResults = expectedResultsKeys.map((key) => scenarioData.expectedResults[key] ?? '');
const rows = Object.entries(ruleRunResults).map(([scenarioName, data]) => [
this.escapeCSVField(scenarioName),
data.resultMatch ? 'Pass' : 'Fail',
...this.mapFields(data.inputs, keys.inputs),
...this.mapFields(data.expectedResults, keys.expectedResults),
...this.mapFields(data.result, keys.result),
]);

return [scenarioName, resultsMatch, ...inputs, ...expectedResults, ...outputs];
});
return [headers, ...rows].map((row) => row.join(',')).join('\n');
}

private prefixKeys(keys: string[], prefix: string): string[] {
return keys.map((key) => `${prefix}: ${key}`);
}

private mapFields(data: Record<string, any>, keys: string[]): string[] {
return keys.map((key) => this.escapeCSVField(data[key]));
}

const csvContent = [headers, ...rows].map((row) => row.join(',')).join('\n');
return csvContent;
private escapeCSVField(field: any): string {
if (field == null) return '';
const stringField = typeof field === 'string' ? field : String(field);
return stringField.includes(',') ? `"${stringField.replace(/"/g, '""')}"` : stringField;
}

/**
Expand Down

0 comments on commit a14d288

Please sign in to comment.