Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate Scenarios for isolation testing of rules. #38

Merged
merged 8 commits into from
Oct 8, 2024

Conversation

brysonjbest
Copy link
Collaborator

@brysonjbest brysonjbest commented Oct 7, 2024

  • Add multiselect handling to klamm inputs.
  • Update CSV exports to UTF-8 to properly render text
  • Update CSV field formatting to handle multiselect option rendering.
  • Create a scenario generation function. This handles the generation of scenarios using the validation information from klamm. This takes in the klamm inputs and rule content, and returns a csv document of these tests with their outputs.

Implementation of this functionality required the creation of sub functions to:

  • Generate possible values for each field. This is currently set to manually generate 10 different options for fields that are number or date ranges. This should be enough for testing purposes, but this has been left as a hardcoded value due to potential memory issues in the deployment environment. If a user selects values on the frontend, these are passed directly to these options, and no additional options are generated. This allows users to define specific fields that should not change.
  • Generate possible combinations of these fields. This uses the cartesian product formula to find all possible combinations of arrays. A limit has been introduced here as well to handle possible memory concerns.
  • Possible combinations are then mapped to their respective fields. Additional randomness is introduced in this step to produce a wide variety of combinations instead of linearly creating them - as that was leading to some combinations being completely cut from the end result if the selection of requested items was not long enough. Duplicates are removed after generation.
  • The final result runs all generated scenarios through the decision of the rulecontent presented, creating a csv file with all scenarios up to the limit requested by the end user. Currently this is capped at 1000 due to memory concerns.

@brysonjbest brysonjbest changed the title Feature/generate scenarios Generate Scenarios for isolation testing of rules. Oct 7, 2024
Copy link
Collaborator

@timwekkenbc timwekkenbc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Not gonna pretend I understood the underworking of every function but I get the basic idea and it runs real slick. I've noted a few minor possible adjustments

Comment on lines 188 to 193
// UTF- 8 encoding with BOM
const bom = '\uFEFF';
const utf8FileContent = bom + fileContent;
res.setHeader('Content-Type', 'text/csv; charset=utf-8');
res.setHeader('Content-Disposition', `attachment; filename=${filepath.replace(/\.json$/, '.csv')}`);
res.status(HttpStatus.OK).send(utf8FileContent);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is almost the same code as in two other places in this file, so you could throw it in a function

Comment on lines +469 to +476
const decisionResult = await this.decisionsService.runDecision(
ruleContent,
filepath,
formattedVariablesObject,
{
trace: true,
},
);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably outside the scope of this, but this has me thinking we may eventually want to update decisionsService to run decisions on bulk (like many different contexts for the same ruleContent). However, you should update this to make use of Promise.all so that it doesn't have to wait for each decision before running the next one.


const combinations = this.generateCombinations(ruleSchema, simulationContext, testScenarioCount).slice(
0,
testScenarioCount || 100,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to have testScenarioCount: number = 100, in the parameters

Comment on lines 506 to 535
const ruleRunResults: RuleRunResults = await this.generateTestScenarios(
filepath,
ruleContent,
simulationContext,
testScenarioCount,
);

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

const headers = [
'Scenario',
'Results Match Expected (Pass/Fail)',
...this.prefixKeys(keys.inputs, 'Input'),
...this.prefixKeys(keys.expectedResults, 'Expected Result'),
...this.prefixKeys(keys.result, 'Result'),
];

const rows = Object.entries(ruleRunResults).map(([scenarioName, data]) => [
this.escapeCSVField(scenarioName),
`n/a`,
...this.mapFields(data.inputs, keys.inputs),
...this.mapFields(data.expectedResults, keys.expectedResults),
...this.mapFields(data.result, keys.result),
]);

return [headers, ...rows].map((row) => row.join(',')).join('\n');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we wrap this whole thing in a try/catch, make sure we're throwing errors in the above functions it uses, and then throw an error here too. This way we could return the proper error to the user in scenarioData.controller if we wanted.

src/utils/csv.ts Outdated
* @param arrays The arrays to generate the product from.
* @returns The generated product.
*/
export const cartesianProduct = (arrays: any[][]): any[][] => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be able to use a generic type "" instead of "any". <T>(arrays: T[][]): T[][]

src/utils/csv.ts Outdated
* @param limit The maximum number of combinations to generate.
* @returns The generated product.
*/
export const complexCartesianProduct = (arrays: any[][], limit: number = 3000): any[][] => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be able to use a generic type "" instead of "any".

export const complexCartesianProduct = <T>(arrays: T[][], limit: number = 3000): T[][] => {
  const result: T[][] = [];

@brysonjbest brysonjbest merged commit 949f04f into dev Oct 8, 2024
2 checks passed
@brysonjbest brysonjbest deleted the feature/generate-scenarios branch December 19, 2024 23:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants