From ca27a3392d4a7af5af2c1c30ae6c849140a586b6 Mon Sep 17 00:00:00 2001 From: redchy Date: Tue, 3 Mar 2026 01:38:43 +0000 Subject: [PATCH 01/19] Update high level design for Function Testing --- .../FunctionTesting/highLevelDesign.md | 41 +++++++++++-------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/doc/TestingApplications/FunctionTesting/highLevelDesign.md b/doc/TestingApplications/FunctionTesting/highLevelDesign.md index 2f477d6a..69e75dbf 100644 --- a/doc/TestingApplications/FunctionTesting/highLevelDesign.md +++ b/doc/TestingApplications/FunctionTesting/highLevelDesign.md @@ -27,34 +27,39 @@ Function Testing automatically checks that each Function implementation ... - **Implementer**: Applies the package of test cases and mock servers on its individual Function implementation - **ContinuousTesting/ContinuousIntegration**: Binding the test packages into an automation chain and executing it with every pull request or merge. -### ?? +### Responsabilities -The ApplicationOwner is writing the spec ???. -The TestEngineer -- creates a description og the testing scenarios (,e.g. expected function, invalid inputs) +**The ApplicationOwner** : writes/owns the Function spec (interface.yaml/variable.yaml + processing dependencies). +**The TestEngineer** : Produces and maintains the executable test package (scenarios, fixtures, mocks, config, error-mapping, generated Jest tests/runner). +**The Implementer** : Implements the function according to the spec and runs the test package locally, fixing code until all scenarios in the scenarios.yml file pass deterministically. +**The ContinuousTesting/ContinuousIntegration** : Runs the tests in scenarios.yml file automatically for every pull request using a fixed setup, saves the test results, and blocks the merge if any test fails. -is creating jest.js modules from the ??? of the Functions that are consumed by the Function under test. -The Implementer executes the jest.js module for the Function under test. - - - for mocking the consumed Functions. +### Description of the Testing Scenarios +The scenarios are described in a yaml file. +The yaml file shall have the following naming "/testing/p1FunctionName/scenarios.yaml". +The scenarios.yaml file shall contain the following information: +- Scenario ID and description (unique name for reporting and traceability) +- the input for the function under test : reference to an input JSON fixture in "testing//Input/" +- the expected output for a given input :reference to an output JSON fixture in "testing//Output/" +- the expected error for a given input : exact string as defined in the spec +- the mocks : for each dependency step from the spec processing section, defining either: + - a return payload (reference to an output JSON fixture) + - an error string to throw (which the function must map deterministically) +- Module configuration : module path + export name for the function under test and each dependency (to keep tests independent of repository structure changes) +- Error mapping : (dependency failure string → function error enum string) to guarantee deterministic error handling across implementations -The ContinuousTesting/ContinuousIntegration +### Naming rules for the input/output +- Scenario IDs : Use a stable, readable ID (e.g happy_path,invalid_missing_MountName ...) +- Input fixtures : shall have the following naming in_.json (e.g in_happy_path.json,in_invalid_missing_MountName.json ...) +- Expected function outputs : success → out__success.json (e.g., out_happy_path_success.json); errors → use error enums from scenarios.yaml +- Mock outputs for dependencies : shall have the following naming mock__.json (e.g mock_p1FieldsFilter_happy_path.json) -### Description of the Testing Scenarios -The scenarios are described in a yaml file. -The yaml file shall have the following naming "/testing/p1FunctionName/scenarios.yaml". -The scenarios.yaml file shall contain the following information: -- the input for the function under test -- the expected output for a given input -- the expected error for a given input -- the mocks From 3565feb23a441b499b4ec04977ec802123506f4f Mon Sep 17 00:00:00 2001 From: redchy Date: Tue, 3 Mar 2026 01:43:17 +0000 Subject: [PATCH 02/19] Remove duplicate responsibilities in highLevelDesign.md Removed duplicate responsibilities section for clarity. --- .../FunctionTesting/highLevelDesign.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/TestingApplications/FunctionTesting/highLevelDesign.md b/doc/TestingApplications/FunctionTesting/highLevelDesign.md index 69e75dbf..42d663aa 100644 --- a/doc/TestingApplications/FunctionTesting/highLevelDesign.md +++ b/doc/TestingApplications/FunctionTesting/highLevelDesign.md @@ -29,10 +29,10 @@ Function Testing automatically checks that each Function implementation ... ### Responsabilities -**The ApplicationOwner** : writes/owns the Function spec (interface.yaml/variable.yaml + processing dependencies). -**The TestEngineer** : Produces and maintains the executable test package (scenarios, fixtures, mocks, config, error-mapping, generated Jest tests/runner). -**The Implementer** : Implements the function according to the spec and runs the test package locally, fixing code until all scenarios in the scenarios.yml file pass deterministically. -**The ContinuousTesting/ContinuousIntegration** : Runs the tests in scenarios.yml file automatically for every pull request using a fixed setup, saves the test results, and blocks the merge if any test fails. +- **The ApplicationOwner** : writes/owns the Function spec (interface.yaml/variable.yaml + processing dependencies). +- **The TestEngineer** : Produces and maintains the executable test package (scenarios, fixtures, mocks, config, error-mapping, generated Jest tests/runner). +- **The Implementer** : Implements the function according to the spec and runs the test package locally, fixing code until all scenarios in the scenarios.yml file pass deterministically. +- **The ContinuousTesting/ContinuousIntegration** : Runs the tests in scenarios.yml file automatically for every pull request using a fixed setup, saves the test results, and blocks the merge if any test fails. From 4a0b46d5691fc539bb5226713cbfbaf0a6204d44 Mon Sep 17 00:00:00 2001 From: redchy Date: Tue, 3 Mar 2026 01:45:34 +0000 Subject: [PATCH 03/19] Update references in highLevelDesign.md for scenarios --- doc/TestingApplications/FunctionTesting/highLevelDesign.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/TestingApplications/FunctionTesting/highLevelDesign.md b/doc/TestingApplications/FunctionTesting/highLevelDesign.md index 42d663aa..cb3fef2b 100644 --- a/doc/TestingApplications/FunctionTesting/highLevelDesign.md +++ b/doc/TestingApplications/FunctionTesting/highLevelDesign.md @@ -42,8 +42,8 @@ The scenarios are described in a yaml file. The yaml file shall have the following naming "/testing/p1FunctionName/scenarios.yaml". The scenarios.yaml file shall contain the following information: - Scenario ID and description (unique name for reporting and traceability) -- the input for the function under test : reference to an input JSON fixture in "testing//Input/" -- the expected output for a given input :reference to an output JSON fixture in "testing//Output/" +- the input for the function under test : reference to an input JSON fixture in "testing/p1FunctionName/Input/" +- the expected output for a given input : reference to an output JSON fixture in "testing/p1FunctionName/Output/" - the expected error for a given input : exact string as defined in the spec - the mocks : for each dependency step from the spec processing section, defining either: - a return payload (reference to an output JSON fixture) From 627296b568f3956ca1358bff549327e15380a6cf Mon Sep 17 00:00:00 2001 From: redchy Date: Tue, 3 Mar 2026 01:57:42 +0000 Subject: [PATCH 04/19] Revise responsibilities and testing workflow details Updated the responsibilities section to clarify roles in the testing workflow and improved the description of the testing scenarios. --- .../FunctionTesting/highLevelDesign.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/TestingApplications/FunctionTesting/highLevelDesign.md b/doc/TestingApplications/FunctionTesting/highLevelDesign.md index cb3fef2b..2869f9c7 100644 --- a/doc/TestingApplications/FunctionTesting/highLevelDesign.md +++ b/doc/TestingApplications/FunctionTesting/highLevelDesign.md @@ -27,12 +27,12 @@ Function Testing automatically checks that each Function implementation ... - **Implementer**: Applies the package of test cases and mock servers on its individual Function implementation - **ContinuousTesting/ContinuousIntegration**: Binding the test packages into an automation chain and executing it with every pull request or merge. -### Responsabilities +### Testing workflow -- **The ApplicationOwner** : writes/owns the Function spec (interface.yaml/variable.yaml + processing dependencies). -- **The TestEngineer** : Produces and maintains the executable test package (scenarios, fixtures, mocks, config, error-mapping, generated Jest tests/runner). -- **The Implementer** : Implements the function according to the spec and runs the test package locally, fixing code until all scenarios in the scenarios.yml file pass deterministically. -- **The ContinuousTesting/ContinuousIntegration** : Runs the tests in scenarios.yml file automatically for every pull request using a fixed setup, saves the test results, and blocks the merge if any test fails. +- **The ApplicationOwner** : writes the function specification, which defines inputs, outputs, and dependencies. +- **The TestEngineer** : Based on this specification creates the testing scenarios (including valid and invalid inputs, expected success outputs, and error cases) and generates the Jest test modules that automatically mock all dependent functions consumed by the function under test . +- **The Implementer** : Implements the function according to the specification and runs these Jest modules against their function implementation to verify correctness. +- **The ContinuousTesting/ContinuousIntegration** : Ensures that all generated tests are executed automatically for every pull request, providing reproducible and deterministic validation of the function behavior. From f1b53ae8769ac3e02778e598890af8631c26c178 Mon Sep 17 00:00:00 2001 From: redchy Date: Tue, 3 Mar 2026 01:58:12 +0000 Subject: [PATCH 05/19] Fix formatting issue in highLevelDesign.md --- doc/TestingApplications/FunctionTesting/highLevelDesign.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/TestingApplications/FunctionTesting/highLevelDesign.md b/doc/TestingApplications/FunctionTesting/highLevelDesign.md index 2869f9c7..d3340e16 100644 --- a/doc/TestingApplications/FunctionTesting/highLevelDesign.md +++ b/doc/TestingApplications/FunctionTesting/highLevelDesign.md @@ -30,7 +30,7 @@ Function Testing automatically checks that each Function implementation ... ### Testing workflow - **The ApplicationOwner** : writes the function specification, which defines inputs, outputs, and dependencies. -- **The TestEngineer** : Based on this specification creates the testing scenarios (including valid and invalid inputs, expected success outputs, and error cases) and generates the Jest test modules that automatically mock all dependent functions consumed by the function under test . +- **The TestEngineer** : Based on this specification creates the testing scenarios (including valid and invalid inputs, expected success outputs, and error cases) and generates the Jest test modules that automatically mock all dependent functions consumed by the function under test. - **The Implementer** : Implements the function according to the specification and runs these Jest modules against their function implementation to verify correctness. - **The ContinuousTesting/ContinuousIntegration** : Ensures that all generated tests are executed automatically for every pull request, providing reproducible and deterministic validation of the function behavior. From 897e6d124a691dcd2b7277b99e66cf42e39f1e56 Mon Sep 17 00:00:00 2001 From: redchy Date: Tue, 3 Mar 2026 01:59:45 +0000 Subject: [PATCH 06/19] Update Implementer role description in testing workflow Clarified that Jest modules are run locally by the Implementer. --- doc/TestingApplications/FunctionTesting/highLevelDesign.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/TestingApplications/FunctionTesting/highLevelDesign.md b/doc/TestingApplications/FunctionTesting/highLevelDesign.md index d3340e16..8aeabd10 100644 --- a/doc/TestingApplications/FunctionTesting/highLevelDesign.md +++ b/doc/TestingApplications/FunctionTesting/highLevelDesign.md @@ -31,7 +31,7 @@ Function Testing automatically checks that each Function implementation ... - **The ApplicationOwner** : writes the function specification, which defines inputs, outputs, and dependencies. - **The TestEngineer** : Based on this specification creates the testing scenarios (including valid and invalid inputs, expected success outputs, and error cases) and generates the Jest test modules that automatically mock all dependent functions consumed by the function under test. -- **The Implementer** : Implements the function according to the specification and runs these Jest modules against their function implementation to verify correctness. +- **The Implementer** : Implements the function according to the specification and runs these Jest modules locally against their function implementation to verify correctness. - **The ContinuousTesting/ContinuousIntegration** : Ensures that all generated tests are executed automatically for every pull request, providing reproducible and deterministic validation of the function behavior. From a7033858581a2f3f81447461f6b7d8f3eb1d2370 Mon Sep 17 00:00:00 2001 From: redchy Date: Wed, 4 Mar 2026 14:35:19 +0000 Subject: [PATCH 07/19] Refine High Level Design for Function Testing --- .../diagrams/highLevelDesignTestFlow.plantUml | 48 ++++++++++++++++++ .../diagrams/highLevelDesignTestFlow.png | Bin 0 -> 49206 bytes .../FunctionTesting/highLevelDesign.md | 2 +- 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 doc/TestingApplications/FunctionTesting/diagrams/highLevelDesignTestFlow.plantUml create mode 100644 doc/TestingApplications/FunctionTesting/diagrams/highLevelDesignTestFlow.png diff --git a/doc/TestingApplications/FunctionTesting/diagrams/highLevelDesignTestFlow.plantUml b/doc/TestingApplications/FunctionTesting/diagrams/highLevelDesignTestFlow.plantUml new file mode 100644 index 00000000..305a7d65 --- /dev/null +++ b/doc/TestingApplications/FunctionTesting/diagrams/highLevelDesignTestFlow.plantUml @@ -0,0 +1,48 @@ +@startuml highLevelDesignTestFlow +skinparam responseMessageBelowArrow true +title +DPMDP - Function testing flow +end title + +skinparam responseMessageBelowArrow true + + +actor ApplicationOwner as AO +actor TestEngineer as TE +actor Implementer as IM +actor "ContinuousTesting/CI" as CI + +participant "Function Spec\n(spec/Functions/**)" as SPEC +participant "Scenario + Fixtures\n(testing//...)" as SCEN +participant "Jest Test Modules\n(generated)" as JEST +participant "Function Implementation\n(src/...)" as FCT +participant "Dependencies\n(mocked modules)" as DEP + +== Authoring == +AO -> SPEC : Write/maintain spec\n(inputs, outputs, dependencies) +TE -> SPEC : Read spec +TE -> SCEN : Create scenarios + fixtures\n(valid/invalid, expected outputs, errors, mocks) +TE -> JEST : Generate Jest modules\n(runner + auto-mocking) + +== Local verification == +IM -> FCT : Implement function according to spec +IM -> JEST : Run tests locally (npm test) + +loop For each scenario + JEST -> SCEN : Load scenario + fixtures + JEST -> JEST : Resolve moduleConfig + errorMapping + JEST -> DEP : Install Jest mocks for all dependencies + JEST -> FCT : Call function with input fixture + alt success expected + FCT --> JEST : Return output + JEST -> JEST : Assert deep-equals expected output fixture + else error expected + FCT --> JEST : Throw/return error enum + JEST -> JEST : Assert exact error enum string + end +end + +== PR validation == +CI -> JEST : Run generated tests on every PR +CI -> CI : Publish JUnit XML + logs\nand enforce pass-as-gate +@enduml \ No newline at end of file diff --git a/doc/TestingApplications/FunctionTesting/diagrams/highLevelDesignTestFlow.png b/doc/TestingApplications/FunctionTesting/diagrams/highLevelDesignTestFlow.png new file mode 100644 index 0000000000000000000000000000000000000000..8a8c35b13f264f279d3d233931dd7e6f18ec1346 GIT binary patch literal 49206 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 RkC0(d5+br6ONI3O{||#*4 Date: Wed, 4 Mar 2026 14:40:46 +0000 Subject: [PATCH 08/19] Naming rules --- doc/TestingApplications/FunctionTesting/highLevelDesign.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/TestingApplications/FunctionTesting/highLevelDesign.md b/doc/TestingApplications/FunctionTesting/highLevelDesign.md index c3b56967..a0e0d603 100644 --- a/doc/TestingApplications/FunctionTesting/highLevelDesign.md +++ b/doc/TestingApplications/FunctionTesting/highLevelDesign.md @@ -54,9 +54,9 @@ The scenarios.yaml file shall contain the following information: ### Naming rules for the input/output - Scenario IDs : Use a stable, readable ID (e.g happy_path,invalid_missing_MountName ...) -- Input fixtures : shall have the following naming in_.json (e.g in_happy_path.json,in_invalid_missing_MountName.json ...) -- Expected function outputs : success → out__success.json (e.g., out_happy_path_success.json); errors → use error enums from scenarios.yaml -- Mock outputs for dependencies : shall have the following naming mock__.json (e.g mock_p1FieldsFilter_happy_path.json) +- Input fixtures : shall have the following naming in_scenarioId.json (e.g in_happy_path.json,in_invalid_missing_MountName.json ...) +- Expected function outputs : success → out_scenarioId_success.json (e.g., out_happy_path_success.json); errors → use error enums from scenarios.yaml +- Mock outputs for dependencies : shall have the following naming mock_dependencyStepName_scenarioId.json (e.g mock_p1FieldsFilter_happy_path.json) From 56192038dc9ce9e79066a26deb605f2d42d4031e Mon Sep 17 00:00:00 2001 From: redchy Date: Wed, 4 Mar 2026 14:43:53 +0000 Subject: [PATCH 09/19] Fix input/output JSON fixture references in documentation Updated references for input and output JSON fixtures to use lowercase directory names. --- .../FunctionTesting/highLevelDesign.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/TestingApplications/FunctionTesting/highLevelDesign.md b/doc/TestingApplications/FunctionTesting/highLevelDesign.md index a0e0d603..c08b71d1 100644 --- a/doc/TestingApplications/FunctionTesting/highLevelDesign.md +++ b/doc/TestingApplications/FunctionTesting/highLevelDesign.md @@ -42,8 +42,8 @@ The scenarios are described in a yaml file. The yaml file shall have the following naming "/testing/p1FunctionName/scenarios.yaml". The scenarios.yaml file shall contain the following information: - Scenario ID and description (unique name for reporting and traceability) -- the input for the function under test : reference to an input JSON fixture in "testing/p1FunctionName/Input/" -- the expected output for a given input : reference to an output JSON fixture in "testing/p1FunctionName/Output/" +- the input for the function under test : reference to an input JSON fixture in "testing/p1FunctionName/input/" +- the expected output for a given input : reference to an output JSON fixture in "testing/p1FunctionName/output/" - the expected error for a given input : exact string as defined in the spec - the mocks : for each dependency step from the spec processing section, defining either: - a return payload (reference to an output JSON fixture) @@ -53,8 +53,8 @@ The scenarios.yaml file shall contain the following information: ### Naming rules for the input/output -- Scenario IDs : Use a stable, readable ID (e.g happy_path,invalid_missing_MountName ...) -- Input fixtures : shall have the following naming in_scenarioId.json (e.g in_happy_path.json,in_invalid_missing_MountName.json ...) +- Scenario IDs : Use a stable, readable ID (e.g happy_path,invalid_missing_mountName ...) +- Input fixtures : shall have the following naming in_scenarioId.json (e.g in_happy_path.json,in_invalid_missing_mountName.json ...) - Expected function outputs : success → out_scenarioId_success.json (e.g., out_happy_path_success.json); errors → use error enums from scenarios.yaml - Mock outputs for dependencies : shall have the following naming mock_dependencyStepName_scenarioId.json (e.g mock_p1FieldsFilter_happy_path.json) From 5c54a9dd64608f5628ee599cc89e3c3d4a17891c Mon Sep 17 00:00:00 2001 From: redchy Date: Thu, 5 Mar 2026 00:57:57 +0000 Subject: [PATCH 10/19] Naming rules --- doc/TestingApplications/FunctionTesting/highLevelDesign.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/TestingApplications/FunctionTesting/highLevelDesign.md b/doc/TestingApplications/FunctionTesting/highLevelDesign.md index c08b71d1..66ee2bd2 100644 --- a/doc/TestingApplications/FunctionTesting/highLevelDesign.md +++ b/doc/TestingApplications/FunctionTesting/highLevelDesign.md @@ -43,12 +43,11 @@ The yaml file shall have the following naming "/testing/p1FunctionName/scenarios The scenarios.yaml file shall contain the following information: - Scenario ID and description (unique name for reporting and traceability) - the input for the function under test : reference to an input JSON fixture in "testing/p1FunctionName/input/" -- the expected output for a given input : reference to an output JSON fixture in "testing/p1FunctionName/output/" -- the expected error for a given input : exact string as defined in the spec +- the expected output for a given input : reference to an output JSON fixture in "testing/p1FunctionName/output/" or the expected error for a given input : exact string as defined in the spec - the mocks : for each dependency step from the spec processing section, defining either: - - a return payload (reference to an output JSON fixture) + - a return payload (reference to an output JSON fixture) or - an error string to throw (which the function must map deterministically) -- Module configuration : module path + export name for the function under test and each dependency (to keep tests independent of repository structure changes) +- Module configuration : module path + export name for the function under test and each dependency - Error mapping : (dependency failure string → function error enum string) to guarantee deterministic error handling across implementations ### Naming rules for the input/output From 0e5650dbf9b9e740f39e14742d3e70d9e06d06df Mon Sep 17 00:00:00 2001 From: redchy Date: Fri, 6 Mar 2026 02:38:17 +0000 Subject: [PATCH 11/19] Adding scenario example and update HLD --- .../FunctionTesting/highLevelDesign.md | 301 ++++++------------ .../FunctionTesting/scenarioExample.yml | 72 +++++ 2 files changed, 172 insertions(+), 201 deletions(-) create mode 100644 doc/TestingApplications/FunctionTesting/scenarioExample.yml diff --git a/doc/TestingApplications/FunctionTesting/highLevelDesign.md b/doc/TestingApplications/FunctionTesting/highLevelDesign.md index 66ee2bd2..fe48145a 100644 --- a/doc/TestingApplications/FunctionTesting/highLevelDesign.md +++ b/doc/TestingApplications/FunctionTesting/highLevelDesign.md @@ -27,7 +27,7 @@ Function Testing automatically checks that each Function implementation ... - **Implementer**: Applies the package of test cases and mock servers on its individual Function implementation - **ContinuousTesting/ContinuousIntegration**: Binding the test packages into an automation chain and executing it with every pull request or merge. -### Testing workflow +### End-to-End Workflow - **The ApplicationOwner** : writes the function specification, which defines inputs, outputs, and dependencies. - **The TestEngineer** : Based on this specification creates the testing scenarios (including valid and invalid inputs, expected success outputs, and error cases) and generates the Jest test modules that automatically mock all dependent functions consumed by the function under test. @@ -36,205 +36,104 @@ Function Testing automatically checks that each Function implementation ... ![Overview](./diagrams/highLevelDesignTestFlow.png) -### Description of the Testing Scenarios - -The scenarios are described in a yaml file. -The yaml file shall have the following naming "/testing/p1FunctionName/scenarios.yaml". -The scenarios.yaml file shall contain the following information: -- Scenario ID and description (unique name for reporting and traceability) -- the input for the function under test : reference to an input JSON fixture in "testing/p1FunctionName/input/" -- the expected output for a given input : reference to an output JSON fixture in "testing/p1FunctionName/output/" or the expected error for a given input : exact string as defined in the spec -- the mocks : for each dependency step from the spec processing section, defining either: - - a return payload (reference to an output JSON fixture) or - - an error string to throw (which the function must map deterministically) -- Module configuration : module path + export name for the function under test and each dependency -- Error mapping : (dependency failure string → function error enum string) to guarantee deterministic error handling across implementations - -### Naming rules for the input/output - -- Scenario IDs : Use a stable, readable ID (e.g happy_path,invalid_missing_mountName ...) -- Input fixtures : shall have the following naming in_scenarioId.json (e.g in_happy_path.json,in_invalid_missing_mountName.json ...) -- Expected function outputs : success → out_scenarioId_success.json (e.g., out_happy_path_success.json); errors → use error enums from scenarios.yaml -- Mock outputs for dependencies : shall have the following naming mock_dependencyStepName_scenarioId.json (e.g mock_p1FieldsFilter_happy_path.json) - - - - - - - - - -### Concept - -Function Testing is **spec-driven** + **scenario-based**. - -### Inputs -1. **Function spec**: `spec/Functions/**/interface.yaml` and `spec/Functions/**/variable.yaml` -This is the definition of the function. It provides: - - **Input schema** - - required fields - - types / formats (when declared) - - **Output schema** - - success output shape - - error output enum strings - - **Dependencies** - - `processing` steps define what the function calls (external calls or sub-functions) -2. **Scenario matrix: `scenarios.yaml` (one per function version)** - -This is the list of test cases. -Each scenario defines: -- which **input fixture** is used -- how each dependency behaves during this scenario: - - return a fixture, or - - throw an error string -- what the function is expected to produce: - - expected success output fixture, or - - expected function-level error enum string -3. **Fixtures: JSON files** - -Fixtures are committed JSON files used by scenarios: -- input fixtures (valid and invalid) -- dependency return payload fixtures -- expected (“golden”) output fixtures -4. **Configuration: `test-config.yaml`** - -This maps spec dependency names to actual module paths and exports: -- function under test -> module path + export name -- dependency step name -> module path + export name -It allows the tests to be independent of repository structure changes. -5. **Error mapping: `error-mapping.yaml`** - -Defines deterministic mapping from: -- “dependency failure string” -> “function error enum string” -This prevents ambiguous or inconsistent error handling between implementations. -### Sandbox Environment -The sandbox environment is a controlled, reproducible setup where Function Tests run the same locally and in CI. -**It includes** : -- a fixed Node.js + npm dependency set (pinned by lockfile / CI image) -- Jest as the test runner (npm test) -- committed test assets (scenarios + JSON fixtures + mappings) -- the function under test (once implemented) - -**Key properties** : -- No external infrastructure is required . -- All dependencies listed in the spec processing section are mocked at module level (Jest module mocking). -## Mocking -### What is mocked? -**All dependencies** listed under `processing` are mocked. -The function under test is the only “real” code executed (once implemented). -Dependencies typically include: -- external calls (e.g., reading from MWDI/ ES) -- sub-functions called as steps -### How mocks are defined (data, not code) -Mocks are defined in `scenarios.yaml`. -Each scenario provides, per dependency: -- **return fixture path** OR **error to throw** -So the “mock implementation” is just: -- “for this step, load this JSON file and return it” -- or “throw this failure string” -### How mocks are applied (scenario runner behavior) -For each scenario test, the scenario runner performs: -1. **Load scenario** - - Read the scenario entry from `scenarios.yaml`. -2. **Resolve module paths** - - Using `test-config.yaml`, determine which module path corresponds to each dependency step. -3. **Install mocks** - - Use Jest module mocking (`jest.mock(modulePath)`) to replace each dependency module. - - Each mocked module’s exported function is configured to: +### Test Package Structure (What exists per Function version) + +For each Function version, the test package consists of: + +- **Scenario definition** + - `testing/FunctionName/version/scenarios.yaml` +- **Fixtures** + - `testing/FunctionName/version/input/*.json` + - `testing/FunctionName/version/output/*.json` +- **Generated Jest test module(s)** + - one generated Jest test file per Function version + - `testing/FunctionName/version/tests/FunctionName.test.js` +- **Runtime environment** + - Node.js + npm dependencies + +### Scenario Definition (`scenarios.yaml`) + +Scenarios are described in a YAML file: + +- Path: `testing/FunctionName/version/scenarios.yaml` + +Each scenario entry contains: + +- **Scenario ID + description** (unique, stable) +- **Input fixture reference** + JSON fixture located in `testing/FunctionName/version/input/` +- **Expected outcome** + - expected success output fixture in `testing/FunctionName/version/output/`, **or** + - expected error enum string (exactly as defined in the spec) +- **Mocks for dependencies** + For each dependency step from the spec `processing` section: + - return payload fixture (JSON), **or** + - error string to throw (mapped deterministically) +- **Module configuration** + module path + export name for: + - the Function under test + - each dependency step +- **Error mapping** + dependency failure string → Function-level error enum string + +### Naming Rules for Fixtures + +- **Scenario IDs**: stable, readable IDs (e.g. `happy_path`, `invalid_missing_mountName`) +- **Input fixtures**: `in_scenarioId.json` + Example: `in_happy_path.json` +- **Expected outputs**: + - success: `out_scenarioId_success.json` + Example: `out_happy_path_success.json` + - errors: use error enum strings in `scenarios.yaml` +- **Dependency mock outputs**: `mock_dependencyStepName_scenarioId.json` + Example: `mock_p1FieldsFilter_happy_path.json` + +### Scenario Execution (Runner Logic) + +**Requirement**: All processing steps to be mocked must be implemented as importable modules (adapters/helpers/sub-functions) so they can be mocked by Jest + +The scenario execution logic is implemented once and reused by all generated Jest test files : + +- Path: `testing/tools/functionTestRunner.js` + +For each scenario ID, the runner performs: + +- **Load test data** + - load `in_scenarioId.json` + - load mock fixtures referenced by the scenario + +- **Install dependency mocks** + - all dependencies listed under `processing` are mocked at module level (Jest module mocking) + - each mocked dependency is configured to: - return the referenced fixture JSON, or - - throw the referenced error string. -4. **Execute function** - - Call the function under test with the input fixture. -5. **Assertions** - - If success: compare actual output to the expected success fixture (deep equality). - - If failure: ensure actual error equals the exact expected error enum string. -### Keeping error handling consistent (error mapping) -When a dependency throws a failure string, the function is expected to map it to a function-level error enum. -The mapping is defined in `error-mapping.yaml`. -This produces: -- consistent results across implementations -- stable error messages for callers -### Mock maintenance rules -When spec changes add or remove a `processing` step: -- update `test-config.yaml` so the runner can mock the new step -- update `scenarios.yaml` so each scenario defines behavior for the new step -- add fixtures for the new step’s expected return payloads -### Specification team input to support maximum automation -To enable maximum automation in creating mocks and test cases, the specification team (ApplicationOwner + TestEngineer) shall provide, per function version: -1. **Scenario matrix (`scenarios.yaml`)** - - happy path scenario(s) - - negative scenarios covering: - - all function-level input validation error enums - - representative dependency failure cases - - each scenario references fixtures and expected outcomes -2. **fixtures (JSON)** - - function input fixtures (valid + invalid) - - dependency output fixtures for each processing dependency: - - success payload examples - - error payload examples (if modeled as return values) or error triggers (if modeled as thrown errors) - - expected function output fixtures for success scenarios -3. **Error mapping rules (`error-mapping.yaml`)** - - explicit mapping from dependency failure messages to function-level error enums - - removes ambiguity and prevents inconsistent behavior across implementations -4. **Dependency configuration (`test-config.yaml` )** - - maps dependency (processing step names) to module path + export name - - maps function under test to its module path + export name - - makes test generation mechanical and repository-structure independent - -![Overview](./diagrams/highLevelDesignDaigram.png) -### Test Cases -#### Design -- Test cases are coded and executed automatically. -- Language / framework: - - JavaScript (CommonJS) + Jest -- Structure: - - 1 generated Jest test file per function version - - 1 test per scenario ID -#### Automatic test case creation -Test cases are automatically generated from: -- `scenarios.yaml` ( list of test cases) -- `test-config.yaml` (module paths/exports) - -![Overview](./diagrams/highLevelDesignTestExecution.png) -#### Reproducibility -**Goal:** A Function Test run must produce the **same result** (pass/fail and outputs) whenever it is executed against the same Git commit—locally or in CI. -We achieve this by applying the following rules: -1) **All test inputs are versioned** -- The complete test definition is stored in git: - - `scenarios.yaml` (which scenarios/tests exist and which dependency behavior to use) - - JSON fixtures (function inputs, dependency outputs, expected outputs) -- Therefore, a test run always uses the exact same test data for a given commit. -2) **No live dependencies** -- Function Tests SHALL NOT call real external systems (e.g., ES/Kafka/DB/HTTP services). -- All dependencies listed in the function spec `processing` section are mocked. -3) **Control variable inputs** -- This is done by: - - using fixed values in fixtures, and/or - - injecting or faking the variable source in tests. -**Outcome:** Tests are deterministic, reviewable, and reliable for PR gating because their behavior depends only on the committed code + committed test assets. -### Test Execution, Result Documentation and Acceptance Process -**Platform** -- Local execution for developers and TestEngineers -- CI execution for commit/PR creation (Jenkins) - -**Execution** -- Command: `npm test` -- The test stage produces a binary result: - - pass: function behavior matches the specification for all scenarios - - fail: behavior deviates from specification, or test assets are incomplete - -**Result documentation** - -Artifacts produced per run: -- JUnit XML (CI-readable) -- logs including scenario ID and mock configuration (for traceability) - -**Acceptance** - -A function implementation is accepted when: -- all mandatory scenarios pass -- output fixtures match for success cases -- error enum strings match exactly for failure cases -- scenario coverage meets the agreed minimum + - throw the referenced error string + +- **Execute the Function under test** + - call the real Function implementation with the loaded input fixture + +- **Assert** + - success: deep-compare with `out_scenarioId_success.json` + - error: exact match with the expected error enum string + +### Deterministic Error Handling + +Dependencies may fail with their own failure strings. +To ensure consistent Function behavior, the Function must map dependency failure strings to Function-level error enums. + +This mapping is defined explicitly in `scenarios.yaml` so that: + +- different implementations behave the same +- error messages remain stable for callers + + +### Maintenance Rules (When the Spec Changes) + +If the spec changes (especially the `processing` section): + +- update `scenarios.yaml` to define behavior for the new/changed step +- add/update fixtures for the new/changed step +- regenerate and re-run the Jest test modules + + + diff --git a/doc/TestingApplications/FunctionTesting/scenarioExample.yml b/doc/TestingApplications/FunctionTesting/scenarioExample.yml new file mode 100644 index 00000000..0d46e322 --- /dev/null +++ b/doc/TestingApplications/FunctionTesting/scenarioExample.yml @@ -0,0 +1,72 @@ +info: + title: p1LoadRawCc test scenarios + version: 1.0.0 + description: list of possible scenarios for p1LoadRawCc function + + +######################################################################################################################## +# moduleConfig +######################################################################################################################## + +moduleConfig: + functionUnderTest: + modulePath: 'src/Functions/p1StreamPmData/p1ProcessDevice/p1LoadRawCc/1.0.0/p1LoadRawCc.js' + exportName: 'p1LoadRawCc' + dependencies: + readControlConstructFromReplica: + modulePath: 'src/Functions/p1StreamPmData/p1ProcessDevice/p1LoadRawCc/1.0.0/readControlConstructFromReplica.js' + exportName: 'readControlConstructFromReplica' + kind: 'api' + p1FieldsFilter: + modulePath: 'src/Functions/p1StreamPmData/p1ProcessDevice/p1LoadRawCc/1.0.0/p1FieldsFilter.js' + exportName: 'p1FieldsFilter' + kind: 'function' + retrieveMostRecentPeriodEndTimesFromDs: + modulePath: 'src/Functions/p1StreamPmData/p1ProcessDevice/p1LoadRawCc/1.0.0/retrieveMostRecentPeriodEndTimesFromDs.js' + exportName: 'retrieveMostRecentPeriodEndTimesFromDs' + kind: 'api' + p1DiscardIrrelevantPmRecords: + modulePath: 'src/Functions/p1StreamPmData/p1ProcessDevice/p1LoadRawCc/1.0.0/p1DiscardIrrelevantPmRecords.js' + exportName: 'p1DiscardIrrelevantPmRecords' + kind: 'function' + addBatchTimestamp: + modulePath: 'src/Functions/p1StreamPmData/p1ProcessDevice/p1LoadRawCc/1.0.0/addBatchTimestamp.js' + exportName: 'addBatchTimestamp' + kind: 'internal' + +######################################################################################################################## +# Error Mapping +######################################################################################################################## + +errorMapping: + # readControlConstructFromReplica dependency failure strings + 'Data structure is missing or invalid': 'rawCc could not be provided' + 'ElasticSearch read error': 'rawCc could not be provided' + 'General processing error during reading': 'General processing error' + + # retrieveMostRecentPeriodEndTimesFromDs dependency failure strings + 'dataStoreUrl not provided': 'dataStoreEsClient not provided' + 'dataStoreUrl invalid': 'dataStoreEsClient invalid' + 'General processing error': 'General processing error' + +######################################################################################################################## +# Scenarios +######################################################################################################################## + +scenarios: + - id: 'happy_path' + description: 'All dependencies succeed' + inputFixture: 'in_happy_path.json' + mocks: + readControlConstructFromReplica: + returnsOutputFixture: 'mock_readControlConstructFromReplica_happy_path.json' + p1FieldsFilter: + returnsOutputFixture: 'mock_p1FieldsFilter_happy_path.json' + retrieveMostRecentPeriodEndTimesFromDs: + returnsOutputFixture: 'mock_retrieveMostRecentPeriodEndTimesFromDs_happy_path.json' + p1DiscardIrrelevantPmRecords: + returnsOutputFixture: 'mock_p1DiscardIrrelevantPmRecords_happy_path.json' + addBatchTimestamp: + returnsOutputFixture: 'mock_addBatchTimestamp_happy_path.json' + expect: + successOutputFixture: 'out_happy_path_success.json' \ No newline at end of file From 2d1699469478228ba7cad41cfa66bcb2dff8865a Mon Sep 17 00:00:00 2001 From: redchy Date: Fri, 6 Mar 2026 02:45:21 +0000 Subject: [PATCH 12/19] Update highLevelDesign.md for scenario entry clarity Clarified the structure of scenario entries and removed redundant sections. --- .../FunctionTesting/highLevelDesign.md | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/doc/TestingApplications/FunctionTesting/highLevelDesign.md b/doc/TestingApplications/FunctionTesting/highLevelDesign.md index fe48145a..a4254562 100644 --- a/doc/TestingApplications/FunctionTesting/highLevelDesign.md +++ b/doc/TestingApplications/FunctionTesting/highLevelDesign.md @@ -57,26 +57,30 @@ Scenarios are described in a YAML file: - Path: `testing/FunctionName/version/scenarios.yaml` -Each scenario entry contains: +The file contains: + +- **Module configuration** + module path + export name for: + - the Function under test + - each dependency step +- **Error mapping** + dependency failure string → Function-level error enum string + +And for each scenario entry: - **Scenario ID + description** (unique, stable) - **Input fixture reference** JSON fixture located in `testing/FunctionName/version/input/` - **Expected outcome** - - expected success output fixture in `testing/FunctionName/version/output/`, **or** + - expected success output fixture in `testing/FunctionName/version/output/`, or - expected error enum string (exactly as defined in the spec) - **Mocks for dependencies** For each dependency step from the spec `processing` section: - - return payload fixture (JSON), **or** + - return payload fixture (JSON), or - error string to throw (mapped deterministically) -- **Module configuration** - module path + export name for: - - the Function under test - - each dependency step -- **Error mapping** - dependency failure string → Function-level error enum string -### Naming Rules for Fixtures + +### Naming Rules - **Scenario IDs**: stable, readable IDs (e.g. `happy_path`, `invalid_missing_mountName`) - **Input fixtures**: `in_scenarioId.json` From e0962cc86acae2be265933b2685f6bf312e9ef9a Mon Sep 17 00:00:00 2001 From: redchy Date: Fri, 6 Mar 2026 03:26:40 +0000 Subject: [PATCH 13/19] Update highLevelDesign.md with test file generator info Added details about the test file generator and its functionality. --- .../FunctionTesting/highLevelDesign.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/doc/TestingApplications/FunctionTesting/highLevelDesign.md b/doc/TestingApplications/FunctionTesting/highLevelDesign.md index a4254562..7be9bfec 100644 --- a/doc/TestingApplications/FunctionTesting/highLevelDesign.md +++ b/doc/TestingApplications/FunctionTesting/highLevelDesign.md @@ -49,7 +49,17 @@ For each Function version, the test package consists of: - one generated Jest test file per Function version - `testing/FunctionName/version/tests/FunctionName.test.js` - **Runtime environment** - - Node.js + npm dependencies + - Node.js + npm dependencies + +### Test File Generator +The test file generator creates the Jest test file for each Function version + +- Path : 'testing/tools/generateFunctionTests.js' + +The generator works as follows: + - read `testing/FunctionName/version/scenarios.yaml` + - generate: `testing/FunctionName/version/tests/FunctionName.test.js` + ### Scenario Definition (`scenarios.yaml`) From 8e9a3dd812f71b5952a9249d75df99be624a4b12 Mon Sep 17 00:00:00 2001 From: redchy Date: Fri, 6 Mar 2026 08:56:58 +0000 Subject: [PATCH 14/19] Refactor high level design documentation formatting Refactor formatting and improve clarity in high level design documentation. --- .../FunctionTesting/highLevelDesign.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/doc/TestingApplications/FunctionTesting/highLevelDesign.md b/doc/TestingApplications/FunctionTesting/highLevelDesign.md index 7be9bfec..953b79b5 100644 --- a/doc/TestingApplications/FunctionTesting/highLevelDesign.md +++ b/doc/TestingApplications/FunctionTesting/highLevelDesign.md @@ -69,16 +69,14 @@ Scenarios are described in a YAML file: The file contains: -- **Module configuration** - module path + export name for: +- **Module configuration** : module path + export name for - the Function under test - each dependency step -- **Error mapping** - dependency failure string → Function-level error enum string +- **Error mapping** : dependency failure string → Function-level error enum string And for each scenario entry: -- **Scenario ID + description** (unique, stable) +- **Scenario ID + description** : (unique, stable) - **Input fixture reference** JSON fixture located in `testing/FunctionName/version/input/` - **Expected outcome** From 388774f884741c86fb94f501815673ecd40ab23a Mon Sep 17 00:00:00 2001 From: redchy Date: Fri, 6 Mar 2026 11:45:34 +0000 Subject: [PATCH 15/19] Revise file paths for scenario inputs and outputs Updated input and output file paths for scenarios in high level design documentation. --- doc/TestingApplications/FunctionTesting/highLevelDesign.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/TestingApplications/FunctionTesting/highLevelDesign.md b/doc/TestingApplications/FunctionTesting/highLevelDesign.md index 953b79b5..5f5a3193 100644 --- a/doc/TestingApplications/FunctionTesting/highLevelDesign.md +++ b/doc/TestingApplications/FunctionTesting/highLevelDesign.md @@ -43,8 +43,9 @@ For each Function version, the test package consists of: - **Scenario definition** - `testing/FunctionName/version/scenarios.yaml` - **Fixtures** - - `testing/FunctionName/version/input/*.json` - - `testing/FunctionName/version/output/*.json` + - `testing/FunctionName/version/scenarioId/input.json` + - `testing/FunctionName/version/scenarioId/output.json` + - `testing/FunctionName/version/scenarioId/p1ConsumedFunctionName.json` - **Generated Jest test module(s)** - one generated Jest test file per Function version - `testing/FunctionName/version/tests/FunctionName.test.js` From 0dbc69cd5e046922ee0965e011984467192b74f1 Mon Sep 17 00:00:00 2001 From: redchy Date: Mon, 9 Mar 2026 16:12:21 +0000 Subject: [PATCH 16/19] Refine test file paths and naming rules in documentation Updated paths and naming conventions for test file generator and scenario execution. --- .../FunctionTesting/highLevelDesign.md | 26 +++++-------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/doc/TestingApplications/FunctionTesting/highLevelDesign.md b/doc/TestingApplications/FunctionTesting/highLevelDesign.md index 5f5a3193..2e98dc60 100644 --- a/doc/TestingApplications/FunctionTesting/highLevelDesign.md +++ b/doc/TestingApplications/FunctionTesting/highLevelDesign.md @@ -55,7 +55,7 @@ For each Function version, the test package consists of: ### Test File Generator The test file generator creates the Jest test file for each Function version -- Path : 'testing/tools/generateFunctionTests.js' +- Path : `testing/tools/generateFunctionTests.js` The generator works as follows: - read `testing/FunctionName/version/scenarios.yaml` @@ -79,28 +79,16 @@ And for each scenario entry: - **Scenario ID + description** : (unique, stable) - **Input fixture reference** - JSON fixture located in `testing/FunctionName/version/input/` + JSON fixture located in `testing/FunctionName/version/scenarioId/input.json` - **Expected outcome** - - expected success output fixture in `testing/FunctionName/version/output/`, or + - expected success output fixture in `testing/FunctionName/version/scenarioId/output.json`, or - expected error enum string (exactly as defined in the spec) - **Mocks for dependencies** For each dependency step from the spec `processing` section: - - return payload fixture (JSON), or + - return payload fixture in `testing/FunctionName/version/scenarioId/p1ConsumedFunctionName.json`, or - error string to throw (mapped deterministically) -### Naming Rules - -- **Scenario IDs**: stable, readable IDs (e.g. `happy_path`, `invalid_missing_mountName`) -- **Input fixtures**: `in_scenarioId.json` - Example: `in_happy_path.json` -- **Expected outputs**: - - success: `out_scenarioId_success.json` - Example: `out_happy_path_success.json` - - errors: use error enum strings in `scenarios.yaml` -- **Dependency mock outputs**: `mock_dependencyStepName_scenarioId.json` - Example: `mock_p1FieldsFilter_happy_path.json` - ### Scenario Execution (Runner Logic) **Requirement**: All processing steps to be mocked must be implemented as importable modules (adapters/helpers/sub-functions) so they can be mocked by Jest @@ -112,8 +100,8 @@ The scenario execution logic is implemented once and reused by all generated Je For each scenario ID, the runner performs: - **Load test data** - - load `in_scenarioId.json` - - load mock fixtures referenced by the scenario + - load `testing/FunctionName/version/scenarioId/input.json` + - load mock(s) fixtures referenced in `testing/FunctionName/version/scenarioId/p1ConsumedFunctionName.json` - **Install dependency mocks** - all dependencies listed under `processing` are mocked at module level (Jest module mocking) @@ -125,7 +113,7 @@ For each scenario ID, the runner performs: - call the real Function implementation with the loaded input fixture - **Assert** - - success: deep-compare with `out_scenarioId_success.json` + - success: deep-compare with `testing/FunctionName/version/scenarioId/output.json` - error: exact match with the expected error enum string ### Deterministic Error Handling From 13294cb891a5f7900b21cb1daead0e7e941ddf36 Mon Sep 17 00:00:00 2001 From: redchy Date: Fri, 13 Mar 2026 01:17:15 +0000 Subject: [PATCH 17/19] Enhance documentation for function testing structure Clarified the purpose of the scenarios.yaml file and provided explanations for input and output JSON files. --- .../FunctionTesting/highLevelDesign.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/TestingApplications/FunctionTesting/highLevelDesign.md b/doc/TestingApplications/FunctionTesting/highLevelDesign.md index 2e98dc60..67ce7642 100644 --- a/doc/TestingApplications/FunctionTesting/highLevelDesign.md +++ b/doc/TestingApplications/FunctionTesting/highLevelDesign.md @@ -41,11 +41,11 @@ Function Testing automatically checks that each Function implementation ... For each Function version, the test package consists of: - **Scenario definition** - - `testing/FunctionName/version/scenarios.yaml` + - `testing/FunctionName/version/scenarios.yaml`: file acts as the test configuration, allowing new scenarios to be added without modifying the test code - **Fixtures** - - `testing/FunctionName/version/scenarioId/input.json` - - `testing/FunctionName/version/scenarioId/output.json` - - `testing/FunctionName/version/scenarioId/p1ConsumedFunctionName.json` + - `testing/FunctionName/version/scenarioId/input.json` : the request payload passed to the function under test + - `testing/FunctionName/version/scenarioId/output.json` : the expected response produced by the function + - `testing/FunctionName/version/scenarioId/p1ConsumedFunctionName.json` : mocked response for a consumed sub-function, allowing tests to run without calling external services - **Generated Jest test module(s)** - one generated Jest test file per Function version - `testing/FunctionName/version/tests/FunctionName.test.js` From 249bc6adcbae8df5699457a4927edf4f6bad5105 Mon Sep 17 00:00:00 2001 From: redchy Date: Fri, 13 Mar 2026 01:19:56 +0000 Subject: [PATCH 18/19] Clarify fixture descriptions in highLevelDesign.md Revised the descriptions of fixtures in the high-level design document for clarity. --- doc/TestingApplications/FunctionTesting/highLevelDesign.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/TestingApplications/FunctionTesting/highLevelDesign.md b/doc/TestingApplications/FunctionTesting/highLevelDesign.md index 67ce7642..589aa2db 100644 --- a/doc/TestingApplications/FunctionTesting/highLevelDesign.md +++ b/doc/TestingApplications/FunctionTesting/highLevelDesign.md @@ -43,9 +43,9 @@ For each Function version, the test package consists of: - **Scenario definition** - `testing/FunctionName/version/scenarios.yaml`: file acts as the test configuration, allowing new scenarios to be added without modifying the test code - **Fixtures** - - `testing/FunctionName/version/scenarioId/input.json` : the request payload passed to the function under test - - `testing/FunctionName/version/scenarioId/output.json` : the expected response produced by the function - - `testing/FunctionName/version/scenarioId/p1ConsumedFunctionName.json` : mocked response for a consumed sub-function, allowing tests to run without calling external services + - The request payload passed to the function under test : `testing/FunctionName/version/scenarioId/input.json` + - The expected response produced by the function : `testing/FunctionName/version/scenarioId/output.json` + - The mocked response for a consumed sub-function, allowing tests to run without calling external services : `testing/FunctionName/version/scenarioId/p1ConsumedFunctionName.json` - **Generated Jest test module(s)** - one generated Jest test file per Function version - `testing/FunctionName/version/tests/FunctionName.test.js` From bbe47197f5f9e68f09ebc39565456a6ba246f5c5 Mon Sep 17 00:00:00 2001 From: redchy Date: Fri, 13 Mar 2026 09:09:42 +0000 Subject: [PATCH 19/19] Update module paths and input/output fixtures in YAML --- .../FunctionTesting/scenarioExample.yml | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/doc/TestingApplications/FunctionTesting/scenarioExample.yml b/doc/TestingApplications/FunctionTesting/scenarioExample.yml index 0d46e322..d57e2240 100644 --- a/doc/TestingApplications/FunctionTesting/scenarioExample.yml +++ b/doc/TestingApplications/FunctionTesting/scenarioExample.yml @@ -18,7 +18,7 @@ moduleConfig: exportName: 'readControlConstructFromReplica' kind: 'api' p1FieldsFilter: - modulePath: 'src/Functions/p1StreamPmData/p1ProcessDevice/p1LoadRawCc/1.0.0/p1FieldsFilter.js' + modulePath: 'src/Functions/p1FieldsFilter/1.0.0/p1FieldsFilter.js' exportName: 'p1FieldsFilter' kind: 'function' retrieveMostRecentPeriodEndTimesFromDs: @@ -26,7 +26,7 @@ moduleConfig: exportName: 'retrieveMostRecentPeriodEndTimesFromDs' kind: 'api' p1DiscardIrrelevantPmRecords: - modulePath: 'src/Functions/p1StreamPmData/p1ProcessDevice/p1LoadRawCc/1.0.0/p1DiscardIrrelevantPmRecords.js' + modulePath: 'src/Functions/p1DiscardIrrelevantPmRecords/1.0.0/p1DiscardIrrelevantPmRecords.js' exportName: 'p1DiscardIrrelevantPmRecords' kind: 'function' addBatchTimestamp: @@ -56,17 +56,15 @@ errorMapping: scenarios: - id: 'happy_path' description: 'All dependencies succeed' - inputFixture: 'in_happy_path.json' + inputFixture: 'happy_path/input.json' mocks: readControlConstructFromReplica: - returnsOutputFixture: 'mock_readControlConstructFromReplica_happy_path.json' + returnsOutputFixture: 'happy_path/consumedReadControlConstructFromReplica.json' p1FieldsFilter: - returnsOutputFixture: 'mock_p1FieldsFilter_happy_path.json' + returnsOutputFixture: 'happy_path/p1ConsumedFieldsFilter.json' retrieveMostRecentPeriodEndTimesFromDs: - returnsOutputFixture: 'mock_retrieveMostRecentPeriodEndTimesFromDs_happy_path.json' + returnsOutputFixture: 'happy_path/consumedRetrieveMostRecentPeriodEndTimesFromDs.json' p1DiscardIrrelevantPmRecords: - returnsOutputFixture: 'mock_p1DiscardIrrelevantPmRecords_happy_path.json' - addBatchTimestamp: - returnsOutputFixture: 'mock_addBatchTimestamp_happy_path.json' + returnsOutputFixture: 'happy_path/p1ConsumedDiscardIrrelevantPmRecords.json' expect: - successOutputFixture: 'out_happy_path_success.json' \ No newline at end of file + successOutputFixture: 'happy_path/output.json'