From 0b23d038dccdf61adccaf2f7b51e0817fff34060 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 10:54:08 +0100 Subject: [PATCH 01/72] test_smoke_tests.py created --- tests/e2e/tests/test_smoke_tests.py | 64 +++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 tests/e2e/tests/test_smoke_tests.py diff --git a/tests/e2e/tests/test_smoke_tests.py b/tests/e2e/tests/test_smoke_tests.py new file mode 100644 index 000000000..fc598bbaf --- /dev/null +++ b/tests/e2e/tests/test_smoke_tests.py @@ -0,0 +1,64 @@ +import json +import os + +import pytest + +from tests.e2e.tests.test_config import * +from tests.e2e.utils.data_loader import load_all_expected_responses +from tests.e2e.utils.new_functions import initialise_tests +from tests.e2e.utils.s3ConfigManager import S3ConfigManager + +# UPDATE THE BELOW WITH LOCATION OF TEST DATA, EXPECTED RESPONSES AND CONFIGS IN DEFINED IN test_config.py # +############################################################################################################# +all_data, dto = initialise_tests(SMOKE_TEST_DATA) +all_expected_responses = load_all_expected_responses(SMOKE_TEST_RESPONSES) +config_path = SMOKE_TEST_CONFIGS +############################################################################################################# +s3_manager = S3ConfigManager(S3_BUCKET, S3_PREFIX) + + +param_list = list(all_data.items()) +id_list = [ + f"{filename} - {scenario.get('scenario_name', 'No Scenario')}" + for filename, scenario in param_list +] + +@pytest.mark.smoketest +@pytest.mark.parametrize("filename, scenario", param_list, ids=id_list) +def test_run_smoke_case(filename, scenario, eligibility_client): + # get the nhs_number + nhs_number = scenario["nhs_number"] + # get the associated campaign config file + config_filename = scenario.get("config_filename", "") + # upload that config file to s3 if it + s3_manager.upload_if_missing_or_changed(os.path.abspath(os.path.join(config_path, config_filename))) + + actual_response = eligibility_client.make_request(nhs_number, strict_ssl=False) + expected_response = all_expected_responses.get(filename).get("response_items", {}) + + # Assert and show details on failure + assert actual_response == expected_response, ( + f"\nโŒ Mismatch in test: {filename}\n" + f"NHS Number: {nhs_number}\n" + f"Expected: {expected_response}\n" + f"Actual: {actual_response}\n" + ) + +@pytest.mark.smoketest +def test_check_for_missing_person(eligibility_client): + nhs_number = "1234567890" + + expected_body = { + "issue": [{ + "code": "nhs-number-not-found", + "diagnostics": f'NHS Number "{nhs_number}" not found.', + "severity": "information" + }], + "resourceType": "OperationOutcome" + } + + response = eligibility_client.make_request(nhs_number, raise_on_error=False) + + assert response["status_code"] == 404 + assert response["body"] == expected_body + From 6d1856224d8a3441caf3936dca01a831c384c8a1 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 11:04:35 +0100 Subject: [PATCH 02/72] moved more files --- tests/e2e/utils/data_loader.py | 137 +++++++++++++++++++++++++++++ tests/e2e/utils/new_functions.py | 14 +++ tests/e2e/utils/s3ConfigManager.py | 75 ++++++++++++++++ 3 files changed, 226 insertions(+) create mode 100644 tests/e2e/utils/data_loader.py create mode 100644 tests/e2e/utils/new_functions.py create mode 100644 tests/e2e/utils/s3ConfigManager.py diff --git a/tests/e2e/utils/data_loader.py b/tests/e2e/utils/data_loader.py new file mode 100644 index 000000000..1f6fff0b2 --- /dev/null +++ b/tests/e2e/utils/data_loader.py @@ -0,0 +1,137 @@ +import os +import json + +from .common_test_helpers import clean_response +from .placeholder_utils import resolve_placeholders +from .placeholder_context import ResolvedPlaceholderContext, PlaceholderDTO + + +def resolve_placeholders_in_data(data, context, file_name): + if isinstance(data, dict): + return {k: resolve_placeholders_in_data(v, context, file_name) for k, v in data.items()} + elif isinstance(data, list): + return [resolve_placeholders_in_data(item, context, file_name) for item in data] + else: + return resolve_placeholders(data, context, file_name) + + +def load_test_scenario(file_path): + with open(file_path, "r") as f: + raw_data = json.load(f) + + file_name = os.path.basename(file_path) + context = ResolvedPlaceholderContext() + resolved_data = resolve_placeholders_in_data(raw_data["data"], context, file_name) + + return { + "file": file_name, + "scenario_name": raw_data.get("scenario_name"), + "data": resolved_data, + "placeholders": context.all() # Now just placeholder โ†’ value + } + + +def extract_nhs_number_from_data(data): + def find_nhs(obj): + if isinstance(obj, dict): + for k, v in obj.items(): + if k.lower().replace("_", "") == "nhsnumber": + return v + elif isinstance(v, (dict, list)): + result = find_nhs(v) + if result: + return result + elif isinstance(obj, list): + for item in obj: + result = find_nhs(item) + if result: + return result + return None + + return find_nhs(data) or "UNKNOWN" + +def load_all_test_scenarios_old(folder_path): + all_data = {} + dto = PlaceholderDTO() # Shared across all files + + for filename in os.listdir(folder_path): + if not filename.endswith(".json"): + continue + + full_path = os.path.join(folder_path, filename) + + # Load JSON + with open(full_path, "r") as f: + raw_json = json.load(f) + + raw_data = raw_json["data"] + # Resolve placeholders into a shared DTO + resolved_data = resolve_placeholders_in_data(raw_data, dto, filename) + + # Extract NHS number from resolved data (e.g. first item or key field) + nhs_number = extract_nhs_number_from_data(resolved_data) # You must define this! + + all_data[filename] = { + "dynamo_items": resolved_data, + "nhs_number": nhs_number, + } + + return all_data, dto + +def load_all_expected_responses(folder_path): + all_data = {} + dto = PlaceholderDTO() # Shared across all files + + for filename in os.listdir(folder_path): + if not filename.endswith(".json"): + continue + + full_path = os.path.join(folder_path, filename) + + # Load JSON + with open(full_path, "r") as f: + raw_json = json.load(f) + + resolved_data = resolve_placeholders_in_data(raw_json, dto, filename) + cleaned_data = clean_response(resolved_data,) + + all_data[filename] = { + "response_items": cleaned_data + } + + return all_data + +def load_all_test_scenarios(folder_path, config_folder_path="tests/e2e/data/configs"): + all_data = {} + dto = PlaceholderDTO() # Shared across all files + + for filename in os.listdir(folder_path): + if not filename.endswith(".json"): + continue + + full_path = os.path.join(folder_path, filename) + + # Load scenario JSON + with open(full_path, "r") as f: + raw_json = json.load(f) + + raw_data = raw_json["data"] + + config_filename = raw_json.get("config_filename") # Just the name + scenario_name = raw_json.get("scenario_name") # Just the name + + # Resolve placeholders with shared DTO + resolved_data = resolve_placeholders_in_data(raw_data, dto, filename) + + # Extract NHS number + nhs_number = extract_nhs_number_from_data(resolved_data) + + # Add resolved scenario + all_data[filename] = { + "dynamo_items": resolved_data, + "nhs_number": nhs_number, + "config_filename": config_filename, + "scenario_name": scenario_name, + } + + return all_data, dto diff --git a/tests/e2e/utils/new_functions.py b/tests/e2e/utils/new_functions.py new file mode 100644 index 000000000..5dd663553 --- /dev/null +++ b/tests/e2e/utils/new_functions.py @@ -0,0 +1,14 @@ +import os +from tests.e2e.utils.dynamo_helper import insert_into_dynamo +from tests.e2e.utils.data_loader import load_all_test_scenarios + + +def initialise_tests(folder): + folder_path = os.path.abspath(folder) + all_data, dto = load_all_test_scenarios(folder_path) + + # Insert to Dynamo (placeholder) + for scenario in all_data.values(): + insert_into_dynamo(scenario["dynamo_items"]) + + return all_data, dto diff --git a/tests/e2e/utils/s3ConfigManager.py b/tests/e2e/utils/s3ConfigManager.py new file mode 100644 index 000000000..f723be788 --- /dev/null +++ b/tests/e2e/utils/s3ConfigManager.py @@ -0,0 +1,75 @@ +import boto3 +import botocore +import hashlib +import os + +from dotenv import load_dotenv +load_dotenv() + +class S3ConfigManager: + def __init__(self, bucket_name: str, s3_prefix: str = ""): + self.bucket_name = bucket_name + self.s3_prefix = s3_prefix + self.s3_client = boto3.client("s3") + + def _s3_key(self, filename: str) -> str: + return os.path.join(self.s3_prefix, filename) + + def _calculate_file_hash(self, file_path: str) -> str: + """SHA256 hash to compare local and S3 content.""" + sha256 = hashlib.sha256() + with open(file_path, "rb") as f: + for chunk in iter(lambda: f.read(4096), b""): + sha256.update(chunk) + return sha256.hexdigest() + + def upload_if_missing_or_changed(self, local_path: str): + filename = os.path.basename(local_path) + s3_key = self._s3_key(filename) + + try: + if self.config_exists_and_matches(local_path, s3_key): + print(f"โœ… Config '{filename}' already exists and matches in S3. Skipping upload.") + return + else: + print(f"๐Ÿงน Config '{filename}' exists in S3 but is different. Deleting old version...") + self.s3_client.delete_object(Bucket=self.bucket_name, Key=s3_key) + except self.s3_client.exceptions.NoSuchKey: + print(f"๐Ÿ†• Config '{filename}' does not exist in S3. Proceeding to upload.") + except botocore.exceptions.ClientError as e: + if e.response["Error"]["Code"] == "NoSuchKey": + print(f"๐Ÿ†• Config '{filename}' does not exist in S3. Proceeding to upload.") + else: + raise + + print(f"โฌ†๏ธ Uploading new config '{filename}' to S3...") + self.s3_client.upload_file(local_path, self.bucket_name, s3_key) + print(f"โœ… Uploaded to s3://{self.bucket_name}/{s3_key}") + + + + # def config_exists_and_matches(self, local_path: str, s3_key: str) -> bool: + # try: + # s3_obj = self.s3_client.get_object(Bucket=self.bucket_name, Key=s3_key) + # s3_data = s3_obj["Body"].read() + # s3_hash = hashlib.sha256(s3_data).hexdigest() + # local_hash = self._calculate_file_hash(local_path) + # return s3_hash == local_hash + # except self.s3_client.exceptions.NoSuchKey: + # return False + # except botocore.exceptions.ClientError as e: + # if e.response["Error"]["Code"] == "NoSuchKey": + # return False + # raise + # + # def upload_if_missing_or_changed(self, local_path: str): + # filename = os.path.basename(local_path) + # s3_key = self._s3_key(filename) + # + # if self.config_exists_and_matches(local_path, s3_key): + # print(f"โœ… Config '{filename}' already exists and matches in S3. Skipping upload.") + # return + + # print(f"โฌ†๏ธ Uploading new config '{filename}' to S3 as it is missing or changed...") + # self.s3_client.upload_file(local_path, self.bucket_name, s3_key) + # print(f"โœ… Uploaded to s3://{self.bucket_name}/{s3_key}") From f469772a3117bf78cdb14132c18cca9f684ac596 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 11:09:40 +0100 Subject: [PATCH 03/72] moved more files --- tests/e2e/tests/test_config.py | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tests/e2e/tests/test_config.py diff --git a/tests/e2e/tests/test_config.py b/tests/e2e/tests/test_config.py new file mode 100644 index 000000000..04bc82dd5 --- /dev/null +++ b/tests/e2e/tests/test_config.py @@ -0,0 +1,8 @@ +S3_BUCKET = "eligibility-signposting-api-test-eli-rules" +S3_PREFIX = "manual-uploads" + +############################################################################# +SMOKE_TEST_DATA = "tests/e2e/data/dynamoDB/smokeTestData/" +SMOKE_TEST_RESPONSES = "tests/e2e/data/responses/smokeTestResponses/" +SMOKE_TEST_CONFIGS = "tests/e2e/data/configs/smokeTestConfigs" +############################################################################# From c5765f7904b6220f817d29322db824a661e114d5 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 11:14:26 +0100 Subject: [PATCH 04/72] moved more files --- tests/e2e/utils/common_test_helpers.py | 15 +++++++ tests/e2e/utils/placeholder_context.py | 30 +++++++++++++ tests/e2e/utils/placeholder_utils.py | 58 ++++++++++++++++++++++++++ 3 files changed, 103 insertions(+) create mode 100644 tests/e2e/utils/common_test_helpers.py create mode 100644 tests/e2e/utils/placeholder_context.py create mode 100644 tests/e2e/utils/placeholder_utils.py diff --git a/tests/e2e/utils/common_test_helpers.py b/tests/e2e/utils/common_test_helpers.py new file mode 100644 index 000000000..09554f545 --- /dev/null +++ b/tests/e2e/utils/common_test_helpers.py @@ -0,0 +1,15 @@ +def clean_response(data: dict) -> dict: + keys_to_ignore = ["responseId", "lastUpdated"] + return remove_volatile_fields(data, keys_to_ignore) + + +def remove_volatile_fields(data, keys_to_remove): + if isinstance(data, dict): + return { + key: remove_volatile_fields(value, keys_to_remove) + for key, value in data.items() + if key not in keys_to_remove + } + elif isinstance(data, list): + return [remove_volatile_fields(item, keys_to_remove) for item in data] + return data diff --git a/tests/e2e/utils/placeholder_context.py b/tests/e2e/utils/placeholder_context.py new file mode 100644 index 000000000..0e304cff9 --- /dev/null +++ b/tests/e2e/utils/placeholder_context.py @@ -0,0 +1,30 @@ +class ResolvedPlaceholderContext: + def __init__(self): + self.values = {} + + def add(self, placeholder: str, resolved_value: str, file_name: str = None): + self.values[placeholder] = resolved_value # Drop file_name key nesting + + def get(self, placeholder: str) -> str: + return self.values.get(placeholder) + + def all(self): + return self.values + +class PlaceholderDTO: + def __init__(self): + self.placeholders = {} # key: filename, value: dict of placeholders per file + + def add(self, key: str, value: str, file_name: str): + if file_name not in self.placeholders: + self.placeholders[file_name] = {} + self.placeholders[file_name][key] = value + + def get(self, key: str, file_name: str): + return self.placeholders.get(file_name, {}).get(key) + + def get_all_for_file(self, file_name: str): + return self.placeholders.get(file_name, {}).copy() + + def all(self): + return self.placeholders.copy() diff --git a/tests/e2e/utils/placeholder_utils.py b/tests/e2e/utils/placeholder_utils.py new file mode 100644 index 000000000..437c00503 --- /dev/null +++ b/tests/e2e/utils/placeholder_utils.py @@ -0,0 +1,58 @@ +import re +from datetime import datetime, timedelta +from dateutil.relativedelta import relativedelta +from calendar import isleap + +def resolve_placeholders(value, context=None, file_name=None): + if not isinstance(value, str): + return value + + match = re.search(r"<<(.*?)>>", value) + if not match: + return value # No placeholder to resolve + + placeholder = match.group(1) + parts = placeholder.split("_") + + try: + if placeholder in ["IGNORE_RESPONSE_ID", "IGNORE_DATE"]: + return value.replace(f"<<{placeholder}>>", placeholder) + elif len(parts) != 3 or parts[0] not in ["DATE", "RDATE", "IGNORE"]: + return value # Unrecognized format + + date_type, arg = parts[1], parts[2] + today = datetime.today() + + if date_type == "AGE": + target_year = today.year - int(arg) + try: + result_date = today.replace(year=target_year) + except ValueError: + if today.month == 2 and today.day == 29 and not isleap(target_year): + result_date = datetime(target_year, 2, 28) + else: + raise + + elif date_type == "DAY": + result_date = today + timedelta(days=int(arg)) + + elif date_type == "MONTH": + result_date = today + relativedelta(months=int(arg)) + + elif date_type == "YEAR": + result_date = today + relativedelta(years=int(arg)) + + else: + return value + + resolved = result_date.strftime("%Y%m%d") if parts[0] == "DATE" else result_date.strftime("%-d %B %Y") + + if context: + context.add(placeholder, resolved, file_name) + + # Replace the single placeholder in the original string + return value.replace(f"<<{placeholder}>>", resolved) + + except Exception as e: + print(f"[ERROR] Could not resolve placeholder '{placeholder}': {e}") + return value From 6296aad0dd36a4400aba7f520069cb93cbf8373a Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 11:20:55 +0100 Subject: [PATCH 05/72] moved more files --- .gitignore | 1 + .../configs => .temp}/global_config.json | 0 .../{ => smokeTestData}/AUTO_RSV_SB_001.json | 0 .../{ => smokeTestData}/AUTO_RSV_SB_002.json | 0 .../{ => smokeTestData}/AUTO_RSV_SB_003.json | 0 .../{ => smokeTestData}/AUTO_RSV_SB_004.json | 0 .../{ => smokeTestData}/AUTO_RSV_SB_005.json | 0 .../{ => smokeTestData}/AUTO_RSV_SB_006.json | 0 .../{ => smokeTestData}/AUTO_RSV_SB_007.json | 0 .../{ => smokeTestData}/AUTO_RSV_SB_008.json | 0 .../{ => smokeTestData}/AUTO_RSV_SB_009.json | 0 .../{ => smokeTestData}/AUTO_RSV_SB_010.json | 0 .../{ => smokeTestData}/AUTO_RSV_SB_011.json | 0 .../{ => smokeTestData}/AUTO_RSV_SB_012.json | 0 .../{ => smokeTestData}/AUTO_RSV_SB_013.json | 0 .../{ => smokeTestData}/AUTO_RSV_SB_014.json | 0 .../AUTO_RSV_SB_001.json | 0 .../AUTO_RSV_SB_002.json | 0 .../AUTO_RSV_SB_003.json | 0 .../AUTO_RSV_SB_004.json | 0 .../AUTO_RSV_SB_005.json | 0 .../AUTO_RSV_SB_006.json | 0 .../AUTO_RSV_SB_007.json | 0 .../AUTO_RSV_SB_008.json | 0 .../AUTO_RSV_SB_009.json | 0 .../AUTO_RSV_SB_010.json | 0 .../AUTO_RSV_SB_011.json | 0 .../AUTO_RSV_SB_012.json | 0 .../AUTO_RSV_SB_013.json | 0 .../AUTO_RSV_SB_014.json | 0 tests/e2e/utils/dynamo_helper.py | 59 +++++++++++++++++++ 31 files changed, 60 insertions(+) rename tests/e2e/{data/configs => .temp}/global_config.json (100%) rename tests/e2e/data/dynamoDB/{ => smokeTestData}/AUTO_RSV_SB_001.json (100%) rename tests/e2e/data/dynamoDB/{ => smokeTestData}/AUTO_RSV_SB_002.json (100%) rename tests/e2e/data/dynamoDB/{ => smokeTestData}/AUTO_RSV_SB_003.json (100%) rename tests/e2e/data/dynamoDB/{ => smokeTestData}/AUTO_RSV_SB_004.json (100%) rename tests/e2e/data/dynamoDB/{ => smokeTestData}/AUTO_RSV_SB_005.json (100%) rename tests/e2e/data/dynamoDB/{ => smokeTestData}/AUTO_RSV_SB_006.json (100%) rename tests/e2e/data/dynamoDB/{ => smokeTestData}/AUTO_RSV_SB_007.json (100%) rename tests/e2e/data/dynamoDB/{ => smokeTestData}/AUTO_RSV_SB_008.json (100%) rename tests/e2e/data/dynamoDB/{ => smokeTestData}/AUTO_RSV_SB_009.json (100%) rename tests/e2e/data/dynamoDB/{ => smokeTestData}/AUTO_RSV_SB_010.json (100%) rename tests/e2e/data/dynamoDB/{ => smokeTestData}/AUTO_RSV_SB_011.json (100%) rename tests/e2e/data/dynamoDB/{ => smokeTestData}/AUTO_RSV_SB_012.json (100%) rename tests/e2e/data/dynamoDB/{ => smokeTestData}/AUTO_RSV_SB_013.json (100%) rename tests/e2e/data/dynamoDB/{ => smokeTestData}/AUTO_RSV_SB_014.json (100%) rename tests/e2e/data/responses/{ => smokeTestResponses}/AUTO_RSV_SB_001.json (100%) rename tests/e2e/data/responses/{ => smokeTestResponses}/AUTO_RSV_SB_002.json (100%) rename tests/e2e/data/responses/{ => smokeTestResponses}/AUTO_RSV_SB_003.json (100%) rename tests/e2e/data/responses/{ => smokeTestResponses}/AUTO_RSV_SB_004.json (100%) rename tests/e2e/data/responses/{ => smokeTestResponses}/AUTO_RSV_SB_005.json (100%) rename tests/e2e/data/responses/{ => smokeTestResponses}/AUTO_RSV_SB_006.json (100%) rename tests/e2e/data/responses/{ => smokeTestResponses}/AUTO_RSV_SB_007.json (100%) rename tests/e2e/data/responses/{ => smokeTestResponses}/AUTO_RSV_SB_008.json (100%) rename tests/e2e/data/responses/{ => smokeTestResponses}/AUTO_RSV_SB_009.json (100%) rename tests/e2e/data/responses/{ => smokeTestResponses}/AUTO_RSV_SB_010.json (100%) rename tests/e2e/data/responses/{ => smokeTestResponses}/AUTO_RSV_SB_011.json (100%) rename tests/e2e/data/responses/{ => smokeTestResponses}/AUTO_RSV_SB_012.json (100%) rename tests/e2e/data/responses/{ => smokeTestResponses}/AUTO_RSV_SB_013.json (100%) rename tests/e2e/data/responses/{ => smokeTestResponses}/AUTO_RSV_SB_014.json (100%) create mode 100644 tests/e2e/utils/dynamo_helper.py diff --git a/.gitignore b/.gitignore index b863bfb62..9829e47e8 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,4 @@ sandbox/specification/* /specification/tmp/* /tests/e2e/data/out* /tests/e2e/reports/* +/tests/e2e/.temp/ diff --git a/tests/e2e/data/configs/global_config.json b/tests/e2e/.temp/global_config.json similarity index 100% rename from tests/e2e/data/configs/global_config.json rename to tests/e2e/.temp/global_config.json diff --git a/tests/e2e/data/dynamoDB/AUTO_RSV_SB_001.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json similarity index 100% rename from tests/e2e/data/dynamoDB/AUTO_RSV_SB_001.json rename to tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json diff --git a/tests/e2e/data/dynamoDB/AUTO_RSV_SB_002.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json similarity index 100% rename from tests/e2e/data/dynamoDB/AUTO_RSV_SB_002.json rename to tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json diff --git a/tests/e2e/data/dynamoDB/AUTO_RSV_SB_003.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json similarity index 100% rename from tests/e2e/data/dynamoDB/AUTO_RSV_SB_003.json rename to tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json diff --git a/tests/e2e/data/dynamoDB/AUTO_RSV_SB_004.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_004.json similarity index 100% rename from tests/e2e/data/dynamoDB/AUTO_RSV_SB_004.json rename to tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_004.json diff --git a/tests/e2e/data/dynamoDB/AUTO_RSV_SB_005.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_005.json similarity index 100% rename from tests/e2e/data/dynamoDB/AUTO_RSV_SB_005.json rename to tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_005.json diff --git a/tests/e2e/data/dynamoDB/AUTO_RSV_SB_006.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_006.json similarity index 100% rename from tests/e2e/data/dynamoDB/AUTO_RSV_SB_006.json rename to tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_006.json diff --git a/tests/e2e/data/dynamoDB/AUTO_RSV_SB_007.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_007.json similarity index 100% rename from tests/e2e/data/dynamoDB/AUTO_RSV_SB_007.json rename to tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_007.json diff --git a/tests/e2e/data/dynamoDB/AUTO_RSV_SB_008.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_008.json similarity index 100% rename from tests/e2e/data/dynamoDB/AUTO_RSV_SB_008.json rename to tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_008.json diff --git a/tests/e2e/data/dynamoDB/AUTO_RSV_SB_009.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_009.json similarity index 100% rename from tests/e2e/data/dynamoDB/AUTO_RSV_SB_009.json rename to tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_009.json diff --git a/tests/e2e/data/dynamoDB/AUTO_RSV_SB_010.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json similarity index 100% rename from tests/e2e/data/dynamoDB/AUTO_RSV_SB_010.json rename to tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json diff --git a/tests/e2e/data/dynamoDB/AUTO_RSV_SB_011.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_011.json similarity index 100% rename from tests/e2e/data/dynamoDB/AUTO_RSV_SB_011.json rename to tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_011.json diff --git a/tests/e2e/data/dynamoDB/AUTO_RSV_SB_012.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_012.json similarity index 100% rename from tests/e2e/data/dynamoDB/AUTO_RSV_SB_012.json rename to tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_012.json diff --git a/tests/e2e/data/dynamoDB/AUTO_RSV_SB_013.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_013.json similarity index 100% rename from tests/e2e/data/dynamoDB/AUTO_RSV_SB_013.json rename to tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_013.json diff --git a/tests/e2e/data/dynamoDB/AUTO_RSV_SB_014.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_014.json similarity index 100% rename from tests/e2e/data/dynamoDB/AUTO_RSV_SB_014.json rename to tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_014.json diff --git a/tests/e2e/data/responses/AUTO_RSV_SB_001.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_001.json similarity index 100% rename from tests/e2e/data/responses/AUTO_RSV_SB_001.json rename to tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_001.json diff --git a/tests/e2e/data/responses/AUTO_RSV_SB_002.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_002.json similarity index 100% rename from tests/e2e/data/responses/AUTO_RSV_SB_002.json rename to tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_002.json diff --git a/tests/e2e/data/responses/AUTO_RSV_SB_003.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_003.json similarity index 100% rename from tests/e2e/data/responses/AUTO_RSV_SB_003.json rename to tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_003.json diff --git a/tests/e2e/data/responses/AUTO_RSV_SB_004.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_004.json similarity index 100% rename from tests/e2e/data/responses/AUTO_RSV_SB_004.json rename to tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_004.json diff --git a/tests/e2e/data/responses/AUTO_RSV_SB_005.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_005.json similarity index 100% rename from tests/e2e/data/responses/AUTO_RSV_SB_005.json rename to tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_005.json diff --git a/tests/e2e/data/responses/AUTO_RSV_SB_006.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_006.json similarity index 100% rename from tests/e2e/data/responses/AUTO_RSV_SB_006.json rename to tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_006.json diff --git a/tests/e2e/data/responses/AUTO_RSV_SB_007.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_007.json similarity index 100% rename from tests/e2e/data/responses/AUTO_RSV_SB_007.json rename to tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_007.json diff --git a/tests/e2e/data/responses/AUTO_RSV_SB_008.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_008.json similarity index 100% rename from tests/e2e/data/responses/AUTO_RSV_SB_008.json rename to tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_008.json diff --git a/tests/e2e/data/responses/AUTO_RSV_SB_009.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_009.json similarity index 100% rename from tests/e2e/data/responses/AUTO_RSV_SB_009.json rename to tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_009.json diff --git a/tests/e2e/data/responses/AUTO_RSV_SB_010.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_010.json similarity index 100% rename from tests/e2e/data/responses/AUTO_RSV_SB_010.json rename to tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_010.json diff --git a/tests/e2e/data/responses/AUTO_RSV_SB_011.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_011.json similarity index 100% rename from tests/e2e/data/responses/AUTO_RSV_SB_011.json rename to tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_011.json diff --git a/tests/e2e/data/responses/AUTO_RSV_SB_012.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_012.json similarity index 100% rename from tests/e2e/data/responses/AUTO_RSV_SB_012.json rename to tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_012.json diff --git a/tests/e2e/data/responses/AUTO_RSV_SB_013.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_013.json similarity index 100% rename from tests/e2e/data/responses/AUTO_RSV_SB_013.json rename to tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_013.json diff --git a/tests/e2e/data/responses/AUTO_RSV_SB_014.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_014.json similarity index 100% rename from tests/e2e/data/responses/AUTO_RSV_SB_014.json rename to tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_014.json diff --git a/tests/e2e/utils/dynamo_helper.py b/tests/e2e/utils/dynamo_helper.py new file mode 100644 index 000000000..757aba835 --- /dev/null +++ b/tests/e2e/utils/dynamo_helper.py @@ -0,0 +1,59 @@ +import os +from dotenv import load_dotenv +import boto3 +from botocore.exceptions import ClientError + +# Load credentials from .env +load_dotenv() + + +class DynamoDBHelper: + def __init__(self, table_name): + # Create DynamoDB resource using credentials from env + self.dynamodb = boto3.resource('dynamodb') + self.table = self.dynamodb.Table(table_name) + + def insert_item(self, item: dict): + """ + Insert a single item into the table. + """ + try: + response = self.table.put_item(Item=item) + return response + except ClientError as e: + print(f"Failed to insert item: {e.response['Error']['Message']}") + raise + + def insert_items(self, items: list): + """ + Insert multiple items using batch_writer. + """ + try: + with self.table.batch_writer() as batch: + for item in items: + batch.put_item(Item=item) + print("Batch insert complete.") + except ClientError as e: + print(f"Batch insert failed: {e.response['Error']['Message']}") + raise + + def get_item(self, key: dict): + """ + Retrieve a single item by primary key. + """ + try: + response = self.table.get_item(Key=key) + return response.get("Item") + except ClientError as e: + print(f"Failed to get item: {e.response['Error']['Message']}") + raise + +def insert_into_dynamo(data): + print("Inserting into Dynamo (fake):", data) + table = DynamoDBHelper("eligibility-signposting-api-test-eligibility_datastore") + for item in data: + try: + table.insert_item(item) + print(f"โœ… Inserted: {item}") + except ClientError as e: + print(f"โŒ Failed to insert {item}: {e.response['Error']['Message']}") From 020de9e880325846af29df246de67724fa893470 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 11:27:40 +0100 Subject: [PATCH 06/72] moved more files --- .../smokeTestData/AUTO_RSV_SB_001.json | 40 ---- .../smokeTestData/AUTO_RSV_SB_002.json | 40 ---- .../smokeTestData/AUTO_RSV_SB_003.json | 40 ---- .../smokeTestData/AUTO_RSV_SB_004.json | 46 ---- .../smokeTestData/AUTO_RSV_SB_005.json | 46 ---- .../smokeTestData/AUTO_RSV_SB_006.json | 45 ---- .../smokeTestData/AUTO_RSV_SB_007.json | 45 ---- .../smokeTestData/AUTO_RSV_SB_008.json | 45 ---- .../smokeTestData/AUTO_RSV_SB_009.json | 45 ---- .../smokeTestData/AUTO_RSV_SB_010.json | 45 ---- .../smokeTestData/AUTO_RSV_SB_011.json | 40 ---- .../smokeTestData/AUTO_RSV_SB_012.json | 35 --- .../smokeTestData/AUTO_RSV_SB_013.json | 45 ---- .../smokeTestData/AUTO_RSV_SB_014.json | 40 ---- tests/e2e/data/dynamoDB/test_data.json | 220 ------------------ .../smokeTestResponses/AUTO_RSV_SB_001.json | 30 --- .../smokeTestResponses/AUTO_RSV_SB_002.json | 28 --- .../smokeTestResponses/AUTO_RSV_SB_003.json | 28 --- .../smokeTestResponses/AUTO_RSV_SB_004.json | 24 -- .../smokeTestResponses/AUTO_RSV_SB_005.json | 22 -- .../smokeTestResponses/AUTO_RSV_SB_006.json | 22 -- .../smokeTestResponses/AUTO_RSV_SB_007.json | 28 --- .../smokeTestResponses/AUTO_RSV_SB_008.json | 7 - .../smokeTestResponses/AUTO_RSV_SB_009.json | 28 --- .../smokeTestResponses/AUTO_RSV_SB_010.json | 28 --- .../smokeTestResponses/AUTO_RSV_SB_011.json | 28 --- .../smokeTestResponses/AUTO_RSV_SB_012.json | 28 --- .../smokeTestResponses/AUTO_RSV_SB_013.json | 22 -- .../smokeTestResponses/AUTO_RSV_SB_014.json | 33 --- 29 files changed, 1173 deletions(-) delete mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json delete mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json delete mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json delete mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_004.json delete mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_005.json delete mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_006.json delete mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_007.json delete mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_008.json delete mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_009.json delete mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json delete mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_011.json delete mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_012.json delete mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_013.json delete mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_014.json delete mode 100644 tests/e2e/data/dynamoDB/test_data.json delete mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_001.json delete mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_002.json delete mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_003.json delete mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_004.json delete mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_005.json delete mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_006.json delete mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_007.json delete mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_008.json delete mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_009.json delete mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_010.json delete mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_011.json delete mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_012.json delete mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_013.json delete mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_014.json diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json deleted file mode 100644 index 457437429..000000000 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested actions (with booking)", - "data": [ - { - "NHS_NUMBER": "5000000001", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_75_rolling": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "5000000001", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "LS1 1AB", - "POSTCODE_SECTOR": "LS1", - "POSTCODE_OUTCODE": "1AB", - "MSOA": "E02001111", - "LSOA": "E01005348", - "GP_PRACTICE_CODE": "B87008", - "PCN": "U43084", - "ICB": "QWO", - "COMMISSIONING_REGION": "Y63", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json deleted file mode 100644 index 78026a684..000000000 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested action (not booking)", - "data": [ - { - "NHS_NUMBER": "5000000002", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_75_rolling": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "5000000002", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "LS1 1AB", - "POSTCODE_SECTOR": "LS1", - "POSTCODE_OUTCODE": "1AB", - "MSOA": "E02001111", - "LSOA": "E01005348", - "GP_PRACTICE_CODE": "B87008", - "PCN": "U43084", - "ICB": "QWO", - "COMMISSIONING_REGION": "Y63", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json deleted file mode 100644 index df7ceced6..000000000 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "scenario_name": "RSV - Actionable due to membership of an alternative Age Cohort incl. suggested action", - "data": [ - { - "NHS_NUMBER": "5000000003", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_75to79_2024": { - "M": { - "dateJoined": { - "S": "20221120" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "5000000003", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "LS1 1AB", - "POSTCODE_SECTOR": "LS1", - "POSTCODE_OUTCODE": "1AB", - "MSOA": "E02001111", - "LSOA": "E01005348", - "GP_PRACTICE_CODE": "B87008", - "PCN": "U43084", - "ICB": "QWO", - "COMMISSIONING_REGION": "Y63", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_004.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_004.json deleted file mode 100644 index ed6764ede..000000000 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_004.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested action (existing national booking)", - "data": [ - { - "NHS_NUMBER": "5000000004", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_75_rolling": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "5000000004", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "LS1 1AB", - "POSTCODE_SECTOR": "LS1", - "POSTCODE_OUTCODE": "1AB", - "MSOA": "E02001111", - "LSOA": "E01005348", - "GP_PRACTICE_CODE": "B87008", - "PCN": "U43084", - "ICB": "QWO", - "COMMISSIONING_REGION": "Y63", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - }, - { - "NHS_NUMBER": "5000000004", - "ATTRIBUTE_TYPE": "RSV", - "BOOKED_APPOINTMENT_DATE": "<>", - "BOOKED_APPOINTMENT_PROVIDER": "NBS" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_005.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_005.json deleted file mode 100644 index 1892dd217..000000000 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_005.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested actions (with local booking)", - "data": [ - { - "NHS_NUMBER": "5000000005", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_75_rolling": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "5000000005", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "LS1 1AB", - "POSTCODE_SECTOR": "LS1", - "POSTCODE_OUTCODE": "1AB", - "MSOA": "E02001111", - "LSOA": "E01005348", - "GP_PRACTICE_CODE": "B87008", - "PCN": "U43084", - "ICB": "QWO", - "COMMISSIONING_REGION": "Y63", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - }, - { - "NHS_NUMBER": "5000000005", - "ATTRIBUTE_TYPE": "RSV", - "BOOKED_APPOINTMENT_DATE": "<>", - "BOOKED_APPOINTMENT_PROVIDER": "ACC" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_006.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_006.json deleted file mode 100644 index f1101ccc4..000000000 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_006.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "scenario_name": "RSV - Not Actionable despite membership of an Age Cohort, already vaccinated", - "data": [ - { - "NHS_NUMBER": "5000000006", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_75_rolling": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "5000000006", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "LS1 1AB", - "POSTCODE_SECTOR": "LS1", - "POSTCODE_OUTCODE": "1AB", - "MSOA": "E02001111", - "LSOA": "E01005348", - "GP_PRACTICE_CODE": "B87008", - "PCN": "U43084", - "ICB": "QWO", - "COMMISSIONING_REGION": "Y63", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - }, - { - "NHS_NUMBER": "5000000006", - "ATTRIBUTE_TYPE": "RSV", - "LAST_SUCCESSFUL_DATE": "<>" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_007.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_007.json deleted file mode 100644 index 3c9c46c5e..000000000 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_007.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "scenario_name": "RSV - Not Actionable, membership of Age Cohort, no available vaccinations (not available type 1)", - "data": [ - { - "NHS_NUMBER": "5000000007", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_75_rolling": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "5000000007", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "LS1 1AB", - "POSTCODE_SECTOR": "LS1", - "POSTCODE_OUTCODE": "1AB", - "MSOA": "E02001111", - "LSOA": "E01005348", - "GP_PRACTICE_CODE": "B87008", - "PCN": "U43084", - "ICB": "<>", - "COMMISSIONING_REGION": "Y63", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - }, - { - "NHS_NUMBER": "5000000007", - "ATTRIBUTE_TYPE": "RSV", - "LAST_SUCCESSFUL_DATE": "<>" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_008.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_008.json deleted file mode 100644 index 25f7e19fa..000000000 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_008.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "scenario_name": "RSV - No RSV response as no active campaign (not available type 2)", - "data": [ - { - "NHS_NUMBER": "5000000008", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_75_rolling": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "5000000008", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "LS1 1AB", - "POSTCODE_SECTOR": "LS1", - "POSTCODE_OUTCODE": "1AB", - "MSOA": "E02001111", - "LSOA": "E01005348", - "GP_PRACTICE_CODE": "B87008", - "PCN": "U43084", - "ICB": "QWO", - "COMMISSIONING_REGION": "Y63", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - }, - { - "NHS_NUMBER": "5000000008", - "ATTRIBUTE_TYPE": "RSV", - "BOOKED_APPOINTMENT_DATE": "2024-07-01" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_009.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_009.json deleted file mode 100644 index 753398d0a..000000000 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_009.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "scenario_name": "RSV - Not Actionable, membership of Age Cohort, dose not yet due", - "data": [ - { - "NHS_NUMBER": "5000000009", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_75_rolling": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "5000000009", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "LS1 1AB", - "POSTCODE_SECTOR": "LS1", - "POSTCODE_OUTCODE": "1AB", - "MSOA": "E02001111", - "LSOA": "E01005348", - "GP_PRACTICE_CODE": "B87008", - "PCN": "U43084", - "ICB": "QWO", - "COMMISSIONING_REGION": "Y63", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - }, - { - "NHS_NUMBER": "5000000009", - "ATTRIBUTE_TYPE": "RSV", - "LAST_SUCCESSFUL_DATE": "2023-07-01" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json deleted file mode 100644 index 7eb7d0488..000000000 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "scenario_name": "RSV - Not Actionable, membership of Age Cohort, dose not far enough apart", - "data": [ - { - "NHS_NUMBER": "5000000010", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_75_rolling": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "5000000010", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "LS1 1AB", - "POSTCODE_SECTOR": "LS1", - "POSTCODE_OUTCODE": "1AB", - "MSOA": "E02001111", - "LSOA": "E01005348", - "GP_PRACTICE_CODE": "B87008", - "PCN": "U43084", - "ICB": "QWO", - "COMMISSIONING_REGION": "Y63", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - }, - { - "NHS_NUMBER": "5000000010", - "ATTRIBUTE_TYPE": "RSV", - "LAST_SUCCESSFUL_DATE": "2023-07-01" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_011.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_011.json deleted file mode 100644 index 9626840b3..000000000 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_011.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "scenario_name": "RSV - Not Actionable despite to membership of an Age Cohort with reasoning of vaccination given in other setting (e.g. care home)", - "data": [ - { - "NHS_NUMBER": "5000000011", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_75_rolling": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "5000000011", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "LS1 1AB", - "POSTCODE_SECTOR": "LS1", - "POSTCODE_OUTCODE": "1AB", - "MSOA": "E02001111", - "LSOA": "E01005348", - "GP_PRACTICE_CODE": "B87008", - "PCN": "U43084", - "ICB": "QWO", - "COMMISSIONING_REGION": "Y63", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "Y", - "DE_FLAG": "N" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_012.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_012.json deleted file mode 100644 index b7ff528f2..000000000 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_012.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "scenario_name": "RSV - Not Actionable despite no cohort membership with reasoning of already vaccinated (type 1 includes unknown cohort)", - "data": [ - { - "NHS_NUMBER": "5000000012", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": {} - } - }, - { - "NHS_NUMBER": "5000000012", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "LS1 1AB", - "POSTCODE_SECTOR": "LS1", - "POSTCODE_OUTCODE": "1AB", - "MSOA": "E02001111", - "LSOA": "E01005348", - "GP_PRACTICE_CODE": "B87008", - "PCN": "U43084", - "ICB": "QWO", - "COMMISSIONING_REGION": "Y63", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - }, - { - "NHS_NUMBER": "5000000012", - "ATTRIBUTE_TYPE": "RSV", - "LAST_SUCCESSFUL_DATE": "<>" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_013.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_013.json deleted file mode 100644 index c0f69b5dc..000000000 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_013.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "scenario_name": "RSV - Not Actionable despite no cohort membership with reasoning of already vaccinated (type 2 includes no cohorts)", - "data": [ - { - "NHS_NUMBER": "5000000013", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "covid_16+_immunosuppression": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "5000000013", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "LS1 1AB", - "POSTCODE_SECTOR": "LS1", - "POSTCODE_OUTCODE": "1AB", - "MSOA": "E02001111", - "LSOA": "E01005348", - "GP_PRACTICE_CODE": "B87008", - "PCN": "U43084", - "ICB": "QWO", - "COMMISSIONING_REGION": "Y63", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - }, - { - "NHS_NUMBER": "50000000014", - "ATTRIBUTE_TYPE": "RSV", - "LAST_SUCCESSFUL_DATE": "<>" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_014.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_014.json deleted file mode 100644 index 5cd1e21d7..000000000 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_014.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "scenario_name": "RSV - Not Eligible", - "data": [ - { - "NHS_NUMBER": "5000000014", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "covid_16+_immunosuppression": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "50000000014", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "LS1 1AB", - "POSTCODE_SECTOR": "LS1", - "POSTCODE_OUTCODE": "1AB", - "MSOA": "E02001111", - "LSOA": "E01005348", - "GP_PRACTICE_CODE": "B87008", - "PCN": "U43084", - "ICB": "QWO", - "COMMISSIONING_REGION": "Y63", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/test_data.json b/tests/e2e/data/dynamoDB/test_data.json deleted file mode 100644 index f5c80e4b5..000000000 --- a/tests/e2e/data/dynamoDB/test_data.json +++ /dev/null @@ -1,220 +0,0 @@ -[ - { - "NHS_NUMBER": "0000000001", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "19960302", - "GENDER": 1, - "POSTCODE": "S18 C1X", - "POSTCODE_SECTOR": "S181", - "POSTCODE_OUTCODE": "S18", - "GP_PRACTICE_CODE": "U69384", - "PCN": "QJ2" - }, - { - "NHS_NUMBER": "0000000001", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "Flu", - "STATUS": "Actionable" - }, - { - "NHS_NUMBER": "0000000001", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "RSV", - "STATUS": "NotEligible" - }, - { - "NHS_NUMBER": "0000000002", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "19890518", - "GENDER": 1, - "POSTCODE": "S18 B1X", - "POSTCODE_SECTOR": "S181", - "POSTCODE_OUTCODE": "S18", - "GP_PRACTICE_CODE": "U33827", - "PCN": "RJ3" - }, - { - "NHS_NUMBER": "0000000002", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "Flu", - "STATUS": "Actionable" - }, - { - "NHS_NUMBER": "0000000002", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "RSV", - "STATUS": "NotActionable" - }, - { - "NHS_NUMBER": "0000000003", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "19780323", - "GENDER": 1, - "POSTCODE": "S18 X9X", - "POSTCODE_SECTOR": "S189", - "POSTCODE_OUTCODE": "S18", - "GP_PRACTICE_CODE": "U90732", - "PCN": "QJ2" - }, - { - "NHS_NUMBER": "0000000003", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "Flu", - "STATUS": "Actionable" - }, - { - "NHS_NUMBER": "0000000004", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "19570120", - "GENDER": 0, - "POSTCODE": "S18 B6X", - "POSTCODE_SECTOR": "S186", - "POSTCODE_OUTCODE": "S18", - "GP_PRACTICE_CODE": "U52353", - "PCN": "LM8" - }, - { - "NHS_NUMBER": "0000000004", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "RSV", - "STATUS": "Actionable" - }, - { - "NHS_NUMBER": "0000000004", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "RSV", - "STATUS": "NotEligible" - }, - { - "NHS_NUMBER": "0000000005", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "19910411", - "GENDER": 1, - "POSTCODE": "S18 B5X", - "POSTCODE_SECTOR": "S185", - "POSTCODE_OUTCODE": "S18", - "GP_PRACTICE_CODE": "U54464", - "PCN": "LM8" - }, - { - "NHS_NUMBER": "0000000005", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "RSV", - "STATUS": "NotActionable" - }, - { - "NHS_NUMBER": "0000000005", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "Covid", - "STATUS": "NotEligible" - }, - { - "NHS_NUMBER": "0000000006", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "19450206", - "GENDER": 1, - "POSTCODE": "S18 B9X", - "POSTCODE_SECTOR": "S189", - "POSTCODE_OUTCODE": "S18", - "GP_PRACTICE_CODE": "U97038", - "PCN": "RJ3" - }, - { - "NHS_NUMBER": "0000000006", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "RSV", - "STATUS": "Actionable" - }, - { - "NHS_NUMBER": "0000000006", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "Flu", - "STATUS": "NotEligible" - }, - { - "NHS_NUMBER": "0000000007", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "19590705", - "GENDER": 0, - "POSTCODE": "S18 B3X", - "POSTCODE_SECTOR": "S183", - "POSTCODE_OUTCODE": "S18", - "GP_PRACTICE_CODE": "U56174", - "PCN": "TK9" - }, - { - "NHS_NUMBER": "0000000007", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "Covid", - "STATUS": "NotEligible" - }, - { - "NHS_NUMBER": "0000000007", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "Covid", - "STATUS": "Actionable" - }, - { - "NHS_NUMBER": "0000000008", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "19990716", - "GENDER": 0, - "POSTCODE": "S18 A5X", - "POSTCODE_SECTOR": "S185", - "POSTCODE_OUTCODE": "S18", - "GP_PRACTICE_CODE": "U33386", - "PCN": "QJ2" - }, - { - "NHS_NUMBER": "0000000008", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "Flu", - "STATUS": "NotActionable" - }, - { - "NHS_NUMBER": "0000000009", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "20100426", - "GENDER": 0, - "POSTCODE": "S18 X4X", - "POSTCODE_SECTOR": "S184", - "POSTCODE_OUTCODE": "S18", - "GP_PRACTICE_CODE": "U94864", - "PCN": "LM8" - }, - { - "NHS_NUMBER": "0000000009", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "Flu", - "STATUS": "Actionable" - }, - { - "NHS_NUMBER": "0000000009", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "Flu", - "STATUS": "NotActionable" - }, - { - "NHS_NUMBER": "0000000010", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "19931007", - "GENDER": 1, - "POSTCODE": "S18 B5X", - "POSTCODE_SECTOR": "S185", - "POSTCODE_OUTCODE": "S18", - "GP_PRACTICE_CODE": "U74914", - "PCN": "LM8" - }, - { - "NHS_NUMBER": "0000000010", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "RSV", - "STATUS": "NotEligible" - }, - { - "NHS_NUMBER": "0000000010", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "Flu", - "STATUS": "NotEligible" - } -] diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_001.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_001.json deleted file mode 100644 index fc1326c0a..000000000 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_001.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "responseId": "<>", - "meta": { - "lastUpdated": "<>" - }, - "processedSuggestions": [ - { - "condition": "RSV", - "status": "Actionable", - "statusText": "You should have the RSV vaccine", - "eligibilityCohorts": [ - { - "cohortCode": "rsv_age_rolling", - "cohortText": "You are aged 75 to 79 years old.", - "cohortStatus": "Actionable" - } - ], - "suitabilityRules": [], - "actions": [ - { - "actionType": "ButtonAuthLink", - "actionCode": "BookNBS", - "description": "", - "urlLink": "http://www.nhs.uk/book-rsv", - "urlLabel": "Continue to booking" - } - ] - } - ] -} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_002.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_002.json deleted file mode 100644 index e45a371df..000000000 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_002.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "responseId": "<>", - "meta": { - "lastUpdated": "<>" - }, - "processedSuggestions": [ - { - "condition": "RSV", - "status": "Actionable", - "statusText": "You should have the RSV vaccine", - "eligibilityCohorts": [ - { - "cohortCode": "rsv_age_rolling", - "cohortText": "You are aged 75 to 79 years old.", - "cohortStatus": "Actionable" - } - ], - "suitabilityRules": [], - "actions": [ - { - "actionType": "CareCardWithText", - "actionCode": "BookLocal", - "description": "##Getting the vaccine\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination." - } - ] - } - ] -} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_003.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_003.json deleted file mode 100644 index e814e90b3..000000000 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_003.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "responseId": "<>", - "meta": { - "lastUpdated": "<>" - }, - "processedSuggestions": [ - { - "condition": "RSV", - "status": "Actionable", - "statusText": "You should have the RSV vaccine", - "eligibilityCohorts": [ - { - "cohortCode": "rsv_age_catchup", - "cohortText": "You turned 80 between 2nd September 2024 and 31st August 2025", - "cohortStatus": "Actionable" - } - ], - "suitabilityRules": [], - "actions": [ - { - "actionType": "CareCardWithText", - "actionCode": "BookLocal", - "description": "##Getting the vaccine\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination." - } - ] - } - ] -} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_004.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_004.json deleted file mode 100644 index ed4663779..000000000 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_004.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "responseId": "<>", - "meta": { - "lastUpdated": "<>" - }, - "processedSuggestions": [ - { - "condition": "RSV", - "status": "Actionable", - "statusText": "You should have the RSV vaccine", - "eligibilityCohorts": [], - "suitabilityRules": [], - "actions": [ - { - "actionType": "CardWithAuthButton", - "actionCode": "AmendNBS", - "description": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", - "urlLink": "http://www.nhs.uk/book-rsv", - "urlLabel": "Manage your appointment" - } - ] - } - ] -} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_005.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_005.json deleted file mode 100644 index 687b05405..000000000 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_005.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "responseId": "<>", - "meta": { - "lastUpdated": "<>" - }, - "processedSuggestions": [ - { - "condition": "RSV", - "status": "Actionable", - "statusText": "You should have the RSV vaccine", - "eligibilityCohorts": [], - "suitabilityRules": [], - "actions": [ - { - "actionType": "CardWithText", - "actionCode": "ManageLocal", - "description": "##You have an RSV vaccination appointment\nContact your healthcare provider to change or cancel your appointment." - } - ] - } - ] -} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_006.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_006.json deleted file mode 100644 index 3413b999b..000000000 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_006.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "responseId": "<>", - "meta": { - "lastUpdated": "<>" - }, - "processedSuggestions": [ - { - "condition": "RSV", - "status": "NotActionable", - "statusText": "You should have the RSV vaccine", - "eligibilityCohorts": [], - "suitabilityRules": [ - { - "ruleType": "S", - "ruleCode": "AlreadyVaccinated", - "ruleText": "##You've had your RSV vaccination\nBased on our records, you recently had this vaccination. You do not need to do anything." - } - ], - "actions": [] - } - ] -} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_007.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_007.json deleted file mode 100644 index e292c3719..000000000 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_007.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "responseId": "<>", - "meta": { - "lastUpdated": "<>" - }, - "processedSuggestions": [ - { - "condition": "RSV", - "status": "NotActionable", - "statusText": "You should have the RSV vaccine", - "eligibilityCohorts": [ - { - "cohortCode": "rsv_age_rolling", - "cohortText": "You are aged 75 to 79 years old.", - "cohortStatus": "NotActionable" - } - ], - "suitabilityRules": [ - { - "ruleType": "S", - "ruleCode": "NotAvailable", - "ruleText": "Vaccinations are not currently available" - } - ], - "actions": [] - } - ] -} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_008.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_008.json deleted file mode 100644 index 01fef4fb6..000000000 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_008.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "responseId": "<>", - "meta": { - "lastUpdated": "<>" - }, - "processedSuggestions": [] -} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_009.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_009.json deleted file mode 100644 index 65ecaf7a5..000000000 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_009.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "responseId": "<>", - "meta": { - "lastUpdated": "<>" - }, - "processedSuggestions": [ - { - "condition": "RSV", - "status": "NotActionable", - "statusText": "You should have the RSV vaccine", - "eligibilityCohorts": [ - { - "cohortCode": "rsv_age_rolling", - "cohortText": "You are aged 75 to 79 years old.", - "cohortStatus": "NotActionable" - } - ], - "suitabilityRules": [ - { - "ruleType": "S", - "ruleCode": "NotYetDue", - "ruleText": "Your next dose is not yet due." - } - ], - "actions": [] - } - ] -} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_010.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_010.json deleted file mode 100644 index 0b9dc7258..000000000 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_010.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "responseId": "<>", - "meta": { - "lastUpdated": "<>" - }, - "processedSuggestions": [ - { - "condition": "RSV", - "status": "NotActionable", - "statusText": "You should have the RSV vaccine", - "eligibilityCohorts": [ - { - "cohortCode": "rsv_age_rolling", - "cohortText": "You are aged 75 to 79 years old.", - "cohortStatus": "NotActionable" - } - ], - "suitabilityRules": [ - { - "ruleType": "S", - "ruleCode": "TooClose", - "ruleText": "Your previous vaccination was less than 91 days ago." - } - ], - "actions": [] - } - ] -} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_011.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_011.json deleted file mode 100644 index 3070205d0..000000000 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_011.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "responseId": "<>", - "meta": { - "lastUpdated": "<>" - }, - "processedSuggestions": [ - { - "condition": "RSV", - "status": "NotActionable", - "statusText": "You should have the RSV vaccine", - "eligibilityCohorts": [ - { - "cohortCode": "rsv_age_rolling", - "cohortText": "You are aged 75 to 79 years old.", - "cohortStatus": "NotActionable" - } - ], - "suitabilityRules": [ - { - "ruleType": "S", - "ruleCode": "OtherSetting", - "ruleText": "##Getting the vaccine\nOur record show you're living in a setting where care is provided.\nIf you think you should have the RSV vaccine, speak to a member of staff where you live." - } - ], - "actions": [] - } - ] -} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_012.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_012.json deleted file mode 100644 index 4996198d2..000000000 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_012.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "responseId": "<>", - "meta": { - "lastUpdated": "<>" - }, - "processedSuggestions": [ - { - "condition": "RSV", - "status": "NotActionable", - "statusText": "You should have the RSV vaccine", - "eligibilityCohorts": [ - { - "cohortCode": "unknown_cohort_membership", - "cohortText": "Our records do not say why you are eligible", - "cohortStatus": "NotActionable" - } - ], - "suitabilityRules": [ - { - "ruleType": "S", - "ruleCode": "AlreadyVaccinated", - "ruleText": "##You've had your RSV vaccination\nBased on our records, you recently had this vaccination.You do not need to do anything." - } - ], - "actions": [] - } - ] -} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_013.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_013.json deleted file mode 100644 index 3413b999b..000000000 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_013.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "responseId": "<>", - "meta": { - "lastUpdated": "<>" - }, - "processedSuggestions": [ - { - "condition": "RSV", - "status": "NotActionable", - "statusText": "You should have the RSV vaccine", - "eligibilityCohorts": [], - "suitabilityRules": [ - { - "ruleType": "S", - "ruleCode": "AlreadyVaccinated", - "ruleText": "##You've had your RSV vaccination\nBased on our records, you recently had this vaccination. You do not need to do anything." - } - ], - "actions": [] - } - ] -} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_014.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_014.json deleted file mode 100644 index 3858968e4..000000000 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_014.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "responseId": "<>", - "meta": { - "lastUpdated": "<>" - }, - "processedSuggestions": [ - { - "condition": "RSV", - "status": "NotEligible", - "statusText": "We do not believe you should have this vaccine", - "eligibilityCohorts": [ - { - "cohortCode": "rsv_age_rolling", - "cohortText": "You are not aged 75 to 79 years old.", - "cohortStatus": "NotEligible" - }, - { - "cohortCode": "rsv_age_catchup", - "cohortText": "You did not turn 80 between 2nd September 2024 and 31st August 2025", - "cohortStatus": "NotEligible" - } - ], - "suitabilityRules": [], - "actions": [ - { - "actionType": "CardWithText", - "actionCode": "HealtchareProInfo", - "description": "##If you think you need this vaccine\nSpeak to your healthcare professional if you think you should be offered this vaccination." - } - ] - } - ] -} From 4c69e6875d5a2117aeb85d51a3cc829eb1dfb8af Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 11:29:02 +0100 Subject: [PATCH 07/72] moved more files --- .../smokeTestConfigs/AUTO_RSV_SB_001.json | 235 ++++++++++++++++++ .../smokeTestData/AUTO_RSV_SB_001.json | 41 +++ .../smokeTestData/AUTO_RSV_SB_002.json | 41 +++ .../smokeTestData/AUTO_RSV_SB_003.json | 41 +++ .../smokeTestData/AUTO_RSV_SB_004.json | 47 ++++ .../smokeTestData/AUTO_RSV_SB_005.json | 47 ++++ .../smokeTestData/AUTO_RSV_SB_006.json | 46 ++++ .../smokeTestData/AUTO_RSV_SB_007.json | 41 +++ .../smokeTestData/AUTO_RSV_SB_008.json | 46 ++++ .../smokeTestData/AUTO_RSV_SB_009.json | 46 ++++ .../smokeTestData/AUTO_RSV_SB_010.json | 46 ++++ .../smokeTestData/AUTO_RSV_SB_011.json | 41 +++ .../smokeTestData/AUTO_RSV_SB_012.json | 36 +++ .../smokeTestData/AUTO_RSV_SB_013.json | 46 ++++ .../responses/.temp/AUTO_RSV_SB_001_old.json | 30 +++ .../smokeTestResponses/AUTO_RSV_SB_001.json | 29 +++ .../smokeTestResponses/AUTO_RSV_SB_002.json | 28 +++ .../smokeTestResponses/AUTO_RSV_SB_003.json | 28 +++ .../smokeTestResponses/AUTO_RSV_SB_004.json | 24 ++ .../smokeTestResponses/AUTO_RSV_SB_005.json | 22 ++ .../smokeTestResponses/AUTO_RSV_SB_006.json | 22 ++ .../smokeTestResponses/AUTO_RSV_SB_007.json | 28 +++ .../smokeTestResponses/AUTO_RSV_SB_008.json | 7 + .../smokeTestResponses/AUTO_RSV_SB_009.json | 28 +++ .../smokeTestResponses/AUTO_RSV_SB_010.json | 28 +++ .../smokeTestResponses/AUTO_RSV_SB_011.json | 28 +++ .../smokeTestResponses/AUTO_RSV_SB_012.json | 28 +++ .../smokeTestResponses/AUTO_RSV_SB_013.json | 22 ++ .../smokeTestResponses/AUTO_RSV_SB_014.json | 33 +++ 29 files changed, 1185 insertions(+) create mode 100644 tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_001.json create mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json create mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json create mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json create mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_004.json create mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_005.json create mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_006.json create mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_007.json create mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_008.json create mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_009.json create mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json create mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_011.json create mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_012.json create mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_013.json create mode 100644 tests/e2e/data/responses/.temp/AUTO_RSV_SB_001_old.json create mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_001.json create mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_002.json create mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_003.json create mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_004.json create mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_005.json create mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_006.json create mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_007.json create mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_008.json create mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_009.json create mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_010.json create mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_011.json create mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_012.json create mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_013.json create mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_014.json diff --git a/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_001.json b/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_001.json new file mode 100644 index 000000000..bf9ed746c --- /dev/null +++ b/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_001.json @@ -0,0 +1,235 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "Automation RSV - Smoke Test Config", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "BOOK_LOCAL", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "BOOK_LOCAL", + "Version": "1", + "Name": "Automation RSV - Smoke Test Config Iteration", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "M", + "IterationCohorts": [ + { + "CohortLabel": "rsv_75_rolling", + "CohortGroup": "rsv_age_rolling", + "PositiveDescription": "You are aged 75 to 79 years old.", + "NegativeDescription": "You are not aged 75 to 79 years old.", + "Priority": 0 + }, + { + "CohortLabel": "rsv_75to79_2024", + "CohortGroup": "rsv_age_catchup", + "PositiveDescription": "You turned 80 between 2nd September 2024 and 31st August 2025", + "NegativeDescription": "You did not turn 80 after 1 September 2024 and get vaccinated by 31 August 2025", + "Priority": 10 + }, + { + "CohortLabel": "elid_all_people", + "CohortGroup": "elid_all_people", + "PositiveDescription": "", + "NegativeDescription": "", + "Priority": 20 + }, + { + "CohortLabel": "no_group_description", + "CohortGroup": "", + "PositiveDescription": "", + "NegativeDescription": "", + "Priority": 30 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "Assure only already vaccinated taken from magic cohort", + "Description": "Exclude anyone who has NOT been given a dose of RSV Vaccination from the magic cohort", + "Operator": "is_empty", + "Comparator": "", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "elid_all_people", + "Priority": 100 + }, + { + "Type": "F", + "Name": "Under Age - Under 75 Years on day of execution", + "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", + "Priority": 120, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75" + }, + { + "Type": "F", + "Name": "Exclude Too OLD", + "Description": "Exclude anyone over 80", + "Priority": 130, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "<", + "Comparator": "-80" + }, + { + "Type": "S", + "Name": "AlreadyVaccinated", + "Description": "##You've had your RSV vaccination\nWe believe you had your vaccination on <>.", + "Priority": 500, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "Y>=", + "Comparator": "-25", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "NotAvailable", + "Description": "NotAvailable|Vaccinations are not currently available.", + "Priority": 510, + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "Operator": "=", + "Comparator": "SUPPRESSED_ICB" + }, + { + "Type": "R", + "Name": "Actionable Not Vaccinated", + "Description": "Book An Appointment", + "Priority": 1010, + "Operator": "is_empty", + "Comparator": "", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CommsRouting": "BOOK_NBS" + }, + { + "Type": "R", + "Name": "Actionable Not Vaccinated", + "Description": "Book An Appointment", + "Priority": 1010, + "Operator": "=", + "Comparator": "LS2", + "AttributeLevel": "PERSON", + "AttributeName": "POSTCODE_SECTOR", + "CommsRouting": "BOOK_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1020, + "Operator": ">=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1020, + "Operator": "=", + "Comparator": "NBS", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1030, + "Operator": ">=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "MANAGE_LOCAL" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1030, + "Operator": "!=", + "Comparator": "NBS", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", + "CommsRouting": "MANAGE_LOCAL" + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + }, + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Manage your appointment" + }, + "CONTACT_GP": { + "ExternalRoutingCode": "ContactGP", + "ActionDescription": "Contact your GP", + "ActionType": "InfoText", + "UrlLink": "", + "UrlLabel": "" + }, + "BOOK_LOCAL": { + "ExternalRoutingCode": "BookLocal", + "ActionDescription": "##Getting the vaccine\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "ActionType": "InfoText", + "UrlLink": "", + "UrlLabel": "" + }, + "MANAGE_LOCAL": { + "ExternalRoutingCode": "ManageLocal", + "ActionDescription": "##You have an RSV vaccination appointment\nContact your healthcare provider to change or cancel your appointment.", + "ActionType": "CardWithText", + "UrlLink": "", + "UrlLabel": "" + }, + "CHECK_CORRECT": { + "ExternalRoutingCode": "CheckCorrect", + "ActionDescription": "##If you think this is incorrect\\nIf you have not had this vaccination and you think you should, speak to your healthcare professional.", + "ActionType": "InfoText", + "UrlLink": "", + "UrlLabel": "" + } + } + } + ] + } +} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json new file mode 100644 index 000000000..429c0c9da --- /dev/null +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json @@ -0,0 +1,41 @@ +{ + "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested actions (with booking)", + "config_filename": "AUTO_RSV_SB_001.json", + "data": [ + { + "NHS_NUMBER": "5000000001", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_75_rolling": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "5000000001", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS2", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json new file mode 100644 index 000000000..fa00e7ef8 --- /dev/null +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json @@ -0,0 +1,41 @@ +{ + "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested action (not booking)", + "config_filename": "AUTO_RSV_SB_001.json", + "data": [ + { + "NHS_NUMBER": "5000000002", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_75_rolling": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "5000000002", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json new file mode 100644 index 000000000..dbece05d1 --- /dev/null +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json @@ -0,0 +1,41 @@ +{ + "scenario_name": "RSV - Actionable due to membership of an alternative Age Cohort incl. suggested action", + "config_filename": "AUTO_RSV_SB_001.json", + "data": [ + { + "NHS_NUMBER": "5000000003", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_75to79_2024": { + "M": { + "dateJoined": { + "S": "20221120" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "5000000003", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_004.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_004.json new file mode 100644 index 000000000..ceae22035 --- /dev/null +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_004.json @@ -0,0 +1,47 @@ +{ + "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested action (existing national booking)", + "config_filename": "AUTO_RSV_SB_001.json", + "data": [ + { + "NHS_NUMBER": "5000000004", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "no_group_description": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "5000000004", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "5000000004", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_005.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_005.json new file mode 100644 index 000000000..e71b7c0c7 --- /dev/null +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_005.json @@ -0,0 +1,47 @@ +{ + "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested actions (with local booking)", + "config_filename": "AUTO_RSV_SB_001.json", + "data": [ + { + "NHS_NUMBER": "5000000005", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "no_group_description": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "5000000005", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "5000000005", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "ACC" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_006.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_006.json new file mode 100644 index 000000000..0a38a4223 --- /dev/null +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_006.json @@ -0,0 +1,46 @@ +{ + "scenario_name": "RSV - Not Actionable despite membership of an Age Cohort, already vaccinated", + "config_filename": "AUTO_RSV_SB_001.json", + "data": [ + { + "NHS_NUMBER": "5000000006", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_75_rolling": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "5000000006", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "5000000006", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_007.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_007.json new file mode 100644 index 000000000..e132f791a --- /dev/null +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_007.json @@ -0,0 +1,41 @@ +{ + "scenario_name": "RSV - Not Actionable, membership of Age Cohort, no available vaccinations (not available type 1)", + "config_filename": "AUTO_RSV_SB_001.json", + "data": [ + { + "NHS_NUMBER": "5000000007", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_75_rolling": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "5000000007", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "SUPPRESSED_ICB", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_008.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_008.json new file mode 100644 index 000000000..9b9e6187a --- /dev/null +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_008.json @@ -0,0 +1,46 @@ +{ + "scenario_name": "RSV - No RSV response as no active campaign (not available type 2)", + "config_filename": "AUTO_RSV_SB_001.json", + "data": [ + { + "NHS_NUMBER": "5000000008", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_75_rolling": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "5000000008", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "5000000008", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_009.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_009.json new file mode 100644 index 000000000..19fc51cfb --- /dev/null +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_009.json @@ -0,0 +1,46 @@ +{ + "scenario_name": "RSV - Not Actionable, membership of Age Cohort, dose not yet due", + "config_filename": "AUTO_RSV_SB_001.json", + "data": [ + { + "NHS_NUMBER": "5000000009", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_75_rolling": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "5000000009", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "5000000009", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json new file mode 100644 index 000000000..febabd9de --- /dev/null +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json @@ -0,0 +1,46 @@ +{ + "scenario_name": "RSV - Not Actionable, membership of Age Cohort, dose not far enough apart", + "config_filename": "AUTO_RSV_SB_001.json", + "data": [ + { + "NHS_NUMBER": "5000000010", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_75_rolling": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "5000000010", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "5000000010", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_011.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_011.json new file mode 100644 index 000000000..4f245bf8c --- /dev/null +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_011.json @@ -0,0 +1,41 @@ +{ + "scenario_name": "RSV - Not Actionable despite to membership of an Age Cohort with reasoning of vaccination given in other setting (e.g. care home)", + "config_filename": "AUTO_RSV_SB_001.json", + "data": [ + { + "NHS_NUMBER": "5000000011", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_75_rolling": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "5000000011", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "Y", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_012.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_012.json new file mode 100644 index 000000000..3323bd404 --- /dev/null +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_012.json @@ -0,0 +1,36 @@ +{ + "scenario_name": "RSV - Not Actionable despite no cohort membership with reasoning of already vaccinated (type 1 includes unknown cohort)", + "config_filename": "AUTO_RSV_SB_001.json", + "data": [ + { + "NHS_NUMBER": "5000000012", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": {} + } + }, + { + "NHS_NUMBER": "5000000012", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "5000000012", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_013.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_013.json new file mode 100644 index 000000000..e84b7fa90 --- /dev/null +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_013.json @@ -0,0 +1,46 @@ +{ + "scenario_name": "RSV - Not Actionable despite no cohort membership with reasoning of already vaccinated (type 2 includes no cohorts)", + "config_filename": "AUTO_RSV_SB_001.json", + "data": [ + { + "NHS_NUMBER": "5000000013", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "covid_16+_immunosuppression": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "5000000013", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "5000000013", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/responses/.temp/AUTO_RSV_SB_001_old.json b/tests/e2e/data/responses/.temp/AUTO_RSV_SB_001_old.json new file mode 100644 index 000000000..92056db6f --- /dev/null +++ b/tests/e2e/data/responses/.temp/AUTO_RSV_SB_001_old.json @@ -0,0 +1,30 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_rolling", + "cohortStatus": "Actionable", + "cohortText": "You are aged 75 to 79 years old." + } + ], + "suitabilityRules": [], + "actions": [ + { + "actionType": "ButtonWithAuthLink", + "actionCode": "BookNBS", + "description": "", + "urlLink": "http://www.nhs.uk/book-rsv", + "urlLabel": "Continue to booking" + } + ] + } + ] +} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_001.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_001.json new file mode 100644 index 000000000..0c28e2dd1 --- /dev/null +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_001.json @@ -0,0 +1,29 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "Actionable", + "statusText": "Status.actionable", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_rolling", + "cohortStatus": "Actionable", + "cohortText": "You are aged 75 to 79 years old." + } + ], + "suitabilityRules": [], + "actions": [ + { + "action_type": "ButtonWithAuthLink", + "action_code": "BookNBS", + "url_link": "http://www.nhs.uk/book-rsv", + "url_label": "Continue to booking" + } + ] + } + ] +} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_002.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_002.json new file mode 100644 index 000000000..d51710fd9 --- /dev/null +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_002.json @@ -0,0 +1,28 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "Actionable", + "statusText": "Status.actionable", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_rolling", + "cohortText": "You are aged 75 to 79 years old.", + "cohortStatus": "Actionable" + } + ], + "suitabilityRules": [], + "actions": [ + { + "action_type": "InfoText", + "action_code": "BookLocal", + "action_description": "##Getting the vaccine\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination." + } + ] + } + ] +} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_003.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_003.json new file mode 100644 index 000000000..d8388e6d6 --- /dev/null +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_003.json @@ -0,0 +1,28 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "Actionable", + "statusText": "Status.actionable", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_catchup", + "cohortText": "You turned 80 between 2nd September 2024 and 31st August 2025", + "cohortStatus": "Actionable" + } + ], + "suitabilityRules": [], + "actions": [ + { + "action_type": "InfoText", + "action_code": "BookLocal", + "action_description": "##Getting the vaccine\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination." + } + ] + } + ] +} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_004.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_004.json new file mode 100644 index 000000000..ed4663779 --- /dev/null +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_004.json @@ -0,0 +1,24 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "eligibilityCohorts": [], + "suitabilityRules": [], + "actions": [ + { + "actionType": "CardWithAuthButton", + "actionCode": "AmendNBS", + "description": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", + "urlLink": "http://www.nhs.uk/book-rsv", + "urlLabel": "Manage your appointment" + } + ] + } + ] +} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_005.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_005.json new file mode 100644 index 000000000..687b05405 --- /dev/null +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_005.json @@ -0,0 +1,22 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "eligibilityCohorts": [], + "suitabilityRules": [], + "actions": [ + { + "actionType": "CardWithText", + "actionCode": "ManageLocal", + "description": "##You have an RSV vaccination appointment\nContact your healthcare provider to change or cancel your appointment." + } + ] + } + ] +} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_006.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_006.json new file mode 100644 index 000000000..ad80f0baa --- /dev/null +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_006.json @@ -0,0 +1,22 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "eligibilityCohorts": [], + "suitabilityRules": [ + { + "ruleType": "S", + "ruleCode": "AlreadyVaccinated", + "ruleText": "##You've had your RSV vaccination\nWe believe you had your vaccination on <>." + } + ], + "actions": [] + } + ] +} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_007.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_007.json new file mode 100644 index 000000000..d555621c0 --- /dev/null +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_007.json @@ -0,0 +1,28 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_rolling", + "cohortStatus": "NotActionable", + "cohortText": "You are aged 75 to 79 years old." + } + ], + "suitabilityRules": [ + { + "ruleCode": "NotAvailable", + "ruleText": "Vaccinations are not currently available", + "ruleType": "S" + } + ], + "actions": [] + } + ] +} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_008.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_008.json new file mode 100644 index 000000000..01fef4fb6 --- /dev/null +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_008.json @@ -0,0 +1,7 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [] +} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_009.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_009.json new file mode 100644 index 000000000..65ecaf7a5 --- /dev/null +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_009.json @@ -0,0 +1,28 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_rolling", + "cohortText": "You are aged 75 to 79 years old.", + "cohortStatus": "NotActionable" + } + ], + "suitabilityRules": [ + { + "ruleType": "S", + "ruleCode": "NotYetDue", + "ruleText": "Your next dose is not yet due." + } + ], + "actions": [] + } + ] +} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_010.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_010.json new file mode 100644 index 000000000..0b9dc7258 --- /dev/null +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_010.json @@ -0,0 +1,28 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_rolling", + "cohortText": "You are aged 75 to 79 years old.", + "cohortStatus": "NotActionable" + } + ], + "suitabilityRules": [ + { + "ruleType": "S", + "ruleCode": "TooClose", + "ruleText": "Your previous vaccination was less than 91 days ago." + } + ], + "actions": [] + } + ] +} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_011.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_011.json new file mode 100644 index 000000000..3070205d0 --- /dev/null +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_011.json @@ -0,0 +1,28 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_rolling", + "cohortText": "You are aged 75 to 79 years old.", + "cohortStatus": "NotActionable" + } + ], + "suitabilityRules": [ + { + "ruleType": "S", + "ruleCode": "OtherSetting", + "ruleText": "##Getting the vaccine\nOur record show you're living in a setting where care is provided.\nIf you think you should have the RSV vaccine, speak to a member of staff where you live." + } + ], + "actions": [] + } + ] +} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_012.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_012.json new file mode 100644 index 000000000..4996198d2 --- /dev/null +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_012.json @@ -0,0 +1,28 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "eligibilityCohorts": [ + { + "cohortCode": "unknown_cohort_membership", + "cohortText": "Our records do not say why you are eligible", + "cohortStatus": "NotActionable" + } + ], + "suitabilityRules": [ + { + "ruleType": "S", + "ruleCode": "AlreadyVaccinated", + "ruleText": "##You've had your RSV vaccination\nBased on our records, you recently had this vaccination.You do not need to do anything." + } + ], + "actions": [] + } + ] +} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_013.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_013.json new file mode 100644 index 000000000..3413b999b --- /dev/null +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_013.json @@ -0,0 +1,22 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "eligibilityCohorts": [], + "suitabilityRules": [ + { + "ruleType": "S", + "ruleCode": "AlreadyVaccinated", + "ruleText": "##You've had your RSV vaccination\nBased on our records, you recently had this vaccination. You do not need to do anything." + } + ], + "actions": [] + } + ] +} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_014.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_014.json new file mode 100644 index 000000000..3858968e4 --- /dev/null +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_014.json @@ -0,0 +1,33 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "NotEligible", + "statusText": "We do not believe you should have this vaccine", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_rolling", + "cohortText": "You are not aged 75 to 79 years old.", + "cohortStatus": "NotEligible" + }, + { + "cohortCode": "rsv_age_catchup", + "cohortText": "You did not turn 80 between 2nd September 2024 and 31st August 2025", + "cohortStatus": "NotEligible" + } + ], + "suitabilityRules": [], + "actions": [ + { + "actionType": "CardWithText", + "actionCode": "HealtchareProInfo", + "description": "##If you think you need this vaccine\nSpeak to your healthcare professional if you think you should be offered this vaccination." + } + ] + } + ] +} From ec70f7b5092a266a0397336df559f3b2d1af3bc5 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 11:29:09 +0100 Subject: [PATCH 08/72] moved more files --- .../smokeTestData/AUTO_RSV_SB_014.json | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_014.json diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_014.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_014.json new file mode 100644 index 000000000..7861cab99 --- /dev/null +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_014.json @@ -0,0 +1,41 @@ +{ + "scenario_name": "RSV - Not Eligible", + "config_filename": "AUTO_RSV_SB_001.json", + "data": [ + { + "NHS_NUMBER": "5000000014", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "covid_16+_immunosuppression": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "5000000014", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} From c97feb69a69eca89b90bcf748d6f87d8ab75ba6a Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 11:43:13 +0100 Subject: [PATCH 09/72] moved more files --- tests/e2e/tests/conftest.py | 9 +- tests/e2e/utils/eligibilityApiClient.py | 122 ++++++++++++++++++++++++ tests/e2e/utils/s3ConfigManager.py | 48 +++++----- 3 files changed, 154 insertions(+), 25 deletions(-) create mode 100644 tests/e2e/utils/eligibilityApiClient.py diff --git a/tests/e2e/tests/conftest.py b/tests/e2e/tests/conftest.py index f220cb1db..c0c070914 100644 --- a/tests/e2e/tests/conftest.py +++ b/tests/e2e/tests/conftest.py @@ -7,6 +7,8 @@ import pytest from dotenv import load_dotenv +from tests.e2e.utils.eligibilityApiClient import EligibilityApiClient + # Load environment variables from .env.local load_dotenv(dotenv_path=".env.local") @@ -28,7 +30,7 @@ def pytest_addoption(parser): parser.addoption("--keep-seed", action="store_true", default=False, help="Keep DynamoDB seed data after tests") -@pytest.fixture(scope="session", autouse=True) +@pytest.fixture(scope="session", autouse=False) def setup_dynamodb_data(request): """Insert test data into DynamoDB before tests and optionally clean up after.""" logger.info("[โš™] Connecting to DynamoDB table: %s in region %s", DYNAMODB_TABLE_NAME, AWS_REGION) @@ -78,3 +80,8 @@ def setup_dynamodb_data(request): except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError): logger.exception("[x] Failed to delete item %s", item.get("PK", "")) logger.info("[โœ“] Deleted %d/%d items", delete_count, len(items)) + +@pytest.fixture(scope="session") +def eligibility_client(): + base_url = "https://test.eligibility-signposting-api.nhs.uk/patient-check" + return EligibilityApiClient(base_url) diff --git a/tests/e2e/utils/eligibilityApiClient.py b/tests/e2e/utils/eligibilityApiClient.py new file mode 100644 index 000000000..f1a39e685 --- /dev/null +++ b/tests/e2e/utils/eligibilityApiClient.py @@ -0,0 +1,122 @@ +import os +import json +import boto3 +import requests +from pathlib import Path +from dotenv import load_dotenv +from botocore.exceptions import ClientError + +class EligibilityApiClient: + def __init__(self, api_url: str, cert_dir="tests/e2e/certs"): + load_dotenv(dotenv_path=os.path.join(os.path.dirname(__file__), "../.env")) + + self.api_url = api_url + self.region = os.getenv("AWS_REGION") + self.aws_access_key_id = os.getenv("AWS_ACCESS_KEY_ID") + self.aws_secret_access_key = os.getenv("AWS_SECRET_ACCESS_KEY") + self.aws_session_token = os.getenv("AWS_SESSION_TOKEN") + + self.cert_dir = Path(cert_dir) + self.cert_dir.mkdir(parents=True, exist_ok=True) + + self.cert_paths = { + "private_key": self.cert_dir / "api_private_key_cert.pem", + "client_cert": self.cert_dir / "api_client_cert.pem", + "ca_cert": self.cert_dir / "api_ca_cert.pem" + } + + self.ssm_params = { + "private_key": "/test/mtls/api_private_key_cert", + "client_cert": "/test/mtls/api_client_cert", + "ca_cert": "/test/mtls/api_ca_cert" + } + + self._ensure_certs_present() + + def _get_ssm_parameter(self, param_name: str, decrypt=True) -> str: + try: + client = boto3.client( + "ssm", + region_name=self.region, + aws_access_key_id=self.aws_access_key_id, + aws_secret_access_key=self.aws_secret_access_key, + aws_session_token=self.aws_session_token, + ) + response = client.get_parameter(Name=param_name, WithDecryption=decrypt) + return response["Parameter"]["Value"] + except ClientError as e: + raise RuntimeError(f"Error retrieving {param_name} from SSM: {e}") + + def _ensure_certs_present(self): + missing = [k for k, path in self.cert_paths.items() if not path.exists()] + if not missing: + return + + for cert_type in missing: + param_name = self.ssm_params[cert_type] + cert_value = self._get_ssm_parameter(param_name) + with open(self.cert_paths[cert_type], "w") as f: + f.write(cert_value) + + def make_request( + self, + nhs_number: str, + method="GET", + payload=None, + strict_ssl=False, + raise_on_error=True, + ): + url = f"{self.api_url.rstrip('/')}/{nhs_number}" + cert = (str(self.cert_paths["client_cert"]), str(self.cert_paths["private_key"])) + verify = str(self.cert_paths["ca_cert"]) if strict_ssl else False + + try: + response = requests.request( + method=method.upper(), + url=url, + cert=cert, + verify=verify, + json=payload, + timeout=10, + ) + + if raise_on_error: + response.raise_for_status() + + return self._parse_response(response) + + except requests.exceptions.SSLError as ssl_err: + raise RuntimeError(f"SSL error during request: {ssl_err}") + except requests.exceptions.RequestException as req_err: + if response := getattr(req_err, 'response', None): + return self._parse_response(response) + raise RuntimeError(f"Request error: {req_err}") + + def _parse_response(self, response: requests.Response): + try: + data = response.json() + cleaned = self._clean_response(data) + except json.JSONDecodeError: + cleaned = response.text + + return { + "status_code": response.status_code, + "headers": dict(response.headers), + "body": cleaned, + "ok": response.ok, + } + + def _clean_response(self, data: dict) -> dict: + keys_to_ignore = ["responseId", "lastUpdated"] + return self._remove_volatile_fields(data, keys_to_ignore) + + def _remove_volatile_fields(self, data, keys_to_remove): + if isinstance(data, dict): + return { + key: self._remove_volatile_fields(value, keys_to_remove) + for key, value in data.items() + if key not in keys_to_remove + } + elif isinstance(data, list): + return [self._remove_volatile_fields(item, keys_to_remove) for item in data] + return data diff --git a/tests/e2e/utils/s3ConfigManager.py b/tests/e2e/utils/s3ConfigManager.py index f723be788..77d1e1ce7 100644 --- a/tests/e2e/utils/s3ConfigManager.py +++ b/tests/e2e/utils/s3ConfigManager.py @@ -48,28 +48,28 @@ def upload_if_missing_or_changed(self, local_path: str): - # def config_exists_and_matches(self, local_path: str, s3_key: str) -> bool: - # try: - # s3_obj = self.s3_client.get_object(Bucket=self.bucket_name, Key=s3_key) - # s3_data = s3_obj["Body"].read() - # s3_hash = hashlib.sha256(s3_data).hexdigest() - # local_hash = self._calculate_file_hash(local_path) - # return s3_hash == local_hash - # except self.s3_client.exceptions.NoSuchKey: - # return False - # except botocore.exceptions.ClientError as e: - # if e.response["Error"]["Code"] == "NoSuchKey": - # return False - # raise - # - # def upload_if_missing_or_changed(self, local_path: str): - # filename = os.path.basename(local_path) - # s3_key = self._s3_key(filename) - # - # if self.config_exists_and_matches(local_path, s3_key): - # print(f"โœ… Config '{filename}' already exists and matches in S3. Skipping upload.") - # return + def config_exists_and_matches(self, local_path: str, s3_key: str) -> bool: + try: + s3_obj = self.s3_client.get_object(Bucket=self.bucket_name, Key=s3_key) + s3_data = s3_obj["Body"].read() + s3_hash = hashlib.sha256(s3_data).hexdigest() + local_hash = self._calculate_file_hash(local_path) + return s3_hash == local_hash + except self.s3_client.exceptions.NoSuchKey: + return False + except botocore.exceptions.ClientError as e: + if e.response["Error"]["Code"] == "NoSuchKey": + return False + raise + + def upload_if_missing_or_changed(self, local_path: str): + filename = os.path.basename(local_path) + s3_key = self._s3_key(filename) + + if self.config_exists_and_matches(local_path, s3_key): + print(f"โœ… Config '{filename}' already exists and matches in S3. Skipping upload.") + return - # print(f"โฌ†๏ธ Uploading new config '{filename}' to S3 as it is missing or changed...") - # self.s3_client.upload_file(local_path, self.bucket_name, s3_key) - # print(f"โœ… Uploaded to s3://{self.bucket_name}/{s3_key}") + print(f"โฌ†๏ธ Uploading new config '{filename}' to S3 as it is missing or changed...") + self.s3_client.upload_file(local_path, self.bucket_name, s3_key) + print(f"โœ… Uploaded to s3://{self.bucket_name}/{s3_key}") From 50763dd8e091486d70b2087e126ab5bacbcef0ae Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 11:46:57 +0100 Subject: [PATCH 10/72] moved more files --- tests/e2e/utils/s3ConfigManager.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/e2e/utils/s3ConfigManager.py b/tests/e2e/utils/s3ConfigManager.py index 77d1e1ce7..f22800873 100644 --- a/tests/e2e/utils/s3ConfigManager.py +++ b/tests/e2e/utils/s3ConfigManager.py @@ -62,14 +62,14 @@ def config_exists_and_matches(self, local_path: str, s3_key: str) -> bool: return False raise - def upload_if_missing_or_changed(self, local_path: str): - filename = os.path.basename(local_path) - s3_key = self._s3_key(filename) - - if self.config_exists_and_matches(local_path, s3_key): - print(f"โœ… Config '{filename}' already exists and matches in S3. Skipping upload.") - return - - print(f"โฌ†๏ธ Uploading new config '{filename}' to S3 as it is missing or changed...") - self.s3_client.upload_file(local_path, self.bucket_name, s3_key) - print(f"โœ… Uploaded to s3://{self.bucket_name}/{s3_key}") + # def upload_if_missing_or_changed(self, local_path: str): + # filename = os.path.basename(local_path) + # s3_key = self._s3_key(filename) + # + # if self.config_exists_and_matches(local_path, s3_key): + # print(f"โœ… Config '{filename}' already exists and matches in S3. Skipping upload.") + # return + # + # print(f"โฌ†๏ธ Uploading new config '{filename}' to S3 as it is missing or changed...") + # self.s3_client.upload_file(local_path, self.bucket_name, s3_key) + # print(f"โœ… Uploaded to s3://{self.bucket_name}/{s3_key}") From 9e57e636248878277843c5dd6ae3db3e965e0759 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 11:56:08 +0100 Subject: [PATCH 11/72] .gitignore and small change to test --- .gitignore | 1 + tests/e2e/tests/test_smoke_tests.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 9829e47e8..2f0da506e 100644 --- a/.gitignore +++ b/.gitignore @@ -49,3 +49,4 @@ sandbox/specification/* /tests/e2e/data/out* /tests/e2e/reports/* /tests/e2e/.temp/ +/tests/e2e/certs/ diff --git a/tests/e2e/tests/test_smoke_tests.py b/tests/e2e/tests/test_smoke_tests.py index fc598bbaf..615c6ca40 100644 --- a/tests/e2e/tests/test_smoke_tests.py +++ b/tests/e2e/tests/test_smoke_tests.py @@ -37,7 +37,8 @@ def test_run_smoke_case(filename, scenario, eligibility_client): expected_response = all_expected_responses.get(filename).get("response_items", {}) # Assert and show details on failure - assert actual_response == expected_response, ( + assert actual_response["status_code"] == 200 + assert actual_response["body"] == expected_response, ( f"\nโŒ Mismatch in test: {filename}\n" f"NHS Number: {nhs_number}\n" f"Expected: {expected_response}\n" From 3fc15c03700f5fec4558be873e7a862e9927d1ee Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 12:04:51 +0100 Subject: [PATCH 12/72] tidy before push --- tests/e2e/tests/test_smoke_tests.py | 3 +-- tests/e2e/utils/data_loader.py | 10 ++++++++++ tests/e2e/utils/new_functions.py | 14 -------------- tests/e2e/utils/s3ConfigManager.py | 12 ------------ 4 files changed, 11 insertions(+), 28 deletions(-) delete mode 100644 tests/e2e/utils/new_functions.py diff --git a/tests/e2e/tests/test_smoke_tests.py b/tests/e2e/tests/test_smoke_tests.py index 615c6ca40..a2d0cd541 100644 --- a/tests/e2e/tests/test_smoke_tests.py +++ b/tests/e2e/tests/test_smoke_tests.py @@ -4,8 +4,7 @@ import pytest from tests.e2e.tests.test_config import * -from tests.e2e.utils.data_loader import load_all_expected_responses -from tests.e2e.utils.new_functions import initialise_tests +from tests.e2e.utils.data_loader import load_all_expected_responses, initialise_tests from tests.e2e.utils.s3ConfigManager import S3ConfigManager # UPDATE THE BELOW WITH LOCATION OF TEST DATA, EXPECTED RESPONSES AND CONFIGS IN DEFINED IN test_config.py # diff --git a/tests/e2e/utils/data_loader.py b/tests/e2e/utils/data_loader.py index 1f6fff0b2..2460fa772 100644 --- a/tests/e2e/utils/data_loader.py +++ b/tests/e2e/utils/data_loader.py @@ -2,9 +2,19 @@ import json from .common_test_helpers import clean_response +from .dynamo_helper import insert_into_dynamo from .placeholder_utils import resolve_placeholders from .placeholder_context import ResolvedPlaceholderContext, PlaceholderDTO +def initialise_tests(folder): + folder_path = os.path.abspath(folder) + all_data, dto = load_all_test_scenarios(folder_path) + + # Insert to Dynamo (placeholder) + for scenario in all_data.values(): + insert_into_dynamo(scenario["dynamo_items"]) + + return all_data, dto def resolve_placeholders_in_data(data, context, file_name): if isinstance(data, dict): diff --git a/tests/e2e/utils/new_functions.py b/tests/e2e/utils/new_functions.py deleted file mode 100644 index 5dd663553..000000000 --- a/tests/e2e/utils/new_functions.py +++ /dev/null @@ -1,14 +0,0 @@ -import os -from tests.e2e.utils.dynamo_helper import insert_into_dynamo -from tests.e2e.utils.data_loader import load_all_test_scenarios - - -def initialise_tests(folder): - folder_path = os.path.abspath(folder) - all_data, dto = load_all_test_scenarios(folder_path) - - # Insert to Dynamo (placeholder) - for scenario in all_data.values(): - insert_into_dynamo(scenario["dynamo_items"]) - - return all_data, dto diff --git a/tests/e2e/utils/s3ConfigManager.py b/tests/e2e/utils/s3ConfigManager.py index f22800873..2491cd3d7 100644 --- a/tests/e2e/utils/s3ConfigManager.py +++ b/tests/e2e/utils/s3ConfigManager.py @@ -61,15 +61,3 @@ def config_exists_and_matches(self, local_path: str, s3_key: str) -> bool: if e.response["Error"]["Code"] == "NoSuchKey": return False raise - - # def upload_if_missing_or_changed(self, local_path: str): - # filename = os.path.basename(local_path) - # s3_key = self._s3_key(filename) - # - # if self.config_exists_and_matches(local_path, s3_key): - # print(f"โœ… Config '{filename}' already exists and matches in S3. Skipping upload.") - # return - # - # print(f"โฌ†๏ธ Uploading new config '{filename}' to S3 as it is missing or changed...") - # self.s3_client.upload_file(local_path, self.bucket_name, s3_key) - # print(f"โœ… Uploaded to s3://{self.bucket_name}/{s3_key}") From f79240bbf4a547fc6a5bb10b24ab1485c438f18c Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 13:11:35 +0100 Subject: [PATCH 13/72] linting changes and s3configmanager update --- tests/e2e/tests/test_config.py | 14 ++--- tests/e2e/tests/test_smoke_tests.py | 1 - tests/e2e/utils/data_loader.py | 47 +++++++---------- tests/e2e/utils/eligibilityApiClient.py | 68 ++++++++++++++----------- tests/e2e/utils/s3ConfigManager.py | 64 +++++++++++++++-------- 5 files changed, 107 insertions(+), 87 deletions(-) diff --git a/tests/e2e/tests/test_config.py b/tests/e2e/tests/test_config.py index 04bc82dd5..16b77822a 100644 --- a/tests/e2e/tests/test_config.py +++ b/tests/e2e/tests/test_config.py @@ -1,8 +1,8 @@ -S3_BUCKET = "eligibility-signposting-api-test-eli-rules" -S3_PREFIX = "manual-uploads" +# S3 Configuration +S3_BUCKET: str = "eligibility-signposting-api-test-eli-rules" +S3_PREFIX: str = "manual-uploads" -############################################################################# -SMOKE_TEST_DATA = "tests/e2e/data/dynamoDB/smokeTestData/" -SMOKE_TEST_RESPONSES = "tests/e2e/data/responses/smokeTestResponses/" -SMOKE_TEST_CONFIGS = "tests/e2e/data/configs/smokeTestConfigs" -############################################################################# +# Smoke Test Data Paths +SMOKE_TEST_DATA: str = "tests/e2e/data/dynamoDB/smokeTestData/" +SMOKE_TEST_RESPONSES: str = "tests/e2e/data/responses/smokeTestResponses/" +SMOKE_TEST_CONFIGS: str = "tests/e2e/data/configs/smokeTestConfigs" diff --git a/tests/e2e/tests/test_smoke_tests.py b/tests/e2e/tests/test_smoke_tests.py index a2d0cd541..9efadb2f8 100644 --- a/tests/e2e/tests/test_smoke_tests.py +++ b/tests/e2e/tests/test_smoke_tests.py @@ -1,4 +1,3 @@ -import json import os import pytest diff --git a/tests/e2e/utils/data_loader.py b/tests/e2e/utils/data_loader.py index 2460fa772..b347ca46b 100644 --- a/tests/e2e/utils/data_loader.py +++ b/tests/e2e/utils/data_loader.py @@ -1,7 +1,6 @@ import os import json -from .common_test_helpers import clean_response from .dynamo_helper import insert_into_dynamo from .placeholder_utils import resolve_placeholders from .placeholder_context import ResolvedPlaceholderContext, PlaceholderDTO @@ -60,33 +59,6 @@ def find_nhs(obj): return find_nhs(data) or "UNKNOWN" -def load_all_test_scenarios_old(folder_path): - all_data = {} - dto = PlaceholderDTO() # Shared across all files - - for filename in os.listdir(folder_path): - if not filename.endswith(".json"): - continue - - full_path = os.path.join(folder_path, filename) - - # Load JSON - with open(full_path, "r") as f: - raw_json = json.load(f) - - raw_data = raw_json["data"] - # Resolve placeholders into a shared DTO - resolved_data = resolve_placeholders_in_data(raw_data, dto, filename) - - # Extract NHS number from resolved data (e.g. first item or key field) - nhs_number = extract_nhs_number_from_data(resolved_data) # You must define this! - - all_data[filename] = { - "dynamo_items": resolved_data, - "nhs_number": nhs_number, - } - - return all_data, dto def load_all_expected_responses(folder_path): all_data = {} @@ -103,7 +75,7 @@ def load_all_expected_responses(folder_path): raw_json = json.load(f) resolved_data = resolve_placeholders_in_data(raw_json, dto, filename) - cleaned_data = clean_response(resolved_data,) + cleaned_data = clean_expected_response(resolved_data,) all_data[filename] = { "response_items": cleaned_data @@ -145,3 +117,20 @@ def load_all_test_scenarios(folder_path, config_folder_path="tests/e2e/data/conf } return all_data, dto + +def clean_expected_response(data: dict) -> dict: + keys_to_ignore = ["responseId", "lastUpdated"] + return _remove_volatile_fields(data, keys_to_ignore) + + +def _remove_volatile_fields(data, keys_to_remove): + if isinstance(data, dict): + return { + key: _remove_volatile_fields(value, keys_to_remove) + for key, value in data.items() + if key not in keys_to_remove + } + elif isinstance(data, list): + return [_remove_volatile_fields(item, keys_to_remove) for item in data] + return data + diff --git a/tests/e2e/utils/eligibilityApiClient.py b/tests/e2e/utils/eligibilityApiClient.py index f1a39e685..0f8261e7e 100644 --- a/tests/e2e/utils/eligibilityApiClient.py +++ b/tests/e2e/utils/eligibilityApiClient.py @@ -1,39 +1,43 @@ import os import json +from pathlib import Path +from typing import Any, Dict, Optional, Union + import boto3 import requests -from pathlib import Path from dotenv import load_dotenv from botocore.exceptions import ClientError +from requests import Response + class EligibilityApiClient: - def __init__(self, api_url: str, cert_dir="tests/e2e/certs"): + def __init__(self, api_url: str, cert_dir: str = "tests/e2e/certs") -> None: load_dotenv(dotenv_path=os.path.join(os.path.dirname(__file__), "../.env")) - self.api_url = api_url - self.region = os.getenv("AWS_REGION") - self.aws_access_key_id = os.getenv("AWS_ACCESS_KEY_ID") - self.aws_secret_access_key = os.getenv("AWS_SECRET_ACCESS_KEY") - self.aws_session_token = os.getenv("AWS_SESSION_TOKEN") + self.api_url: str = api_url + self.region: Optional[str] = os.getenv("AWS_REGION") + self.aws_access_key_id: Optional[str] = os.getenv("AWS_ACCESS_KEY_ID") + self.aws_secret_access_key: Optional[str] = os.getenv("AWS_SECRET_ACCESS_KEY") + self.aws_session_token: Optional[str] = os.getenv("AWS_SESSION_TOKEN") - self.cert_dir = Path(cert_dir) + self.cert_dir: Path = Path(cert_dir) self.cert_dir.mkdir(parents=True, exist_ok=True) - self.cert_paths = { + self.cert_paths: Dict[str, Path] = { "private_key": self.cert_dir / "api_private_key_cert.pem", "client_cert": self.cert_dir / "api_client_cert.pem", - "ca_cert": self.cert_dir / "api_ca_cert.pem" + "ca_cert": self.cert_dir / "api_ca_cert.pem", } - self.ssm_params = { + self.ssm_params: Dict[str, str] = { "private_key": "/test/mtls/api_private_key_cert", "client_cert": "/test/mtls/api_client_cert", - "ca_cert": "/test/mtls/api_ca_cert" + "ca_cert": "/test/mtls/api_ca_cert", } self._ensure_certs_present() - def _get_ssm_parameter(self, param_name: str, decrypt=True) -> str: + def _get_ssm_parameter(self, param_name: str, decrypt: bool = True) -> str: try: client = boto3.client( "ssm", @@ -45,9 +49,9 @@ def _get_ssm_parameter(self, param_name: str, decrypt=True) -> str: response = client.get_parameter(Name=param_name, WithDecryption=decrypt) return response["Parameter"]["Value"] except ClientError as e: - raise RuntimeError(f"Error retrieving {param_name} from SSM: {e}") + raise RuntimeError(f"Error retrieving {param_name} from SSM: {e}") from e - def _ensure_certs_present(self): + def _ensure_certs_present(self) -> None: missing = [k for k, path in self.cert_paths.items() if not path.exists()] if not missing: return @@ -55,20 +59,23 @@ def _ensure_certs_present(self): for cert_type in missing: param_name = self.ssm_params[cert_type] cert_value = self._get_ssm_parameter(param_name) - with open(self.cert_paths[cert_type], "w") as f: + with open(self.cert_paths[cert_type], "w", encoding="utf-8") as f: f.write(cert_value) def make_request( self, nhs_number: str, - method="GET", - payload=None, - strict_ssl=False, - raise_on_error=True, - ): + method: str = "GET", + payload: Optional[Union[Dict[str, Any], list]] = None, + strict_ssl: bool = False, + raise_on_error: bool = True, + ) -> Dict[str, Any]: url = f"{self.api_url.rstrip('/')}/{nhs_number}" - cert = (str(self.cert_paths["client_cert"]), str(self.cert_paths["private_key"])) - verify = str(self.cert_paths["ca_cert"]) if strict_ssl else False + cert = ( + str(self.cert_paths["client_cert"]), + str(self.cert_paths["private_key"]), + ) + verify: Union[bool, str] = str(self.cert_paths["ca_cert"]) if strict_ssl else False try: response = requests.request( @@ -86,13 +93,14 @@ def make_request( return self._parse_response(response) except requests.exceptions.SSLError as ssl_err: - raise RuntimeError(f"SSL error during request: {ssl_err}") + raise RuntimeError(f"SSL error during request: {ssl_err}") from ssl_err except requests.exceptions.RequestException as req_err: - if response := getattr(req_err, 'response', None): + response = getattr(req_err, "response", None) + if isinstance(response, Response): return self._parse_response(response) - raise RuntimeError(f"Request error: {req_err}") + raise RuntimeError(f"Request error: {req_err}") from req_err - def _parse_response(self, response: requests.Response): + def _parse_response(self, response: Response) -> Dict[str, Any]: try: data = response.json() cleaned = self._clean_response(data) @@ -106,11 +114,13 @@ def _parse_response(self, response: requests.Response): "ok": response.ok, } - def _clean_response(self, data: dict) -> dict: + def _clean_response(self, data: Any) -> Any: keys_to_ignore = ["responseId", "lastUpdated"] return self._remove_volatile_fields(data, keys_to_ignore) - def _remove_volatile_fields(self, data, keys_to_remove): + def _remove_volatile_fields( + self, data: Union[Dict[str, Any], list, Any], keys_to_remove: list + ) -> Any: if isinstance(data, dict): return { key: self._remove_volatile_fields(value, keys_to_remove) diff --git a/tests/e2e/utils/s3ConfigManager.py b/tests/e2e/utils/s3ConfigManager.py index 2491cd3d7..22d2df7d5 100644 --- a/tests/e2e/utils/s3ConfigManager.py +++ b/tests/e2e/utils/s3ConfigManager.py @@ -1,44 +1,52 @@ +import os +import hashlib import boto3 import botocore -import hashlib -import os +from typing import Optional from dotenv import load_dotenv + load_dotenv() + class S3ConfigManager: - def __init__(self, bucket_name: str, s3_prefix: str = ""): - self.bucket_name = bucket_name - self.s3_prefix = s3_prefix + def __init__(self, bucket_name: str, s3_prefix: str = "") -> None: + self.bucket_name: str = bucket_name + self.s3_prefix: str = s3_prefix self.s3_client = boto3.client("s3") def _s3_key(self, filename: str) -> str: return os.path.join(self.s3_prefix, filename) def _calculate_file_hash(self, file_path: str) -> str: - """SHA256 hash to compare local and S3 content.""" + """Return SHA256 hash of the given file.""" sha256 = hashlib.sha256() - with open(file_path, "rb") as f: - for chunk in iter(lambda: f.read(4096), b""): + with open(file_path, "rb") as file: + for chunk in iter(lambda: file.read(4096), b""): sha256.update(chunk) return sha256.hexdigest() - def upload_if_missing_or_changed(self, local_path: str): + def upload_if_missing_or_changed(self, local_path: str) -> None: filename = os.path.basename(local_path) s3_key = self._s3_key(filename) try: if self.config_exists_and_matches(local_path, s3_key): - print(f"โœ… Config '{filename}' already exists and matches in S3. Skipping upload.") + print( + f"โœ… Config '{filename}' already exists and matches in S3. " + "Skipping upload." + ) return - else: - print(f"๐Ÿงน Config '{filename}' exists in S3 but is different. Deleting old version...") - self.s3_client.delete_object(Bucket=self.bucket_name, Key=s3_key) + print( + f"๐Ÿงน A different config exists under '{self.s3_prefix}/'. " + "Deleting all existing files..." + ) + self._delete_all_in_prefix() except self.s3_client.exceptions.NoSuchKey: - print(f"๐Ÿ†• Config '{filename}' does not exist in S3. Proceeding to upload.") - except botocore.exceptions.ClientError as e: - if e.response["Error"]["Code"] == "NoSuchKey": - print(f"๐Ÿ†• Config '{filename}' does not exist in S3. Proceeding to upload.") + print(f"๐Ÿ†• No config found under '{self.s3_prefix}/'. Proceeding to upload.") + except botocore.exceptions.ClientError as error: + if error.response.get("Error", {}).get("Code") == "NoSuchKey": + print(f"๐Ÿ†• No config found under '{self.s3_prefix}/'. Proceeding to upload.") else: raise @@ -46,8 +54,6 @@ def upload_if_missing_or_changed(self, local_path: str): self.s3_client.upload_file(local_path, self.bucket_name, s3_key) print(f"โœ… Uploaded to s3://{self.bucket_name}/{s3_key}") - - def config_exists_and_matches(self, local_path: str, s3_key: str) -> bool: try: s3_obj = self.s3_client.get_object(Bucket=self.bucket_name, Key=s3_key) @@ -57,7 +63,23 @@ def config_exists_and_matches(self, local_path: str, s3_key: str) -> bool: return s3_hash == local_hash except self.s3_client.exceptions.NoSuchKey: return False - except botocore.exceptions.ClientError as e: - if e.response["Error"]["Code"] == "NoSuchKey": + except botocore.exceptions.ClientError as error: + if error.response.get("Error", {}).get("Code") == "NoSuchKey": return False raise + + def _delete_all_in_prefix(self) -> None: + """Delete all S3 objects under the current prefix.""" + response = self.s3_client.list_objects_v2( + Bucket=self.bucket_name, Prefix=self.s3_prefix + ) + + if "Contents" in response: + to_delete = [{"Key": obj["Key"]} for obj in response["Contents"]] + self.s3_client.delete_objects( + Bucket=self.bucket_name, + Delete={"Objects": to_delete} + ) + print(f"๐Ÿ—‘๏ธ Deleted {len(to_delete)} file(s) under prefix '{self.s3_prefix}/'.") + else: + print(f"๐Ÿ“ญ Nothing to delete under prefix '{self.s3_prefix}/'.") From 235b4cbe828d0423c91c3617abeb0ac9219c150b Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 13:12:05 +0100 Subject: [PATCH 14/72] removal of common helpers --- tests/e2e/utils/common_test_helpers.py | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 tests/e2e/utils/common_test_helpers.py diff --git a/tests/e2e/utils/common_test_helpers.py b/tests/e2e/utils/common_test_helpers.py deleted file mode 100644 index 09554f545..000000000 --- a/tests/e2e/utils/common_test_helpers.py +++ /dev/null @@ -1,15 +0,0 @@ -def clean_response(data: dict) -> dict: - keys_to_ignore = ["responseId", "lastUpdated"] - return remove_volatile_fields(data, keys_to_ignore) - - -def remove_volatile_fields(data, keys_to_remove): - if isinstance(data, dict): - return { - key: remove_volatile_fields(value, keys_to_remove) - for key, value in data.items() - if key not in keys_to_remove - } - elif isinstance(data, list): - return [remove_volatile_fields(item, keys_to_remove) for item in data] - return data From 4c540e77c6b8062ed86893ae0ef26b51eab9bd31 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 13:21:36 +0100 Subject: [PATCH 15/72] MORE TIDYING --- tests/e2e/tests/test_smoke_tests.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/e2e/tests/test_smoke_tests.py b/tests/e2e/tests/test_smoke_tests.py index 9efadb2f8..a5f780635 100644 --- a/tests/e2e/tests/test_smoke_tests.py +++ b/tests/e2e/tests/test_smoke_tests.py @@ -6,12 +6,11 @@ from tests.e2e.utils.data_loader import load_all_expected_responses, initialise_tests from tests.e2e.utils.s3ConfigManager import S3ConfigManager -# UPDATE THE BELOW WITH LOCATION OF TEST DATA, EXPECTED RESPONSES AND CONFIGS IN DEFINED IN test_config.py # -############################################################################################################# +# Update the below with the configuration values specified in test_config.py all_data, dto = initialise_tests(SMOKE_TEST_DATA) all_expected_responses = load_all_expected_responses(SMOKE_TEST_RESPONSES) config_path = SMOKE_TEST_CONFIGS -############################################################################################################# + s3_manager = S3ConfigManager(S3_BUCKET, S3_PREFIX) @@ -24,11 +23,11 @@ @pytest.mark.smoketest @pytest.mark.parametrize("filename, scenario", param_list, ids=id_list) def test_run_smoke_case(filename, scenario, eligibility_client): - # get the nhs_number + # get the nhs_number from the scenario nhs_number = scenario["nhs_number"] - # get the associated campaign config file + # get the associated campaign config file from the scenario config_filename = scenario.get("config_filename", "") - # upload that config file to s3 if it + # upload that config file to s3 if it is missing or has changed s3_manager.upload_if_missing_or_changed(os.path.abspath(os.path.join(config_path, config_filename))) actual_response = eligibility_client.make_request(nhs_number, strict_ssl=False) From b4c5e7ea6d3cbadb567c3e7ea114dcbdf6b83950 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 14:20:00 +0100 Subject: [PATCH 16/72] added and updated story test for ELI-155 and ELI-291 --- .../storyTestConfigs/AUTO_RSV_ELI-155.json | 74 ++++++++++++ .../storyTestConfigs/AUTO_RSV_ELI-219-1.json | 102 ++++++++++++++++ .../storyTestConfigs/AUTO_RSV_ELI-219-2.json | 109 ++++++++++++++++++ .../storyTestData/AUTO_RSV_ELI-155.json | 46 ++++++++ .../storyTestData/AUTO_RSV_ELI-219-1.json | 47 ++++++++ .../storyTestData/AUTO_RSV_ELI-219-2.json | 47 ++++++++ .../storyTestResponses/AUTO_RSV_ELI-155.json | 30 +++++ .../AUTO_RSV_ELI-219-1.json | 30 +++++ tests/e2e/tests/test_config.py | 4 + tests/e2e/tests/test_story_tests.py | 46 ++++++++ 10 files changed, 535 insertions(+) create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-155.json create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-1.json create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-2.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-2.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-155.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-1.json create mode 100644 tests/e2e/tests/test_story_tests.py diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-155.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-155.json new file mode 100644 index 000000000..adf1635f5 --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-155.json @@ -0,0 +1,74 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "ELI-155 - cohort_label is not supported in R rules", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "CONTACT_GP", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "CONTACT_GP", + "Version": "1", + "Name": "ELI-155 - cohort_label is not supported in R rules", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "M", + "IterationCohorts": [ + { + "CohortLabel": "eli_155_in", + "CohortGroup": "eli_155_in_group", + "PositiveDescription": "You are currently in eli_155_in", + "NegativeDescription": "You are not currently in eli_155_in", + "Priority": 0 + }, + { + "CohortLabel": "eli_155_out", + "CohortGroup": "eli_155_out_group", + "PositiveDescription": "You are currently in eli_155_out", + "NegativeDescription": "You are not currently in eli_155_out", + "Priority": 0 + } + ], + "IterationRules": [ + { + "Type": "R", + "Name": "Actionable if future appointment is booked", + "Description": "Book An Appointment", + "Priority": 1000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D>=", + "Comparator": "0", + "CommsRouting": "AMEND_NBS", + "CohortLabel": "eli_155_out" + } + ], + "ActionsMapper": { + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "##You have an RSV vaccination appointment\\nYou can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Manage your appointment" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-1.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-1.json new file mode 100644 index 000000000..b8303b6a5 --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-1.json @@ -0,0 +1,102 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "ELI-219 - Support Actions R Rules - 9000000002", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "DEFAULT_ACTION", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "DEFAULT_ACTION", + "Version": "1", + "Name": "ELI-155 - Support Actions R Rules", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "M", + "IterationCohorts": [ + { + "CohortLabel": "eli_291_cohort_1", + "CohortGroup": "eli_291_cohort_1_group", + "PositiveDescription": "You are currently in eli_291_cohort_1", + "NegativeDescription": "You are not in eli_291_cohort_1", + "Priority": 0 + } + ], + "IterationRules": [ + { + "Type": "R", + "Name": "Actionable if future appointment is booked with ACC", + "Description": "Book An Appointment", + "Priority": 1000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D>=", + "Comparator": "0", + "CommsRouting": "ACTION_ONE" + }, + { + "Type": "R", + "Name": "Actionable if future appointment is booked with ACC", + "Description": "Book An Appointment", + "Priority": 1000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "is_not_null", + "Comparator": "", + "CommsRouting": "ACTION_ONE" + }, + { + "Type": "R", + "Name": "Actionable if future appointment is booked with ACC", + "Description": "Book An Appointment", + "Priority": 1000, + "AttributeLevel": "COHORT", + "Operator": "in", + "Comparator": "eli_291_cohort_2", + "CommsRouting": "ACTION_ONE" + } + ], + "ActionsMapper": { + "ACTION_ONE": { + "ExternalRoutingCode": "ActionOneRoutingCode", + "ActionDescription": "ActionOneDescription", + "ActionType": "ActionOneActionType", + "UrlLink": "http://www.actiononeurl.com", + "UrlLabel": "ActionOneUrlLabel" + }, + "ACTION_TWO": { + "ExternalRoutingCode": "ActionTwoRoutingCode", + "ActionDescription": "ActionTwoDescription", + "ActionType": "ActionTwoActionType", + "UrlLink": "http://www.actiontwourl.com", + "UrlLabel": "ActionTwoUrlLabel" + }, + "DEFAULT_ACTION": { + "ExternalRoutingCode": "DefaultAction", + "ActionDescription": "DefaultActionDescription", + "ActionType": "DefaultActionType", + "UrlLink": "DefaultActionUrl", + "UrlLabel": "DefaultLabel" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-2.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-2.json new file mode 100644 index 000000000..6ed902f21 --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-2.json @@ -0,0 +1,109 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "ELI-219 - Support Actions R Rules - 9000000003", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "DEFAULT_ACTION", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "DEFAULT_ACTION", + "Version": "1", + "Name": "ELI-155 - Support Actions R Rules", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "M", + "IterationCohorts": [ + { + "CohortLabel": "eli_291_cohort_2", + "CohortGroup": "eli_291_cohort_2_group", + "PositiveDescription": "You are currently in eli_291_cohort_2", + "NegativeDescription": "You are not in eli_291_cohort_2", + "Priority": 1 + } + ], + "IterationRules": [ + { + "Type": "R", + "Name": "Actionable if future appointment is booked with ACC", + "Description": "Book An Appointment", + "Priority": 1000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D<=", + "Comparator": "0", + "CommsRouting": "ACTION_ONE" + }, + { + "Type": "R", + "Name": "Actionable if future appointment is booked with ACC", + "Description": "Book An Appointment", + "Priority": 1030, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "is_not_null", + "Comparator": "", + "CommsRouting": "ACTION_THREE" + }, + { + "Type": "R", + "Name": "Actionable if future appointment is booked with ACC", + "Description": "Book An Appointment", + "Priority": 1020, + "AttributeLevel": "COHORT", + "Operator": "=", + "Comparator": "eli_291_cohort_2", + "CommsRouting": "ACTION_TWO" + } + ], + "ActionsMapper": { + "ACTION_ONE": { + "ExternalRoutingCode": "ActionOneRoutingCode", + "ActionDescription": "ActionOneDescription", + "ActionType": "ActionOneActionType", + "UrlLink": "http://www.actiononeurl.com", + "UrlLabel": "ActionOneUrlLabel" + }, + "ACTION_TWO": { + "ExternalRoutingCode": "ActionTwoRoutingCode", + "ActionDescription": "ActionTwoDescription", + "ActionType": "ActionTwoActionType", + "UrlLink": "http://www.actiontwourl.com", + "UrlLabel": "ActionTwoUrlLabel" + }, + "ACTION_THREE": { + "ExternalRoutingCode": "ActionThreeRoutingCode", + "ActionDescription": "ActionThreeDescription", + "ActionType": "ActionThreeActionType", + "UrlLink": "http://www.actionthreeurl.com", + "UrlLabel": "ActionThreeUrlLabel" + }, + "DEFAULT_ACTION": { + "ExternalRoutingCode": "DefaultAction", + "ActionDescription": "DefaultActionDescription", + "ActionType": "DefaultActionType", + "UrlLink": "DefaultActionUrl", + "UrlLabel": "DefaultLabel" + } + } + } + ] + } +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json new file mode 100644 index 000000000..755627dc6 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json @@ -0,0 +1,46 @@ +{ + "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested actions (with booking)", + "config_filename": "AUTO_RSV_ELI-155.json", + "data": [ + { + "NHS_NUMBER": "9000000001", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_155_in": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9000000001", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9000000001", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json new file mode 100644 index 000000000..77276d502 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json @@ -0,0 +1,47 @@ +{ + "scenario_name": "ELI-219 - Fails and Rules and Returns Default", + "config_filename": "AUTO_RSV_ELI-219-1.json", + "data": [ + { + "NHS_NUMBER": "9000000002", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_291_cohort_1": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9000000002", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9000000002", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-2.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-2.json new file mode 100644 index 000000000..69ec4e273 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-2.json @@ -0,0 +1,47 @@ +{ + "scenario_name": "ELI-219 - Returns the highest Priority Action", + "config_filename": "AUTO_RSV_ELI-219-2.json", + "data": [ + { + "NHS_NUMBER": "9000000003", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_291_cohort_2": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9000000003", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9000000002", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-155.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-155.json new file mode 100644 index 000000000..ffba018dc --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-155.json @@ -0,0 +1,30 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "Actionable", + "statusText": "Status.actionable", + "eligibilityCohorts": [ + { + "cohortCode": "eli_155_in_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in eli_155_in" + } + ], + "suitabilityRules": [], + "actions": [ + { + "action_code": "AmendNBS", + "action_description": "##You have an RSV vaccination appointment\\nYou can view, change or cancel your appointment below.", + "action_type": "ButtonWithAuthLink", + "url_link": "http://www.nhs.uk/book-rsv", + "url_label": "Manage your appointment" + } + ] + } + ] +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-1.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-1.json new file mode 100644 index 000000000..f6cc88345 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-1.json @@ -0,0 +1,30 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "Actionable", + "statusText": "Status.actionable", + "eligibilityCohorts": [ + { + "cohortCode": "eli_291_cohort_1_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in eli_291_cohort_1" + } + ], + "suitabilityRules": [], + "actions": [ + { + "action_type": "DefaultActionType", + "action_code": "DefaultAction", + "action_description": "DefaultActionDescription", + "url_label": "DefaultLabel", + "url_link": "DefaultActionUrl" + } + ] + } + ] +} diff --git a/tests/e2e/tests/test_config.py b/tests/e2e/tests/test_config.py index 16b77822a..04d26bd92 100644 --- a/tests/e2e/tests/test_config.py +++ b/tests/e2e/tests/test_config.py @@ -6,3 +6,7 @@ SMOKE_TEST_DATA: str = "tests/e2e/data/dynamoDB/smokeTestData/" SMOKE_TEST_RESPONSES: str = "tests/e2e/data/responses/smokeTestResponses/" SMOKE_TEST_CONFIGS: str = "tests/e2e/data/configs/smokeTestConfigs" +# Story Test Data Paths +STORY_TEST_DATA: str = "tests/e2e/data/dynamoDB/storyTestData/" +STORY_TEST_RESPONSES: str = "tests/e2e/data/responses/storyTestResponses/" +STORY_TEST_CONFIGS: str = "tests/e2e/data/configs/storyTestConfigs" diff --git a/tests/e2e/tests/test_story_tests.py b/tests/e2e/tests/test_story_tests.py new file mode 100644 index 000000000..7c8713a36 --- /dev/null +++ b/tests/e2e/tests/test_story_tests.py @@ -0,0 +1,46 @@ +import os + +import pytest + +from tests.e2e.tests.test_config import * +from tests.e2e.utils.data_loader import load_all_expected_responses, initialise_tests +from tests.e2e.utils.s3ConfigManager import S3ConfigManager + +# Update the below with the configuration values specified in test_config.py +all_data, dto = initialise_tests(STORY_TEST_DATA) +all_expected_responses = load_all_expected_responses(STORY_TEST_RESPONSES) +config_path = STORY_TEST_CONFIGS + +s3_manager = S3ConfigManager(S3_BUCKET, S3_PREFIX) + + +param_list = list(all_data.items()) +id_list = [ + f"{filename} - {scenario.get('scenario_name', 'No Scenario')}" + for filename, scenario in param_list +] + +@pytest.mark.storytest +@pytest.mark.parametrize("filename, scenario", param_list, ids=id_list) +def test_run_story_test_cases(filename, scenario, eligibility_client): + # get the nhs_number from the scenario + nhs_number = scenario["nhs_number"] + # get the associated campaign config file from the scenario + config_filename = scenario.get("config_filename", "") + # upload that config file to s3 if it is missing or has changed + s3_manager.upload_if_missing_or_changed(os.path.abspath(os.path.join(config_path, config_filename))) + + actual_response = eligibility_client.make_request(nhs_number, strict_ssl=False) + expected_response = all_expected_responses.get(filename).get("response_items", {}) + + # Assert and show details on failure + assert actual_response["status_code"] == 200 + assert actual_response["body"] == expected_response, ( + f"\nโŒ Mismatch in test: {filename}\n" + f"NHS Number: {nhs_number}\n" + f"Expected: {expected_response}\n" + f"Actual: {actual_response}\n" + ) + + + From 248b5b9eb922bf0509b521f2a285a96c8a84b16b Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 14:20:13 +0100 Subject: [PATCH 17/72] added and updated story test for ELI-155 and ELI-291 --- .../storyTestConfigs/AUTO_RSV_ELI-219-3.json | 99 +++++++++++++++++++ .../storyTestData/AUTO_RSV_ELI-219-3.json | 47 +++++++++ .../AUTO_RSV_ELI-219-2.json | 30 ++++++ .../AUTO_RSV_ELI-219-3.json | 30 ++++++ 4 files changed, 206 insertions(+) create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-3.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-3.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-2.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-3.json diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-3.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-3.json new file mode 100644 index 000000000..f274ade2b --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-3.json @@ -0,0 +1,99 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "ELI-219 - Support Actions R Rules - 9000000004", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "DEFAULT_ACTION", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "DEFAULT_ACTION", + "Version": "1", + "Name": "ELI-219 - Support Actions R Rules - 9000000004", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "M", + "IterationCohorts": [ + { + "CohortLabel": "eli_291_cohort_3", + "CohortGroup": "eli_291_cohort_3_group", + "PositiveDescription": "You are currently in eli_291_cohort_3", + "NegativeDescription": "You are not in eli_291_cohort_3", + "Priority": 1 + } + ], + "IterationRules": [ + { + "Type": "R", + "Name": "Actionable if future appointment is booked", + "Description": "Book An Appointment", + "Priority": 1000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D>=", + "Comparator": "0", + "CommsRouting": "ACTION_ONE" + }, + { + "Type": "R", + "Name": "Actionable if Vaccinated", + "Description": "You are Vaccinated", + "Priority": 1010, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "is_not_null", + "Comparator": "", + "CommsRouting": "ACTION_TWO" + } + ], + "ActionsMapper": { + "ACTION_ONE": { + "ExternalRoutingCode": "ActionOneRoutingCode", + "ActionDescription": "ActionOneDescription", + "ActionType": "ActionOneActionType", + "UrlLink": "http://www.actiononeurl.com", + "UrlLabel": "ActionOneUrlLabel" + }, + "ACTION_TWO": { + "ExternalRoutingCode": "ActionTwoRoutingCode", + "ActionDescription": "ActionTwoDescription", + "ActionType": "ActionTwoActionType", + "UrlLink": "http://www.actiontwourl.com", + "UrlLabel": "ActionTwoUrlLabel" + }, + "ACTION_THREE": { + "ExternalRoutingCode": "ActionThreeRoutingCode", + "ActionDescription": "ActionThreeDescription", + "ActionType": "ActionThreeActionType", + "UrlLink": "http://www.actionthreeurl.com", + "UrlLabel": "ActionThreeUrlLabel" + }, + "DEFAULT_ACTION": { + "ExternalRoutingCode": "DefaultAction", + "ActionDescription": "DefaultActionDescription", + "ActionType": "DefaultActionType", + "UrlLink": "DefaultActionUrl", + "UrlLabel": "DefaultLabel" + } + } + } + ] + } +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-3.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-3.json new file mode 100644 index 000000000..952328aa2 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-3.json @@ -0,0 +1,47 @@ +{ + "scenario_name": "ELI-219 - Returns the highest Priority Action", + "config_filename": "AUTO_RSV_ELI-219-3.json", + "data": [ + { + "NHS_NUMBER": "9000000004", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_291_cohort_3": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9000000004", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9000000004", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-2.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-2.json new file mode 100644 index 000000000..82ecaecde --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-2.json @@ -0,0 +1,30 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "Actionable", + "statusText": "Status.actionable", + "eligibilityCohorts": [ + { + "cohortCode": "eli_291_cohort_2_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in eli_291_cohort_2" + } + ], + "suitabilityRules": [], + "actions": [ + { + "action_type": "ActionTwoActionType", + "action_code": "ActionTwoRoutingCode", + "action_description": "ActionTwoDescription", + "url_link": "http://www.actiontwourl.com", + "url_label": "ActionTwoUrlLabel" + } + ] + } + ] +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-3.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-3.json new file mode 100644 index 000000000..4df9ef7ef --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-3.json @@ -0,0 +1,30 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "Actionable", + "statusText": "Status.actionable", + "eligibilityCohorts": [ + { + "cohortCode": "eli_291_cohort_3_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in eli_291_cohort_3" + } + ], + "suitabilityRules": [], + "actions": [ + { + "action_code": "ActionOneRoutingCode", + "action_description": "ActionOneDescription", + "action_type": "ActionOneActionType", + "url_label": "ActionOneUrlLabel", + "url_link": "http://www.actiononeurl.com" + } + ] + } + ] +} From c1b720afa3e708c21650a5ec18708deac2d103c1 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 14:25:00 +0100 Subject: [PATCH 18/72] some linting fixes --- tests/e2e/tests/conftest.py | 1 + tests/e2e/tests/test_smoke_tests.py | 4 ++-- tests/e2e/tests/test_story_tests.py | 5 +---- tests/e2e/utils/data_loader.py | 9 ++++++--- tests/e2e/utils/dynamo_helper.py | 1 + tests/e2e/utils/eligibilityApiClient.py | 14 +++++++------- tests/e2e/utils/placeholder_context.py | 1 + tests/e2e/utils/placeholder_utils.py | 3 ++- 8 files changed, 21 insertions(+), 17 deletions(-) diff --git a/tests/e2e/tests/conftest.py b/tests/e2e/tests/conftest.py index c0c070914..40549aef7 100644 --- a/tests/e2e/tests/conftest.py +++ b/tests/e2e/tests/conftest.py @@ -81,6 +81,7 @@ def setup_dynamodb_data(request): logger.exception("[x] Failed to delete item %s", item.get("PK", "")) logger.info("[โœ“] Deleted %d/%d items", delete_count, len(items)) + @pytest.fixture(scope="session") def eligibility_client(): base_url = "https://test.eligibility-signposting-api.nhs.uk/patient-check" diff --git a/tests/e2e/tests/test_smoke_tests.py b/tests/e2e/tests/test_smoke_tests.py index a5f780635..19579b1ad 100644 --- a/tests/e2e/tests/test_smoke_tests.py +++ b/tests/e2e/tests/test_smoke_tests.py @@ -13,13 +13,13 @@ s3_manager = S3ConfigManager(S3_BUCKET, S3_PREFIX) - param_list = list(all_data.items()) id_list = [ f"{filename} - {scenario.get('scenario_name', 'No Scenario')}" for filename, scenario in param_list ] + @pytest.mark.smoketest @pytest.mark.parametrize("filename, scenario", param_list, ids=id_list) def test_run_smoke_case(filename, scenario, eligibility_client): @@ -42,6 +42,7 @@ def test_run_smoke_case(filename, scenario, eligibility_client): f"Actual: {actual_response}\n" ) + @pytest.mark.smoketest def test_check_for_missing_person(eligibility_client): nhs_number = "1234567890" @@ -59,4 +60,3 @@ def test_check_for_missing_person(eligibility_client): assert response["status_code"] == 404 assert response["body"] == expected_body - diff --git a/tests/e2e/tests/test_story_tests.py b/tests/e2e/tests/test_story_tests.py index 7c8713a36..d01299dc7 100644 --- a/tests/e2e/tests/test_story_tests.py +++ b/tests/e2e/tests/test_story_tests.py @@ -13,13 +13,13 @@ s3_manager = S3ConfigManager(S3_BUCKET, S3_PREFIX) - param_list = list(all_data.items()) id_list = [ f"{filename} - {scenario.get('scenario_name', 'No Scenario')}" for filename, scenario in param_list ] + @pytest.mark.storytest @pytest.mark.parametrize("filename, scenario", param_list, ids=id_list) def test_run_story_test_cases(filename, scenario, eligibility_client): @@ -41,6 +41,3 @@ def test_run_story_test_cases(filename, scenario, eligibility_client): f"Expected: {expected_response}\n" f"Actual: {actual_response}\n" ) - - - diff --git a/tests/e2e/utils/data_loader.py b/tests/e2e/utils/data_loader.py index b347ca46b..02cb4487b 100644 --- a/tests/e2e/utils/data_loader.py +++ b/tests/e2e/utils/data_loader.py @@ -5,6 +5,7 @@ from .placeholder_utils import resolve_placeholders from .placeholder_context import ResolvedPlaceholderContext, PlaceholderDTO + def initialise_tests(folder): folder_path = os.path.abspath(folder) all_data, dto = load_all_test_scenarios(folder_path) @@ -15,6 +16,7 @@ def initialise_tests(folder): return all_data, dto + def resolve_placeholders_in_data(data, context, file_name): if isinstance(data, dict): return {k: resolve_placeholders_in_data(v, context, file_name) for k, v in data.items()} @@ -72,10 +74,10 @@ def load_all_expected_responses(folder_path): # Load JSON with open(full_path, "r") as f: - raw_json = json.load(f) + raw_json = json.load(f) resolved_data = resolve_placeholders_in_data(raw_json, dto, filename) - cleaned_data = clean_expected_response(resolved_data,) + cleaned_data = clean_expected_response(resolved_data, ) all_data[filename] = { "response_items": cleaned_data @@ -83,6 +85,7 @@ def load_all_expected_responses(folder_path): return all_data + def load_all_test_scenarios(folder_path, config_folder_path="tests/e2e/data/configs"): all_data = {} dto = PlaceholderDTO() # Shared across all files @@ -118,6 +121,7 @@ def load_all_test_scenarios(folder_path, config_folder_path="tests/e2e/data/conf return all_data, dto + def clean_expected_response(data: dict) -> dict: keys_to_ignore = ["responseId", "lastUpdated"] return _remove_volatile_fields(data, keys_to_ignore) @@ -133,4 +137,3 @@ def _remove_volatile_fields(data, keys_to_remove): elif isinstance(data, list): return [_remove_volatile_fields(item, keys_to_remove) for item in data] return data - diff --git a/tests/e2e/utils/dynamo_helper.py b/tests/e2e/utils/dynamo_helper.py index 757aba835..2202cb85f 100644 --- a/tests/e2e/utils/dynamo_helper.py +++ b/tests/e2e/utils/dynamo_helper.py @@ -48,6 +48,7 @@ def get_item(self, key: dict): print(f"Failed to get item: {e.response['Error']['Message']}") raise + def insert_into_dynamo(data): print("Inserting into Dynamo (fake):", data) table = DynamoDBHelper("eligibility-signposting-api-test-eligibility_datastore") diff --git a/tests/e2e/utils/eligibilityApiClient.py b/tests/e2e/utils/eligibilityApiClient.py index 0f8261e7e..a5ec98129 100644 --- a/tests/e2e/utils/eligibilityApiClient.py +++ b/tests/e2e/utils/eligibilityApiClient.py @@ -63,12 +63,12 @@ def _ensure_certs_present(self) -> None: f.write(cert_value) def make_request( - self, - nhs_number: str, - method: str = "GET", - payload: Optional[Union[Dict[str, Any], list]] = None, - strict_ssl: bool = False, - raise_on_error: bool = True, + self, + nhs_number: str, + method: str = "GET", + payload: Optional[Union[Dict[str, Any], list]] = None, + strict_ssl: bool = False, + raise_on_error: bool = True, ) -> Dict[str, Any]: url = f"{self.api_url.rstrip('/')}/{nhs_number}" cert = ( @@ -119,7 +119,7 @@ def _clean_response(self, data: Any) -> Any: return self._remove_volatile_fields(data, keys_to_ignore) def _remove_volatile_fields( - self, data: Union[Dict[str, Any], list, Any], keys_to_remove: list + self, data: Union[Dict[str, Any], list, Any], keys_to_remove: list ) -> Any: if isinstance(data, dict): return { diff --git a/tests/e2e/utils/placeholder_context.py b/tests/e2e/utils/placeholder_context.py index 0e304cff9..4809407d0 100644 --- a/tests/e2e/utils/placeholder_context.py +++ b/tests/e2e/utils/placeholder_context.py @@ -11,6 +11,7 @@ def get(self, placeholder: str) -> str: def all(self): return self.values + class PlaceholderDTO: def __init__(self): self.placeholders = {} # key: filename, value: dict of placeholders per file diff --git a/tests/e2e/utils/placeholder_utils.py b/tests/e2e/utils/placeholder_utils.py index 437c00503..5b106a63d 100644 --- a/tests/e2e/utils/placeholder_utils.py +++ b/tests/e2e/utils/placeholder_utils.py @@ -3,6 +3,7 @@ from dateutil.relativedelta import relativedelta from calendar import isleap + def resolve_placeholders(value, context=None, file_name=None): if not isinstance(value, str): return value @@ -15,7 +16,7 @@ def resolve_placeholders(value, context=None, file_name=None): parts = placeholder.split("_") try: - if placeholder in ["IGNORE_RESPONSE_ID", "IGNORE_DATE"]: + if placeholder in ["IGNORE_RESPONSE_ID", "IGNORE_DATE"]: return value.replace(f"<<{placeholder}>>", placeholder) elif len(parts) != 3 or parts[0] not in ["DATE", "RDATE", "IGNORE"]: return value # Unrecognized format From 890b9a699735a891f40f957e785da14f6981788c Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Tue, 1 Jul 2025 09:35:47 +0100 Subject: [PATCH 19/72] adding header functionality to the GET request --- .../storyTestConfigs/AUTO_RSV_ELI-219-4.json | 87 +++++++++++++++++++ .../storyTestData/AUTO_RSV_ELI-155.json | 1 + .../storyTestData/AUTO_RSV_ELI-219-4.json | 47 ++++++++++ .../AUTO_RSV_ELI-219-4.json | 30 +++++++ tests/e2e/data/s3/patient_s3_sample.json | 59 ------------- tests/e2e/tests/test_smoke_tests.py | 3 +- tests/e2e/tests/test_story_tests.py | 9 +- tests/e2e/utils/data_loader.py | 6 +- tests/e2e/utils/eligibilityApiClient.py | 40 +++++++++ 9 files changed, 215 insertions(+), 67 deletions(-) create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-4.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-4.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-4.json delete mode 100644 tests/e2e/data/s3/patient_s3_sample.json diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-4.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-4.json new file mode 100644 index 000000000..884bb6c9e --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-4.json @@ -0,0 +1,87 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "ELI-219 - Empty Actions - 9000000005", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "DEFAULT_ACTION", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "DEFAULT_ACTION", + "Version": "1", + "Name": "ELI-219 - Support Actions R Rules - 9000000004", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "M", + "IterationCohorts": [ + { + "CohortLabel": "eli_291_cohort_4", + "CohortGroup": "eli_291_cohort_4_group", + "PositiveDescription": "You are currently in eli_291_cohort_4", + "NegativeDescription": "You are not in eli_291_cohort_4", + "Priority": 1 + } + ], + "IterationRules": [ + { + "Type": "R", + "Name": "Actionable if future appointment is booked", + "Description": "Book An Appointment", + "Priority": 1000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D>=", + "Comparator": "0", + "CommsRouting": "EMPTY_ACTION" + } + ], + "ActionsMapper": { + "EMPTY_ACTION": { + "ExternalRoutingCode": "", + "ActionDescription": "", + "ActionType": "", + "UrlLink": "", + "UrlLabel": "" + }, + "ACTION_TWO": { + "ExternalRoutingCode": "ActionTwoRoutingCode", + "ActionDescription": "ActionTwoDescription", + "ActionType": "ActionTwoActionType", + "UrlLink": "http://www.actiontwourl.com", + "UrlLabel": "ActionTwoUrlLabel" + }, + "ACTION_THREE": { + "ExternalRoutingCode": "ActionThreeRoutingCode", + "ActionDescription": "ActionThreeDescription", + "ActionType": "ActionThreeActionType", + "UrlLink": "http://www.actionthreeurl.com", + "UrlLabel": "ActionThreeUrlLabel" + }, + "DEFAULT_ACTION": { + "ExternalRoutingCode": "DefaultAction", + "ActionDescription": "DefaultActionDescription", + "ActionType": "DefaultActionType", + "UrlLink": "DefaultActionUrl", + "UrlLabel": "DefaultLabel" + } + } + } + ] + } +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json index 755627dc6..477c69982 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json @@ -1,6 +1,7 @@ { "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested actions (with booking)", "config_filename": "AUTO_RSV_ELI-155.json", + "request_headers": {"authenticated_nhs_number": "9000000001","includeActions": "N"}, "data": [ { "NHS_NUMBER": "9000000001", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-4.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-4.json new file mode 100644 index 000000000..83aba304f --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-4.json @@ -0,0 +1,47 @@ +{ + "scenario_name": "ELI-219 - Testing the new requirement from Vita around empty actions ", + "config_filename": "AUTO_RSV_ELI-219-4.json", + "data": [ + { + "NHS_NUMBER": "9000000005", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_291_cohort_4": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9000000005", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9000000005", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-4.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-4.json new file mode 100644 index 000000000..472dda0c6 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-4.json @@ -0,0 +1,30 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "Actionable", + "statusText": "Status.actionable", + "eligibilityCohorts": [ + { + "cohortCode": "eli_291_cohort_4_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in eli_291_cohort_4" + } + ], + "suitabilityRules": [], + "actions": [ + { + "action_code": "", + "action_description": "", + "action_type": "", + "url_label": "", + "url_link": "" + } + ] + } + ] +} diff --git a/tests/e2e/data/s3/patient_s3_sample.json b/tests/e2e/data/s3/patient_s3_sample.json deleted file mode 100644 index 3e7b93748..000000000 --- a/tests/e2e/data/s3/patient_s3_sample.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "PATIENT": { - "NHS_NUMBER": "9876543210", - "SCENARIO": "SCN005", - "PERSON_ATTRIBUTES": { - "DATE_OF_BIRTH": "19880615", - "GENDER": "1", - "POSTCODE": "M1 1AA", - "POSTCODE_SECTOR": "M1", - "POSTCODE_OUTCODE": "1AA", - "MSOA": "E05000003", - "LSOA": "E01000003", - "GP_PRACTICE_CODE": "C98765", - "PCN": "PCN003", - "ICB": "ICB002", - "COMMISSIONING_REGION": "RegionZ", - "13Q_FLAG": "Y", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - }, - "TARGET_CONDITIONS": { - "COVID": { - "VALID_DOSES_COUNT": "2", - "INVALID_DOSES_COUNT": "0", - "LAST_SUCCESSFUL_DATE": "20241201", - "LAST_VALID_DOSE_DATE": "20241201", - "BOOKED_APPOINTMENT_DATE": "20250110", - "BOOKED_APPOINTMENT_PROVIDER": "Local Clinic", - "LAST_INVITE_DATE": "20241115", - "LAST_INVITE_STATUS": "Accepted", - "OPTOUT": "N" - }, - "FLU": { - "VALID_DOSES_COUNT": "1", - "INVALID_DOSES_COUNT": "0", - "LAST_SUCCESSFUL_DATE": "20241020", - "LAST_VALID_DOSE_DATE": "20241020", - "LAST_INVITE_DATE": "20240901", - "LAST_INVITE_STATUS": "Delivered", - "OPTOUT": "N" - }, - "RSV": { - "LAST_SUCCESSFUL_DATE": "20241105", - "LAST_VALID_DOSE_DATE": "20241105", - "OPTOUT": "N" - } - }, - "COHORT_MEMBERSHIP": [ - { - "cohort_label": "Trial_Group_A", - "date_joined": "20250210" - }, - { - "cohort_label": "Manchester_Residents_2024", - "date_joined": "20240701" - } - ] - } -} diff --git a/tests/e2e/tests/test_smoke_tests.py b/tests/e2e/tests/test_smoke_tests.py index 19579b1ad..8e798e9f8 100644 --- a/tests/e2e/tests/test_smoke_tests.py +++ b/tests/e2e/tests/test_smoke_tests.py @@ -1,3 +1,4 @@ +import http import os import pytest @@ -58,5 +59,5 @@ def test_check_for_missing_person(eligibility_client): response = eligibility_client.make_request(nhs_number, raise_on_error=False) - assert response["status_code"] == 404 + assert response["status_code"] == http.HTTPStatus.NOT_FOUND assert response["body"] == expected_body diff --git a/tests/e2e/tests/test_story_tests.py b/tests/e2e/tests/test_story_tests.py index d01299dc7..aab7c6f2e 100644 --- a/tests/e2e/tests/test_story_tests.py +++ b/tests/e2e/tests/test_story_tests.py @@ -1,3 +1,4 @@ +import http import os import pytest @@ -20,21 +21,19 @@ ] -@pytest.mark.storytest +@pytest.mark.storytest_all @pytest.mark.parametrize("filename, scenario", param_list, ids=id_list) def test_run_story_test_cases(filename, scenario, eligibility_client): - # get the nhs_number from the scenario nhs_number = scenario["nhs_number"] - # get the associated campaign config file from the scenario config_filename = scenario.get("config_filename", "") - # upload that config file to s3 if it is missing or has changed + request_headers = scenario.get("request_headers", {}) s3_manager.upload_if_missing_or_changed(os.path.abspath(os.path.join(config_path, config_filename))) actual_response = eligibility_client.make_request(nhs_number, strict_ssl=False) expected_response = all_expected_responses.get(filename).get("response_items", {}) # Assert and show details on failure - assert actual_response["status_code"] == 200 + assert actual_response["status_code"] == http.HTTPStatus.OK assert actual_response["body"] == expected_response, ( f"\nโŒ Mismatch in test: {filename}\n" f"NHS Number: {nhs_number}\n" diff --git a/tests/e2e/utils/data_loader.py b/tests/e2e/utils/data_loader.py index 02cb4487b..f1e472754 100644 --- a/tests/e2e/utils/data_loader.py +++ b/tests/e2e/utils/data_loader.py @@ -102,8 +102,9 @@ def load_all_test_scenarios(folder_path, config_folder_path="tests/e2e/data/conf raw_data = raw_json["data"] - config_filename = raw_json.get("config_filename") # Just the name - scenario_name = raw_json.get("scenario_name") # Just the name + config_filename = raw_json.get("config_filename") + scenario_name = raw_json.get("scenario_name") + request_headers = raw_json.get("request_headers") # Resolve placeholders with shared DTO resolved_data = resolve_placeholders_in_data(raw_data, dto, filename) @@ -116,6 +117,7 @@ def load_all_test_scenarios(folder_path, config_folder_path="tests/e2e/data/conf "dynamo_items": resolved_data, "nhs_number": nhs_number, "config_filename": config_filename, + "request_headers": request_headers, "scenario_name": scenario_name, } diff --git a/tests/e2e/utils/eligibilityApiClient.py b/tests/e2e/utils/eligibilityApiClient.py index a5ec98129..4614ef3bf 100644 --- a/tests/e2e/utils/eligibilityApiClient.py +++ b/tests/e2e/utils/eligibilityApiClient.py @@ -63,6 +63,46 @@ def _ensure_certs_present(self) -> None: f.write(cert_value) def make_request( + self, + nhs_number: str, + method: str = "GET", + payload: Optional[Union[Dict[str, Any], list]] = None, + headers: Optional[Dict[str, str]] = None, + strict_ssl: bool = False, + raise_on_error: bool = True, + ) -> Dict[str, Any]: + url = f"{self.api_url.rstrip('/')}/{nhs_number}" + cert = ( + str(self.cert_paths["client_cert"]), + str(self.cert_paths["private_key"]), + ) + verify: Union[bool, str] = str(self.cert_paths["ca_cert"]) if strict_ssl else False + + try: + response = requests.request( + method=method.upper(), + url=url, + cert=cert, + verify=verify, + json=payload, + headers=headers, + timeout=10, + ) + + if raise_on_error: + response.raise_for_status() + + return self._parse_response(response) + + except requests.exceptions.SSLError as ssl_err: + raise RuntimeError(f"SSL error during request: {ssl_err}") from ssl_err + except requests.exceptions.RequestException as req_err: + response = getattr(req_err, "response", None) + if isinstance(response, Response): + return self._parse_response(response) + raise RuntimeError(f"Request error: {req_err}") from req_err + + def make_request_old( self, nhs_number: str, method: str = "GET", From 33aa4f5e27a1bdfa755f23160b3511062b00c485 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Tue, 1 Jul 2025 13:13:09 +0100 Subject: [PATCH 20/72] adding header functionality to the GET request --- .../storyTestConfigs/AUTO_RSV_ELI-216.json | 101 ++++++++++++++++++ .../storyTestData/AUTO_RSV_ELI-155.json | 2 +- .../storyTestData/AUTO_RSV_ELI-216-1.json | 48 +++++++++ .../storyTestData/AUTO_RSV_ELI-216-2.json | 48 +++++++++ .../storyTestData/AUTO_RSV_ELI-216-3.json | 48 +++++++++ .../storyTestData/AUTO_RSV_ELI-219-1.json | 2 +- .../storyTestResponses/AUTO_RSV_ELI-216.json | 30 ++++++ 7 files changed, 277 insertions(+), 2 deletions(-) create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-1.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-2.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-3.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216.json diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json new file mode 100644 index 000000000..b3395986e --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json @@ -0,0 +1,101 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "ELI-216 - NHS Number check (NHS login) - 9000000006", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "DEFAULT_ACTION", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "DEFAULT_ACTION", + "Version": "1", + "Name": "ELI-155 - Support Actions R Rules", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "M", + "IterationCohorts": [ + { + "CohortLabel": "eli_216_cohort", + "CohortGroup": "eli_216_cohort_group", + "PositiveDescription": "You are currently in eli_216_cohort", + "NegativeDescription": "You are not in eli_216_cohort", + "Priority": 0 + } + ], + "IterationRules": [ + { + "Type": "R", + "Name": "Actionable if future appointment is booked with ACC", + "Description": "Book An Appointment", + "Priority": 1000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D>=", + "Comparator": "0", + "CommsRouting": "ACTION_ONE" + }, + { + "Type": "R", + "Name": "Actionable if future appointment is booked with ACC", + "Description": "Book An Appointment", + "Priority": 1000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "is_not_null", + "Comparator": "", + "CommsRouting": "ACTION_ONE" + }, + { + "Type": "R", + "Name": "Actionable if future appointment is booked with ACC", + "Description": "Book An Appointment", + "Priority": 1000, + "AttributeLevel": "COHORT", + "Operator": "in", + "CommsRouting": "ACTION_ONE" + } + ], + "ActionsMapper": { + "ACTION_ONE": { + "ExternalRoutingCode": "ActionOneRoutingCode", + "ActionDescription": "ActionOneDescription", + "ActionType": "ActionOneActionType", + "UrlLink": "http://www.actiononeurl.com", + "UrlLabel": "ActionOneUrlLabel" + }, + "ACTION_TWO": { + "ExternalRoutingCode": "ActionTwoRoutingCode", + "ActionDescription": "ActionTwoDescription", + "ActionType": "ActionTwoActionType", + "UrlLink": "http://www.actiontwourl.com", + "UrlLabel": "ActionTwoUrlLabel" + }, + "DEFAULT_ACTION": { + "ExternalRoutingCode": "DefaultAction", + "ActionDescription": "DefaultActionDescription", + "ActionType": "DefaultActionType", + "UrlLink": "DefaultActionUrl", + "UrlLabel": "DefaultLabel" + } + } + } + ] + } +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json index 477c69982..949761440 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json @@ -1,7 +1,7 @@ { "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested actions (with booking)", "config_filename": "AUTO_RSV_ELI-155.json", - "request_headers": {"authenticated_nhs_number": "9000000001","includeActions": "N"}, + "request_headers": {"authenticated_nhs_number": "9000000001"}, "data": [ { "NHS_NUMBER": "9000000001", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-1.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-1.json new file mode 100644 index 000000000..60f0c706b --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-1.json @@ -0,0 +1,48 @@ +{ + "scenario_name": "ELI-216 - NHS Number check (NHS login) - Correct NHS_number", + "config_filename": "AUTO_RSV_ELI-216.json", + "request_headers": {"authenticated_nhs_number": "9000000006"}, + "data": [ + { + "NHS_NUMBER": "9000000006", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_216_cohort": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9000000006", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9000000006", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-2.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-2.json new file mode 100644 index 000000000..c0f878741 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-2.json @@ -0,0 +1,48 @@ +{ + "scenario_name": "ELI-216 - NHS Number check (NHS login) - Incorrect NHS_number", + "config_filename": "AUTO_RSV_ELI-216.json", + "request_headers": {"authenticated_nhs_number": "9000000001"}, + "data": [ + { + "NHS_NUMBER": "9000000006", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_216_cohort": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9000000006", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9000000006", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-3.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-3.json new file mode 100644 index 000000000..6de7ffeea --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-3.json @@ -0,0 +1,48 @@ +{ + "scenario_name": "ELI-216 - NHS Number check (NHS login) - No Header", + "config_filename": "AUTO_RSV_ELI-216.json", + "request_headers": {}, + "data": [ + { + "NHS_NUMBER": "9000000006", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_216_cohort": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9000000006", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9000000006", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json index 77276d502..13c517845 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json @@ -1,6 +1,6 @@ { "scenario_name": "ELI-219 - Fails and Rules and Returns Default", - "config_filename": "AUTO_RSV_ELI-219-1.json", + "config_filename": "AUTO_RSV_ELI-21-91.json", "data": [ { "NHS_NUMBER": "9000000002", diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216.json new file mode 100644 index 000000000..1e064316c --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216.json @@ -0,0 +1,30 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "Actionable", + "statusText": "Status.actionable", + "eligibilityCohorts": [ + { + "cohortCode": "eli_216_cohort_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in eli_216_cohort" + } + ], + "suitabilityRules": [], + "actions": [ + { + "action_type": "DefaultActionType", + "action_code": "DefaultAction", + "action_description": "DefaultActionDescription", + "url_label": "DefaultLabel", + "url_link": "DefaultActionUrl" + } + ] + } + ] +} From d2eb09020e871d2dbffe90d29d6e063d354da817 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Tue, 1 Jul 2025 13:13:18 +0100 Subject: [PATCH 21/72] adding header functionality to the GET request --- tests/e2e/tests/test_error_scenario_tests.py | 24 ++++++++++++++++++++ tests/e2e/tests/test_smoke_tests.py | 4 +++- 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 tests/e2e/tests/test_error_scenario_tests.py diff --git a/tests/e2e/tests/test_error_scenario_tests.py b/tests/e2e/tests/test_error_scenario_tests.py new file mode 100644 index 000000000..e2929b80b --- /dev/null +++ b/tests/e2e/tests/test_error_scenario_tests.py @@ -0,0 +1,24 @@ +import http + +import pytest + + +@pytest.mark.smoketest +def test_check_for_missing_person(eligibility_client): + nhs_number = "1234567890" + + request_headers = {"authenticated_nhs_number": "1234567890"} + + expected_body = { + "issue": [{ + "code": "nhs-number-not-found", + "diagnostics": f'NHS Number "{nhs_number}" not found.', + "severity": "information" + }], + "resourceType": "OperationOutcome" + } + + response = eligibility_client.make_request(nhs_number, headers=request_headers, raise_on_error=False) + + assert response["status_code"] == http.HTTPStatus.NOT_FOUND + assert response["body"] == expected_body diff --git a/tests/e2e/tests/test_smoke_tests.py b/tests/e2e/tests/test_smoke_tests.py index 8e798e9f8..0df3c11eb 100644 --- a/tests/e2e/tests/test_smoke_tests.py +++ b/tests/e2e/tests/test_smoke_tests.py @@ -48,6 +48,8 @@ def test_run_smoke_case(filename, scenario, eligibility_client): def test_check_for_missing_person(eligibility_client): nhs_number = "1234567890" + request_headers = {"authenticated_nhs_number": "1234567890"}, + expected_body = { "issue": [{ "code": "nhs-number-not-found", @@ -57,7 +59,7 @@ def test_check_for_missing_person(eligibility_client): "resourceType": "OperationOutcome" } - response = eligibility_client.make_request(nhs_number, raise_on_error=False) + response = eligibility_client.make_request(nhs_number, headers= request_headers, raise_on_error=False) assert response["status_code"] == http.HTTPStatus.NOT_FOUND assert response["body"] == expected_body From 9ef7f40cdbcf3df369362c2d3a2dd0c7168f12f3 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Tue, 1 Jul 2025 21:48:40 +0100 Subject: [PATCH 22/72] adding new scenarios --- .../AUTO_RSV_REG_001.json | 207 ++++++++++++++++++ .../storyTestConfigs/AUTO_RSV_ELI-216.json | 9 - .../regressionTestData/AUTO_RSV_REG_001.json | 42 ++++ .../regressionTestData/AUTO_RSV_REG_002.json | 49 +++++ .../regressionTestData/AUTO_RSV_REG_003.json | 49 +++++ .../regressionTestData/AUTO_RSV_REG_010.json | 42 ++++ .../regressionTestData/AUTO_RSV_REG_011.json | 42 ++++ .../AUTO_RSV_ELI-216-2.json | 3 +- .../AUTO_RSV_ELI-216-3.json | 1 + .../AUTO_RSV_ELI-219-4.json | 1 + .../storyTestData/AUTO_RSV_ELI-155.json | 2 +- .../storyTestData/AUTO_RSV_ELI-216-1.json | 2 +- .../storyTestData/AUTO_RSV_ELI-219-1.json | 3 +- .../storyTestData/AUTO_RSV_ELI-219-2.json | 1 + .../storyTestData/AUTO_RSV_ELI-219-3.json | 1 + .../AUTO_RSV_REG_001.json} | 32 +-- .../AUTO_RSV_REG_002.json | 35 +++ .../AUTO_RSV_REG_003.json | 30 +++ .../AUTO_RSV_REG_010.json | 27 +++ .../AUTO_RSV_REG_011.json | 27 +++ .../AUTO_RSV_ELI-216-1.json | 30 +++ .../AUTO_RSV_ELI-216-2.json | 3 + .../AUTO_RSV_ELI-216-3.json | 3 + tests/e2e/tests/conftest.py | 5 +- tests/e2e/tests/test_config.py | 4 + tests/e2e/tests/test_error_scenario_tests.py | 64 +++++- tests/e2e/tests/test_regression_tests.py | 42 ++++ tests/e2e/tests/test_smoke_tests.py | 23 +- tests/e2e/tests/test_story_tests.py | 9 +- tests/e2e/utils/data_loader.py | 2 + tests/e2e/utils/dynamo_helper.py | 2 +- tests/e2e/utils/eligibilityApiClient.py | 37 ---- tests/e2e/utils/s3ConfigManager.py | 10 +- 33 files changed, 738 insertions(+), 101 deletions(-) create mode 100644 tests/e2e/data/configs/regressionTestConfigs/AUTO_RSV_REG_001.json create mode 100644 tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_001.json create mode 100644 tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_002.json create mode 100644 tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_003.json create mode 100644 tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_010.json create mode 100644 tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_011.json rename tests/e2e/data/dynamoDB/storyTestData/{ => .ignore_folder}/AUTO_RSV_ELI-216-2.json (92%) rename tests/e2e/data/dynamoDB/storyTestData/{ => .ignore_folder}/AUTO_RSV_ELI-216-3.json (97%) rename tests/e2e/data/dynamoDB/storyTestData/{ => .ignore_folder}/AUTO_RSV_ELI-219-4.json (95%) rename tests/e2e/data/responses/{storyTestResponses/AUTO_RSV_ELI-216.json => regressionTestResponses/AUTO_RSV_REG_001.json} (68%) create mode 100644 tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_002.json create mode 100644 tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_003.json create mode 100644 tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_010.json create mode 100644 tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_011.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-1.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-2.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-3.json create mode 100644 tests/e2e/tests/test_regression_tests.py diff --git a/tests/e2e/data/configs/regressionTestConfigs/AUTO_RSV_REG_001.json b/tests/e2e/data/configs/regressionTestConfigs/AUTO_RSV_REG_001.json new file mode 100644 index 000000000..861e0ae33 --- /dev/null +++ b/tests/e2e/data/configs/regressionTestConfigs/AUTO_RSV_REG_001.json @@ -0,0 +1,207 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "Automation RSV - Regression Test Config", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "DEFAULT_ACTION", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "DEFAULT_ACTION", + "Version": "1", + "Name": "Automation RSV - Regression Test Config Iteration", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "M", + "IterationCohorts": [ + { + "CohortLabel": "rsv_cohort_1", + "CohortGroup": "rsv_age_group", + "PositiveDescription": "You are in an age group 1.", + "NegativeDescription": "You are not in an age group 1.", + "Priority": 10 + }, + { + "CohortLabel": "rsv_cohort_2", + "CohortGroup": "rsv_age_group", + "PositiveDescription": "You are in an age group 2.", + "NegativeDescription": "You are not in an age group 2.", + "Priority": 0 + }, + { + "CohortLabel": "rsv_cohort_3", + "CohortGroup": "rsv_other_group", + "PositiveDescription": "You are in an another group.", + "NegativeDescription": "You are not in an other group.", + "Priority": 20 + }, + { + "CohortLabel": "elid_all_people", + "CohortGroup": "elid_all_people", + "PositiveDescription": "", + "NegativeDescription": "", + "Priority": 30 + }, + { + "CohortLabel": "no_group_description", + "CohortGroup": "", + "PositiveDescription": "", + "NegativeDescription": "", + "Priority": 40 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "Assure only already vaccinated taken from magic cohort", + "Description": "Exclude anyone who has NOT been given a dose of RSV Vaccination from the magic cohort", + "Operator": "is_empty", + "Comparator": "", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "elid_all_people", + "Priority": 100 + }, + { + "Type": "F", + "Name": "Under Age - Under 75 Years on day of execution", + "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", + "Priority": 120, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75" + }, + { + "Type": "F", + "Name": "Exclude Too OLD", + "Description": "Exclude anyone over 80", + "Priority": 130, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "<", + "Comparator": "-80" + }, + { + "Type": "S", + "Name": "AlreadyVaccinated", + "Description": "##You've had your RSV vaccination\nWe believe you had your vaccination on <>.", + "Priority": 500, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "Y>=", + "Comparator": "-25", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "NotAvailable", + "Description": "NotAvailable|Vaccinations are not currently available.", + "Priority": 510, + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "Operator": "=", + "Comparator": "SUPPRESSED_ICB" + }, + { + "Type": "R", + "Name": "Actionable Not Vaccinated", + "Description": "Book An Appointment", + "Priority": 1010, + "Operator": "is_empty", + "Comparator": "", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CommsRouting": "BOOK_NBS" + }, + { + "Type": "R", + "Name": "Actionable Not Vaccinated", + "Description": "Book An Appointment", + "Priority": 1010, + "Operator": "=", + "Comparator": "LS2", + "AttributeLevel": "PERSON", + "AttributeName": "POSTCODE_SECTOR", + "CommsRouting": "BOOK_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1020, + "Operator": ">=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1020, + "Operator": "=", + "Comparator": "NBS", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1030, + "Operator": ">=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "MANAGE_LOCAL" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1030, + "Operator": "!=", + "Comparator": "NBS", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", + "CommsRouting": "MANAGE_LOCAL" + } + ], + "ActionsMapper": { + "DEFAULT_ACTION": { + "ExternalRoutingCode": "DefaultAction", + "ActionDescription": "DefaultActionDescription", + "ActionType": "DefaultActionType", + "UrlLink": "DefaultActionUrl", + "UrlLabel": "DefaultLabel" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json index b3395986e..33cba2f1a 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json @@ -61,15 +61,6 @@ "Operator": "is_not_null", "Comparator": "", "CommsRouting": "ACTION_ONE" - }, - { - "Type": "R", - "Name": "Actionable if future appointment is booked with ACC", - "Description": "Book An Appointment", - "Priority": 1000, - "AttributeLevel": "COHORT", - "Operator": "in", - "CommsRouting": "ACTION_ONE" } ], "ActionsMapper": { diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_001.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_001.json new file mode 100644 index 000000000..b1f7a93e4 --- /dev/null +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_001.json @@ -0,0 +1,42 @@ +{ + "scenario_name": "RSV - Regression - Actionable, Single Eligible Cohort", + "request_headers": {"nhs-login-nhs-number": "1000000001"}, + "config_filename": "AUTO_RSV_REG_001.json", + "data": [ + { + "NHS_NUMBER": "1000000001", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_cohort_2": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "1000000001", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS2", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_002.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_002.json new file mode 100644 index 000000000..2d03e4cc1 --- /dev/null +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_002.json @@ -0,0 +1,49 @@ +{ + "scenario_name": "RSV - Regression - Actionable, Two Eligible Cohorts, Different Groups", + "request_headers": {"nhs-login-nhs-number": "1000000002"}, + "config_filename": "AUTO_RSV_REG_001.json", + "data": [ + { + "NHS_NUMBER": "1000000002", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_cohort_2": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + }, + "rsv_cohort_3": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "1000000002", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS2", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_003.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_003.json new file mode 100644 index 000000000..fa9b12fac --- /dev/null +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_003.json @@ -0,0 +1,49 @@ +{ + "scenario_name": "RSV - Regression - Actionable, Two Eligible Cohorts, Same Group", + "request_headers": {"nhs-login-nhs-number": "1000000003"}, + "config_filename": "AUTO_RSV_REG_001.json", + "data": [ + { + "NHS_NUMBER": "1000000003", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_cohort_2": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + }, + "rsv_cohort_1": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "1000000003", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS2", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_010.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_010.json new file mode 100644 index 000000000..265428f30 --- /dev/null +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_010.json @@ -0,0 +1,42 @@ +{ + "scenario_name": "RSV - Regression - Not Eligible, Not in Cohort", + "request_headers": {"nhs-login-nhs-number": "1000000010"}, + "config_filename": "AUTO_RSV_REG_001.json", + "data": [ + { + "NHS_NUMBER": "1000000010", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_cohort_22": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "1000000010", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS2", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_011.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_011.json new file mode 100644 index 000000000..7a7699f97 --- /dev/null +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_011.json @@ -0,0 +1,42 @@ +{ + "scenario_name": "RSV - Regression - Not Eligible, In Cohort, Filter Rule", + "request_headers": {"nhs-login-nhs-number": "1000000011"}, + "config_filename": "AUTO_RSV_REG_001.json", + "data": [ + { + "NHS_NUMBER": "1000000011", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_cohort_1": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "1000000011", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS2", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-2.json b/tests/e2e/data/dynamoDB/storyTestData/.ignore_folder/AUTO_RSV_ELI-216-2.json similarity index 92% rename from tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-2.json rename to tests/e2e/data/dynamoDB/storyTestData/.ignore_folder/AUTO_RSV_ELI-216-2.json index c0f878741..e6f6b01d0 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-2.json +++ b/tests/e2e/data/dynamoDB/storyTestData/.ignore_folder/AUTO_RSV_ELI-216-2.json @@ -1,7 +1,8 @@ { "scenario_name": "ELI-216 - NHS Number check (NHS login) - Incorrect NHS_number", "config_filename": "AUTO_RSV_ELI-216.json", - "request_headers": {"authenticated_nhs_number": "9000000001"}, + "request_headers": {"nhs-login-nhs-number": "9000000001"}, + "expected_response_code": 403, "data": [ { "NHS_NUMBER": "9000000006", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-3.json b/tests/e2e/data/dynamoDB/storyTestData/.ignore_folder/AUTO_RSV_ELI-216-3.json similarity index 97% rename from tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-3.json rename to tests/e2e/data/dynamoDB/storyTestData/.ignore_folder/AUTO_RSV_ELI-216-3.json index 6de7ffeea..15e70cdc5 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-3.json +++ b/tests/e2e/data/dynamoDB/storyTestData/.ignore_folder/AUTO_RSV_ELI-216-3.json @@ -2,6 +2,7 @@ "scenario_name": "ELI-216 - NHS Number check (NHS login) - No Header", "config_filename": "AUTO_RSV_ELI-216.json", "request_headers": {}, + "expected_response_code": 403, "data": [ { "NHS_NUMBER": "9000000006", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-4.json b/tests/e2e/data/dynamoDB/storyTestData/.ignore_folder/AUTO_RSV_ELI-219-4.json similarity index 95% rename from tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-4.json rename to tests/e2e/data/dynamoDB/storyTestData/.ignore_folder/AUTO_RSV_ELI-219-4.json index 83aba304f..37f291ca5 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-4.json +++ b/tests/e2e/data/dynamoDB/storyTestData/.ignore_folder/AUTO_RSV_ELI-219-4.json @@ -1,6 +1,7 @@ { "scenario_name": "ELI-219 - Testing the new requirement from Vita around empty actions ", "config_filename": "AUTO_RSV_ELI-219-4.json", + "request_headers": {"nhs-login-nhs-number": "9000000005"}, "data": [ { "NHS_NUMBER": "9000000005", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json index 949761440..998ef48a2 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json @@ -1,7 +1,7 @@ { "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested actions (with booking)", "config_filename": "AUTO_RSV_ELI-155.json", - "request_headers": {"authenticated_nhs_number": "9000000001"}, + "request_headers": {"nhs-login-nhs-number": "9000000001"}, "data": [ { "NHS_NUMBER": "9000000001", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-1.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-1.json index 60f0c706b..e2c7dd67c 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-1.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-1.json @@ -1,7 +1,7 @@ { "scenario_name": "ELI-216 - NHS Number check (NHS login) - Correct NHS_number", "config_filename": "AUTO_RSV_ELI-216.json", - "request_headers": {"authenticated_nhs_number": "9000000006"}, + "request_headers": {"nhs-login-nhs-number": "9000000006"}, "data": [ { "NHS_NUMBER": "9000000006", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json index 13c517845..2e30eabf2 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json @@ -1,6 +1,7 @@ { "scenario_name": "ELI-219 - Fails and Rules and Returns Default", - "config_filename": "AUTO_RSV_ELI-21-91.json", + "config_filename": "AUTO_RSV_ELI-219-1.json", + "request_headers": {"nhs-login-nhs-number": "9000000002"}, "data": [ { "NHS_NUMBER": "9000000002", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-2.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-2.json index 69ec4e273..cccfac6d1 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-2.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-2.json @@ -1,6 +1,7 @@ { "scenario_name": "ELI-219 - Returns the highest Priority Action", "config_filename": "AUTO_RSV_ELI-219-2.json", + "request_headers": {"nhs-login-nhs-number": "9000000003"}, "data": [ { "NHS_NUMBER": "9000000003", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-3.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-3.json index 952328aa2..f12db18d8 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-3.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-3.json @@ -1,6 +1,7 @@ { "scenario_name": "ELI-219 - Returns the highest Priority Action", "config_filename": "AUTO_RSV_ELI-219-3.json", + "request_headers": {"nhs-login-nhs-number": "9000000004"}, "data": [ { "NHS_NUMBER": "9000000004", diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216.json b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_001.json similarity index 68% rename from tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216.json rename to tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_001.json index 1e064316c..138a349a1 100644 --- a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216.json +++ b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_001.json @@ -1,30 +1,30 @@ { - "responseId": "<>", "meta": { - "lastUpdated": "<>" + "lastUpdated": "2025-07-01T19:52:24.529127+00:00" }, "processedSuggestions": [ { - "condition": "RSV", - "status": "Actionable", - "statusText": "Status.actionable", - "eligibilityCohorts": [ - { - "cohortCode": "eli_216_cohort_group", - "cohortStatus": "Actionable", - "cohortText": "You are currently in eli_216_cohort" - } - ], - "suitabilityRules": [], "actions": [ { - "action_type": "DefaultActionType", "action_code": "DefaultAction", "action_description": "DefaultActionDescription", + "action_type": "DefaultActionType", "url_label": "DefaultLabel", "url_link": "DefaultActionUrl" } - ] + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_group", + "cohortStatus": "Actionable", + "cohortText": "You are in an age group 2." + } + ], + "status": "Actionable", + "statusText": "Status.actionable", + "suitabilityRules": [] } - ] + ], + "responseId": "a53d2e37-7e25-4edd-99c1-9f487c95b347" } diff --git a/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_002.json b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_002.json new file mode 100644 index 000000000..cbdbadda3 --- /dev/null +++ b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_002.json @@ -0,0 +1,35 @@ +{ + "meta": { + "lastUpdated": "2025-07-01T19:57:39.626808+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "action_code": "DefaultAction", + "action_description": "DefaultActionDescription", + "action_type": "DefaultActionType", + "url_label": "DefaultLabel", + "url_link": "DefaultActionUrl" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_group", + "cohortStatus": "Actionable", + "cohortText": "You are in an age group 2." + }, + { + "cohortCode": "rsv_other_group", + "cohortStatus": "Actionable", + "cohortText": "You are in an another group." + } + ], + "status": "Actionable", + "statusText": "Status.actionable", + "suitabilityRules": [] + } + ], + "responseId": "c01bcba2-7ae3-4346-bb03-416dd1e307f8" +} diff --git a/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_003.json b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_003.json new file mode 100644 index 000000000..e7ef07bd5 --- /dev/null +++ b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_003.json @@ -0,0 +1,30 @@ +{ + "meta": { + "lastUpdated": "2025-07-01T19:57:39.626808+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "action_code": "DefaultAction", + "action_description": "DefaultActionDescription", + "action_type": "DefaultActionType", + "url_label": "DefaultLabel", + "url_link": "DefaultActionUrl" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_group", + "cohortStatus": "Actionable", + "cohortText": "You are in an age group 2." + } + ], + "status": "Actionable", + "statusText": "Status.actionable", + "suitabilityRules": [] + } + ], + "responseId": "c01bcba2-7ae3-4346-bb03-416dd1e307f8" +} diff --git a/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_010.json b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_010.json new file mode 100644 index 000000000..1030e0ffb --- /dev/null +++ b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_010.json @@ -0,0 +1,27 @@ +{ + "meta": { + "lastUpdated": "2025-07-01T20:20:47.764973+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not in an age group 2." + }, + { + "cohortCode": "rsv_other_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not in an other group." + } + ], + "status": "NotEligible", + "statusText": "Status.not_eligible", + "suitabilityRules": [] + } + ], + "responseId": "49db2d41-326f-45d4-afaf-0dd6897413af" +} diff --git a/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_011.json b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_011.json new file mode 100644 index 000000000..1030e0ffb --- /dev/null +++ b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_011.json @@ -0,0 +1,27 @@ +{ + "meta": { + "lastUpdated": "2025-07-01T20:20:47.764973+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not in an age group 2." + }, + { + "cohortCode": "rsv_other_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not in an other group." + } + ], + "status": "NotEligible", + "statusText": "Status.not_eligible", + "suitabilityRules": [] + } + ], + "responseId": "49db2d41-326f-45d4-afaf-0dd6897413af" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-1.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-1.json new file mode 100644 index 000000000..46781b583 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-1.json @@ -0,0 +1,30 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "Actionable", + "statusText": "Status.actionable", + "eligibilityCohorts": [ + { + "cohortCode": "eli_216_cohort_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in eli_216_cohort" + } + ], + "suitabilityRules": [], + "actions": [ + { + "action_code": "ActionOneRoutingCode", + "action_description": "ActionOneDescription", + "action_type": "ActionOneActionType", + "url_label": "ActionOneUrlLabel", + "url_link": "http://www.actiononeurl.com" + } + ] + } + ] +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-2.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-2.json new file mode 100644 index 000000000..d959499f3 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-2.json @@ -0,0 +1,3 @@ +{ + "response_items": "NHS number mismatch" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-3.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-3.json new file mode 100644 index 000000000..d959499f3 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-3.json @@ -0,0 +1,3 @@ +{ + "response_items": "NHS number mismatch" +} diff --git a/tests/e2e/tests/conftest.py b/tests/e2e/tests/conftest.py index 40549aef7..842407c9a 100644 --- a/tests/e2e/tests/conftest.py +++ b/tests/e2e/tests/conftest.py @@ -13,7 +13,7 @@ load_dotenv(dotenv_path=".env.local") # Constants -BASE_URL = os.getenv("BASE_URL", "https://sandbox.api.service.nhs.uk/eligibility-signposting-api") +BASE_URL = os.getenv("BASE_URL", "https://test.eligibility-signposting-api.nhs.uk/patient-check") API_KEY = os.getenv("API_KEY", "") VALID_NHS_NUMBER = os.getenv("VALID_NHS_NUMBER", "50000000004") DYNAMODB_TABLE_NAME = os.getenv("DYNAMODB_TABLE_NAME", "eligibility_data_store") @@ -84,5 +84,4 @@ def setup_dynamodb_data(request): @pytest.fixture(scope="session") def eligibility_client(): - base_url = "https://test.eligibility-signposting-api.nhs.uk/patient-check" - return EligibilityApiClient(base_url) + return EligibilityApiClient(BASE_URL) diff --git a/tests/e2e/tests/test_config.py b/tests/e2e/tests/test_config.py index 04d26bd92..34344e71b 100644 --- a/tests/e2e/tests/test_config.py +++ b/tests/e2e/tests/test_config.py @@ -10,3 +10,7 @@ STORY_TEST_DATA: str = "tests/e2e/data/dynamoDB/storyTestData/" STORY_TEST_RESPONSES: str = "tests/e2e/data/responses/storyTestResponses/" STORY_TEST_CONFIGS: str = "tests/e2e/data/configs/storyTestConfigs" +# Story Test Data Paths +REGRESSION_TEST_DATA: str = "tests/e2e/data/dynamoDB/regressionTestData/" +REGRESSION_RESPONSES: str = "tests/e2e/data/responses/regressionTestResponses/" +REGRESSION_CONFIGS: str = "tests/e2e/data/configs/regressionTestConfigs" diff --git a/tests/e2e/tests/test_error_scenario_tests.py b/tests/e2e/tests/test_error_scenario_tests.py index e2929b80b..b242fa25b 100644 --- a/tests/e2e/tests/test_error_scenario_tests.py +++ b/tests/e2e/tests/test_error_scenario_tests.py @@ -1,5 +1,4 @@ import http - import pytest @@ -7,7 +6,7 @@ def test_check_for_missing_person(eligibility_client): nhs_number = "1234567890" - request_headers = {"authenticated_nhs_number": "1234567890"} + request_headers = {"nhs-login-nhs-number": "1234567890"} expected_body = { "issue": [{ @@ -22,3 +21,64 @@ def test_check_for_missing_person(eligibility_client): assert response["status_code"] == http.HTTPStatus.NOT_FOUND assert response["body"] == expected_body + + + +@pytest.mark.smoketest +@pytest.mark.parametrize( + "scenario, nhs_number, request_headers, expected_status, expected_body", + [ + ( + "correct header - NHS number exists but not found in data", + "1234567890", + {"nhs-login-nhs-number": "1234567890"}, + http.HTTPStatus.NOT_FOUND, + { + "issue": [{ + "code": "nhs-number-not-found", + "diagnostics": 'NHS Number "1234567890" not found.', + "severity": "information" + }], + "resourceType": "OperationOutcome" + }, + ), + ( + "incorrect header - NHS number mismatch", + "1234567890", + {"nhs-login-nhs-number": "12345678900"}, + http.HTTPStatus.FORBIDDEN, + "NHS number mismatch", + ), + ( + "missing header - NHS number required", + "1234567890", + {}, + http.HTTPStatus.FORBIDDEN, + "NHS number mismatch", + ), + ], + ids=[ + "correct-header", + "incorrect-header", + "missing-header" + ] +) +def test_nhs_login_header_handling( + eligibility_client, + scenario, + nhs_number, + request_headers, + expected_status, + expected_body +): + response = eligibility_client.make_request( + nhs_number, + headers=request_headers, + raise_on_error=False + ) + + response_ststus = response["status_code"] + + assert response["status_code"] == expected_status, f"{scenario} failed on status code" + assert response["body"] == expected_body, f"{scenario} failed on response body" + diff --git a/tests/e2e/tests/test_regression_tests.py b/tests/e2e/tests/test_regression_tests.py new file mode 100644 index 000000000..0f8e2419f --- /dev/null +++ b/tests/e2e/tests/test_regression_tests.py @@ -0,0 +1,42 @@ +import http +import os + +import pytest + +from tests.e2e.tests.test_config import * +from tests.e2e.utils.data_loader import load_all_expected_responses, initialise_tests +from tests.e2e.utils.s3ConfigManager import S3ConfigManager + +# Update the below with the configuration values specified in test_config.py +all_data, dto = initialise_tests(REGRESSION_TEST_DATA) +all_expected_responses = load_all_expected_responses(REGRESSION_RESPONSES) +config_path = REGRESSION_CONFIGS + +s3_manager = S3ConfigManager(S3_BUCKET, S3_PREFIX) + +param_list = list(all_data.items()) +id_list = [ + f"{filename} - {scenario.get('scenario_name', 'No Scenario')}" + for filename, scenario in param_list +] + + +@pytest.mark.regressiontest +@pytest.mark.parametrize("filename, scenario", param_list, ids=id_list) +def test_run_regression_tests(filename, scenario, eligibility_client): + nhs_number = scenario["nhs_number"] + config_filename = scenario.get("config_filename", "") + request_headers = scenario.get("request_headers", {}) + s3_manager.upload_if_missing_or_changed(os.path.abspath(os.path.join(config_path, config_filename))) + + actual_response = eligibility_client.make_request(nhs_number, headers= request_headers, strict_ssl=False) + expected_response = all_expected_responses.get(filename).get("response_items", {}) + + # Assert and show details on failure + assert actual_response["status_code"] == http.HTTPStatus.OK + assert actual_response["body"] == expected_response, ( + f"\nโŒ Mismatch in test: {filename}\n" + f"NHS Number: {nhs_number}\n" + f"Expected: {expected_response}\n" + f"Actual: {actual_response}\n" + ) diff --git a/tests/e2e/tests/test_smoke_tests.py b/tests/e2e/tests/test_smoke_tests.py index 0df3c11eb..34888cf9e 100644 --- a/tests/e2e/tests/test_smoke_tests.py +++ b/tests/e2e/tests/test_smoke_tests.py @@ -35,31 +35,10 @@ def test_run_smoke_case(filename, scenario, eligibility_client): expected_response = all_expected_responses.get(filename).get("response_items", {}) # Assert and show details on failure - assert actual_response["status_code"] == 200 + assert actual_response["status_code"] == http.HTTPStatus.OK assert actual_response["body"] == expected_response, ( f"\nโŒ Mismatch in test: {filename}\n" f"NHS Number: {nhs_number}\n" f"Expected: {expected_response}\n" f"Actual: {actual_response}\n" ) - - -@pytest.mark.smoketest -def test_check_for_missing_person(eligibility_client): - nhs_number = "1234567890" - - request_headers = {"authenticated_nhs_number": "1234567890"}, - - expected_body = { - "issue": [{ - "code": "nhs-number-not-found", - "diagnostics": f'NHS Number "{nhs_number}" not found.', - "severity": "information" - }], - "resourceType": "OperationOutcome" - } - - response = eligibility_client.make_request(nhs_number, headers= request_headers, raise_on_error=False) - - assert response["status_code"] == http.HTTPStatus.NOT_FOUND - assert response["body"] == expected_body diff --git a/tests/e2e/tests/test_story_tests.py b/tests/e2e/tests/test_story_tests.py index aab7c6f2e..fb4e74e01 100644 --- a/tests/e2e/tests/test_story_tests.py +++ b/tests/e2e/tests/test_story_tests.py @@ -27,13 +27,18 @@ def test_run_story_test_cases(filename, scenario, eligibility_client): nhs_number = scenario["nhs_number"] config_filename = scenario.get("config_filename", "") request_headers = scenario.get("request_headers", {}) + expected_response_code = scenario.get("expected_response_code", None) s3_manager.upload_if_missing_or_changed(os.path.abspath(os.path.join(config_path, config_filename))) - actual_response = eligibility_client.make_request(nhs_number, strict_ssl=False) + actual_response = eligibility_client.make_request(nhs_number, headers=request_headers, strict_ssl=False) expected_response = all_expected_responses.get(filename).get("response_items", {}) # Assert and show details on failure - assert actual_response["status_code"] == http.HTTPStatus.OK + if expected_response_code: + assert actual_response["status_code"] == expected_response_code + else: + assert actual_response["status_code"] == http.HTTPStatus.OK + assert actual_response["body"] == expected_response, ( f"\nโŒ Mismatch in test: {filename}\n" f"NHS Number: {nhs_number}\n" diff --git a/tests/e2e/utils/data_loader.py b/tests/e2e/utils/data_loader.py index f1e472754..3f4a5c9a0 100644 --- a/tests/e2e/utils/data_loader.py +++ b/tests/e2e/utils/data_loader.py @@ -105,6 +105,7 @@ def load_all_test_scenarios(folder_path, config_folder_path="tests/e2e/data/conf config_filename = raw_json.get("config_filename") scenario_name = raw_json.get("scenario_name") request_headers = raw_json.get("request_headers") + expected_response_code = raw_json.get("expected_response_code") # Resolve placeholders with shared DTO resolved_data = resolve_placeholders_in_data(raw_data, dto, filename) @@ -117,6 +118,7 @@ def load_all_test_scenarios(folder_path, config_folder_path="tests/e2e/data/conf "dynamo_items": resolved_data, "nhs_number": nhs_number, "config_filename": config_filename, + "expected_response_code": expected_response_code, "request_headers": request_headers, "scenario_name": scenario_name, } diff --git a/tests/e2e/utils/dynamo_helper.py b/tests/e2e/utils/dynamo_helper.py index 2202cb85f..3dcc72886 100644 --- a/tests/e2e/utils/dynamo_helper.py +++ b/tests/e2e/utils/dynamo_helper.py @@ -50,7 +50,7 @@ def get_item(self, key: dict): def insert_into_dynamo(data): - print("Inserting into Dynamo (fake):", data) + print("Inserting into Dynamo:", data) table = DynamoDBHelper("eligibility-signposting-api-test-eligibility_datastore") for item in data: try: diff --git a/tests/e2e/utils/eligibilityApiClient.py b/tests/e2e/utils/eligibilityApiClient.py index 4614ef3bf..98e35f1b6 100644 --- a/tests/e2e/utils/eligibilityApiClient.py +++ b/tests/e2e/utils/eligibilityApiClient.py @@ -102,43 +102,6 @@ def make_request( return self._parse_response(response) raise RuntimeError(f"Request error: {req_err}") from req_err - def make_request_old( - self, - nhs_number: str, - method: str = "GET", - payload: Optional[Union[Dict[str, Any], list]] = None, - strict_ssl: bool = False, - raise_on_error: bool = True, - ) -> Dict[str, Any]: - url = f"{self.api_url.rstrip('/')}/{nhs_number}" - cert = ( - str(self.cert_paths["client_cert"]), - str(self.cert_paths["private_key"]), - ) - verify: Union[bool, str] = str(self.cert_paths["ca_cert"]) if strict_ssl else False - - try: - response = requests.request( - method=method.upper(), - url=url, - cert=cert, - verify=verify, - json=payload, - timeout=10, - ) - - if raise_on_error: - response.raise_for_status() - - return self._parse_response(response) - - except requests.exceptions.SSLError as ssl_err: - raise RuntimeError(f"SSL error during request: {ssl_err}") from ssl_err - except requests.exceptions.RequestException as req_err: - response = getattr(req_err, "response", None) - if isinstance(response, Response): - return self._parse_response(response) - raise RuntimeError(f"Request error: {req_err}") from req_err def _parse_response(self, response: Response) -> Dict[str, Any]: try: diff --git a/tests/e2e/utils/s3ConfigManager.py b/tests/e2e/utils/s3ConfigManager.py index 22d2df7d5..9ca6b5f9a 100644 --- a/tests/e2e/utils/s3ConfigManager.py +++ b/tests/e2e/utils/s3ConfigManager.py @@ -33,26 +33,26 @@ def upload_if_missing_or_changed(self, local_path: str) -> None: try: if self.config_exists_and_matches(local_path, s3_key): print( - f"โœ… Config '{filename}' already exists and matches in S3. " + f"\n๐Ÿ” Config '{filename}' already exists and matches in S3. " "Skipping upload." ) return print( - f"๐Ÿงน A different config exists under '{self.s3_prefix}/'. " + f"\n๐Ÿงน A different config exists under '{self.s3_prefix}/'. " "Deleting all existing files..." ) self._delete_all_in_prefix() except self.s3_client.exceptions.NoSuchKey: - print(f"๐Ÿ†• No config found under '{self.s3_prefix}/'. Proceeding to upload.") + print(f"\n๐Ÿ†• No config found under '{self.s3_prefix}/'. Proceeding to upload.") except botocore.exceptions.ClientError as error: if error.response.get("Error", {}).get("Code") == "NoSuchKey": - print(f"๐Ÿ†• No config found under '{self.s3_prefix}/'. Proceeding to upload.") + print(f"\n๐Ÿ†• No config found under '{self.s3_prefix}/'. Proceeding to upload.") else: raise print(f"โฌ†๏ธ Uploading new config '{filename}' to S3...") self.s3_client.upload_file(local_path, self.bucket_name, s3_key) - print(f"โœ… Uploaded to s3://{self.bucket_name}/{s3_key}") + print(f"๐Ÿ“„ Uploaded to s3://{self.bucket_name}/{s3_key}") def config_exists_and_matches(self, local_path: str, s3_key: str) -> bool: try: From 142ede70434c6083d864a6b411b2fe5b09f9b28e Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Wed, 2 Jul 2025 15:57:44 +0100 Subject: [PATCH 23/72] updates to fix 90+ formatting and linting issues :) --- .../smokeTestData/AUTO_RSV_SB_001.json | 1 + .../smokeTestData/AUTO_RSV_SB_002.json | 1 + .../smokeTestData/AUTO_RSV_SB_003.json | 1 + tests/e2e/tests/conftest.py | 2 +- tests/e2e/tests/test_error_scenario_tests.py | 102 ++++++++---------- tests/e2e/tests/test_regression_tests.py | 27 +++-- tests/e2e/tests/test_smoke_tests.py | 31 +++--- tests/e2e/tests/test_story_tests.py | 25 ++--- tests/e2e/utils/data_loader.py | 53 ++++----- tests/e2e/utils/dynamo_helper.py | 29 ++--- ...ApiClient.py => eligibility_api_client.py} | 61 +++++------ tests/e2e/utils/placeholder_context.py | 2 +- tests/e2e/utils/placeholder_utils.py | 87 +++++++++------ ...3ConfigManager.py => s3_config_manager.py} | 53 ++++----- 14 files changed, 231 insertions(+), 244 deletions(-) rename tests/e2e/utils/{eligibilityApiClient.py => eligibility_api_client.py} (68%) rename tests/e2e/utils/{s3ConfigManager.py => s3_config_manager.py} (55%) diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json index 429c0c9da..d462e22a9 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json @@ -1,5 +1,6 @@ { "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested actions (with booking)", + "request_headers": {"nhs-login-nhs-number": "5000000001"}, "config_filename": "AUTO_RSV_SB_001.json", "data": [ { diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json index fa00e7ef8..6e2d77050 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json @@ -1,5 +1,6 @@ { "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested action (not booking)", + "request_headers": {"nhs-login-nhs-number": "5000000002"}, "config_filename": "AUTO_RSV_SB_001.json", "data": [ { diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json index dbece05d1..c5ab64bdb 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json @@ -1,5 +1,6 @@ { "scenario_name": "RSV - Actionable due to membership of an alternative Age Cohort incl. suggested action", + "request_headers": {"nhs-login-nhs-number": "5000000003"}, "config_filename": "AUTO_RSV_SB_001.json", "data": [ { diff --git a/tests/e2e/tests/conftest.py b/tests/e2e/tests/conftest.py index 842407c9a..2814eaad5 100644 --- a/tests/e2e/tests/conftest.py +++ b/tests/e2e/tests/conftest.py @@ -7,7 +7,7 @@ import pytest from dotenv import load_dotenv -from tests.e2e.utils.eligibilityApiClient import EligibilityApiClient +from tests.e2e.utils.eligibility_api_client import EligibilityApiClient # Load environment variables from .env.local load_dotenv(dotenv_path=".env.local") diff --git a/tests/e2e/tests/test_error_scenario_tests.py b/tests/e2e/tests/test_error_scenario_tests.py index b242fa25b..25956058f 100644 --- a/tests/e2e/tests/test_error_scenario_tests.py +++ b/tests/e2e/tests/test_error_scenario_tests.py @@ -1,4 +1,5 @@ import http + import pytest @@ -9,12 +10,14 @@ def test_check_for_missing_person(eligibility_client): request_headers = {"nhs-login-nhs-number": "1234567890"} expected_body = { - "issue": [{ - "code": "nhs-number-not-found", - "diagnostics": f'NHS Number "{nhs_number}" not found.', - "severity": "information" - }], - "resourceType": "OperationOutcome" + "issue": [ + { + "code": "nhs-number-not-found", + "diagnostics": f'NHS Number "{nhs_number}" not found.', + "severity": "information", + } + ], + "resourceType": "OperationOutcome", } response = eligibility_client.make_request(nhs_number, headers=request_headers, raise_on_error=False) @@ -23,62 +26,49 @@ def test_check_for_missing_person(eligibility_client): assert response["body"] == expected_body - @pytest.mark.smoketest @pytest.mark.parametrize( - "scenario, nhs_number, request_headers, expected_status, expected_body", + "test_case", [ - ( - "correct header - NHS number exists but not found in data", - "1234567890", - {"nhs-login-nhs-number": "1234567890"}, - http.HTTPStatus.NOT_FOUND, - { - "issue": [{ - "code": "nhs-number-not-found", - "diagnostics": 'NHS Number "1234567890" not found.', - "severity": "information" - }], - "resourceType": "OperationOutcome" + { + "scenario": "correct header - NHS number exists but not found in data", + "nhs_number": "1234567890", + "request_headers": {"nhs-login-nhs-number": "1234567890"}, + "expected_status": http.HTTPStatus.NOT_FOUND, + "expected_body": { + "issue": [ + { + "code": "nhs-number-not-found", + "diagnostics": 'NHS Number "1234567890" not found.', + "severity": "information", + } + ], + "resourceType": "OperationOutcome", }, - ), - ( - "incorrect header - NHS number mismatch", - "1234567890", - {"nhs-login-nhs-number": "12345678900"}, - http.HTTPStatus.FORBIDDEN, - "NHS number mismatch", - ), - ( - "missing header - NHS number required", - "1234567890", - {}, - http.HTTPStatus.FORBIDDEN, - "NHS number mismatch", - ), + }, + { + "scenario": "incorrect header - NHS number mismatch", + "nhs_number": "1234567890", + "request_headers": {"nhs-login-nhs-number": "12345678900"}, + "expected_status": http.HTTPStatus.FORBIDDEN, + "expected_body": "NHS number mismatch", + }, + { + "scenario": "missing header - NHS number required", + "nhs_number": "1234567890", + "request_headers": {}, + "expected_status": http.HTTPStatus.FORBIDDEN, + "expected_body": "NHS number mismatch", + }, ], - ids=[ - "correct-header", - "incorrect-header", - "missing-header" - ] + ids=["correct-header", "incorrect-header", "missing-header"], ) -def test_nhs_login_header_handling( - eligibility_client, - scenario, - nhs_number, - request_headers, - expected_status, - expected_body -): +def test_nhs_login_header_handling(eligibility_client, test_case): response = eligibility_client.make_request( - nhs_number, - headers=request_headers, - raise_on_error=False + test_case["nhs_number"], + headers=test_case["request_headers"], + raise_on_error=False, ) - response_ststus = response["status_code"] - - assert response["status_code"] == expected_status, f"{scenario} failed on status code" - assert response["body"] == expected_body, f"{scenario} failed on response body" - + assert response["status_code"] == test_case["expected_status"], f"{test_case['scenario']} failed on status code" + assert response["body"] == test_case["expected_body"], f"{test_case['scenario']} failed on response body" diff --git a/tests/e2e/tests/test_regression_tests.py b/tests/e2e/tests/test_regression_tests.py index 0f8e2419f..f6b062811 100644 --- a/tests/e2e/tests/test_regression_tests.py +++ b/tests/e2e/tests/test_regression_tests.py @@ -1,35 +1,32 @@ import http -import os +from pathlib import Path import pytest -from tests.e2e.tests.test_config import * -from tests.e2e.utils.data_loader import load_all_expected_responses, initialise_tests -from tests.e2e.utils.s3ConfigManager import S3ConfigManager +from tests.e2e.tests import test_config +from tests.e2e.utils.data_loader import initialise_tests, load_all_expected_responses +from tests.e2e.utils.s3_config_manager import S3ConfigManager # Update the below with the configuration values specified in test_config.py -all_data, dto = initialise_tests(REGRESSION_TEST_DATA) -all_expected_responses = load_all_expected_responses(REGRESSION_RESPONSES) -config_path = REGRESSION_CONFIGS +all_data, dto = initialise_tests(test_config.REGRESSION_TEST_DATA) +all_expected_responses = load_all_expected_responses(test_config.REGRESSION_RESPONSES) +config_path = test_config.REGRESSION_CONFIGS -s3_manager = S3ConfigManager(S3_BUCKET, S3_PREFIX) +s3_manager = S3ConfigManager(test_config.S3_BUCKET, test_config.S3_PREFIX) param_list = list(all_data.items()) -id_list = [ - f"{filename} - {scenario.get('scenario_name', 'No Scenario')}" - for filename, scenario in param_list -] +id_list = [f"{filename} - {scenario.get('scenario_name', 'No Scenario')}" for filename, scenario in param_list] @pytest.mark.regressiontest -@pytest.mark.parametrize("filename, scenario", param_list, ids=id_list) +@pytest.mark.parametrize(("filename", "scenario"), param_list, ids=id_list) def test_run_regression_tests(filename, scenario, eligibility_client): nhs_number = scenario["nhs_number"] config_filename = scenario.get("config_filename", "") request_headers = scenario.get("request_headers", {}) - s3_manager.upload_if_missing_or_changed(os.path.abspath(os.path.join(config_path, config_filename))) + s3_manager.upload_if_missing_or_changed((Path(config_path) / config_filename).resolve()) - actual_response = eligibility_client.make_request(nhs_number, headers= request_headers, strict_ssl=False) + actual_response = eligibility_client.make_request(nhs_number, headers=request_headers, strict_ssl=False) expected_response = all_expected_responses.get(filename).get("response_items", {}) # Assert and show details on failure diff --git a/tests/e2e/tests/test_smoke_tests.py b/tests/e2e/tests/test_smoke_tests.py index 34888cf9e..85f5e01d4 100644 --- a/tests/e2e/tests/test_smoke_tests.py +++ b/tests/e2e/tests/test_smoke_tests.py @@ -1,37 +1,32 @@ import http -import os +from pathlib import Path import pytest -from tests.e2e.tests.test_config import * -from tests.e2e.utils.data_loader import load_all_expected_responses, initialise_tests -from tests.e2e.utils.s3ConfigManager import S3ConfigManager +from tests.e2e.tests import test_config +from tests.e2e.utils.data_loader import initialise_tests, load_all_expected_responses +from tests.e2e.utils.s3_config_manager import S3ConfigManager # Update the below with the configuration values specified in test_config.py -all_data, dto = initialise_tests(SMOKE_TEST_DATA) -all_expected_responses = load_all_expected_responses(SMOKE_TEST_RESPONSES) -config_path = SMOKE_TEST_CONFIGS +all_data, dto = initialise_tests(test_config.SMOKE_TEST_DATA) +all_expected_responses = load_all_expected_responses(test_config.SMOKE_TEST_RESPONSES) +config_path = test_config.SMOKE_TEST_CONFIGS -s3_manager = S3ConfigManager(S3_BUCKET, S3_PREFIX) +s3_manager = S3ConfigManager(test_config.S3_BUCKET, test_config.S3_PREFIX) param_list = list(all_data.items()) -id_list = [ - f"{filename} - {scenario.get('scenario_name', 'No Scenario')}" - for filename, scenario in param_list -] +id_list = [f"{filename} - {scenario.get('scenario_name', 'No Scenario')}" for filename, scenario in param_list] @pytest.mark.smoketest -@pytest.mark.parametrize("filename, scenario", param_list, ids=id_list) +@pytest.mark.parametrize(("filename", "scenario"), param_list, ids=id_list) def test_run_smoke_case(filename, scenario, eligibility_client): - # get the nhs_number from the scenario nhs_number = scenario["nhs_number"] - # get the associated campaign config file from the scenario config_filename = scenario.get("config_filename", "") - # upload that config file to s3 if it is missing or has changed - s3_manager.upload_if_missing_or_changed(os.path.abspath(os.path.join(config_path, config_filename))) + request_headers = scenario.get("request_headers", {}) + s3_manager.upload_if_missing_or_changed((Path(config_path) / config_filename).resolve()) - actual_response = eligibility_client.make_request(nhs_number, strict_ssl=False) + actual_response = eligibility_client.make_request(nhs_number, headers=request_headers, strict_ssl=False) expected_response = all_expected_responses.get(filename).get("response_items", {}) # Assert and show details on failure diff --git a/tests/e2e/tests/test_story_tests.py b/tests/e2e/tests/test_story_tests.py index fb4e74e01..3e2938c6c 100644 --- a/tests/e2e/tests/test_story_tests.py +++ b/tests/e2e/tests/test_story_tests.py @@ -1,34 +1,31 @@ import http -import os +from pathlib import Path import pytest -from tests.e2e.tests.test_config import * -from tests.e2e.utils.data_loader import load_all_expected_responses, initialise_tests -from tests.e2e.utils.s3ConfigManager import S3ConfigManager +from tests.e2e.tests import test_config +from tests.e2e.utils.data_loader import initialise_tests, load_all_expected_responses +from tests.e2e.utils.s3_config_manager import S3ConfigManager # Update the below with the configuration values specified in test_config.py -all_data, dto = initialise_tests(STORY_TEST_DATA) -all_expected_responses = load_all_expected_responses(STORY_TEST_RESPONSES) -config_path = STORY_TEST_CONFIGS +all_data, dto = initialise_tests(test_config.STORY_TEST_DATA) +all_expected_responses = load_all_expected_responses(test_config.STORY_TEST_RESPONSES) +config_path = test_config.STORY_TEST_CONFIGS -s3_manager = S3ConfigManager(S3_BUCKET, S3_PREFIX) +s3_manager = S3ConfigManager(test_config.S3_BUCKET, test_config.S3_PREFIX) param_list = list(all_data.items()) -id_list = [ - f"{filename} - {scenario.get('scenario_name', 'No Scenario')}" - for filename, scenario in param_list -] +id_list = [f"{filename} - {scenario.get('scenario_name', 'No Scenario')}" for filename, scenario in param_list] @pytest.mark.storytest_all -@pytest.mark.parametrize("filename, scenario", param_list, ids=id_list) +@pytest.mark.parametrize(("filename", "scenario"), param_list, ids=id_list) def test_run_story_test_cases(filename, scenario, eligibility_client): nhs_number = scenario["nhs_number"] config_filename = scenario.get("config_filename", "") request_headers = scenario.get("request_headers", {}) expected_response_code = scenario.get("expected_response_code", None) - s3_manager.upload_if_missing_or_changed(os.path.abspath(os.path.join(config_path, config_filename))) + s3_manager.upload_if_missing_or_changed((Path(config_path) / config_filename).resolve()) actual_response = eligibility_client.make_request(nhs_number, headers=request_headers, strict_ssl=False) expected_response = all_expected_responses.get(filename).get("response_items", {}) diff --git a/tests/e2e/utils/data_loader.py b/tests/e2e/utils/data_loader.py index 3f4a5c9a0..8e7a9787c 100644 --- a/tests/e2e/utils/data_loader.py +++ b/tests/e2e/utils/data_loader.py @@ -1,13 +1,13 @@ -import os import json +from pathlib import Path from .dynamo_helper import insert_into_dynamo +from .placeholder_context import PlaceholderDTO, ResolvedPlaceholderContext from .placeholder_utils import resolve_placeholders -from .placeholder_context import ResolvedPlaceholderContext, PlaceholderDTO def initialise_tests(folder): - folder_path = os.path.abspath(folder) + folder_path = Path(folder).resolve() all_data, dto = load_all_test_scenarios(folder_path) # Insert to Dynamo (placeholder) @@ -20,17 +20,16 @@ def initialise_tests(folder): def resolve_placeholders_in_data(data, context, file_name): if isinstance(data, dict): return {k: resolve_placeholders_in_data(v, context, file_name) for k, v in data.items()} - elif isinstance(data, list): + if isinstance(data, list): return [resolve_placeholders_in_data(item, context, file_name) for item in data] - else: - return resolve_placeholders(data, context, file_name) + return resolve_placeholders(data, context, file_name) def load_test_scenario(file_path): - with open(file_path, "r") as f: + with Path.open(file_path) as f: raw_data = json.load(f) - file_name = os.path.basename(file_path) + file_name = Path(file_path).name context = ResolvedPlaceholderContext() resolved_data = resolve_placeholders_in_data(raw_data["data"], context, file_name) @@ -38,7 +37,7 @@ def load_test_scenario(file_path): "file": file_name, "scenario_name": raw_data.get("scenario_name"), "data": resolved_data, - "placeholders": context.all() # Now just placeholder โ†’ value + "placeholders": context.all(), # Now just placeholder โ†’ value } @@ -48,7 +47,7 @@ def find_nhs(obj): for k, v in obj.items(): if k.lower().replace("_", "") == "nhsnumber": return v - elif isinstance(v, (dict, list)): + if isinstance(v, (dict, list)): result = find_nhs(v) if result: return result @@ -66,38 +65,30 @@ def load_all_expected_responses(folder_path): all_data = {} dto = PlaceholderDTO() # Shared across all files - for filename in os.listdir(folder_path): - if not filename.endswith(".json"): + for path in Path(folder_path).iterdir(): + if path.suffix != ".json": continue - full_path = os.path.join(folder_path, filename) - - # Load JSON - with open(full_path, "r") as f: + with path.open() as f: raw_json = json.load(f) - resolved_data = resolve_placeholders_in_data(raw_json, dto, filename) - cleaned_data = clean_expected_response(resolved_data, ) + resolved_data = resolve_placeholders_in_data(raw_json, dto, path.name) + cleaned_data = clean_expected_response(resolved_data) - all_data[filename] = { - "response_items": cleaned_data - } + all_data[path.name] = {"response_items": cleaned_data} return all_data -def load_all_test_scenarios(folder_path, config_folder_path="tests/e2e/data/configs"): +def load_all_test_scenarios(folder_path): all_data = {} dto = PlaceholderDTO() # Shared across all files - for filename in os.listdir(folder_path): - if not filename.endswith(".json"): + for path in Path(folder_path).iterdir(): + if path.suffix != ".json": continue - full_path = os.path.join(folder_path, filename) - - # Load scenario JSON - with open(full_path, "r") as f: + with path.open() as f: raw_json = json.load(f) raw_data = raw_json["data"] @@ -108,13 +99,13 @@ def load_all_test_scenarios(folder_path, config_folder_path="tests/e2e/data/conf expected_response_code = raw_json.get("expected_response_code") # Resolve placeholders with shared DTO - resolved_data = resolve_placeholders_in_data(raw_data, dto, filename) + resolved_data = resolve_placeholders_in_data(raw_data, dto, path.name) # Extract NHS number nhs_number = extract_nhs_number_from_data(resolved_data) # Add resolved scenario - all_data[filename] = { + all_data[path.name] = { "dynamo_items": resolved_data, "nhs_number": nhs_number, "config_filename": config_filename, @@ -138,6 +129,6 @@ def _remove_volatile_fields(data, keys_to_remove): for key, value in data.items() if key not in keys_to_remove } - elif isinstance(data, list): + if isinstance(data, list): return [_remove_volatile_fields(item, keys_to_remove) for item in data] return data diff --git a/tests/e2e/utils/dynamo_helper.py b/tests/e2e/utils/dynamo_helper.py index 3dcc72886..5a749ed57 100644 --- a/tests/e2e/utils/dynamo_helper.py +++ b/tests/e2e/utils/dynamo_helper.py @@ -1,16 +1,18 @@ -import os -from dotenv import load_dotenv +import logging + import boto3 from botocore.exceptions import ClientError +from dotenv import load_dotenv # Load credentials from .env load_dotenv() +logger = logging.getLogger(__name__) class DynamoDBHelper: def __init__(self, table_name): # Create DynamoDB resource using credentials from env - self.dynamodb = boto3.resource('dynamodb') + self.dynamodb = boto3.resource("dynamodb") self.table = self.dynamodb.Table(table_name) def insert_item(self, item: dict): @@ -19,10 +21,11 @@ def insert_item(self, item: dict): """ try: response = self.table.put_item(Item=item) - return response except ClientError as e: - print(f"Failed to insert item: {e.response['Error']['Message']}") + logger.exception("Failed to insert item: %s", e.response["Error"]["Message"]) raise + else: + return response def insert_items(self, items: list): """ @@ -32,10 +35,11 @@ def insert_items(self, items: list): with self.table.batch_writer() as batch: for item in items: batch.put_item(Item=item) - print("Batch insert complete.") except ClientError as e: - print(f"Batch insert failed: {e.response['Error']['Message']}") + logger.exception("Batch insert failed: %s", e.response["Error"]["Message"]) raise + else: + logger.info("Batch insert complete.") def get_item(self, key: dict): """ @@ -43,18 +47,19 @@ def get_item(self, key: dict): """ try: response = self.table.get_item(Key=key) - return response.get("Item") except ClientError as e: - print(f"Failed to get item: {e.response['Error']['Message']}") + logger.exception("Failed to get item: %s", e.response["Error"]["Message"]) raise + else: + return response.get("Item") def insert_into_dynamo(data): - print("Inserting into Dynamo:", data) + logger.info("Inserting into Dynamo: %s", data) table = DynamoDBHelper("eligibility-signposting-api-test-eligibility_datastore") for item in data: try: table.insert_item(item) - print(f"โœ… Inserted: {item}") + logger.info("โœ… Inserted: %s", item) except ClientError as e: - print(f"โŒ Failed to insert {item}: {e.response['Error']['Message']}") + logger.exception("โŒ Failed to insert %s: %s", item, e.response["Error"]["Message"]) diff --git a/tests/e2e/utils/eligibilityApiClient.py b/tests/e2e/utils/eligibility_api_client.py similarity index 68% rename from tests/e2e/utils/eligibilityApiClient.py rename to tests/e2e/utils/eligibility_api_client.py index 98e35f1b6..d69be6bfb 100644 --- a/tests/e2e/utils/eligibilityApiClient.py +++ b/tests/e2e/utils/eligibility_api_client.py @@ -1,35 +1,35 @@ -import os import json +import os from pathlib import Path -from typing import Any, Dict, Optional, Union +from typing import Any import boto3 import requests -from dotenv import load_dotenv from botocore.exceptions import ClientError +from dotenv import load_dotenv from requests import Response class EligibilityApiClient: def __init__(self, api_url: str, cert_dir: str = "tests/e2e/certs") -> None: - load_dotenv(dotenv_path=os.path.join(os.path.dirname(__file__), "../.env")) + load_dotenv(dotenv_path=Path(__file__).resolve().parent / "../.env") self.api_url: str = api_url - self.region: Optional[str] = os.getenv("AWS_REGION") - self.aws_access_key_id: Optional[str] = os.getenv("AWS_ACCESS_KEY_ID") - self.aws_secret_access_key: Optional[str] = os.getenv("AWS_SECRET_ACCESS_KEY") - self.aws_session_token: Optional[str] = os.getenv("AWS_SESSION_TOKEN") + self.region: str | None = os.getenv("AWS_REGION") + self.aws_access_key_id: str | None = os.getenv("AWS_ACCESS_KEY_ID") + self.aws_secret_access_key: str | None = os.getenv("AWS_SECRET_ACCESS_KEY") + self.aws_session_token: str | None = os.getenv("AWS_SESSION_TOKEN") self.cert_dir: Path = Path(cert_dir) self.cert_dir.mkdir(parents=True, exist_ok=True) - self.cert_paths: Dict[str, Path] = { + self.cert_paths: dict[str, Path] = { "private_key": self.cert_dir / "api_private_key_cert.pem", "client_cert": self.cert_dir / "api_client_cert.pem", "ca_cert": self.cert_dir / "api_ca_cert.pem", } - self.ssm_params: Dict[str, str] = { + self.ssm_params: dict[str, str] = { "private_key": "/test/mtls/api_private_key_cert", "client_cert": "/test/mtls/api_client_cert", "ca_cert": "/test/mtls/api_ca_cert", @@ -37,7 +37,7 @@ def __init__(self, api_url: str, cert_dir: str = "tests/e2e/certs") -> None: self._ensure_certs_present() - def _get_ssm_parameter(self, param_name: str, decrypt: bool = True) -> str: + def _get_ssm_parameter(self, param_name: str, *, decrypt: bool = True) -> str: try: client = boto3.client( "ssm", @@ -49,7 +49,8 @@ def _get_ssm_parameter(self, param_name: str, decrypt: bool = True) -> str: response = client.get_parameter(Name=param_name, WithDecryption=decrypt) return response["Parameter"]["Value"] except ClientError as e: - raise RuntimeError(f"Error retrieving {param_name} from SSM: {e}") from e + msg = f"Error retrieving {param_name} from SSM: {e}" + raise RuntimeError(msg) from e def _ensure_certs_present(self) -> None: missing = [k for k, path in self.cert_paths.items() if not path.exists()] @@ -59,24 +60,25 @@ def _ensure_certs_present(self) -> None: for cert_type in missing: param_name = self.ssm_params[cert_type] cert_value = self._get_ssm_parameter(param_name) - with open(self.cert_paths[cert_type], "w", encoding="utf-8") as f: + with Path.open(self.cert_paths[cert_type], "w", encoding="utf-8") as f: f.write(cert_value) def make_request( - self, - nhs_number: str, - method: str = "GET", - payload: Optional[Union[Dict[str, Any], list]] = None, - headers: Optional[Dict[str, str]] = None, - strict_ssl: bool = False, - raise_on_error: bool = True, - ) -> Dict[str, Any]: + self, + nhs_number: str, + method: str = "GET", + payload: dict[str, Any] | list | None = None, + headers: dict[str, str] | None = None, + **options, + ) -> dict[str, Any]: + strict_ssl = options.get("strict_ssl", False) + raise_on_error = options.get("raise_on_error", True) url = f"{self.api_url.rstrip('/')}/{nhs_number}" cert = ( str(self.cert_paths["client_cert"]), str(self.cert_paths["private_key"]), ) - verify: Union[bool, str] = str(self.cert_paths["ca_cert"]) if strict_ssl else False + verify: bool | str = str(self.cert_paths["ca_cert"]) if strict_ssl else False try: response = requests.request( @@ -95,15 +97,16 @@ def make_request( return self._parse_response(response) except requests.exceptions.SSLError as ssl_err: - raise RuntimeError(f"SSL error during request: {ssl_err}") from ssl_err + msg = "SSL error during request: %s", ssl_err + raise RuntimeError(msg) from ssl_err except requests.exceptions.RequestException as req_err: response = getattr(req_err, "response", None) if isinstance(response, Response): return self._parse_response(response) - raise RuntimeError(f"Request error: {req_err}") from req_err - + msg = "Request error: %s", req_err + raise RuntimeError(msg) from req_err - def _parse_response(self, response: Response) -> Dict[str, Any]: + def _parse_response(self, response: Response) -> dict[str, Any]: try: data = response.json() cleaned = self._clean_response(data) @@ -121,15 +124,13 @@ def _clean_response(self, data: Any) -> Any: keys_to_ignore = ["responseId", "lastUpdated"] return self._remove_volatile_fields(data, keys_to_ignore) - def _remove_volatile_fields( - self, data: Union[Dict[str, Any], list, Any], keys_to_remove: list - ) -> Any: + def _remove_volatile_fields(self, data: dict[str, Any] | list | Any, keys_to_remove: list) -> Any: if isinstance(data, dict): return { key: self._remove_volatile_fields(value, keys_to_remove) for key, value in data.items() if key not in keys_to_remove } - elif isinstance(data, list): + if isinstance(data, list): return [self._remove_volatile_fields(item, keys_to_remove) for item in data] return data diff --git a/tests/e2e/utils/placeholder_context.py b/tests/e2e/utils/placeholder_context.py index 4809407d0..5d22c5534 100644 --- a/tests/e2e/utils/placeholder_context.py +++ b/tests/e2e/utils/placeholder_context.py @@ -2,7 +2,7 @@ class ResolvedPlaceholderContext: def __init__(self): self.values = {} - def add(self, placeholder: str, resolved_value: str, file_name: str = None): + def add(self, placeholder: str, resolved_value: str): self.values[placeholder] = resolved_value # Drop file_name key nesting def get(self, placeholder: str) -> str: diff --git a/tests/e2e/utils/placeholder_utils.py b/tests/e2e/utils/placeholder_utils.py index 5b106a63d..1ddf67f8f 100644 --- a/tests/e2e/utils/placeholder_utils.py +++ b/tests/e2e/utils/placeholder_utils.py @@ -1,7 +1,11 @@ +import logging import re -from datetime import datetime, timedelta -from dateutil.relativedelta import relativedelta from calendar import isleap +from datetime import UTC, datetime, timedelta + +from dateutil.relativedelta import relativedelta + +logger = logging.getLogger(__name__) def resolve_placeholders(value, context=None, file_name=None): @@ -10,50 +14,63 @@ def resolve_placeholders(value, context=None, file_name=None): match = re.search(r"<<(.*?)>>", value) if not match: - return value # No placeholder to resolve + return value placeholder = match.group(1) - parts = placeholder.split("_") try: - if placeholder in ["IGNORE_RESPONSE_ID", "IGNORE_DATE"]: - return value.replace(f"<<{placeholder}>>", placeholder) - elif len(parts) != 3 or parts[0] not in ["DATE", "RDATE", "IGNORE"]: - return value # Unrecognized format + resolved = _resolve_placeholder_value(placeholder) + if context: + context.add(placeholder, resolved, file_name) + return value.replace(f"<<{placeholder}>>", resolved) + except Exception: + logger.exception("[ERROR] Could not resolve placeholder %s:", placeholder) + return value - date_type, arg = parts[1], parts[2] - today = datetime.today() - if date_type == "AGE": - target_year = today.year - int(arg) - try: - result_date = today.replace(year=target_year) - except ValueError: - if today.month == 2 and today.day == 29 and not isleap(target_year): - result_date = datetime(target_year, 2, 28) - else: - raise +def _resolve_placeholder_value(placeholder: str) -> str: + placeholder_parts_length = 3 + valid_placeholder_types = ["DATE", "RDATE", "IGNORE"] + result = f"<<{placeholder}>>" # Default fallback - elif date_type == "DAY": - result_date = today + timedelta(days=int(arg)) + if placeholder in ["IGNORE_RESPONSE_ID", "IGNORE_DATE"]: + return placeholder - elif date_type == "MONTH": - result_date = today + relativedelta(months=int(arg)) + parts = placeholder.split("_") + if len(parts) != placeholder_parts_length or parts[0] not in valid_placeholder_types: + return result - elif date_type == "YEAR": - result_date = today + relativedelta(years=int(arg)) + today = datetime.now(UTC) + date_type, arg = parts[1], parts[2] - else: - return value + try: + if date_type == "AGE": + result = _resolve_age_placeholder(today, int(arg), parts[0]) + elif date_type == "DAY": + result = _format_date(today + timedelta(days=int(arg)), parts[0]) + elif date_type == "MONTH": + result = _format_date(today + relativedelta(months=int(arg)), parts[0]) + elif date_type == "YEAR": + result = _format_date(today + relativedelta(years=int(arg)), parts[0]) + except Exception: + logger.exception("Failed to resolve placeholder: %s", placeholder) + raise + return result - resolved = result_date.strftime("%Y%m%d") if parts[0] == "DATE" else result_date.strftime("%-d %B %Y") - if context: - context.add(placeholder, resolved, file_name) +def _resolve_age_placeholder(today: datetime, years_back: int, format_type: str) -> str: + target_year = today.year - years_back + february = 2 + leap_year_day = 29 + try: + result_date = today.replace(year=target_year) + except ValueError: + if today.month == february and today.day == leap_year_day and not isleap(target_year): + result_date = datetime(target_year, 2, 28, tzinfo=UTC) + else: + raise + return _format_date(result_date, format_type) - # Replace the single placeholder in the original string - return value.replace(f"<<{placeholder}>>", resolved) - except Exception as e: - print(f"[ERROR] Could not resolve placeholder '{placeholder}': {e}") - return value +def _format_date(date: datetime, format_type: str) -> str: + return date.strftime("%Y%m%d") if format_type == "DATE" else date.strftime("%-d %B %Y") diff --git a/tests/e2e/utils/s3ConfigManager.py b/tests/e2e/utils/s3_config_manager.py similarity index 55% rename from tests/e2e/utils/s3ConfigManager.py rename to tests/e2e/utils/s3_config_manager.py index 9ca6b5f9a..6edefe109 100644 --- a/tests/e2e/utils/s3ConfigManager.py +++ b/tests/e2e/utils/s3_config_manager.py @@ -1,12 +1,13 @@ -import os import hashlib +import logging +from pathlib import Path + import boto3 import botocore -from typing import Optional - from dotenv import load_dotenv load_dotenv() +logger = logging.getLogger(__name__) class S3ConfigManager: @@ -16,70 +17,60 @@ def __init__(self, bucket_name: str, s3_prefix: str = "") -> None: self.s3_client = boto3.client("s3") def _s3_key(self, filename: str) -> str: - return os.path.join(self.s3_prefix, filename) + return str(Path(self.s3_prefix) / filename) - def _calculate_file_hash(self, file_path: str) -> str: + def _calculate_file_hash(self, file_path: Path) -> str: """Return SHA256 hash of the given file.""" sha256 = hashlib.sha256() - with open(file_path, "rb") as file: + with file_path.open("rb") as file: for chunk in iter(lambda: file.read(4096), b""): sha256.update(chunk) return sha256.hexdigest() - def upload_if_missing_or_changed(self, local_path: str) -> None: - filename = os.path.basename(local_path) + def upload_if_missing_or_changed(self, local_path: Path) -> None: + filename = Path(local_path).name s3_key = self._s3_key(filename) try: if self.config_exists_and_matches(local_path, s3_key): - print( - f"\n๐Ÿ” Config '{filename}' already exists and matches in S3. " - "Skipping upload." - ) + logger.info("\n๐Ÿ” Config '%s' already exists and matches in S3. Skipping upload.", filename) return - print( - f"\n๐Ÿงน A different config exists under '{self.s3_prefix}/'. " - "Deleting all existing files..." - ) + logger.info("\n๐Ÿงน A different config exists under '%s/'. Deleting all existing files...", self.s3_prefix) self._delete_all_in_prefix() except self.s3_client.exceptions.NoSuchKey: - print(f"\n๐Ÿ†• No config found under '{self.s3_prefix}/'. Proceeding to upload.") + logger.info("\n๐Ÿ†• No config found under '%s/'. Proceeding to upload.", self.s3_prefix) except botocore.exceptions.ClientError as error: if error.response.get("Error", {}).get("Code") == "NoSuchKey": - print(f"\n๐Ÿ†• No config found under '{self.s3_prefix}/'. Proceeding to upload.") + logger.info("\n๐Ÿ†• No config found under '%s/'. Proceeding to upload.", self.s3_prefix) else: raise - print(f"โฌ†๏ธ Uploading new config '{filename}' to S3...") + logger.info("โฌ†๏ธ Uploading new config '%s' to S3...", filename) self.s3_client.upload_file(local_path, self.bucket_name, s3_key) - print(f"๐Ÿ“„ Uploaded to s3://{self.bucket_name}/{s3_key}") + logger.info("๐Ÿ“„ Uploaded to s3://%s/%s", self.bucket_name, s3_key) - def config_exists_and_matches(self, local_path: str, s3_key: str) -> bool: + def config_exists_and_matches(self, local_path: Path, s3_key: str) -> bool: try: s3_obj = self.s3_client.get_object(Bucket=self.bucket_name, Key=s3_key) s3_data = s3_obj["Body"].read() s3_hash = hashlib.sha256(s3_data).hexdigest() local_hash = self._calculate_file_hash(local_path) - return s3_hash == local_hash except self.s3_client.exceptions.NoSuchKey: return False except botocore.exceptions.ClientError as error: if error.response.get("Error", {}).get("Code") == "NoSuchKey": return False raise + else: + return s3_hash == local_hash def _delete_all_in_prefix(self) -> None: """Delete all S3 objects under the current prefix.""" - response = self.s3_client.list_objects_v2( - Bucket=self.bucket_name, Prefix=self.s3_prefix - ) + response = self.s3_client.list_objects_v2(Bucket=self.bucket_name, Prefix=self.s3_prefix) if "Contents" in response: to_delete = [{"Key": obj["Key"]} for obj in response["Contents"]] - self.s3_client.delete_objects( - Bucket=self.bucket_name, - Delete={"Objects": to_delete} - ) - print(f"๐Ÿ—‘๏ธ Deleted {len(to_delete)} file(s) under prefix '{self.s3_prefix}/'.") + self.s3_client.delete_objects(Bucket=self.bucket_name, Delete={"Objects": to_delete}) + logger.info("๐Ÿ—‘๏ธ Deleted %d file(s) under prefix '%s/'.", len(to_delete), self.s3_prefix) else: - print(f"๐Ÿ“ญ Nothing to delete under prefix '{self.s3_prefix}/'.") + logger.info("๐Ÿ“ญ Nothing to delete under prefix '%s/'.", self.s3_prefix) From e9b610f37f4ba0c38ccd6a421e3ff969ad1c09c0 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 10:54:08 +0100 Subject: [PATCH 24/72] test_smoke_tests.py created --- tests/e2e/tests/test_smoke_tests.py | 64 +++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 tests/e2e/tests/test_smoke_tests.py diff --git a/tests/e2e/tests/test_smoke_tests.py b/tests/e2e/tests/test_smoke_tests.py new file mode 100644 index 000000000..fc598bbaf --- /dev/null +++ b/tests/e2e/tests/test_smoke_tests.py @@ -0,0 +1,64 @@ +import json +import os + +import pytest + +from tests.e2e.tests.test_config import * +from tests.e2e.utils.data_loader import load_all_expected_responses +from tests.e2e.utils.new_functions import initialise_tests +from tests.e2e.utils.s3ConfigManager import S3ConfigManager + +# UPDATE THE BELOW WITH LOCATION OF TEST DATA, EXPECTED RESPONSES AND CONFIGS IN DEFINED IN test_config.py # +############################################################################################################# +all_data, dto = initialise_tests(SMOKE_TEST_DATA) +all_expected_responses = load_all_expected_responses(SMOKE_TEST_RESPONSES) +config_path = SMOKE_TEST_CONFIGS +############################################################################################################# +s3_manager = S3ConfigManager(S3_BUCKET, S3_PREFIX) + + +param_list = list(all_data.items()) +id_list = [ + f"{filename} - {scenario.get('scenario_name', 'No Scenario')}" + for filename, scenario in param_list +] + +@pytest.mark.smoketest +@pytest.mark.parametrize("filename, scenario", param_list, ids=id_list) +def test_run_smoke_case(filename, scenario, eligibility_client): + # get the nhs_number + nhs_number = scenario["nhs_number"] + # get the associated campaign config file + config_filename = scenario.get("config_filename", "") + # upload that config file to s3 if it + s3_manager.upload_if_missing_or_changed(os.path.abspath(os.path.join(config_path, config_filename))) + + actual_response = eligibility_client.make_request(nhs_number, strict_ssl=False) + expected_response = all_expected_responses.get(filename).get("response_items", {}) + + # Assert and show details on failure + assert actual_response == expected_response, ( + f"\nโŒ Mismatch in test: {filename}\n" + f"NHS Number: {nhs_number}\n" + f"Expected: {expected_response}\n" + f"Actual: {actual_response}\n" + ) + +@pytest.mark.smoketest +def test_check_for_missing_person(eligibility_client): + nhs_number = "1234567890" + + expected_body = { + "issue": [{ + "code": "nhs-number-not-found", + "diagnostics": f'NHS Number "{nhs_number}" not found.', + "severity": "information" + }], + "resourceType": "OperationOutcome" + } + + response = eligibility_client.make_request(nhs_number, raise_on_error=False) + + assert response["status_code"] == 404 + assert response["body"] == expected_body + From e9be704f90439012fbb4584f87d7aa7e541e1304 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 11:04:35 +0100 Subject: [PATCH 25/72] moved more files --- tests/e2e/utils/data_loader.py | 137 +++++++++++++++++++++++++++++ tests/e2e/utils/new_functions.py | 14 +++ tests/e2e/utils/s3ConfigManager.py | 75 ++++++++++++++++ 3 files changed, 226 insertions(+) create mode 100644 tests/e2e/utils/data_loader.py create mode 100644 tests/e2e/utils/new_functions.py create mode 100644 tests/e2e/utils/s3ConfigManager.py diff --git a/tests/e2e/utils/data_loader.py b/tests/e2e/utils/data_loader.py new file mode 100644 index 000000000..1f6fff0b2 --- /dev/null +++ b/tests/e2e/utils/data_loader.py @@ -0,0 +1,137 @@ +import os +import json + +from .common_test_helpers import clean_response +from .placeholder_utils import resolve_placeholders +from .placeholder_context import ResolvedPlaceholderContext, PlaceholderDTO + + +def resolve_placeholders_in_data(data, context, file_name): + if isinstance(data, dict): + return {k: resolve_placeholders_in_data(v, context, file_name) for k, v in data.items()} + elif isinstance(data, list): + return [resolve_placeholders_in_data(item, context, file_name) for item in data] + else: + return resolve_placeholders(data, context, file_name) + + +def load_test_scenario(file_path): + with open(file_path, "r") as f: + raw_data = json.load(f) + + file_name = os.path.basename(file_path) + context = ResolvedPlaceholderContext() + resolved_data = resolve_placeholders_in_data(raw_data["data"], context, file_name) + + return { + "file": file_name, + "scenario_name": raw_data.get("scenario_name"), + "data": resolved_data, + "placeholders": context.all() # Now just placeholder โ†’ value + } + + +def extract_nhs_number_from_data(data): + def find_nhs(obj): + if isinstance(obj, dict): + for k, v in obj.items(): + if k.lower().replace("_", "") == "nhsnumber": + return v + elif isinstance(v, (dict, list)): + result = find_nhs(v) + if result: + return result + elif isinstance(obj, list): + for item in obj: + result = find_nhs(item) + if result: + return result + return None + + return find_nhs(data) or "UNKNOWN" + +def load_all_test_scenarios_old(folder_path): + all_data = {} + dto = PlaceholderDTO() # Shared across all files + + for filename in os.listdir(folder_path): + if not filename.endswith(".json"): + continue + + full_path = os.path.join(folder_path, filename) + + # Load JSON + with open(full_path, "r") as f: + raw_json = json.load(f) + + raw_data = raw_json["data"] + # Resolve placeholders into a shared DTO + resolved_data = resolve_placeholders_in_data(raw_data, dto, filename) + + # Extract NHS number from resolved data (e.g. first item or key field) + nhs_number = extract_nhs_number_from_data(resolved_data) # You must define this! + + all_data[filename] = { + "dynamo_items": resolved_data, + "nhs_number": nhs_number, + } + + return all_data, dto + +def load_all_expected_responses(folder_path): + all_data = {} + dto = PlaceholderDTO() # Shared across all files + + for filename in os.listdir(folder_path): + if not filename.endswith(".json"): + continue + + full_path = os.path.join(folder_path, filename) + + # Load JSON + with open(full_path, "r") as f: + raw_json = json.load(f) + + resolved_data = resolve_placeholders_in_data(raw_json, dto, filename) + cleaned_data = clean_response(resolved_data,) + + all_data[filename] = { + "response_items": cleaned_data + } + + return all_data + +def load_all_test_scenarios(folder_path, config_folder_path="tests/e2e/data/configs"): + all_data = {} + dto = PlaceholderDTO() # Shared across all files + + for filename in os.listdir(folder_path): + if not filename.endswith(".json"): + continue + + full_path = os.path.join(folder_path, filename) + + # Load scenario JSON + with open(full_path, "r") as f: + raw_json = json.load(f) + + raw_data = raw_json["data"] + + config_filename = raw_json.get("config_filename") # Just the name + scenario_name = raw_json.get("scenario_name") # Just the name + + # Resolve placeholders with shared DTO + resolved_data = resolve_placeholders_in_data(raw_data, dto, filename) + + # Extract NHS number + nhs_number = extract_nhs_number_from_data(resolved_data) + + # Add resolved scenario + all_data[filename] = { + "dynamo_items": resolved_data, + "nhs_number": nhs_number, + "config_filename": config_filename, + "scenario_name": scenario_name, + } + + return all_data, dto diff --git a/tests/e2e/utils/new_functions.py b/tests/e2e/utils/new_functions.py new file mode 100644 index 000000000..5dd663553 --- /dev/null +++ b/tests/e2e/utils/new_functions.py @@ -0,0 +1,14 @@ +import os +from tests.e2e.utils.dynamo_helper import insert_into_dynamo +from tests.e2e.utils.data_loader import load_all_test_scenarios + + +def initialise_tests(folder): + folder_path = os.path.abspath(folder) + all_data, dto = load_all_test_scenarios(folder_path) + + # Insert to Dynamo (placeholder) + for scenario in all_data.values(): + insert_into_dynamo(scenario["dynamo_items"]) + + return all_data, dto diff --git a/tests/e2e/utils/s3ConfigManager.py b/tests/e2e/utils/s3ConfigManager.py new file mode 100644 index 000000000..f723be788 --- /dev/null +++ b/tests/e2e/utils/s3ConfigManager.py @@ -0,0 +1,75 @@ +import boto3 +import botocore +import hashlib +import os + +from dotenv import load_dotenv +load_dotenv() + +class S3ConfigManager: + def __init__(self, bucket_name: str, s3_prefix: str = ""): + self.bucket_name = bucket_name + self.s3_prefix = s3_prefix + self.s3_client = boto3.client("s3") + + def _s3_key(self, filename: str) -> str: + return os.path.join(self.s3_prefix, filename) + + def _calculate_file_hash(self, file_path: str) -> str: + """SHA256 hash to compare local and S3 content.""" + sha256 = hashlib.sha256() + with open(file_path, "rb") as f: + for chunk in iter(lambda: f.read(4096), b""): + sha256.update(chunk) + return sha256.hexdigest() + + def upload_if_missing_or_changed(self, local_path: str): + filename = os.path.basename(local_path) + s3_key = self._s3_key(filename) + + try: + if self.config_exists_and_matches(local_path, s3_key): + print(f"โœ… Config '{filename}' already exists and matches in S3. Skipping upload.") + return + else: + print(f"๐Ÿงน Config '{filename}' exists in S3 but is different. Deleting old version...") + self.s3_client.delete_object(Bucket=self.bucket_name, Key=s3_key) + except self.s3_client.exceptions.NoSuchKey: + print(f"๐Ÿ†• Config '{filename}' does not exist in S3. Proceeding to upload.") + except botocore.exceptions.ClientError as e: + if e.response["Error"]["Code"] == "NoSuchKey": + print(f"๐Ÿ†• Config '{filename}' does not exist in S3. Proceeding to upload.") + else: + raise + + print(f"โฌ†๏ธ Uploading new config '{filename}' to S3...") + self.s3_client.upload_file(local_path, self.bucket_name, s3_key) + print(f"โœ… Uploaded to s3://{self.bucket_name}/{s3_key}") + + + + # def config_exists_and_matches(self, local_path: str, s3_key: str) -> bool: + # try: + # s3_obj = self.s3_client.get_object(Bucket=self.bucket_name, Key=s3_key) + # s3_data = s3_obj["Body"].read() + # s3_hash = hashlib.sha256(s3_data).hexdigest() + # local_hash = self._calculate_file_hash(local_path) + # return s3_hash == local_hash + # except self.s3_client.exceptions.NoSuchKey: + # return False + # except botocore.exceptions.ClientError as e: + # if e.response["Error"]["Code"] == "NoSuchKey": + # return False + # raise + # + # def upload_if_missing_or_changed(self, local_path: str): + # filename = os.path.basename(local_path) + # s3_key = self._s3_key(filename) + # + # if self.config_exists_and_matches(local_path, s3_key): + # print(f"โœ… Config '{filename}' already exists and matches in S3. Skipping upload.") + # return + + # print(f"โฌ†๏ธ Uploading new config '{filename}' to S3 as it is missing or changed...") + # self.s3_client.upload_file(local_path, self.bucket_name, s3_key) + # print(f"โœ… Uploaded to s3://{self.bucket_name}/{s3_key}") From 3dd5368dc3f1f0f714770167d1c6375dfe1e3b42 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 11:09:40 +0100 Subject: [PATCH 26/72] moved more files --- tests/e2e/tests/test_config.py | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tests/e2e/tests/test_config.py diff --git a/tests/e2e/tests/test_config.py b/tests/e2e/tests/test_config.py new file mode 100644 index 000000000..04bc82dd5 --- /dev/null +++ b/tests/e2e/tests/test_config.py @@ -0,0 +1,8 @@ +S3_BUCKET = "eligibility-signposting-api-test-eli-rules" +S3_PREFIX = "manual-uploads" + +############################################################################# +SMOKE_TEST_DATA = "tests/e2e/data/dynamoDB/smokeTestData/" +SMOKE_TEST_RESPONSES = "tests/e2e/data/responses/smokeTestResponses/" +SMOKE_TEST_CONFIGS = "tests/e2e/data/configs/smokeTestConfigs" +############################################################################# From 8cc9a264f8e630d9220def1b6ad503622f8bfb57 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 11:14:26 +0100 Subject: [PATCH 27/72] moved more files --- tests/e2e/utils/common_test_helpers.py | 15 +++++++ tests/e2e/utils/placeholder_context.py | 30 +++++++++++++ tests/e2e/utils/placeholder_utils.py | 58 ++++++++++++++++++++++++++ 3 files changed, 103 insertions(+) create mode 100644 tests/e2e/utils/common_test_helpers.py create mode 100644 tests/e2e/utils/placeholder_context.py create mode 100644 tests/e2e/utils/placeholder_utils.py diff --git a/tests/e2e/utils/common_test_helpers.py b/tests/e2e/utils/common_test_helpers.py new file mode 100644 index 000000000..09554f545 --- /dev/null +++ b/tests/e2e/utils/common_test_helpers.py @@ -0,0 +1,15 @@ +def clean_response(data: dict) -> dict: + keys_to_ignore = ["responseId", "lastUpdated"] + return remove_volatile_fields(data, keys_to_ignore) + + +def remove_volatile_fields(data, keys_to_remove): + if isinstance(data, dict): + return { + key: remove_volatile_fields(value, keys_to_remove) + for key, value in data.items() + if key not in keys_to_remove + } + elif isinstance(data, list): + return [remove_volatile_fields(item, keys_to_remove) for item in data] + return data diff --git a/tests/e2e/utils/placeholder_context.py b/tests/e2e/utils/placeholder_context.py new file mode 100644 index 000000000..0e304cff9 --- /dev/null +++ b/tests/e2e/utils/placeholder_context.py @@ -0,0 +1,30 @@ +class ResolvedPlaceholderContext: + def __init__(self): + self.values = {} + + def add(self, placeholder: str, resolved_value: str, file_name: str = None): + self.values[placeholder] = resolved_value # Drop file_name key nesting + + def get(self, placeholder: str) -> str: + return self.values.get(placeholder) + + def all(self): + return self.values + +class PlaceholderDTO: + def __init__(self): + self.placeholders = {} # key: filename, value: dict of placeholders per file + + def add(self, key: str, value: str, file_name: str): + if file_name not in self.placeholders: + self.placeholders[file_name] = {} + self.placeholders[file_name][key] = value + + def get(self, key: str, file_name: str): + return self.placeholders.get(file_name, {}).get(key) + + def get_all_for_file(self, file_name: str): + return self.placeholders.get(file_name, {}).copy() + + def all(self): + return self.placeholders.copy() diff --git a/tests/e2e/utils/placeholder_utils.py b/tests/e2e/utils/placeholder_utils.py new file mode 100644 index 000000000..437c00503 --- /dev/null +++ b/tests/e2e/utils/placeholder_utils.py @@ -0,0 +1,58 @@ +import re +from datetime import datetime, timedelta +from dateutil.relativedelta import relativedelta +from calendar import isleap + +def resolve_placeholders(value, context=None, file_name=None): + if not isinstance(value, str): + return value + + match = re.search(r"<<(.*?)>>", value) + if not match: + return value # No placeholder to resolve + + placeholder = match.group(1) + parts = placeholder.split("_") + + try: + if placeholder in ["IGNORE_RESPONSE_ID", "IGNORE_DATE"]: + return value.replace(f"<<{placeholder}>>", placeholder) + elif len(parts) != 3 or parts[0] not in ["DATE", "RDATE", "IGNORE"]: + return value # Unrecognized format + + date_type, arg = parts[1], parts[2] + today = datetime.today() + + if date_type == "AGE": + target_year = today.year - int(arg) + try: + result_date = today.replace(year=target_year) + except ValueError: + if today.month == 2 and today.day == 29 and not isleap(target_year): + result_date = datetime(target_year, 2, 28) + else: + raise + + elif date_type == "DAY": + result_date = today + timedelta(days=int(arg)) + + elif date_type == "MONTH": + result_date = today + relativedelta(months=int(arg)) + + elif date_type == "YEAR": + result_date = today + relativedelta(years=int(arg)) + + else: + return value + + resolved = result_date.strftime("%Y%m%d") if parts[0] == "DATE" else result_date.strftime("%-d %B %Y") + + if context: + context.add(placeholder, resolved, file_name) + + # Replace the single placeholder in the original string + return value.replace(f"<<{placeholder}>>", resolved) + + except Exception as e: + print(f"[ERROR] Could not resolve placeholder '{placeholder}': {e}") + return value From 6f81e0db314700780344fbc4508d6c9f4816f8ad Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 11:20:55 +0100 Subject: [PATCH 28/72] moved more files --- .gitignore | 1 + .../configs => .temp}/global_config.json | 0 .../{ => smokeTestData}/AUTO_RSV_SB_001.json | 0 .../{ => smokeTestData}/AUTO_RSV_SB_002.json | 0 .../{ => smokeTestData}/AUTO_RSV_SB_003.json | 0 .../{ => smokeTestData}/AUTO_RSV_SB_004.json | 0 .../{ => smokeTestData}/AUTO_RSV_SB_005.json | 0 .../{ => smokeTestData}/AUTO_RSV_SB_006.json | 0 .../{ => smokeTestData}/AUTO_RSV_SB_007.json | 0 .../{ => smokeTestData}/AUTO_RSV_SB_008.json | 0 .../{ => smokeTestData}/AUTO_RSV_SB_009.json | 0 .../{ => smokeTestData}/AUTO_RSV_SB_010.json | 0 .../{ => smokeTestData}/AUTO_RSV_SB_011.json | 0 .../{ => smokeTestData}/AUTO_RSV_SB_012.json | 0 .../{ => smokeTestData}/AUTO_RSV_SB_013.json | 0 .../{ => smokeTestData}/AUTO_RSV_SB_014.json | 0 .../AUTO_RSV_SB_001.json | 0 .../AUTO_RSV_SB_002.json | 0 .../AUTO_RSV_SB_003.json | 0 .../AUTO_RSV_SB_004.json | 0 .../AUTO_RSV_SB_005.json | 0 .../AUTO_RSV_SB_006.json | 0 .../AUTO_RSV_SB_007.json | 0 .../AUTO_RSV_SB_008.json | 0 .../AUTO_RSV_SB_009.json | 0 .../AUTO_RSV_SB_010.json | 0 .../AUTO_RSV_SB_011.json | 0 .../AUTO_RSV_SB_012.json | 0 .../AUTO_RSV_SB_013.json | 0 .../AUTO_RSV_SB_014.json | 0 tests/e2e/utils/dynamo_helper.py | 59 +++++++++++++++++++ 31 files changed, 60 insertions(+) rename tests/e2e/{data/configs => .temp}/global_config.json (100%) rename tests/e2e/data/dynamoDB/{ => smokeTestData}/AUTO_RSV_SB_001.json (100%) rename tests/e2e/data/dynamoDB/{ => smokeTestData}/AUTO_RSV_SB_002.json (100%) rename tests/e2e/data/dynamoDB/{ => smokeTestData}/AUTO_RSV_SB_003.json (100%) rename tests/e2e/data/dynamoDB/{ => smokeTestData}/AUTO_RSV_SB_004.json (100%) rename tests/e2e/data/dynamoDB/{ => smokeTestData}/AUTO_RSV_SB_005.json (100%) rename tests/e2e/data/dynamoDB/{ => smokeTestData}/AUTO_RSV_SB_006.json (100%) rename tests/e2e/data/dynamoDB/{ => smokeTestData}/AUTO_RSV_SB_007.json (100%) rename tests/e2e/data/dynamoDB/{ => smokeTestData}/AUTO_RSV_SB_008.json (100%) rename tests/e2e/data/dynamoDB/{ => smokeTestData}/AUTO_RSV_SB_009.json (100%) rename tests/e2e/data/dynamoDB/{ => smokeTestData}/AUTO_RSV_SB_010.json (100%) rename tests/e2e/data/dynamoDB/{ => smokeTestData}/AUTO_RSV_SB_011.json (100%) rename tests/e2e/data/dynamoDB/{ => smokeTestData}/AUTO_RSV_SB_012.json (100%) rename tests/e2e/data/dynamoDB/{ => smokeTestData}/AUTO_RSV_SB_013.json (100%) rename tests/e2e/data/dynamoDB/{ => smokeTestData}/AUTO_RSV_SB_014.json (100%) rename tests/e2e/data/responses/{ => smokeTestResponses}/AUTO_RSV_SB_001.json (100%) rename tests/e2e/data/responses/{ => smokeTestResponses}/AUTO_RSV_SB_002.json (100%) rename tests/e2e/data/responses/{ => smokeTestResponses}/AUTO_RSV_SB_003.json (100%) rename tests/e2e/data/responses/{ => smokeTestResponses}/AUTO_RSV_SB_004.json (100%) rename tests/e2e/data/responses/{ => smokeTestResponses}/AUTO_RSV_SB_005.json (100%) rename tests/e2e/data/responses/{ => smokeTestResponses}/AUTO_RSV_SB_006.json (100%) rename tests/e2e/data/responses/{ => smokeTestResponses}/AUTO_RSV_SB_007.json (100%) rename tests/e2e/data/responses/{ => smokeTestResponses}/AUTO_RSV_SB_008.json (100%) rename tests/e2e/data/responses/{ => smokeTestResponses}/AUTO_RSV_SB_009.json (100%) rename tests/e2e/data/responses/{ => smokeTestResponses}/AUTO_RSV_SB_010.json (100%) rename tests/e2e/data/responses/{ => smokeTestResponses}/AUTO_RSV_SB_011.json (100%) rename tests/e2e/data/responses/{ => smokeTestResponses}/AUTO_RSV_SB_012.json (100%) rename tests/e2e/data/responses/{ => smokeTestResponses}/AUTO_RSV_SB_013.json (100%) rename tests/e2e/data/responses/{ => smokeTestResponses}/AUTO_RSV_SB_014.json (100%) create mode 100644 tests/e2e/utils/dynamo_helper.py diff --git a/.gitignore b/.gitignore index b863bfb62..9829e47e8 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,4 @@ sandbox/specification/* /specification/tmp/* /tests/e2e/data/out* /tests/e2e/reports/* +/tests/e2e/.temp/ diff --git a/tests/e2e/data/configs/global_config.json b/tests/e2e/.temp/global_config.json similarity index 100% rename from tests/e2e/data/configs/global_config.json rename to tests/e2e/.temp/global_config.json diff --git a/tests/e2e/data/dynamoDB/AUTO_RSV_SB_001.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json similarity index 100% rename from tests/e2e/data/dynamoDB/AUTO_RSV_SB_001.json rename to tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json diff --git a/tests/e2e/data/dynamoDB/AUTO_RSV_SB_002.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json similarity index 100% rename from tests/e2e/data/dynamoDB/AUTO_RSV_SB_002.json rename to tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json diff --git a/tests/e2e/data/dynamoDB/AUTO_RSV_SB_003.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json similarity index 100% rename from tests/e2e/data/dynamoDB/AUTO_RSV_SB_003.json rename to tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json diff --git a/tests/e2e/data/dynamoDB/AUTO_RSV_SB_004.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_004.json similarity index 100% rename from tests/e2e/data/dynamoDB/AUTO_RSV_SB_004.json rename to tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_004.json diff --git a/tests/e2e/data/dynamoDB/AUTO_RSV_SB_005.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_005.json similarity index 100% rename from tests/e2e/data/dynamoDB/AUTO_RSV_SB_005.json rename to tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_005.json diff --git a/tests/e2e/data/dynamoDB/AUTO_RSV_SB_006.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_006.json similarity index 100% rename from tests/e2e/data/dynamoDB/AUTO_RSV_SB_006.json rename to tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_006.json diff --git a/tests/e2e/data/dynamoDB/AUTO_RSV_SB_007.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_007.json similarity index 100% rename from tests/e2e/data/dynamoDB/AUTO_RSV_SB_007.json rename to tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_007.json diff --git a/tests/e2e/data/dynamoDB/AUTO_RSV_SB_008.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_008.json similarity index 100% rename from tests/e2e/data/dynamoDB/AUTO_RSV_SB_008.json rename to tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_008.json diff --git a/tests/e2e/data/dynamoDB/AUTO_RSV_SB_009.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_009.json similarity index 100% rename from tests/e2e/data/dynamoDB/AUTO_RSV_SB_009.json rename to tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_009.json diff --git a/tests/e2e/data/dynamoDB/AUTO_RSV_SB_010.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json similarity index 100% rename from tests/e2e/data/dynamoDB/AUTO_RSV_SB_010.json rename to tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json diff --git a/tests/e2e/data/dynamoDB/AUTO_RSV_SB_011.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_011.json similarity index 100% rename from tests/e2e/data/dynamoDB/AUTO_RSV_SB_011.json rename to tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_011.json diff --git a/tests/e2e/data/dynamoDB/AUTO_RSV_SB_012.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_012.json similarity index 100% rename from tests/e2e/data/dynamoDB/AUTO_RSV_SB_012.json rename to tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_012.json diff --git a/tests/e2e/data/dynamoDB/AUTO_RSV_SB_013.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_013.json similarity index 100% rename from tests/e2e/data/dynamoDB/AUTO_RSV_SB_013.json rename to tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_013.json diff --git a/tests/e2e/data/dynamoDB/AUTO_RSV_SB_014.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_014.json similarity index 100% rename from tests/e2e/data/dynamoDB/AUTO_RSV_SB_014.json rename to tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_014.json diff --git a/tests/e2e/data/responses/AUTO_RSV_SB_001.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_001.json similarity index 100% rename from tests/e2e/data/responses/AUTO_RSV_SB_001.json rename to tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_001.json diff --git a/tests/e2e/data/responses/AUTO_RSV_SB_002.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_002.json similarity index 100% rename from tests/e2e/data/responses/AUTO_RSV_SB_002.json rename to tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_002.json diff --git a/tests/e2e/data/responses/AUTO_RSV_SB_003.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_003.json similarity index 100% rename from tests/e2e/data/responses/AUTO_RSV_SB_003.json rename to tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_003.json diff --git a/tests/e2e/data/responses/AUTO_RSV_SB_004.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_004.json similarity index 100% rename from tests/e2e/data/responses/AUTO_RSV_SB_004.json rename to tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_004.json diff --git a/tests/e2e/data/responses/AUTO_RSV_SB_005.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_005.json similarity index 100% rename from tests/e2e/data/responses/AUTO_RSV_SB_005.json rename to tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_005.json diff --git a/tests/e2e/data/responses/AUTO_RSV_SB_006.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_006.json similarity index 100% rename from tests/e2e/data/responses/AUTO_RSV_SB_006.json rename to tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_006.json diff --git a/tests/e2e/data/responses/AUTO_RSV_SB_007.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_007.json similarity index 100% rename from tests/e2e/data/responses/AUTO_RSV_SB_007.json rename to tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_007.json diff --git a/tests/e2e/data/responses/AUTO_RSV_SB_008.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_008.json similarity index 100% rename from tests/e2e/data/responses/AUTO_RSV_SB_008.json rename to tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_008.json diff --git a/tests/e2e/data/responses/AUTO_RSV_SB_009.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_009.json similarity index 100% rename from tests/e2e/data/responses/AUTO_RSV_SB_009.json rename to tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_009.json diff --git a/tests/e2e/data/responses/AUTO_RSV_SB_010.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_010.json similarity index 100% rename from tests/e2e/data/responses/AUTO_RSV_SB_010.json rename to tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_010.json diff --git a/tests/e2e/data/responses/AUTO_RSV_SB_011.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_011.json similarity index 100% rename from tests/e2e/data/responses/AUTO_RSV_SB_011.json rename to tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_011.json diff --git a/tests/e2e/data/responses/AUTO_RSV_SB_012.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_012.json similarity index 100% rename from tests/e2e/data/responses/AUTO_RSV_SB_012.json rename to tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_012.json diff --git a/tests/e2e/data/responses/AUTO_RSV_SB_013.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_013.json similarity index 100% rename from tests/e2e/data/responses/AUTO_RSV_SB_013.json rename to tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_013.json diff --git a/tests/e2e/data/responses/AUTO_RSV_SB_014.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_014.json similarity index 100% rename from tests/e2e/data/responses/AUTO_RSV_SB_014.json rename to tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_014.json diff --git a/tests/e2e/utils/dynamo_helper.py b/tests/e2e/utils/dynamo_helper.py new file mode 100644 index 000000000..757aba835 --- /dev/null +++ b/tests/e2e/utils/dynamo_helper.py @@ -0,0 +1,59 @@ +import os +from dotenv import load_dotenv +import boto3 +from botocore.exceptions import ClientError + +# Load credentials from .env +load_dotenv() + + +class DynamoDBHelper: + def __init__(self, table_name): + # Create DynamoDB resource using credentials from env + self.dynamodb = boto3.resource('dynamodb') + self.table = self.dynamodb.Table(table_name) + + def insert_item(self, item: dict): + """ + Insert a single item into the table. + """ + try: + response = self.table.put_item(Item=item) + return response + except ClientError as e: + print(f"Failed to insert item: {e.response['Error']['Message']}") + raise + + def insert_items(self, items: list): + """ + Insert multiple items using batch_writer. + """ + try: + with self.table.batch_writer() as batch: + for item in items: + batch.put_item(Item=item) + print("Batch insert complete.") + except ClientError as e: + print(f"Batch insert failed: {e.response['Error']['Message']}") + raise + + def get_item(self, key: dict): + """ + Retrieve a single item by primary key. + """ + try: + response = self.table.get_item(Key=key) + return response.get("Item") + except ClientError as e: + print(f"Failed to get item: {e.response['Error']['Message']}") + raise + +def insert_into_dynamo(data): + print("Inserting into Dynamo (fake):", data) + table = DynamoDBHelper("eligibility-signposting-api-test-eligibility_datastore") + for item in data: + try: + table.insert_item(item) + print(f"โœ… Inserted: {item}") + except ClientError as e: + print(f"โŒ Failed to insert {item}: {e.response['Error']['Message']}") From 3f5c362e5280d7659a0d9bf71561beb00dd06626 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 11:27:40 +0100 Subject: [PATCH 29/72] moved more files --- .../smokeTestData/AUTO_RSV_SB_001.json | 40 ---- .../smokeTestData/AUTO_RSV_SB_002.json | 40 ---- .../smokeTestData/AUTO_RSV_SB_003.json | 40 ---- .../smokeTestData/AUTO_RSV_SB_004.json | 46 ---- .../smokeTestData/AUTO_RSV_SB_005.json | 46 ---- .../smokeTestData/AUTO_RSV_SB_006.json | 45 ---- .../smokeTestData/AUTO_RSV_SB_007.json | 45 ---- .../smokeTestData/AUTO_RSV_SB_008.json | 45 ---- .../smokeTestData/AUTO_RSV_SB_009.json | 45 ---- .../smokeTestData/AUTO_RSV_SB_010.json | 45 ---- .../smokeTestData/AUTO_RSV_SB_011.json | 40 ---- .../smokeTestData/AUTO_RSV_SB_012.json | 35 --- .../smokeTestData/AUTO_RSV_SB_013.json | 45 ---- .../smokeTestData/AUTO_RSV_SB_014.json | 40 ---- tests/e2e/data/dynamoDB/test_data.json | 220 ------------------ .../smokeTestResponses/AUTO_RSV_SB_001.json | 30 --- .../smokeTestResponses/AUTO_RSV_SB_002.json | 28 --- .../smokeTestResponses/AUTO_RSV_SB_003.json | 28 --- .../smokeTestResponses/AUTO_RSV_SB_004.json | 24 -- .../smokeTestResponses/AUTO_RSV_SB_005.json | 22 -- .../smokeTestResponses/AUTO_RSV_SB_006.json | 22 -- .../smokeTestResponses/AUTO_RSV_SB_007.json | 28 --- .../smokeTestResponses/AUTO_RSV_SB_008.json | 7 - .../smokeTestResponses/AUTO_RSV_SB_009.json | 28 --- .../smokeTestResponses/AUTO_RSV_SB_010.json | 28 --- .../smokeTestResponses/AUTO_RSV_SB_011.json | 28 --- .../smokeTestResponses/AUTO_RSV_SB_012.json | 28 --- .../smokeTestResponses/AUTO_RSV_SB_013.json | 22 -- .../smokeTestResponses/AUTO_RSV_SB_014.json | 33 --- 29 files changed, 1173 deletions(-) delete mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json delete mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json delete mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json delete mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_004.json delete mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_005.json delete mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_006.json delete mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_007.json delete mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_008.json delete mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_009.json delete mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json delete mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_011.json delete mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_012.json delete mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_013.json delete mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_014.json delete mode 100644 tests/e2e/data/dynamoDB/test_data.json delete mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_001.json delete mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_002.json delete mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_003.json delete mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_004.json delete mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_005.json delete mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_006.json delete mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_007.json delete mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_008.json delete mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_009.json delete mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_010.json delete mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_011.json delete mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_012.json delete mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_013.json delete mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_014.json diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json deleted file mode 100644 index 457437429..000000000 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested actions (with booking)", - "data": [ - { - "NHS_NUMBER": "5000000001", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_75_rolling": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "5000000001", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "LS1 1AB", - "POSTCODE_SECTOR": "LS1", - "POSTCODE_OUTCODE": "1AB", - "MSOA": "E02001111", - "LSOA": "E01005348", - "GP_PRACTICE_CODE": "B87008", - "PCN": "U43084", - "ICB": "QWO", - "COMMISSIONING_REGION": "Y63", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json deleted file mode 100644 index 78026a684..000000000 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested action (not booking)", - "data": [ - { - "NHS_NUMBER": "5000000002", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_75_rolling": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "5000000002", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "LS1 1AB", - "POSTCODE_SECTOR": "LS1", - "POSTCODE_OUTCODE": "1AB", - "MSOA": "E02001111", - "LSOA": "E01005348", - "GP_PRACTICE_CODE": "B87008", - "PCN": "U43084", - "ICB": "QWO", - "COMMISSIONING_REGION": "Y63", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json deleted file mode 100644 index df7ceced6..000000000 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "scenario_name": "RSV - Actionable due to membership of an alternative Age Cohort incl. suggested action", - "data": [ - { - "NHS_NUMBER": "5000000003", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_75to79_2024": { - "M": { - "dateJoined": { - "S": "20221120" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "5000000003", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "LS1 1AB", - "POSTCODE_SECTOR": "LS1", - "POSTCODE_OUTCODE": "1AB", - "MSOA": "E02001111", - "LSOA": "E01005348", - "GP_PRACTICE_CODE": "B87008", - "PCN": "U43084", - "ICB": "QWO", - "COMMISSIONING_REGION": "Y63", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_004.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_004.json deleted file mode 100644 index ed6764ede..000000000 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_004.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested action (existing national booking)", - "data": [ - { - "NHS_NUMBER": "5000000004", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_75_rolling": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "5000000004", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "LS1 1AB", - "POSTCODE_SECTOR": "LS1", - "POSTCODE_OUTCODE": "1AB", - "MSOA": "E02001111", - "LSOA": "E01005348", - "GP_PRACTICE_CODE": "B87008", - "PCN": "U43084", - "ICB": "QWO", - "COMMISSIONING_REGION": "Y63", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - }, - { - "NHS_NUMBER": "5000000004", - "ATTRIBUTE_TYPE": "RSV", - "BOOKED_APPOINTMENT_DATE": "<>", - "BOOKED_APPOINTMENT_PROVIDER": "NBS" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_005.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_005.json deleted file mode 100644 index 1892dd217..000000000 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_005.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested actions (with local booking)", - "data": [ - { - "NHS_NUMBER": "5000000005", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_75_rolling": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "5000000005", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "LS1 1AB", - "POSTCODE_SECTOR": "LS1", - "POSTCODE_OUTCODE": "1AB", - "MSOA": "E02001111", - "LSOA": "E01005348", - "GP_PRACTICE_CODE": "B87008", - "PCN": "U43084", - "ICB": "QWO", - "COMMISSIONING_REGION": "Y63", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - }, - { - "NHS_NUMBER": "5000000005", - "ATTRIBUTE_TYPE": "RSV", - "BOOKED_APPOINTMENT_DATE": "<>", - "BOOKED_APPOINTMENT_PROVIDER": "ACC" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_006.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_006.json deleted file mode 100644 index f1101ccc4..000000000 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_006.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "scenario_name": "RSV - Not Actionable despite membership of an Age Cohort, already vaccinated", - "data": [ - { - "NHS_NUMBER": "5000000006", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_75_rolling": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "5000000006", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "LS1 1AB", - "POSTCODE_SECTOR": "LS1", - "POSTCODE_OUTCODE": "1AB", - "MSOA": "E02001111", - "LSOA": "E01005348", - "GP_PRACTICE_CODE": "B87008", - "PCN": "U43084", - "ICB": "QWO", - "COMMISSIONING_REGION": "Y63", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - }, - { - "NHS_NUMBER": "5000000006", - "ATTRIBUTE_TYPE": "RSV", - "LAST_SUCCESSFUL_DATE": "<>" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_007.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_007.json deleted file mode 100644 index 3c9c46c5e..000000000 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_007.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "scenario_name": "RSV - Not Actionable, membership of Age Cohort, no available vaccinations (not available type 1)", - "data": [ - { - "NHS_NUMBER": "5000000007", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_75_rolling": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "5000000007", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "LS1 1AB", - "POSTCODE_SECTOR": "LS1", - "POSTCODE_OUTCODE": "1AB", - "MSOA": "E02001111", - "LSOA": "E01005348", - "GP_PRACTICE_CODE": "B87008", - "PCN": "U43084", - "ICB": "<>", - "COMMISSIONING_REGION": "Y63", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - }, - { - "NHS_NUMBER": "5000000007", - "ATTRIBUTE_TYPE": "RSV", - "LAST_SUCCESSFUL_DATE": "<>" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_008.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_008.json deleted file mode 100644 index 25f7e19fa..000000000 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_008.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "scenario_name": "RSV - No RSV response as no active campaign (not available type 2)", - "data": [ - { - "NHS_NUMBER": "5000000008", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_75_rolling": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "5000000008", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "LS1 1AB", - "POSTCODE_SECTOR": "LS1", - "POSTCODE_OUTCODE": "1AB", - "MSOA": "E02001111", - "LSOA": "E01005348", - "GP_PRACTICE_CODE": "B87008", - "PCN": "U43084", - "ICB": "QWO", - "COMMISSIONING_REGION": "Y63", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - }, - { - "NHS_NUMBER": "5000000008", - "ATTRIBUTE_TYPE": "RSV", - "BOOKED_APPOINTMENT_DATE": "2024-07-01" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_009.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_009.json deleted file mode 100644 index 753398d0a..000000000 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_009.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "scenario_name": "RSV - Not Actionable, membership of Age Cohort, dose not yet due", - "data": [ - { - "NHS_NUMBER": "5000000009", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_75_rolling": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "5000000009", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "LS1 1AB", - "POSTCODE_SECTOR": "LS1", - "POSTCODE_OUTCODE": "1AB", - "MSOA": "E02001111", - "LSOA": "E01005348", - "GP_PRACTICE_CODE": "B87008", - "PCN": "U43084", - "ICB": "QWO", - "COMMISSIONING_REGION": "Y63", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - }, - { - "NHS_NUMBER": "5000000009", - "ATTRIBUTE_TYPE": "RSV", - "LAST_SUCCESSFUL_DATE": "2023-07-01" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json deleted file mode 100644 index 7eb7d0488..000000000 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "scenario_name": "RSV - Not Actionable, membership of Age Cohort, dose not far enough apart", - "data": [ - { - "NHS_NUMBER": "5000000010", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_75_rolling": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "5000000010", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "LS1 1AB", - "POSTCODE_SECTOR": "LS1", - "POSTCODE_OUTCODE": "1AB", - "MSOA": "E02001111", - "LSOA": "E01005348", - "GP_PRACTICE_CODE": "B87008", - "PCN": "U43084", - "ICB": "QWO", - "COMMISSIONING_REGION": "Y63", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - }, - { - "NHS_NUMBER": "5000000010", - "ATTRIBUTE_TYPE": "RSV", - "LAST_SUCCESSFUL_DATE": "2023-07-01" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_011.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_011.json deleted file mode 100644 index 9626840b3..000000000 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_011.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "scenario_name": "RSV - Not Actionable despite to membership of an Age Cohort with reasoning of vaccination given in other setting (e.g. care home)", - "data": [ - { - "NHS_NUMBER": "5000000011", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_75_rolling": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "5000000011", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "LS1 1AB", - "POSTCODE_SECTOR": "LS1", - "POSTCODE_OUTCODE": "1AB", - "MSOA": "E02001111", - "LSOA": "E01005348", - "GP_PRACTICE_CODE": "B87008", - "PCN": "U43084", - "ICB": "QWO", - "COMMISSIONING_REGION": "Y63", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "Y", - "DE_FLAG": "N" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_012.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_012.json deleted file mode 100644 index b7ff528f2..000000000 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_012.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "scenario_name": "RSV - Not Actionable despite no cohort membership with reasoning of already vaccinated (type 1 includes unknown cohort)", - "data": [ - { - "NHS_NUMBER": "5000000012", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": {} - } - }, - { - "NHS_NUMBER": "5000000012", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "LS1 1AB", - "POSTCODE_SECTOR": "LS1", - "POSTCODE_OUTCODE": "1AB", - "MSOA": "E02001111", - "LSOA": "E01005348", - "GP_PRACTICE_CODE": "B87008", - "PCN": "U43084", - "ICB": "QWO", - "COMMISSIONING_REGION": "Y63", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - }, - { - "NHS_NUMBER": "5000000012", - "ATTRIBUTE_TYPE": "RSV", - "LAST_SUCCESSFUL_DATE": "<>" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_013.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_013.json deleted file mode 100644 index c0f69b5dc..000000000 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_013.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "scenario_name": "RSV - Not Actionable despite no cohort membership with reasoning of already vaccinated (type 2 includes no cohorts)", - "data": [ - { - "NHS_NUMBER": "5000000013", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "covid_16+_immunosuppression": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "5000000013", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "LS1 1AB", - "POSTCODE_SECTOR": "LS1", - "POSTCODE_OUTCODE": "1AB", - "MSOA": "E02001111", - "LSOA": "E01005348", - "GP_PRACTICE_CODE": "B87008", - "PCN": "U43084", - "ICB": "QWO", - "COMMISSIONING_REGION": "Y63", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - }, - { - "NHS_NUMBER": "50000000014", - "ATTRIBUTE_TYPE": "RSV", - "LAST_SUCCESSFUL_DATE": "<>" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_014.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_014.json deleted file mode 100644 index 5cd1e21d7..000000000 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_014.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "scenario_name": "RSV - Not Eligible", - "data": [ - { - "NHS_NUMBER": "5000000014", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "covid_16+_immunosuppression": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "50000000014", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "LS1 1AB", - "POSTCODE_SECTOR": "LS1", - "POSTCODE_OUTCODE": "1AB", - "MSOA": "E02001111", - "LSOA": "E01005348", - "GP_PRACTICE_CODE": "B87008", - "PCN": "U43084", - "ICB": "QWO", - "COMMISSIONING_REGION": "Y63", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/test_data.json b/tests/e2e/data/dynamoDB/test_data.json deleted file mode 100644 index f5c80e4b5..000000000 --- a/tests/e2e/data/dynamoDB/test_data.json +++ /dev/null @@ -1,220 +0,0 @@ -[ - { - "NHS_NUMBER": "0000000001", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "19960302", - "GENDER": 1, - "POSTCODE": "S18 C1X", - "POSTCODE_SECTOR": "S181", - "POSTCODE_OUTCODE": "S18", - "GP_PRACTICE_CODE": "U69384", - "PCN": "QJ2" - }, - { - "NHS_NUMBER": "0000000001", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "Flu", - "STATUS": "Actionable" - }, - { - "NHS_NUMBER": "0000000001", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "RSV", - "STATUS": "NotEligible" - }, - { - "NHS_NUMBER": "0000000002", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "19890518", - "GENDER": 1, - "POSTCODE": "S18 B1X", - "POSTCODE_SECTOR": "S181", - "POSTCODE_OUTCODE": "S18", - "GP_PRACTICE_CODE": "U33827", - "PCN": "RJ3" - }, - { - "NHS_NUMBER": "0000000002", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "Flu", - "STATUS": "Actionable" - }, - { - "NHS_NUMBER": "0000000002", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "RSV", - "STATUS": "NotActionable" - }, - { - "NHS_NUMBER": "0000000003", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "19780323", - "GENDER": 1, - "POSTCODE": "S18 X9X", - "POSTCODE_SECTOR": "S189", - "POSTCODE_OUTCODE": "S18", - "GP_PRACTICE_CODE": "U90732", - "PCN": "QJ2" - }, - { - "NHS_NUMBER": "0000000003", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "Flu", - "STATUS": "Actionable" - }, - { - "NHS_NUMBER": "0000000004", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "19570120", - "GENDER": 0, - "POSTCODE": "S18 B6X", - "POSTCODE_SECTOR": "S186", - "POSTCODE_OUTCODE": "S18", - "GP_PRACTICE_CODE": "U52353", - "PCN": "LM8" - }, - { - "NHS_NUMBER": "0000000004", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "RSV", - "STATUS": "Actionable" - }, - { - "NHS_NUMBER": "0000000004", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "RSV", - "STATUS": "NotEligible" - }, - { - "NHS_NUMBER": "0000000005", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "19910411", - "GENDER": 1, - "POSTCODE": "S18 B5X", - "POSTCODE_SECTOR": "S185", - "POSTCODE_OUTCODE": "S18", - "GP_PRACTICE_CODE": "U54464", - "PCN": "LM8" - }, - { - "NHS_NUMBER": "0000000005", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "RSV", - "STATUS": "NotActionable" - }, - { - "NHS_NUMBER": "0000000005", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "Covid", - "STATUS": "NotEligible" - }, - { - "NHS_NUMBER": "0000000006", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "19450206", - "GENDER": 1, - "POSTCODE": "S18 B9X", - "POSTCODE_SECTOR": "S189", - "POSTCODE_OUTCODE": "S18", - "GP_PRACTICE_CODE": "U97038", - "PCN": "RJ3" - }, - { - "NHS_NUMBER": "0000000006", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "RSV", - "STATUS": "Actionable" - }, - { - "NHS_NUMBER": "0000000006", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "Flu", - "STATUS": "NotEligible" - }, - { - "NHS_NUMBER": "0000000007", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "19590705", - "GENDER": 0, - "POSTCODE": "S18 B3X", - "POSTCODE_SECTOR": "S183", - "POSTCODE_OUTCODE": "S18", - "GP_PRACTICE_CODE": "U56174", - "PCN": "TK9" - }, - { - "NHS_NUMBER": "0000000007", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "Covid", - "STATUS": "NotEligible" - }, - { - "NHS_NUMBER": "0000000007", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "Covid", - "STATUS": "Actionable" - }, - { - "NHS_NUMBER": "0000000008", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "19990716", - "GENDER": 0, - "POSTCODE": "S18 A5X", - "POSTCODE_SECTOR": "S185", - "POSTCODE_OUTCODE": "S18", - "GP_PRACTICE_CODE": "U33386", - "PCN": "QJ2" - }, - { - "NHS_NUMBER": "0000000008", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "Flu", - "STATUS": "NotActionable" - }, - { - "NHS_NUMBER": "0000000009", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "20100426", - "GENDER": 0, - "POSTCODE": "S18 X4X", - "POSTCODE_SECTOR": "S184", - "POSTCODE_OUTCODE": "S18", - "GP_PRACTICE_CODE": "U94864", - "PCN": "LM8" - }, - { - "NHS_NUMBER": "0000000009", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "Flu", - "STATUS": "Actionable" - }, - { - "NHS_NUMBER": "0000000009", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "Flu", - "STATUS": "NotActionable" - }, - { - "NHS_NUMBER": "0000000010", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "19931007", - "GENDER": 1, - "POSTCODE": "S18 B5X", - "POSTCODE_SECTOR": "S185", - "POSTCODE_OUTCODE": "S18", - "GP_PRACTICE_CODE": "U74914", - "PCN": "LM8" - }, - { - "NHS_NUMBER": "0000000010", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "RSV", - "STATUS": "NotEligible" - }, - { - "NHS_NUMBER": "0000000010", - "ATTRIBUTE_TYPE": "ELIGIBILITY", - "CONDITION": "Flu", - "STATUS": "NotEligible" - } -] diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_001.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_001.json deleted file mode 100644 index fc1326c0a..000000000 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_001.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "responseId": "<>", - "meta": { - "lastUpdated": "<>" - }, - "processedSuggestions": [ - { - "condition": "RSV", - "status": "Actionable", - "statusText": "You should have the RSV vaccine", - "eligibilityCohorts": [ - { - "cohortCode": "rsv_age_rolling", - "cohortText": "You are aged 75 to 79 years old.", - "cohortStatus": "Actionable" - } - ], - "suitabilityRules": [], - "actions": [ - { - "actionType": "ButtonAuthLink", - "actionCode": "BookNBS", - "description": "", - "urlLink": "http://www.nhs.uk/book-rsv", - "urlLabel": "Continue to booking" - } - ] - } - ] -} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_002.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_002.json deleted file mode 100644 index e45a371df..000000000 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_002.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "responseId": "<>", - "meta": { - "lastUpdated": "<>" - }, - "processedSuggestions": [ - { - "condition": "RSV", - "status": "Actionable", - "statusText": "You should have the RSV vaccine", - "eligibilityCohorts": [ - { - "cohortCode": "rsv_age_rolling", - "cohortText": "You are aged 75 to 79 years old.", - "cohortStatus": "Actionable" - } - ], - "suitabilityRules": [], - "actions": [ - { - "actionType": "CareCardWithText", - "actionCode": "BookLocal", - "description": "##Getting the vaccine\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination." - } - ] - } - ] -} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_003.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_003.json deleted file mode 100644 index e814e90b3..000000000 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_003.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "responseId": "<>", - "meta": { - "lastUpdated": "<>" - }, - "processedSuggestions": [ - { - "condition": "RSV", - "status": "Actionable", - "statusText": "You should have the RSV vaccine", - "eligibilityCohorts": [ - { - "cohortCode": "rsv_age_catchup", - "cohortText": "You turned 80 between 2nd September 2024 and 31st August 2025", - "cohortStatus": "Actionable" - } - ], - "suitabilityRules": [], - "actions": [ - { - "actionType": "CareCardWithText", - "actionCode": "BookLocal", - "description": "##Getting the vaccine\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination." - } - ] - } - ] -} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_004.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_004.json deleted file mode 100644 index ed4663779..000000000 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_004.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "responseId": "<>", - "meta": { - "lastUpdated": "<>" - }, - "processedSuggestions": [ - { - "condition": "RSV", - "status": "Actionable", - "statusText": "You should have the RSV vaccine", - "eligibilityCohorts": [], - "suitabilityRules": [], - "actions": [ - { - "actionType": "CardWithAuthButton", - "actionCode": "AmendNBS", - "description": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", - "urlLink": "http://www.nhs.uk/book-rsv", - "urlLabel": "Manage your appointment" - } - ] - } - ] -} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_005.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_005.json deleted file mode 100644 index 687b05405..000000000 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_005.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "responseId": "<>", - "meta": { - "lastUpdated": "<>" - }, - "processedSuggestions": [ - { - "condition": "RSV", - "status": "Actionable", - "statusText": "You should have the RSV vaccine", - "eligibilityCohorts": [], - "suitabilityRules": [], - "actions": [ - { - "actionType": "CardWithText", - "actionCode": "ManageLocal", - "description": "##You have an RSV vaccination appointment\nContact your healthcare provider to change or cancel your appointment." - } - ] - } - ] -} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_006.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_006.json deleted file mode 100644 index 3413b999b..000000000 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_006.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "responseId": "<>", - "meta": { - "lastUpdated": "<>" - }, - "processedSuggestions": [ - { - "condition": "RSV", - "status": "NotActionable", - "statusText": "You should have the RSV vaccine", - "eligibilityCohorts": [], - "suitabilityRules": [ - { - "ruleType": "S", - "ruleCode": "AlreadyVaccinated", - "ruleText": "##You've had your RSV vaccination\nBased on our records, you recently had this vaccination. You do not need to do anything." - } - ], - "actions": [] - } - ] -} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_007.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_007.json deleted file mode 100644 index e292c3719..000000000 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_007.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "responseId": "<>", - "meta": { - "lastUpdated": "<>" - }, - "processedSuggestions": [ - { - "condition": "RSV", - "status": "NotActionable", - "statusText": "You should have the RSV vaccine", - "eligibilityCohorts": [ - { - "cohortCode": "rsv_age_rolling", - "cohortText": "You are aged 75 to 79 years old.", - "cohortStatus": "NotActionable" - } - ], - "suitabilityRules": [ - { - "ruleType": "S", - "ruleCode": "NotAvailable", - "ruleText": "Vaccinations are not currently available" - } - ], - "actions": [] - } - ] -} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_008.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_008.json deleted file mode 100644 index 01fef4fb6..000000000 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_008.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "responseId": "<>", - "meta": { - "lastUpdated": "<>" - }, - "processedSuggestions": [] -} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_009.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_009.json deleted file mode 100644 index 65ecaf7a5..000000000 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_009.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "responseId": "<>", - "meta": { - "lastUpdated": "<>" - }, - "processedSuggestions": [ - { - "condition": "RSV", - "status": "NotActionable", - "statusText": "You should have the RSV vaccine", - "eligibilityCohorts": [ - { - "cohortCode": "rsv_age_rolling", - "cohortText": "You are aged 75 to 79 years old.", - "cohortStatus": "NotActionable" - } - ], - "suitabilityRules": [ - { - "ruleType": "S", - "ruleCode": "NotYetDue", - "ruleText": "Your next dose is not yet due." - } - ], - "actions": [] - } - ] -} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_010.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_010.json deleted file mode 100644 index 0b9dc7258..000000000 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_010.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "responseId": "<>", - "meta": { - "lastUpdated": "<>" - }, - "processedSuggestions": [ - { - "condition": "RSV", - "status": "NotActionable", - "statusText": "You should have the RSV vaccine", - "eligibilityCohorts": [ - { - "cohortCode": "rsv_age_rolling", - "cohortText": "You are aged 75 to 79 years old.", - "cohortStatus": "NotActionable" - } - ], - "suitabilityRules": [ - { - "ruleType": "S", - "ruleCode": "TooClose", - "ruleText": "Your previous vaccination was less than 91 days ago." - } - ], - "actions": [] - } - ] -} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_011.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_011.json deleted file mode 100644 index 3070205d0..000000000 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_011.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "responseId": "<>", - "meta": { - "lastUpdated": "<>" - }, - "processedSuggestions": [ - { - "condition": "RSV", - "status": "NotActionable", - "statusText": "You should have the RSV vaccine", - "eligibilityCohorts": [ - { - "cohortCode": "rsv_age_rolling", - "cohortText": "You are aged 75 to 79 years old.", - "cohortStatus": "NotActionable" - } - ], - "suitabilityRules": [ - { - "ruleType": "S", - "ruleCode": "OtherSetting", - "ruleText": "##Getting the vaccine\nOur record show you're living in a setting where care is provided.\nIf you think you should have the RSV vaccine, speak to a member of staff where you live." - } - ], - "actions": [] - } - ] -} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_012.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_012.json deleted file mode 100644 index 4996198d2..000000000 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_012.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "responseId": "<>", - "meta": { - "lastUpdated": "<>" - }, - "processedSuggestions": [ - { - "condition": "RSV", - "status": "NotActionable", - "statusText": "You should have the RSV vaccine", - "eligibilityCohorts": [ - { - "cohortCode": "unknown_cohort_membership", - "cohortText": "Our records do not say why you are eligible", - "cohortStatus": "NotActionable" - } - ], - "suitabilityRules": [ - { - "ruleType": "S", - "ruleCode": "AlreadyVaccinated", - "ruleText": "##You've had your RSV vaccination\nBased on our records, you recently had this vaccination.You do not need to do anything." - } - ], - "actions": [] - } - ] -} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_013.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_013.json deleted file mode 100644 index 3413b999b..000000000 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_013.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "responseId": "<>", - "meta": { - "lastUpdated": "<>" - }, - "processedSuggestions": [ - { - "condition": "RSV", - "status": "NotActionable", - "statusText": "You should have the RSV vaccine", - "eligibilityCohorts": [], - "suitabilityRules": [ - { - "ruleType": "S", - "ruleCode": "AlreadyVaccinated", - "ruleText": "##You've had your RSV vaccination\nBased on our records, you recently had this vaccination. You do not need to do anything." - } - ], - "actions": [] - } - ] -} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_014.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_014.json deleted file mode 100644 index 3858968e4..000000000 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_014.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "responseId": "<>", - "meta": { - "lastUpdated": "<>" - }, - "processedSuggestions": [ - { - "condition": "RSV", - "status": "NotEligible", - "statusText": "We do not believe you should have this vaccine", - "eligibilityCohorts": [ - { - "cohortCode": "rsv_age_rolling", - "cohortText": "You are not aged 75 to 79 years old.", - "cohortStatus": "NotEligible" - }, - { - "cohortCode": "rsv_age_catchup", - "cohortText": "You did not turn 80 between 2nd September 2024 and 31st August 2025", - "cohortStatus": "NotEligible" - } - ], - "suitabilityRules": [], - "actions": [ - { - "actionType": "CardWithText", - "actionCode": "HealtchareProInfo", - "description": "##If you think you need this vaccine\nSpeak to your healthcare professional if you think you should be offered this vaccination." - } - ] - } - ] -} From 2a6d55cb7a371cd26cb628cefcaabb69c5154825 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 11:29:02 +0100 Subject: [PATCH 30/72] moved more files --- .../smokeTestConfigs/AUTO_RSV_SB_001.json | 235 ++++++++++++++++++ .../smokeTestData/AUTO_RSV_SB_001.json | 41 +++ .../smokeTestData/AUTO_RSV_SB_002.json | 41 +++ .../smokeTestData/AUTO_RSV_SB_003.json | 41 +++ .../smokeTestData/AUTO_RSV_SB_004.json | 47 ++++ .../smokeTestData/AUTO_RSV_SB_005.json | 47 ++++ .../smokeTestData/AUTO_RSV_SB_006.json | 46 ++++ .../smokeTestData/AUTO_RSV_SB_007.json | 41 +++ .../smokeTestData/AUTO_RSV_SB_008.json | 46 ++++ .../smokeTestData/AUTO_RSV_SB_009.json | 46 ++++ .../smokeTestData/AUTO_RSV_SB_010.json | 46 ++++ .../smokeTestData/AUTO_RSV_SB_011.json | 41 +++ .../smokeTestData/AUTO_RSV_SB_012.json | 36 +++ .../smokeTestData/AUTO_RSV_SB_013.json | 46 ++++ .../responses/.temp/AUTO_RSV_SB_001_old.json | 30 +++ .../smokeTestResponses/AUTO_RSV_SB_001.json | 29 +++ .../smokeTestResponses/AUTO_RSV_SB_002.json | 28 +++ .../smokeTestResponses/AUTO_RSV_SB_003.json | 28 +++ .../smokeTestResponses/AUTO_RSV_SB_004.json | 24 ++ .../smokeTestResponses/AUTO_RSV_SB_005.json | 22 ++ .../smokeTestResponses/AUTO_RSV_SB_006.json | 22 ++ .../smokeTestResponses/AUTO_RSV_SB_007.json | 28 +++ .../smokeTestResponses/AUTO_RSV_SB_008.json | 7 + .../smokeTestResponses/AUTO_RSV_SB_009.json | 28 +++ .../smokeTestResponses/AUTO_RSV_SB_010.json | 28 +++ .../smokeTestResponses/AUTO_RSV_SB_011.json | 28 +++ .../smokeTestResponses/AUTO_RSV_SB_012.json | 28 +++ .../smokeTestResponses/AUTO_RSV_SB_013.json | 22 ++ .../smokeTestResponses/AUTO_RSV_SB_014.json | 33 +++ 29 files changed, 1185 insertions(+) create mode 100644 tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_001.json create mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json create mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json create mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json create mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_004.json create mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_005.json create mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_006.json create mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_007.json create mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_008.json create mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_009.json create mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json create mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_011.json create mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_012.json create mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_013.json create mode 100644 tests/e2e/data/responses/.temp/AUTO_RSV_SB_001_old.json create mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_001.json create mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_002.json create mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_003.json create mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_004.json create mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_005.json create mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_006.json create mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_007.json create mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_008.json create mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_009.json create mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_010.json create mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_011.json create mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_012.json create mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_013.json create mode 100644 tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_014.json diff --git a/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_001.json b/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_001.json new file mode 100644 index 000000000..bf9ed746c --- /dev/null +++ b/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_001.json @@ -0,0 +1,235 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "Automation RSV - Smoke Test Config", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "BOOK_LOCAL", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "BOOK_LOCAL", + "Version": "1", + "Name": "Automation RSV - Smoke Test Config Iteration", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "M", + "IterationCohorts": [ + { + "CohortLabel": "rsv_75_rolling", + "CohortGroup": "rsv_age_rolling", + "PositiveDescription": "You are aged 75 to 79 years old.", + "NegativeDescription": "You are not aged 75 to 79 years old.", + "Priority": 0 + }, + { + "CohortLabel": "rsv_75to79_2024", + "CohortGroup": "rsv_age_catchup", + "PositiveDescription": "You turned 80 between 2nd September 2024 and 31st August 2025", + "NegativeDescription": "You did not turn 80 after 1 September 2024 and get vaccinated by 31 August 2025", + "Priority": 10 + }, + { + "CohortLabel": "elid_all_people", + "CohortGroup": "elid_all_people", + "PositiveDescription": "", + "NegativeDescription": "", + "Priority": 20 + }, + { + "CohortLabel": "no_group_description", + "CohortGroup": "", + "PositiveDescription": "", + "NegativeDescription": "", + "Priority": 30 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "Assure only already vaccinated taken from magic cohort", + "Description": "Exclude anyone who has NOT been given a dose of RSV Vaccination from the magic cohort", + "Operator": "is_empty", + "Comparator": "", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "elid_all_people", + "Priority": 100 + }, + { + "Type": "F", + "Name": "Under Age - Under 75 Years on day of execution", + "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", + "Priority": 120, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75" + }, + { + "Type": "F", + "Name": "Exclude Too OLD", + "Description": "Exclude anyone over 80", + "Priority": 130, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "<", + "Comparator": "-80" + }, + { + "Type": "S", + "Name": "AlreadyVaccinated", + "Description": "##You've had your RSV vaccination\nWe believe you had your vaccination on <>.", + "Priority": 500, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "Y>=", + "Comparator": "-25", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "NotAvailable", + "Description": "NotAvailable|Vaccinations are not currently available.", + "Priority": 510, + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "Operator": "=", + "Comparator": "SUPPRESSED_ICB" + }, + { + "Type": "R", + "Name": "Actionable Not Vaccinated", + "Description": "Book An Appointment", + "Priority": 1010, + "Operator": "is_empty", + "Comparator": "", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CommsRouting": "BOOK_NBS" + }, + { + "Type": "R", + "Name": "Actionable Not Vaccinated", + "Description": "Book An Appointment", + "Priority": 1010, + "Operator": "=", + "Comparator": "LS2", + "AttributeLevel": "PERSON", + "AttributeName": "POSTCODE_SECTOR", + "CommsRouting": "BOOK_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1020, + "Operator": ">=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1020, + "Operator": "=", + "Comparator": "NBS", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1030, + "Operator": ">=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "MANAGE_LOCAL" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1030, + "Operator": "!=", + "Comparator": "NBS", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", + "CommsRouting": "MANAGE_LOCAL" + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + }, + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Manage your appointment" + }, + "CONTACT_GP": { + "ExternalRoutingCode": "ContactGP", + "ActionDescription": "Contact your GP", + "ActionType": "InfoText", + "UrlLink": "", + "UrlLabel": "" + }, + "BOOK_LOCAL": { + "ExternalRoutingCode": "BookLocal", + "ActionDescription": "##Getting the vaccine\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "ActionType": "InfoText", + "UrlLink": "", + "UrlLabel": "" + }, + "MANAGE_LOCAL": { + "ExternalRoutingCode": "ManageLocal", + "ActionDescription": "##You have an RSV vaccination appointment\nContact your healthcare provider to change or cancel your appointment.", + "ActionType": "CardWithText", + "UrlLink": "", + "UrlLabel": "" + }, + "CHECK_CORRECT": { + "ExternalRoutingCode": "CheckCorrect", + "ActionDescription": "##If you think this is incorrect\\nIf you have not had this vaccination and you think you should, speak to your healthcare professional.", + "ActionType": "InfoText", + "UrlLink": "", + "UrlLabel": "" + } + } + } + ] + } +} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json new file mode 100644 index 000000000..429c0c9da --- /dev/null +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json @@ -0,0 +1,41 @@ +{ + "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested actions (with booking)", + "config_filename": "AUTO_RSV_SB_001.json", + "data": [ + { + "NHS_NUMBER": "5000000001", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_75_rolling": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "5000000001", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS2", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json new file mode 100644 index 000000000..fa00e7ef8 --- /dev/null +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json @@ -0,0 +1,41 @@ +{ + "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested action (not booking)", + "config_filename": "AUTO_RSV_SB_001.json", + "data": [ + { + "NHS_NUMBER": "5000000002", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_75_rolling": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "5000000002", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json new file mode 100644 index 000000000..dbece05d1 --- /dev/null +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json @@ -0,0 +1,41 @@ +{ + "scenario_name": "RSV - Actionable due to membership of an alternative Age Cohort incl. suggested action", + "config_filename": "AUTO_RSV_SB_001.json", + "data": [ + { + "NHS_NUMBER": "5000000003", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_75to79_2024": { + "M": { + "dateJoined": { + "S": "20221120" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "5000000003", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_004.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_004.json new file mode 100644 index 000000000..ceae22035 --- /dev/null +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_004.json @@ -0,0 +1,47 @@ +{ + "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested action (existing national booking)", + "config_filename": "AUTO_RSV_SB_001.json", + "data": [ + { + "NHS_NUMBER": "5000000004", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "no_group_description": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "5000000004", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "5000000004", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_005.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_005.json new file mode 100644 index 000000000..e71b7c0c7 --- /dev/null +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_005.json @@ -0,0 +1,47 @@ +{ + "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested actions (with local booking)", + "config_filename": "AUTO_RSV_SB_001.json", + "data": [ + { + "NHS_NUMBER": "5000000005", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "no_group_description": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "5000000005", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "5000000005", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "ACC" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_006.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_006.json new file mode 100644 index 000000000..0a38a4223 --- /dev/null +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_006.json @@ -0,0 +1,46 @@ +{ + "scenario_name": "RSV - Not Actionable despite membership of an Age Cohort, already vaccinated", + "config_filename": "AUTO_RSV_SB_001.json", + "data": [ + { + "NHS_NUMBER": "5000000006", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_75_rolling": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "5000000006", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "5000000006", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_007.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_007.json new file mode 100644 index 000000000..e132f791a --- /dev/null +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_007.json @@ -0,0 +1,41 @@ +{ + "scenario_name": "RSV - Not Actionable, membership of Age Cohort, no available vaccinations (not available type 1)", + "config_filename": "AUTO_RSV_SB_001.json", + "data": [ + { + "NHS_NUMBER": "5000000007", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_75_rolling": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "5000000007", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "SUPPRESSED_ICB", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_008.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_008.json new file mode 100644 index 000000000..9b9e6187a --- /dev/null +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_008.json @@ -0,0 +1,46 @@ +{ + "scenario_name": "RSV - No RSV response as no active campaign (not available type 2)", + "config_filename": "AUTO_RSV_SB_001.json", + "data": [ + { + "NHS_NUMBER": "5000000008", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_75_rolling": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "5000000008", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "5000000008", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_009.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_009.json new file mode 100644 index 000000000..19fc51cfb --- /dev/null +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_009.json @@ -0,0 +1,46 @@ +{ + "scenario_name": "RSV - Not Actionable, membership of Age Cohort, dose not yet due", + "config_filename": "AUTO_RSV_SB_001.json", + "data": [ + { + "NHS_NUMBER": "5000000009", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_75_rolling": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "5000000009", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "5000000009", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json new file mode 100644 index 000000000..febabd9de --- /dev/null +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json @@ -0,0 +1,46 @@ +{ + "scenario_name": "RSV - Not Actionable, membership of Age Cohort, dose not far enough apart", + "config_filename": "AUTO_RSV_SB_001.json", + "data": [ + { + "NHS_NUMBER": "5000000010", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_75_rolling": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "5000000010", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "5000000010", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_011.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_011.json new file mode 100644 index 000000000..4f245bf8c --- /dev/null +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_011.json @@ -0,0 +1,41 @@ +{ + "scenario_name": "RSV - Not Actionable despite to membership of an Age Cohort with reasoning of vaccination given in other setting (e.g. care home)", + "config_filename": "AUTO_RSV_SB_001.json", + "data": [ + { + "NHS_NUMBER": "5000000011", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_75_rolling": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "5000000011", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "Y", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_012.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_012.json new file mode 100644 index 000000000..3323bd404 --- /dev/null +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_012.json @@ -0,0 +1,36 @@ +{ + "scenario_name": "RSV - Not Actionable despite no cohort membership with reasoning of already vaccinated (type 1 includes unknown cohort)", + "config_filename": "AUTO_RSV_SB_001.json", + "data": [ + { + "NHS_NUMBER": "5000000012", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": {} + } + }, + { + "NHS_NUMBER": "5000000012", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "5000000012", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_013.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_013.json new file mode 100644 index 000000000..e84b7fa90 --- /dev/null +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_013.json @@ -0,0 +1,46 @@ +{ + "scenario_name": "RSV - Not Actionable despite no cohort membership with reasoning of already vaccinated (type 2 includes no cohorts)", + "config_filename": "AUTO_RSV_SB_001.json", + "data": [ + { + "NHS_NUMBER": "5000000013", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "covid_16+_immunosuppression": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "5000000013", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "5000000013", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/responses/.temp/AUTO_RSV_SB_001_old.json b/tests/e2e/data/responses/.temp/AUTO_RSV_SB_001_old.json new file mode 100644 index 000000000..92056db6f --- /dev/null +++ b/tests/e2e/data/responses/.temp/AUTO_RSV_SB_001_old.json @@ -0,0 +1,30 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_rolling", + "cohortStatus": "Actionable", + "cohortText": "You are aged 75 to 79 years old." + } + ], + "suitabilityRules": [], + "actions": [ + { + "actionType": "ButtonWithAuthLink", + "actionCode": "BookNBS", + "description": "", + "urlLink": "http://www.nhs.uk/book-rsv", + "urlLabel": "Continue to booking" + } + ] + } + ] +} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_001.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_001.json new file mode 100644 index 000000000..0c28e2dd1 --- /dev/null +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_001.json @@ -0,0 +1,29 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "Actionable", + "statusText": "Status.actionable", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_rolling", + "cohortStatus": "Actionable", + "cohortText": "You are aged 75 to 79 years old." + } + ], + "suitabilityRules": [], + "actions": [ + { + "action_type": "ButtonWithAuthLink", + "action_code": "BookNBS", + "url_link": "http://www.nhs.uk/book-rsv", + "url_label": "Continue to booking" + } + ] + } + ] +} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_002.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_002.json new file mode 100644 index 000000000..d51710fd9 --- /dev/null +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_002.json @@ -0,0 +1,28 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "Actionable", + "statusText": "Status.actionable", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_rolling", + "cohortText": "You are aged 75 to 79 years old.", + "cohortStatus": "Actionable" + } + ], + "suitabilityRules": [], + "actions": [ + { + "action_type": "InfoText", + "action_code": "BookLocal", + "action_description": "##Getting the vaccine\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination." + } + ] + } + ] +} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_003.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_003.json new file mode 100644 index 000000000..d8388e6d6 --- /dev/null +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_003.json @@ -0,0 +1,28 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "Actionable", + "statusText": "Status.actionable", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_catchup", + "cohortText": "You turned 80 between 2nd September 2024 and 31st August 2025", + "cohortStatus": "Actionable" + } + ], + "suitabilityRules": [], + "actions": [ + { + "action_type": "InfoText", + "action_code": "BookLocal", + "action_description": "##Getting the vaccine\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination." + } + ] + } + ] +} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_004.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_004.json new file mode 100644 index 000000000..ed4663779 --- /dev/null +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_004.json @@ -0,0 +1,24 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "eligibilityCohorts": [], + "suitabilityRules": [], + "actions": [ + { + "actionType": "CardWithAuthButton", + "actionCode": "AmendNBS", + "description": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", + "urlLink": "http://www.nhs.uk/book-rsv", + "urlLabel": "Manage your appointment" + } + ] + } + ] +} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_005.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_005.json new file mode 100644 index 000000000..687b05405 --- /dev/null +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_005.json @@ -0,0 +1,22 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "eligibilityCohorts": [], + "suitabilityRules": [], + "actions": [ + { + "actionType": "CardWithText", + "actionCode": "ManageLocal", + "description": "##You have an RSV vaccination appointment\nContact your healthcare provider to change or cancel your appointment." + } + ] + } + ] +} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_006.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_006.json new file mode 100644 index 000000000..ad80f0baa --- /dev/null +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_006.json @@ -0,0 +1,22 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "eligibilityCohorts": [], + "suitabilityRules": [ + { + "ruleType": "S", + "ruleCode": "AlreadyVaccinated", + "ruleText": "##You've had your RSV vaccination\nWe believe you had your vaccination on <>." + } + ], + "actions": [] + } + ] +} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_007.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_007.json new file mode 100644 index 000000000..d555621c0 --- /dev/null +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_007.json @@ -0,0 +1,28 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_rolling", + "cohortStatus": "NotActionable", + "cohortText": "You are aged 75 to 79 years old." + } + ], + "suitabilityRules": [ + { + "ruleCode": "NotAvailable", + "ruleText": "Vaccinations are not currently available", + "ruleType": "S" + } + ], + "actions": [] + } + ] +} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_008.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_008.json new file mode 100644 index 000000000..01fef4fb6 --- /dev/null +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_008.json @@ -0,0 +1,7 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [] +} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_009.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_009.json new file mode 100644 index 000000000..65ecaf7a5 --- /dev/null +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_009.json @@ -0,0 +1,28 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_rolling", + "cohortText": "You are aged 75 to 79 years old.", + "cohortStatus": "NotActionable" + } + ], + "suitabilityRules": [ + { + "ruleType": "S", + "ruleCode": "NotYetDue", + "ruleText": "Your next dose is not yet due." + } + ], + "actions": [] + } + ] +} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_010.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_010.json new file mode 100644 index 000000000..0b9dc7258 --- /dev/null +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_010.json @@ -0,0 +1,28 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_rolling", + "cohortText": "You are aged 75 to 79 years old.", + "cohortStatus": "NotActionable" + } + ], + "suitabilityRules": [ + { + "ruleType": "S", + "ruleCode": "TooClose", + "ruleText": "Your previous vaccination was less than 91 days ago." + } + ], + "actions": [] + } + ] +} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_011.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_011.json new file mode 100644 index 000000000..3070205d0 --- /dev/null +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_011.json @@ -0,0 +1,28 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_rolling", + "cohortText": "You are aged 75 to 79 years old.", + "cohortStatus": "NotActionable" + } + ], + "suitabilityRules": [ + { + "ruleType": "S", + "ruleCode": "OtherSetting", + "ruleText": "##Getting the vaccine\nOur record show you're living in a setting where care is provided.\nIf you think you should have the RSV vaccine, speak to a member of staff where you live." + } + ], + "actions": [] + } + ] +} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_012.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_012.json new file mode 100644 index 000000000..4996198d2 --- /dev/null +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_012.json @@ -0,0 +1,28 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "eligibilityCohorts": [ + { + "cohortCode": "unknown_cohort_membership", + "cohortText": "Our records do not say why you are eligible", + "cohortStatus": "NotActionable" + } + ], + "suitabilityRules": [ + { + "ruleType": "S", + "ruleCode": "AlreadyVaccinated", + "ruleText": "##You've had your RSV vaccination\nBased on our records, you recently had this vaccination.You do not need to do anything." + } + ], + "actions": [] + } + ] +} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_013.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_013.json new file mode 100644 index 000000000..3413b999b --- /dev/null +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_013.json @@ -0,0 +1,22 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "eligibilityCohorts": [], + "suitabilityRules": [ + { + "ruleType": "S", + "ruleCode": "AlreadyVaccinated", + "ruleText": "##You've had your RSV vaccination\nBased on our records, you recently had this vaccination. You do not need to do anything." + } + ], + "actions": [] + } + ] +} diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_014.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_014.json new file mode 100644 index 000000000..3858968e4 --- /dev/null +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_014.json @@ -0,0 +1,33 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "NotEligible", + "statusText": "We do not believe you should have this vaccine", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_rolling", + "cohortText": "You are not aged 75 to 79 years old.", + "cohortStatus": "NotEligible" + }, + { + "cohortCode": "rsv_age_catchup", + "cohortText": "You did not turn 80 between 2nd September 2024 and 31st August 2025", + "cohortStatus": "NotEligible" + } + ], + "suitabilityRules": [], + "actions": [ + { + "actionType": "CardWithText", + "actionCode": "HealtchareProInfo", + "description": "##If you think you need this vaccine\nSpeak to your healthcare professional if you think you should be offered this vaccination." + } + ] + } + ] +} From 7714d7e34625fb94556cdcf4fbf39ac4cfb861a1 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 11:29:09 +0100 Subject: [PATCH 31/72] moved more files --- .../smokeTestData/AUTO_RSV_SB_014.json | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_014.json diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_014.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_014.json new file mode 100644 index 000000000..7861cab99 --- /dev/null +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_014.json @@ -0,0 +1,41 @@ +{ + "scenario_name": "RSV - Not Eligible", + "config_filename": "AUTO_RSV_SB_001.json", + "data": [ + { + "NHS_NUMBER": "5000000014", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "covid_16+_immunosuppression": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "5000000014", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} From a7ab56ce774b6f92a31306d8a0f06e39b0ecfb14 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 11:43:13 +0100 Subject: [PATCH 32/72] moved more files --- tests/e2e/tests/conftest.py | 9 +- tests/e2e/utils/eligibilityApiClient.py | 122 ++++++++++++++++++++++++ tests/e2e/utils/s3ConfigManager.py | 48 +++++----- 3 files changed, 154 insertions(+), 25 deletions(-) create mode 100644 tests/e2e/utils/eligibilityApiClient.py diff --git a/tests/e2e/tests/conftest.py b/tests/e2e/tests/conftest.py index f220cb1db..c0c070914 100644 --- a/tests/e2e/tests/conftest.py +++ b/tests/e2e/tests/conftest.py @@ -7,6 +7,8 @@ import pytest from dotenv import load_dotenv +from tests.e2e.utils.eligibilityApiClient import EligibilityApiClient + # Load environment variables from .env.local load_dotenv(dotenv_path=".env.local") @@ -28,7 +30,7 @@ def pytest_addoption(parser): parser.addoption("--keep-seed", action="store_true", default=False, help="Keep DynamoDB seed data after tests") -@pytest.fixture(scope="session", autouse=True) +@pytest.fixture(scope="session", autouse=False) def setup_dynamodb_data(request): """Insert test data into DynamoDB before tests and optionally clean up after.""" logger.info("[โš™] Connecting to DynamoDB table: %s in region %s", DYNAMODB_TABLE_NAME, AWS_REGION) @@ -78,3 +80,8 @@ def setup_dynamodb_data(request): except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError): logger.exception("[x] Failed to delete item %s", item.get("PK", "")) logger.info("[โœ“] Deleted %d/%d items", delete_count, len(items)) + +@pytest.fixture(scope="session") +def eligibility_client(): + base_url = "https://test.eligibility-signposting-api.nhs.uk/patient-check" + return EligibilityApiClient(base_url) diff --git a/tests/e2e/utils/eligibilityApiClient.py b/tests/e2e/utils/eligibilityApiClient.py new file mode 100644 index 000000000..f1a39e685 --- /dev/null +++ b/tests/e2e/utils/eligibilityApiClient.py @@ -0,0 +1,122 @@ +import os +import json +import boto3 +import requests +from pathlib import Path +from dotenv import load_dotenv +from botocore.exceptions import ClientError + +class EligibilityApiClient: + def __init__(self, api_url: str, cert_dir="tests/e2e/certs"): + load_dotenv(dotenv_path=os.path.join(os.path.dirname(__file__), "../.env")) + + self.api_url = api_url + self.region = os.getenv("AWS_REGION") + self.aws_access_key_id = os.getenv("AWS_ACCESS_KEY_ID") + self.aws_secret_access_key = os.getenv("AWS_SECRET_ACCESS_KEY") + self.aws_session_token = os.getenv("AWS_SESSION_TOKEN") + + self.cert_dir = Path(cert_dir) + self.cert_dir.mkdir(parents=True, exist_ok=True) + + self.cert_paths = { + "private_key": self.cert_dir / "api_private_key_cert.pem", + "client_cert": self.cert_dir / "api_client_cert.pem", + "ca_cert": self.cert_dir / "api_ca_cert.pem" + } + + self.ssm_params = { + "private_key": "/test/mtls/api_private_key_cert", + "client_cert": "/test/mtls/api_client_cert", + "ca_cert": "/test/mtls/api_ca_cert" + } + + self._ensure_certs_present() + + def _get_ssm_parameter(self, param_name: str, decrypt=True) -> str: + try: + client = boto3.client( + "ssm", + region_name=self.region, + aws_access_key_id=self.aws_access_key_id, + aws_secret_access_key=self.aws_secret_access_key, + aws_session_token=self.aws_session_token, + ) + response = client.get_parameter(Name=param_name, WithDecryption=decrypt) + return response["Parameter"]["Value"] + except ClientError as e: + raise RuntimeError(f"Error retrieving {param_name} from SSM: {e}") + + def _ensure_certs_present(self): + missing = [k for k, path in self.cert_paths.items() if not path.exists()] + if not missing: + return + + for cert_type in missing: + param_name = self.ssm_params[cert_type] + cert_value = self._get_ssm_parameter(param_name) + with open(self.cert_paths[cert_type], "w") as f: + f.write(cert_value) + + def make_request( + self, + nhs_number: str, + method="GET", + payload=None, + strict_ssl=False, + raise_on_error=True, + ): + url = f"{self.api_url.rstrip('/')}/{nhs_number}" + cert = (str(self.cert_paths["client_cert"]), str(self.cert_paths["private_key"])) + verify = str(self.cert_paths["ca_cert"]) if strict_ssl else False + + try: + response = requests.request( + method=method.upper(), + url=url, + cert=cert, + verify=verify, + json=payload, + timeout=10, + ) + + if raise_on_error: + response.raise_for_status() + + return self._parse_response(response) + + except requests.exceptions.SSLError as ssl_err: + raise RuntimeError(f"SSL error during request: {ssl_err}") + except requests.exceptions.RequestException as req_err: + if response := getattr(req_err, 'response', None): + return self._parse_response(response) + raise RuntimeError(f"Request error: {req_err}") + + def _parse_response(self, response: requests.Response): + try: + data = response.json() + cleaned = self._clean_response(data) + except json.JSONDecodeError: + cleaned = response.text + + return { + "status_code": response.status_code, + "headers": dict(response.headers), + "body": cleaned, + "ok": response.ok, + } + + def _clean_response(self, data: dict) -> dict: + keys_to_ignore = ["responseId", "lastUpdated"] + return self._remove_volatile_fields(data, keys_to_ignore) + + def _remove_volatile_fields(self, data, keys_to_remove): + if isinstance(data, dict): + return { + key: self._remove_volatile_fields(value, keys_to_remove) + for key, value in data.items() + if key not in keys_to_remove + } + elif isinstance(data, list): + return [self._remove_volatile_fields(item, keys_to_remove) for item in data] + return data diff --git a/tests/e2e/utils/s3ConfigManager.py b/tests/e2e/utils/s3ConfigManager.py index f723be788..77d1e1ce7 100644 --- a/tests/e2e/utils/s3ConfigManager.py +++ b/tests/e2e/utils/s3ConfigManager.py @@ -48,28 +48,28 @@ def upload_if_missing_or_changed(self, local_path: str): - # def config_exists_and_matches(self, local_path: str, s3_key: str) -> bool: - # try: - # s3_obj = self.s3_client.get_object(Bucket=self.bucket_name, Key=s3_key) - # s3_data = s3_obj["Body"].read() - # s3_hash = hashlib.sha256(s3_data).hexdigest() - # local_hash = self._calculate_file_hash(local_path) - # return s3_hash == local_hash - # except self.s3_client.exceptions.NoSuchKey: - # return False - # except botocore.exceptions.ClientError as e: - # if e.response["Error"]["Code"] == "NoSuchKey": - # return False - # raise - # - # def upload_if_missing_or_changed(self, local_path: str): - # filename = os.path.basename(local_path) - # s3_key = self._s3_key(filename) - # - # if self.config_exists_and_matches(local_path, s3_key): - # print(f"โœ… Config '{filename}' already exists and matches in S3. Skipping upload.") - # return + def config_exists_and_matches(self, local_path: str, s3_key: str) -> bool: + try: + s3_obj = self.s3_client.get_object(Bucket=self.bucket_name, Key=s3_key) + s3_data = s3_obj["Body"].read() + s3_hash = hashlib.sha256(s3_data).hexdigest() + local_hash = self._calculate_file_hash(local_path) + return s3_hash == local_hash + except self.s3_client.exceptions.NoSuchKey: + return False + except botocore.exceptions.ClientError as e: + if e.response["Error"]["Code"] == "NoSuchKey": + return False + raise + + def upload_if_missing_or_changed(self, local_path: str): + filename = os.path.basename(local_path) + s3_key = self._s3_key(filename) + + if self.config_exists_and_matches(local_path, s3_key): + print(f"โœ… Config '{filename}' already exists and matches in S3. Skipping upload.") + return - # print(f"โฌ†๏ธ Uploading new config '{filename}' to S3 as it is missing or changed...") - # self.s3_client.upload_file(local_path, self.bucket_name, s3_key) - # print(f"โœ… Uploaded to s3://{self.bucket_name}/{s3_key}") + print(f"โฌ†๏ธ Uploading new config '{filename}' to S3 as it is missing or changed...") + self.s3_client.upload_file(local_path, self.bucket_name, s3_key) + print(f"โœ… Uploaded to s3://{self.bucket_name}/{s3_key}") From 2b47d4fb71847f361e2809b40e62859c7998e6a9 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 11:46:57 +0100 Subject: [PATCH 33/72] moved more files --- tests/e2e/utils/s3ConfigManager.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/e2e/utils/s3ConfigManager.py b/tests/e2e/utils/s3ConfigManager.py index 77d1e1ce7..f22800873 100644 --- a/tests/e2e/utils/s3ConfigManager.py +++ b/tests/e2e/utils/s3ConfigManager.py @@ -62,14 +62,14 @@ def config_exists_and_matches(self, local_path: str, s3_key: str) -> bool: return False raise - def upload_if_missing_or_changed(self, local_path: str): - filename = os.path.basename(local_path) - s3_key = self._s3_key(filename) - - if self.config_exists_and_matches(local_path, s3_key): - print(f"โœ… Config '{filename}' already exists and matches in S3. Skipping upload.") - return - - print(f"โฌ†๏ธ Uploading new config '{filename}' to S3 as it is missing or changed...") - self.s3_client.upload_file(local_path, self.bucket_name, s3_key) - print(f"โœ… Uploaded to s3://{self.bucket_name}/{s3_key}") + # def upload_if_missing_or_changed(self, local_path: str): + # filename = os.path.basename(local_path) + # s3_key = self._s3_key(filename) + # + # if self.config_exists_and_matches(local_path, s3_key): + # print(f"โœ… Config '{filename}' already exists and matches in S3. Skipping upload.") + # return + # + # print(f"โฌ†๏ธ Uploading new config '{filename}' to S3 as it is missing or changed...") + # self.s3_client.upload_file(local_path, self.bucket_name, s3_key) + # print(f"โœ… Uploaded to s3://{self.bucket_name}/{s3_key}") From db553182818d7e8dd3809be9329045c01ebc5c30 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 11:56:08 +0100 Subject: [PATCH 34/72] .gitignore and small change to test --- .gitignore | 1 + tests/e2e/tests/test_smoke_tests.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 9829e47e8..2f0da506e 100644 --- a/.gitignore +++ b/.gitignore @@ -49,3 +49,4 @@ sandbox/specification/* /tests/e2e/data/out* /tests/e2e/reports/* /tests/e2e/.temp/ +/tests/e2e/certs/ diff --git a/tests/e2e/tests/test_smoke_tests.py b/tests/e2e/tests/test_smoke_tests.py index fc598bbaf..615c6ca40 100644 --- a/tests/e2e/tests/test_smoke_tests.py +++ b/tests/e2e/tests/test_smoke_tests.py @@ -37,7 +37,8 @@ def test_run_smoke_case(filename, scenario, eligibility_client): expected_response = all_expected_responses.get(filename).get("response_items", {}) # Assert and show details on failure - assert actual_response == expected_response, ( + assert actual_response["status_code"] == 200 + assert actual_response["body"] == expected_response, ( f"\nโŒ Mismatch in test: {filename}\n" f"NHS Number: {nhs_number}\n" f"Expected: {expected_response}\n" From 88181c73db278ed53fd6ba5a6122fc6c3b84aab8 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 12:04:51 +0100 Subject: [PATCH 35/72] tidy before push --- tests/e2e/tests/test_smoke_tests.py | 3 +-- tests/e2e/utils/data_loader.py | 10 ++++++++++ tests/e2e/utils/new_functions.py | 14 -------------- tests/e2e/utils/s3ConfigManager.py | 12 ------------ 4 files changed, 11 insertions(+), 28 deletions(-) delete mode 100644 tests/e2e/utils/new_functions.py diff --git a/tests/e2e/tests/test_smoke_tests.py b/tests/e2e/tests/test_smoke_tests.py index 615c6ca40..a2d0cd541 100644 --- a/tests/e2e/tests/test_smoke_tests.py +++ b/tests/e2e/tests/test_smoke_tests.py @@ -4,8 +4,7 @@ import pytest from tests.e2e.tests.test_config import * -from tests.e2e.utils.data_loader import load_all_expected_responses -from tests.e2e.utils.new_functions import initialise_tests +from tests.e2e.utils.data_loader import load_all_expected_responses, initialise_tests from tests.e2e.utils.s3ConfigManager import S3ConfigManager # UPDATE THE BELOW WITH LOCATION OF TEST DATA, EXPECTED RESPONSES AND CONFIGS IN DEFINED IN test_config.py # diff --git a/tests/e2e/utils/data_loader.py b/tests/e2e/utils/data_loader.py index 1f6fff0b2..2460fa772 100644 --- a/tests/e2e/utils/data_loader.py +++ b/tests/e2e/utils/data_loader.py @@ -2,9 +2,19 @@ import json from .common_test_helpers import clean_response +from .dynamo_helper import insert_into_dynamo from .placeholder_utils import resolve_placeholders from .placeholder_context import ResolvedPlaceholderContext, PlaceholderDTO +def initialise_tests(folder): + folder_path = os.path.abspath(folder) + all_data, dto = load_all_test_scenarios(folder_path) + + # Insert to Dynamo (placeholder) + for scenario in all_data.values(): + insert_into_dynamo(scenario["dynamo_items"]) + + return all_data, dto def resolve_placeholders_in_data(data, context, file_name): if isinstance(data, dict): diff --git a/tests/e2e/utils/new_functions.py b/tests/e2e/utils/new_functions.py deleted file mode 100644 index 5dd663553..000000000 --- a/tests/e2e/utils/new_functions.py +++ /dev/null @@ -1,14 +0,0 @@ -import os -from tests.e2e.utils.dynamo_helper import insert_into_dynamo -from tests.e2e.utils.data_loader import load_all_test_scenarios - - -def initialise_tests(folder): - folder_path = os.path.abspath(folder) - all_data, dto = load_all_test_scenarios(folder_path) - - # Insert to Dynamo (placeholder) - for scenario in all_data.values(): - insert_into_dynamo(scenario["dynamo_items"]) - - return all_data, dto diff --git a/tests/e2e/utils/s3ConfigManager.py b/tests/e2e/utils/s3ConfigManager.py index f22800873..2491cd3d7 100644 --- a/tests/e2e/utils/s3ConfigManager.py +++ b/tests/e2e/utils/s3ConfigManager.py @@ -61,15 +61,3 @@ def config_exists_and_matches(self, local_path: str, s3_key: str) -> bool: if e.response["Error"]["Code"] == "NoSuchKey": return False raise - - # def upload_if_missing_or_changed(self, local_path: str): - # filename = os.path.basename(local_path) - # s3_key = self._s3_key(filename) - # - # if self.config_exists_and_matches(local_path, s3_key): - # print(f"โœ… Config '{filename}' already exists and matches in S3. Skipping upload.") - # return - # - # print(f"โฌ†๏ธ Uploading new config '{filename}' to S3 as it is missing or changed...") - # self.s3_client.upload_file(local_path, self.bucket_name, s3_key) - # print(f"โœ… Uploaded to s3://{self.bucket_name}/{s3_key}") From a5c2d20194e096f6ae9e5c14fcb46c9b1ea7789b Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 13:11:35 +0100 Subject: [PATCH 36/72] linting changes and s3configmanager update --- tests/e2e/tests/test_config.py | 14 ++--- tests/e2e/tests/test_smoke_tests.py | 1 - tests/e2e/utils/data_loader.py | 47 +++++++---------- tests/e2e/utils/eligibilityApiClient.py | 68 ++++++++++++++----------- tests/e2e/utils/s3ConfigManager.py | 64 +++++++++++++++-------- 5 files changed, 107 insertions(+), 87 deletions(-) diff --git a/tests/e2e/tests/test_config.py b/tests/e2e/tests/test_config.py index 04bc82dd5..16b77822a 100644 --- a/tests/e2e/tests/test_config.py +++ b/tests/e2e/tests/test_config.py @@ -1,8 +1,8 @@ -S3_BUCKET = "eligibility-signposting-api-test-eli-rules" -S3_PREFIX = "manual-uploads" +# S3 Configuration +S3_BUCKET: str = "eligibility-signposting-api-test-eli-rules" +S3_PREFIX: str = "manual-uploads" -############################################################################# -SMOKE_TEST_DATA = "tests/e2e/data/dynamoDB/smokeTestData/" -SMOKE_TEST_RESPONSES = "tests/e2e/data/responses/smokeTestResponses/" -SMOKE_TEST_CONFIGS = "tests/e2e/data/configs/smokeTestConfigs" -############################################################################# +# Smoke Test Data Paths +SMOKE_TEST_DATA: str = "tests/e2e/data/dynamoDB/smokeTestData/" +SMOKE_TEST_RESPONSES: str = "tests/e2e/data/responses/smokeTestResponses/" +SMOKE_TEST_CONFIGS: str = "tests/e2e/data/configs/smokeTestConfigs" diff --git a/tests/e2e/tests/test_smoke_tests.py b/tests/e2e/tests/test_smoke_tests.py index a2d0cd541..9efadb2f8 100644 --- a/tests/e2e/tests/test_smoke_tests.py +++ b/tests/e2e/tests/test_smoke_tests.py @@ -1,4 +1,3 @@ -import json import os import pytest diff --git a/tests/e2e/utils/data_loader.py b/tests/e2e/utils/data_loader.py index 2460fa772..b347ca46b 100644 --- a/tests/e2e/utils/data_loader.py +++ b/tests/e2e/utils/data_loader.py @@ -1,7 +1,6 @@ import os import json -from .common_test_helpers import clean_response from .dynamo_helper import insert_into_dynamo from .placeholder_utils import resolve_placeholders from .placeholder_context import ResolvedPlaceholderContext, PlaceholderDTO @@ -60,33 +59,6 @@ def find_nhs(obj): return find_nhs(data) or "UNKNOWN" -def load_all_test_scenarios_old(folder_path): - all_data = {} - dto = PlaceholderDTO() # Shared across all files - - for filename in os.listdir(folder_path): - if not filename.endswith(".json"): - continue - - full_path = os.path.join(folder_path, filename) - - # Load JSON - with open(full_path, "r") as f: - raw_json = json.load(f) - - raw_data = raw_json["data"] - # Resolve placeholders into a shared DTO - resolved_data = resolve_placeholders_in_data(raw_data, dto, filename) - - # Extract NHS number from resolved data (e.g. first item or key field) - nhs_number = extract_nhs_number_from_data(resolved_data) # You must define this! - - all_data[filename] = { - "dynamo_items": resolved_data, - "nhs_number": nhs_number, - } - - return all_data, dto def load_all_expected_responses(folder_path): all_data = {} @@ -103,7 +75,7 @@ def load_all_expected_responses(folder_path): raw_json = json.load(f) resolved_data = resolve_placeholders_in_data(raw_json, dto, filename) - cleaned_data = clean_response(resolved_data,) + cleaned_data = clean_expected_response(resolved_data,) all_data[filename] = { "response_items": cleaned_data @@ -145,3 +117,20 @@ def load_all_test_scenarios(folder_path, config_folder_path="tests/e2e/data/conf } return all_data, dto + +def clean_expected_response(data: dict) -> dict: + keys_to_ignore = ["responseId", "lastUpdated"] + return _remove_volatile_fields(data, keys_to_ignore) + + +def _remove_volatile_fields(data, keys_to_remove): + if isinstance(data, dict): + return { + key: _remove_volatile_fields(value, keys_to_remove) + for key, value in data.items() + if key not in keys_to_remove + } + elif isinstance(data, list): + return [_remove_volatile_fields(item, keys_to_remove) for item in data] + return data + diff --git a/tests/e2e/utils/eligibilityApiClient.py b/tests/e2e/utils/eligibilityApiClient.py index f1a39e685..0f8261e7e 100644 --- a/tests/e2e/utils/eligibilityApiClient.py +++ b/tests/e2e/utils/eligibilityApiClient.py @@ -1,39 +1,43 @@ import os import json +from pathlib import Path +from typing import Any, Dict, Optional, Union + import boto3 import requests -from pathlib import Path from dotenv import load_dotenv from botocore.exceptions import ClientError +from requests import Response + class EligibilityApiClient: - def __init__(self, api_url: str, cert_dir="tests/e2e/certs"): + def __init__(self, api_url: str, cert_dir: str = "tests/e2e/certs") -> None: load_dotenv(dotenv_path=os.path.join(os.path.dirname(__file__), "../.env")) - self.api_url = api_url - self.region = os.getenv("AWS_REGION") - self.aws_access_key_id = os.getenv("AWS_ACCESS_KEY_ID") - self.aws_secret_access_key = os.getenv("AWS_SECRET_ACCESS_KEY") - self.aws_session_token = os.getenv("AWS_SESSION_TOKEN") + self.api_url: str = api_url + self.region: Optional[str] = os.getenv("AWS_REGION") + self.aws_access_key_id: Optional[str] = os.getenv("AWS_ACCESS_KEY_ID") + self.aws_secret_access_key: Optional[str] = os.getenv("AWS_SECRET_ACCESS_KEY") + self.aws_session_token: Optional[str] = os.getenv("AWS_SESSION_TOKEN") - self.cert_dir = Path(cert_dir) + self.cert_dir: Path = Path(cert_dir) self.cert_dir.mkdir(parents=True, exist_ok=True) - self.cert_paths = { + self.cert_paths: Dict[str, Path] = { "private_key": self.cert_dir / "api_private_key_cert.pem", "client_cert": self.cert_dir / "api_client_cert.pem", - "ca_cert": self.cert_dir / "api_ca_cert.pem" + "ca_cert": self.cert_dir / "api_ca_cert.pem", } - self.ssm_params = { + self.ssm_params: Dict[str, str] = { "private_key": "/test/mtls/api_private_key_cert", "client_cert": "/test/mtls/api_client_cert", - "ca_cert": "/test/mtls/api_ca_cert" + "ca_cert": "/test/mtls/api_ca_cert", } self._ensure_certs_present() - def _get_ssm_parameter(self, param_name: str, decrypt=True) -> str: + def _get_ssm_parameter(self, param_name: str, decrypt: bool = True) -> str: try: client = boto3.client( "ssm", @@ -45,9 +49,9 @@ def _get_ssm_parameter(self, param_name: str, decrypt=True) -> str: response = client.get_parameter(Name=param_name, WithDecryption=decrypt) return response["Parameter"]["Value"] except ClientError as e: - raise RuntimeError(f"Error retrieving {param_name} from SSM: {e}") + raise RuntimeError(f"Error retrieving {param_name} from SSM: {e}") from e - def _ensure_certs_present(self): + def _ensure_certs_present(self) -> None: missing = [k for k, path in self.cert_paths.items() if not path.exists()] if not missing: return @@ -55,20 +59,23 @@ def _ensure_certs_present(self): for cert_type in missing: param_name = self.ssm_params[cert_type] cert_value = self._get_ssm_parameter(param_name) - with open(self.cert_paths[cert_type], "w") as f: + with open(self.cert_paths[cert_type], "w", encoding="utf-8") as f: f.write(cert_value) def make_request( self, nhs_number: str, - method="GET", - payload=None, - strict_ssl=False, - raise_on_error=True, - ): + method: str = "GET", + payload: Optional[Union[Dict[str, Any], list]] = None, + strict_ssl: bool = False, + raise_on_error: bool = True, + ) -> Dict[str, Any]: url = f"{self.api_url.rstrip('/')}/{nhs_number}" - cert = (str(self.cert_paths["client_cert"]), str(self.cert_paths["private_key"])) - verify = str(self.cert_paths["ca_cert"]) if strict_ssl else False + cert = ( + str(self.cert_paths["client_cert"]), + str(self.cert_paths["private_key"]), + ) + verify: Union[bool, str] = str(self.cert_paths["ca_cert"]) if strict_ssl else False try: response = requests.request( @@ -86,13 +93,14 @@ def make_request( return self._parse_response(response) except requests.exceptions.SSLError as ssl_err: - raise RuntimeError(f"SSL error during request: {ssl_err}") + raise RuntimeError(f"SSL error during request: {ssl_err}") from ssl_err except requests.exceptions.RequestException as req_err: - if response := getattr(req_err, 'response', None): + response = getattr(req_err, "response", None) + if isinstance(response, Response): return self._parse_response(response) - raise RuntimeError(f"Request error: {req_err}") + raise RuntimeError(f"Request error: {req_err}") from req_err - def _parse_response(self, response: requests.Response): + def _parse_response(self, response: Response) -> Dict[str, Any]: try: data = response.json() cleaned = self._clean_response(data) @@ -106,11 +114,13 @@ def _parse_response(self, response: requests.Response): "ok": response.ok, } - def _clean_response(self, data: dict) -> dict: + def _clean_response(self, data: Any) -> Any: keys_to_ignore = ["responseId", "lastUpdated"] return self._remove_volatile_fields(data, keys_to_ignore) - def _remove_volatile_fields(self, data, keys_to_remove): + def _remove_volatile_fields( + self, data: Union[Dict[str, Any], list, Any], keys_to_remove: list + ) -> Any: if isinstance(data, dict): return { key: self._remove_volatile_fields(value, keys_to_remove) diff --git a/tests/e2e/utils/s3ConfigManager.py b/tests/e2e/utils/s3ConfigManager.py index 2491cd3d7..22d2df7d5 100644 --- a/tests/e2e/utils/s3ConfigManager.py +++ b/tests/e2e/utils/s3ConfigManager.py @@ -1,44 +1,52 @@ +import os +import hashlib import boto3 import botocore -import hashlib -import os +from typing import Optional from dotenv import load_dotenv + load_dotenv() + class S3ConfigManager: - def __init__(self, bucket_name: str, s3_prefix: str = ""): - self.bucket_name = bucket_name - self.s3_prefix = s3_prefix + def __init__(self, bucket_name: str, s3_prefix: str = "") -> None: + self.bucket_name: str = bucket_name + self.s3_prefix: str = s3_prefix self.s3_client = boto3.client("s3") def _s3_key(self, filename: str) -> str: return os.path.join(self.s3_prefix, filename) def _calculate_file_hash(self, file_path: str) -> str: - """SHA256 hash to compare local and S3 content.""" + """Return SHA256 hash of the given file.""" sha256 = hashlib.sha256() - with open(file_path, "rb") as f: - for chunk in iter(lambda: f.read(4096), b""): + with open(file_path, "rb") as file: + for chunk in iter(lambda: file.read(4096), b""): sha256.update(chunk) return sha256.hexdigest() - def upload_if_missing_or_changed(self, local_path: str): + def upload_if_missing_or_changed(self, local_path: str) -> None: filename = os.path.basename(local_path) s3_key = self._s3_key(filename) try: if self.config_exists_and_matches(local_path, s3_key): - print(f"โœ… Config '{filename}' already exists and matches in S3. Skipping upload.") + print( + f"โœ… Config '{filename}' already exists and matches in S3. " + "Skipping upload." + ) return - else: - print(f"๐Ÿงน Config '{filename}' exists in S3 but is different. Deleting old version...") - self.s3_client.delete_object(Bucket=self.bucket_name, Key=s3_key) + print( + f"๐Ÿงน A different config exists under '{self.s3_prefix}/'. " + "Deleting all existing files..." + ) + self._delete_all_in_prefix() except self.s3_client.exceptions.NoSuchKey: - print(f"๐Ÿ†• Config '{filename}' does not exist in S3. Proceeding to upload.") - except botocore.exceptions.ClientError as e: - if e.response["Error"]["Code"] == "NoSuchKey": - print(f"๐Ÿ†• Config '{filename}' does not exist in S3. Proceeding to upload.") + print(f"๐Ÿ†• No config found under '{self.s3_prefix}/'. Proceeding to upload.") + except botocore.exceptions.ClientError as error: + if error.response.get("Error", {}).get("Code") == "NoSuchKey": + print(f"๐Ÿ†• No config found under '{self.s3_prefix}/'. Proceeding to upload.") else: raise @@ -46,8 +54,6 @@ def upload_if_missing_or_changed(self, local_path: str): self.s3_client.upload_file(local_path, self.bucket_name, s3_key) print(f"โœ… Uploaded to s3://{self.bucket_name}/{s3_key}") - - def config_exists_and_matches(self, local_path: str, s3_key: str) -> bool: try: s3_obj = self.s3_client.get_object(Bucket=self.bucket_name, Key=s3_key) @@ -57,7 +63,23 @@ def config_exists_and_matches(self, local_path: str, s3_key: str) -> bool: return s3_hash == local_hash except self.s3_client.exceptions.NoSuchKey: return False - except botocore.exceptions.ClientError as e: - if e.response["Error"]["Code"] == "NoSuchKey": + except botocore.exceptions.ClientError as error: + if error.response.get("Error", {}).get("Code") == "NoSuchKey": return False raise + + def _delete_all_in_prefix(self) -> None: + """Delete all S3 objects under the current prefix.""" + response = self.s3_client.list_objects_v2( + Bucket=self.bucket_name, Prefix=self.s3_prefix + ) + + if "Contents" in response: + to_delete = [{"Key": obj["Key"]} for obj in response["Contents"]] + self.s3_client.delete_objects( + Bucket=self.bucket_name, + Delete={"Objects": to_delete} + ) + print(f"๐Ÿ—‘๏ธ Deleted {len(to_delete)} file(s) under prefix '{self.s3_prefix}/'.") + else: + print(f"๐Ÿ“ญ Nothing to delete under prefix '{self.s3_prefix}/'.") From 342dd627538a7bd2ab6dec1da03ac8316921d290 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 13:12:05 +0100 Subject: [PATCH 37/72] removal of common helpers --- tests/e2e/utils/common_test_helpers.py | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 tests/e2e/utils/common_test_helpers.py diff --git a/tests/e2e/utils/common_test_helpers.py b/tests/e2e/utils/common_test_helpers.py deleted file mode 100644 index 09554f545..000000000 --- a/tests/e2e/utils/common_test_helpers.py +++ /dev/null @@ -1,15 +0,0 @@ -def clean_response(data: dict) -> dict: - keys_to_ignore = ["responseId", "lastUpdated"] - return remove_volatile_fields(data, keys_to_ignore) - - -def remove_volatile_fields(data, keys_to_remove): - if isinstance(data, dict): - return { - key: remove_volatile_fields(value, keys_to_remove) - for key, value in data.items() - if key not in keys_to_remove - } - elif isinstance(data, list): - return [remove_volatile_fields(item, keys_to_remove) for item in data] - return data From 9596a07bbc80073a8904f218823227fa3ebf124c Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 13:21:36 +0100 Subject: [PATCH 38/72] MORE TIDYING --- tests/e2e/tests/test_smoke_tests.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/e2e/tests/test_smoke_tests.py b/tests/e2e/tests/test_smoke_tests.py index 9efadb2f8..a5f780635 100644 --- a/tests/e2e/tests/test_smoke_tests.py +++ b/tests/e2e/tests/test_smoke_tests.py @@ -6,12 +6,11 @@ from tests.e2e.utils.data_loader import load_all_expected_responses, initialise_tests from tests.e2e.utils.s3ConfigManager import S3ConfigManager -# UPDATE THE BELOW WITH LOCATION OF TEST DATA, EXPECTED RESPONSES AND CONFIGS IN DEFINED IN test_config.py # -############################################################################################################# +# Update the below with the configuration values specified in test_config.py all_data, dto = initialise_tests(SMOKE_TEST_DATA) all_expected_responses = load_all_expected_responses(SMOKE_TEST_RESPONSES) config_path = SMOKE_TEST_CONFIGS -############################################################################################################# + s3_manager = S3ConfigManager(S3_BUCKET, S3_PREFIX) @@ -24,11 +23,11 @@ @pytest.mark.smoketest @pytest.mark.parametrize("filename, scenario", param_list, ids=id_list) def test_run_smoke_case(filename, scenario, eligibility_client): - # get the nhs_number + # get the nhs_number from the scenario nhs_number = scenario["nhs_number"] - # get the associated campaign config file + # get the associated campaign config file from the scenario config_filename = scenario.get("config_filename", "") - # upload that config file to s3 if it + # upload that config file to s3 if it is missing or has changed s3_manager.upload_if_missing_or_changed(os.path.abspath(os.path.join(config_path, config_filename))) actual_response = eligibility_client.make_request(nhs_number, strict_ssl=False) From 21ecc48ac98094975e937c0b507b6422e21b059b Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 14:20:00 +0100 Subject: [PATCH 39/72] added and updated story test for ELI-155 and ELI-291 --- .../storyTestConfigs/AUTO_RSV_ELI-155.json | 74 ++++++++++++ .../storyTestConfigs/AUTO_RSV_ELI-219-1.json | 102 ++++++++++++++++ .../storyTestConfigs/AUTO_RSV_ELI-219-2.json | 109 ++++++++++++++++++ .../storyTestData/AUTO_RSV_ELI-155.json | 46 ++++++++ .../storyTestData/AUTO_RSV_ELI-219-1.json | 47 ++++++++ .../storyTestData/AUTO_RSV_ELI-219-2.json | 47 ++++++++ .../storyTestResponses/AUTO_RSV_ELI-155.json | 30 +++++ .../AUTO_RSV_ELI-219-1.json | 30 +++++ tests/e2e/tests/test_config.py | 4 + tests/e2e/tests/test_story_tests.py | 46 ++++++++ 10 files changed, 535 insertions(+) create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-155.json create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-1.json create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-2.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-2.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-155.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-1.json create mode 100644 tests/e2e/tests/test_story_tests.py diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-155.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-155.json new file mode 100644 index 000000000..adf1635f5 --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-155.json @@ -0,0 +1,74 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "ELI-155 - cohort_label is not supported in R rules", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "CONTACT_GP", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "CONTACT_GP", + "Version": "1", + "Name": "ELI-155 - cohort_label is not supported in R rules", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "M", + "IterationCohorts": [ + { + "CohortLabel": "eli_155_in", + "CohortGroup": "eli_155_in_group", + "PositiveDescription": "You are currently in eli_155_in", + "NegativeDescription": "You are not currently in eli_155_in", + "Priority": 0 + }, + { + "CohortLabel": "eli_155_out", + "CohortGroup": "eli_155_out_group", + "PositiveDescription": "You are currently in eli_155_out", + "NegativeDescription": "You are not currently in eli_155_out", + "Priority": 0 + } + ], + "IterationRules": [ + { + "Type": "R", + "Name": "Actionable if future appointment is booked", + "Description": "Book An Appointment", + "Priority": 1000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D>=", + "Comparator": "0", + "CommsRouting": "AMEND_NBS", + "CohortLabel": "eli_155_out" + } + ], + "ActionsMapper": { + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "##You have an RSV vaccination appointment\\nYou can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Manage your appointment" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-1.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-1.json new file mode 100644 index 000000000..b8303b6a5 --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-1.json @@ -0,0 +1,102 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "ELI-219 - Support Actions R Rules - 9000000002", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "DEFAULT_ACTION", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "DEFAULT_ACTION", + "Version": "1", + "Name": "ELI-155 - Support Actions R Rules", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "M", + "IterationCohorts": [ + { + "CohortLabel": "eli_291_cohort_1", + "CohortGroup": "eli_291_cohort_1_group", + "PositiveDescription": "You are currently in eli_291_cohort_1", + "NegativeDescription": "You are not in eli_291_cohort_1", + "Priority": 0 + } + ], + "IterationRules": [ + { + "Type": "R", + "Name": "Actionable if future appointment is booked with ACC", + "Description": "Book An Appointment", + "Priority": 1000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D>=", + "Comparator": "0", + "CommsRouting": "ACTION_ONE" + }, + { + "Type": "R", + "Name": "Actionable if future appointment is booked with ACC", + "Description": "Book An Appointment", + "Priority": 1000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "is_not_null", + "Comparator": "", + "CommsRouting": "ACTION_ONE" + }, + { + "Type": "R", + "Name": "Actionable if future appointment is booked with ACC", + "Description": "Book An Appointment", + "Priority": 1000, + "AttributeLevel": "COHORT", + "Operator": "in", + "Comparator": "eli_291_cohort_2", + "CommsRouting": "ACTION_ONE" + } + ], + "ActionsMapper": { + "ACTION_ONE": { + "ExternalRoutingCode": "ActionOneRoutingCode", + "ActionDescription": "ActionOneDescription", + "ActionType": "ActionOneActionType", + "UrlLink": "http://www.actiononeurl.com", + "UrlLabel": "ActionOneUrlLabel" + }, + "ACTION_TWO": { + "ExternalRoutingCode": "ActionTwoRoutingCode", + "ActionDescription": "ActionTwoDescription", + "ActionType": "ActionTwoActionType", + "UrlLink": "http://www.actiontwourl.com", + "UrlLabel": "ActionTwoUrlLabel" + }, + "DEFAULT_ACTION": { + "ExternalRoutingCode": "DefaultAction", + "ActionDescription": "DefaultActionDescription", + "ActionType": "DefaultActionType", + "UrlLink": "DefaultActionUrl", + "UrlLabel": "DefaultLabel" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-2.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-2.json new file mode 100644 index 000000000..6ed902f21 --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-2.json @@ -0,0 +1,109 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "ELI-219 - Support Actions R Rules - 9000000003", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "DEFAULT_ACTION", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "DEFAULT_ACTION", + "Version": "1", + "Name": "ELI-155 - Support Actions R Rules", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "M", + "IterationCohorts": [ + { + "CohortLabel": "eli_291_cohort_2", + "CohortGroup": "eli_291_cohort_2_group", + "PositiveDescription": "You are currently in eli_291_cohort_2", + "NegativeDescription": "You are not in eli_291_cohort_2", + "Priority": 1 + } + ], + "IterationRules": [ + { + "Type": "R", + "Name": "Actionable if future appointment is booked with ACC", + "Description": "Book An Appointment", + "Priority": 1000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D<=", + "Comparator": "0", + "CommsRouting": "ACTION_ONE" + }, + { + "Type": "R", + "Name": "Actionable if future appointment is booked with ACC", + "Description": "Book An Appointment", + "Priority": 1030, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "is_not_null", + "Comparator": "", + "CommsRouting": "ACTION_THREE" + }, + { + "Type": "R", + "Name": "Actionable if future appointment is booked with ACC", + "Description": "Book An Appointment", + "Priority": 1020, + "AttributeLevel": "COHORT", + "Operator": "=", + "Comparator": "eli_291_cohort_2", + "CommsRouting": "ACTION_TWO" + } + ], + "ActionsMapper": { + "ACTION_ONE": { + "ExternalRoutingCode": "ActionOneRoutingCode", + "ActionDescription": "ActionOneDescription", + "ActionType": "ActionOneActionType", + "UrlLink": "http://www.actiononeurl.com", + "UrlLabel": "ActionOneUrlLabel" + }, + "ACTION_TWO": { + "ExternalRoutingCode": "ActionTwoRoutingCode", + "ActionDescription": "ActionTwoDescription", + "ActionType": "ActionTwoActionType", + "UrlLink": "http://www.actiontwourl.com", + "UrlLabel": "ActionTwoUrlLabel" + }, + "ACTION_THREE": { + "ExternalRoutingCode": "ActionThreeRoutingCode", + "ActionDescription": "ActionThreeDescription", + "ActionType": "ActionThreeActionType", + "UrlLink": "http://www.actionthreeurl.com", + "UrlLabel": "ActionThreeUrlLabel" + }, + "DEFAULT_ACTION": { + "ExternalRoutingCode": "DefaultAction", + "ActionDescription": "DefaultActionDescription", + "ActionType": "DefaultActionType", + "UrlLink": "DefaultActionUrl", + "UrlLabel": "DefaultLabel" + } + } + } + ] + } +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json new file mode 100644 index 000000000..755627dc6 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json @@ -0,0 +1,46 @@ +{ + "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested actions (with booking)", + "config_filename": "AUTO_RSV_ELI-155.json", + "data": [ + { + "NHS_NUMBER": "9000000001", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_155_in": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9000000001", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9000000001", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json new file mode 100644 index 000000000..77276d502 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json @@ -0,0 +1,47 @@ +{ + "scenario_name": "ELI-219 - Fails and Rules and Returns Default", + "config_filename": "AUTO_RSV_ELI-219-1.json", + "data": [ + { + "NHS_NUMBER": "9000000002", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_291_cohort_1": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9000000002", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9000000002", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-2.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-2.json new file mode 100644 index 000000000..69ec4e273 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-2.json @@ -0,0 +1,47 @@ +{ + "scenario_name": "ELI-219 - Returns the highest Priority Action", + "config_filename": "AUTO_RSV_ELI-219-2.json", + "data": [ + { + "NHS_NUMBER": "9000000003", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_291_cohort_2": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9000000003", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9000000002", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-155.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-155.json new file mode 100644 index 000000000..ffba018dc --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-155.json @@ -0,0 +1,30 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "Actionable", + "statusText": "Status.actionable", + "eligibilityCohorts": [ + { + "cohortCode": "eli_155_in_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in eli_155_in" + } + ], + "suitabilityRules": [], + "actions": [ + { + "action_code": "AmendNBS", + "action_description": "##You have an RSV vaccination appointment\\nYou can view, change or cancel your appointment below.", + "action_type": "ButtonWithAuthLink", + "url_link": "http://www.nhs.uk/book-rsv", + "url_label": "Manage your appointment" + } + ] + } + ] +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-1.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-1.json new file mode 100644 index 000000000..f6cc88345 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-1.json @@ -0,0 +1,30 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "Actionable", + "statusText": "Status.actionable", + "eligibilityCohorts": [ + { + "cohortCode": "eli_291_cohort_1_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in eli_291_cohort_1" + } + ], + "suitabilityRules": [], + "actions": [ + { + "action_type": "DefaultActionType", + "action_code": "DefaultAction", + "action_description": "DefaultActionDescription", + "url_label": "DefaultLabel", + "url_link": "DefaultActionUrl" + } + ] + } + ] +} diff --git a/tests/e2e/tests/test_config.py b/tests/e2e/tests/test_config.py index 16b77822a..04d26bd92 100644 --- a/tests/e2e/tests/test_config.py +++ b/tests/e2e/tests/test_config.py @@ -6,3 +6,7 @@ SMOKE_TEST_DATA: str = "tests/e2e/data/dynamoDB/smokeTestData/" SMOKE_TEST_RESPONSES: str = "tests/e2e/data/responses/smokeTestResponses/" SMOKE_TEST_CONFIGS: str = "tests/e2e/data/configs/smokeTestConfigs" +# Story Test Data Paths +STORY_TEST_DATA: str = "tests/e2e/data/dynamoDB/storyTestData/" +STORY_TEST_RESPONSES: str = "tests/e2e/data/responses/storyTestResponses/" +STORY_TEST_CONFIGS: str = "tests/e2e/data/configs/storyTestConfigs" diff --git a/tests/e2e/tests/test_story_tests.py b/tests/e2e/tests/test_story_tests.py new file mode 100644 index 000000000..7c8713a36 --- /dev/null +++ b/tests/e2e/tests/test_story_tests.py @@ -0,0 +1,46 @@ +import os + +import pytest + +from tests.e2e.tests.test_config import * +from tests.e2e.utils.data_loader import load_all_expected_responses, initialise_tests +from tests.e2e.utils.s3ConfigManager import S3ConfigManager + +# Update the below with the configuration values specified in test_config.py +all_data, dto = initialise_tests(STORY_TEST_DATA) +all_expected_responses = load_all_expected_responses(STORY_TEST_RESPONSES) +config_path = STORY_TEST_CONFIGS + +s3_manager = S3ConfigManager(S3_BUCKET, S3_PREFIX) + + +param_list = list(all_data.items()) +id_list = [ + f"{filename} - {scenario.get('scenario_name', 'No Scenario')}" + for filename, scenario in param_list +] + +@pytest.mark.storytest +@pytest.mark.parametrize("filename, scenario", param_list, ids=id_list) +def test_run_story_test_cases(filename, scenario, eligibility_client): + # get the nhs_number from the scenario + nhs_number = scenario["nhs_number"] + # get the associated campaign config file from the scenario + config_filename = scenario.get("config_filename", "") + # upload that config file to s3 if it is missing or has changed + s3_manager.upload_if_missing_or_changed(os.path.abspath(os.path.join(config_path, config_filename))) + + actual_response = eligibility_client.make_request(nhs_number, strict_ssl=False) + expected_response = all_expected_responses.get(filename).get("response_items", {}) + + # Assert and show details on failure + assert actual_response["status_code"] == 200 + assert actual_response["body"] == expected_response, ( + f"\nโŒ Mismatch in test: {filename}\n" + f"NHS Number: {nhs_number}\n" + f"Expected: {expected_response}\n" + f"Actual: {actual_response}\n" + ) + + + From e355d3c6b7fc420ff5b99bab748baa98b98b2e76 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 14:20:13 +0100 Subject: [PATCH 40/72] added and updated story test for ELI-155 and ELI-291 --- .../storyTestConfigs/AUTO_RSV_ELI-219-3.json | 99 +++++++++++++++++++ .../storyTestData/AUTO_RSV_ELI-219-3.json | 47 +++++++++ .../AUTO_RSV_ELI-219-2.json | 30 ++++++ .../AUTO_RSV_ELI-219-3.json | 30 ++++++ 4 files changed, 206 insertions(+) create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-3.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-3.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-2.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-3.json diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-3.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-3.json new file mode 100644 index 000000000..f274ade2b --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-3.json @@ -0,0 +1,99 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "ELI-219 - Support Actions R Rules - 9000000004", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "DEFAULT_ACTION", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "DEFAULT_ACTION", + "Version": "1", + "Name": "ELI-219 - Support Actions R Rules - 9000000004", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "M", + "IterationCohorts": [ + { + "CohortLabel": "eli_291_cohort_3", + "CohortGroup": "eli_291_cohort_3_group", + "PositiveDescription": "You are currently in eli_291_cohort_3", + "NegativeDescription": "You are not in eli_291_cohort_3", + "Priority": 1 + } + ], + "IterationRules": [ + { + "Type": "R", + "Name": "Actionable if future appointment is booked", + "Description": "Book An Appointment", + "Priority": 1000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D>=", + "Comparator": "0", + "CommsRouting": "ACTION_ONE" + }, + { + "Type": "R", + "Name": "Actionable if Vaccinated", + "Description": "You are Vaccinated", + "Priority": 1010, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "is_not_null", + "Comparator": "", + "CommsRouting": "ACTION_TWO" + } + ], + "ActionsMapper": { + "ACTION_ONE": { + "ExternalRoutingCode": "ActionOneRoutingCode", + "ActionDescription": "ActionOneDescription", + "ActionType": "ActionOneActionType", + "UrlLink": "http://www.actiononeurl.com", + "UrlLabel": "ActionOneUrlLabel" + }, + "ACTION_TWO": { + "ExternalRoutingCode": "ActionTwoRoutingCode", + "ActionDescription": "ActionTwoDescription", + "ActionType": "ActionTwoActionType", + "UrlLink": "http://www.actiontwourl.com", + "UrlLabel": "ActionTwoUrlLabel" + }, + "ACTION_THREE": { + "ExternalRoutingCode": "ActionThreeRoutingCode", + "ActionDescription": "ActionThreeDescription", + "ActionType": "ActionThreeActionType", + "UrlLink": "http://www.actionthreeurl.com", + "UrlLabel": "ActionThreeUrlLabel" + }, + "DEFAULT_ACTION": { + "ExternalRoutingCode": "DefaultAction", + "ActionDescription": "DefaultActionDescription", + "ActionType": "DefaultActionType", + "UrlLink": "DefaultActionUrl", + "UrlLabel": "DefaultLabel" + } + } + } + ] + } +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-3.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-3.json new file mode 100644 index 000000000..952328aa2 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-3.json @@ -0,0 +1,47 @@ +{ + "scenario_name": "ELI-219 - Returns the highest Priority Action", + "config_filename": "AUTO_RSV_ELI-219-3.json", + "data": [ + { + "NHS_NUMBER": "9000000004", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_291_cohort_3": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9000000004", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9000000004", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-2.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-2.json new file mode 100644 index 000000000..82ecaecde --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-2.json @@ -0,0 +1,30 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "Actionable", + "statusText": "Status.actionable", + "eligibilityCohorts": [ + { + "cohortCode": "eli_291_cohort_2_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in eli_291_cohort_2" + } + ], + "suitabilityRules": [], + "actions": [ + { + "action_type": "ActionTwoActionType", + "action_code": "ActionTwoRoutingCode", + "action_description": "ActionTwoDescription", + "url_link": "http://www.actiontwourl.com", + "url_label": "ActionTwoUrlLabel" + } + ] + } + ] +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-3.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-3.json new file mode 100644 index 000000000..4df9ef7ef --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-3.json @@ -0,0 +1,30 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "Actionable", + "statusText": "Status.actionable", + "eligibilityCohorts": [ + { + "cohortCode": "eli_291_cohort_3_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in eli_291_cohort_3" + } + ], + "suitabilityRules": [], + "actions": [ + { + "action_code": "ActionOneRoutingCode", + "action_description": "ActionOneDescription", + "action_type": "ActionOneActionType", + "url_label": "ActionOneUrlLabel", + "url_link": "http://www.actiononeurl.com" + } + ] + } + ] +} From 47324aad61296e6506d4c5a1ebec38f3f1a9a097 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 30 Jun 2025 14:25:00 +0100 Subject: [PATCH 41/72] some linting fixes --- tests/e2e/tests/conftest.py | 1 + tests/e2e/tests/test_smoke_tests.py | 4 ++-- tests/e2e/tests/test_story_tests.py | 5 +---- tests/e2e/utils/data_loader.py | 9 ++++++--- tests/e2e/utils/dynamo_helper.py | 1 + tests/e2e/utils/eligibilityApiClient.py | 14 +++++++------- tests/e2e/utils/placeholder_context.py | 1 + tests/e2e/utils/placeholder_utils.py | 3 ++- 8 files changed, 21 insertions(+), 17 deletions(-) diff --git a/tests/e2e/tests/conftest.py b/tests/e2e/tests/conftest.py index c0c070914..40549aef7 100644 --- a/tests/e2e/tests/conftest.py +++ b/tests/e2e/tests/conftest.py @@ -81,6 +81,7 @@ def setup_dynamodb_data(request): logger.exception("[x] Failed to delete item %s", item.get("PK", "")) logger.info("[โœ“] Deleted %d/%d items", delete_count, len(items)) + @pytest.fixture(scope="session") def eligibility_client(): base_url = "https://test.eligibility-signposting-api.nhs.uk/patient-check" diff --git a/tests/e2e/tests/test_smoke_tests.py b/tests/e2e/tests/test_smoke_tests.py index a5f780635..19579b1ad 100644 --- a/tests/e2e/tests/test_smoke_tests.py +++ b/tests/e2e/tests/test_smoke_tests.py @@ -13,13 +13,13 @@ s3_manager = S3ConfigManager(S3_BUCKET, S3_PREFIX) - param_list = list(all_data.items()) id_list = [ f"{filename} - {scenario.get('scenario_name', 'No Scenario')}" for filename, scenario in param_list ] + @pytest.mark.smoketest @pytest.mark.parametrize("filename, scenario", param_list, ids=id_list) def test_run_smoke_case(filename, scenario, eligibility_client): @@ -42,6 +42,7 @@ def test_run_smoke_case(filename, scenario, eligibility_client): f"Actual: {actual_response}\n" ) + @pytest.mark.smoketest def test_check_for_missing_person(eligibility_client): nhs_number = "1234567890" @@ -59,4 +60,3 @@ def test_check_for_missing_person(eligibility_client): assert response["status_code"] == 404 assert response["body"] == expected_body - diff --git a/tests/e2e/tests/test_story_tests.py b/tests/e2e/tests/test_story_tests.py index 7c8713a36..d01299dc7 100644 --- a/tests/e2e/tests/test_story_tests.py +++ b/tests/e2e/tests/test_story_tests.py @@ -13,13 +13,13 @@ s3_manager = S3ConfigManager(S3_BUCKET, S3_PREFIX) - param_list = list(all_data.items()) id_list = [ f"{filename} - {scenario.get('scenario_name', 'No Scenario')}" for filename, scenario in param_list ] + @pytest.mark.storytest @pytest.mark.parametrize("filename, scenario", param_list, ids=id_list) def test_run_story_test_cases(filename, scenario, eligibility_client): @@ -41,6 +41,3 @@ def test_run_story_test_cases(filename, scenario, eligibility_client): f"Expected: {expected_response}\n" f"Actual: {actual_response}\n" ) - - - diff --git a/tests/e2e/utils/data_loader.py b/tests/e2e/utils/data_loader.py index b347ca46b..02cb4487b 100644 --- a/tests/e2e/utils/data_loader.py +++ b/tests/e2e/utils/data_loader.py @@ -5,6 +5,7 @@ from .placeholder_utils import resolve_placeholders from .placeholder_context import ResolvedPlaceholderContext, PlaceholderDTO + def initialise_tests(folder): folder_path = os.path.abspath(folder) all_data, dto = load_all_test_scenarios(folder_path) @@ -15,6 +16,7 @@ def initialise_tests(folder): return all_data, dto + def resolve_placeholders_in_data(data, context, file_name): if isinstance(data, dict): return {k: resolve_placeholders_in_data(v, context, file_name) for k, v in data.items()} @@ -72,10 +74,10 @@ def load_all_expected_responses(folder_path): # Load JSON with open(full_path, "r") as f: - raw_json = json.load(f) + raw_json = json.load(f) resolved_data = resolve_placeholders_in_data(raw_json, dto, filename) - cleaned_data = clean_expected_response(resolved_data,) + cleaned_data = clean_expected_response(resolved_data, ) all_data[filename] = { "response_items": cleaned_data @@ -83,6 +85,7 @@ def load_all_expected_responses(folder_path): return all_data + def load_all_test_scenarios(folder_path, config_folder_path="tests/e2e/data/configs"): all_data = {} dto = PlaceholderDTO() # Shared across all files @@ -118,6 +121,7 @@ def load_all_test_scenarios(folder_path, config_folder_path="tests/e2e/data/conf return all_data, dto + def clean_expected_response(data: dict) -> dict: keys_to_ignore = ["responseId", "lastUpdated"] return _remove_volatile_fields(data, keys_to_ignore) @@ -133,4 +137,3 @@ def _remove_volatile_fields(data, keys_to_remove): elif isinstance(data, list): return [_remove_volatile_fields(item, keys_to_remove) for item in data] return data - diff --git a/tests/e2e/utils/dynamo_helper.py b/tests/e2e/utils/dynamo_helper.py index 757aba835..2202cb85f 100644 --- a/tests/e2e/utils/dynamo_helper.py +++ b/tests/e2e/utils/dynamo_helper.py @@ -48,6 +48,7 @@ def get_item(self, key: dict): print(f"Failed to get item: {e.response['Error']['Message']}") raise + def insert_into_dynamo(data): print("Inserting into Dynamo (fake):", data) table = DynamoDBHelper("eligibility-signposting-api-test-eligibility_datastore") diff --git a/tests/e2e/utils/eligibilityApiClient.py b/tests/e2e/utils/eligibilityApiClient.py index 0f8261e7e..a5ec98129 100644 --- a/tests/e2e/utils/eligibilityApiClient.py +++ b/tests/e2e/utils/eligibilityApiClient.py @@ -63,12 +63,12 @@ def _ensure_certs_present(self) -> None: f.write(cert_value) def make_request( - self, - nhs_number: str, - method: str = "GET", - payload: Optional[Union[Dict[str, Any], list]] = None, - strict_ssl: bool = False, - raise_on_error: bool = True, + self, + nhs_number: str, + method: str = "GET", + payload: Optional[Union[Dict[str, Any], list]] = None, + strict_ssl: bool = False, + raise_on_error: bool = True, ) -> Dict[str, Any]: url = f"{self.api_url.rstrip('/')}/{nhs_number}" cert = ( @@ -119,7 +119,7 @@ def _clean_response(self, data: Any) -> Any: return self._remove_volatile_fields(data, keys_to_ignore) def _remove_volatile_fields( - self, data: Union[Dict[str, Any], list, Any], keys_to_remove: list + self, data: Union[Dict[str, Any], list, Any], keys_to_remove: list ) -> Any: if isinstance(data, dict): return { diff --git a/tests/e2e/utils/placeholder_context.py b/tests/e2e/utils/placeholder_context.py index 0e304cff9..4809407d0 100644 --- a/tests/e2e/utils/placeholder_context.py +++ b/tests/e2e/utils/placeholder_context.py @@ -11,6 +11,7 @@ def get(self, placeholder: str) -> str: def all(self): return self.values + class PlaceholderDTO: def __init__(self): self.placeholders = {} # key: filename, value: dict of placeholders per file diff --git a/tests/e2e/utils/placeholder_utils.py b/tests/e2e/utils/placeholder_utils.py index 437c00503..5b106a63d 100644 --- a/tests/e2e/utils/placeholder_utils.py +++ b/tests/e2e/utils/placeholder_utils.py @@ -3,6 +3,7 @@ from dateutil.relativedelta import relativedelta from calendar import isleap + def resolve_placeholders(value, context=None, file_name=None): if not isinstance(value, str): return value @@ -15,7 +16,7 @@ def resolve_placeholders(value, context=None, file_name=None): parts = placeholder.split("_") try: - if placeholder in ["IGNORE_RESPONSE_ID", "IGNORE_DATE"]: + if placeholder in ["IGNORE_RESPONSE_ID", "IGNORE_DATE"]: return value.replace(f"<<{placeholder}>>", placeholder) elif len(parts) != 3 or parts[0] not in ["DATE", "RDATE", "IGNORE"]: return value # Unrecognized format From 2bbf091fdfb8db5e9cbe3162cfeb62d716f457d6 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Tue, 1 Jul 2025 09:35:47 +0100 Subject: [PATCH 42/72] adding header functionality to the GET request --- .../storyTestConfigs/AUTO_RSV_ELI-219-4.json | 87 +++++++++++++++++++ .../storyTestData/AUTO_RSV_ELI-155.json | 1 + .../storyTestData/AUTO_RSV_ELI-219-4.json | 47 ++++++++++ .../AUTO_RSV_ELI-219-4.json | 30 +++++++ tests/e2e/data/s3/patient_s3_sample.json | 59 ------------- tests/e2e/tests/test_smoke_tests.py | 3 +- tests/e2e/tests/test_story_tests.py | 9 +- tests/e2e/utils/data_loader.py | 6 +- tests/e2e/utils/eligibilityApiClient.py | 40 +++++++++ 9 files changed, 215 insertions(+), 67 deletions(-) create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-4.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-4.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-4.json delete mode 100644 tests/e2e/data/s3/patient_s3_sample.json diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-4.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-4.json new file mode 100644 index 000000000..884bb6c9e --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-4.json @@ -0,0 +1,87 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "ELI-219 - Empty Actions - 9000000005", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "DEFAULT_ACTION", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "DEFAULT_ACTION", + "Version": "1", + "Name": "ELI-219 - Support Actions R Rules - 9000000004", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "M", + "IterationCohorts": [ + { + "CohortLabel": "eli_291_cohort_4", + "CohortGroup": "eli_291_cohort_4_group", + "PositiveDescription": "You are currently in eli_291_cohort_4", + "NegativeDescription": "You are not in eli_291_cohort_4", + "Priority": 1 + } + ], + "IterationRules": [ + { + "Type": "R", + "Name": "Actionable if future appointment is booked", + "Description": "Book An Appointment", + "Priority": 1000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D>=", + "Comparator": "0", + "CommsRouting": "EMPTY_ACTION" + } + ], + "ActionsMapper": { + "EMPTY_ACTION": { + "ExternalRoutingCode": "", + "ActionDescription": "", + "ActionType": "", + "UrlLink": "", + "UrlLabel": "" + }, + "ACTION_TWO": { + "ExternalRoutingCode": "ActionTwoRoutingCode", + "ActionDescription": "ActionTwoDescription", + "ActionType": "ActionTwoActionType", + "UrlLink": "http://www.actiontwourl.com", + "UrlLabel": "ActionTwoUrlLabel" + }, + "ACTION_THREE": { + "ExternalRoutingCode": "ActionThreeRoutingCode", + "ActionDescription": "ActionThreeDescription", + "ActionType": "ActionThreeActionType", + "UrlLink": "http://www.actionthreeurl.com", + "UrlLabel": "ActionThreeUrlLabel" + }, + "DEFAULT_ACTION": { + "ExternalRoutingCode": "DefaultAction", + "ActionDescription": "DefaultActionDescription", + "ActionType": "DefaultActionType", + "UrlLink": "DefaultActionUrl", + "UrlLabel": "DefaultLabel" + } + } + } + ] + } +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json index 755627dc6..477c69982 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json @@ -1,6 +1,7 @@ { "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested actions (with booking)", "config_filename": "AUTO_RSV_ELI-155.json", + "request_headers": {"authenticated_nhs_number": "9000000001","includeActions": "N"}, "data": [ { "NHS_NUMBER": "9000000001", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-4.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-4.json new file mode 100644 index 000000000..83aba304f --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-4.json @@ -0,0 +1,47 @@ +{ + "scenario_name": "ELI-219 - Testing the new requirement from Vita around empty actions ", + "config_filename": "AUTO_RSV_ELI-219-4.json", + "data": [ + { + "NHS_NUMBER": "9000000005", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_291_cohort_4": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9000000005", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9000000005", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-4.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-4.json new file mode 100644 index 000000000..472dda0c6 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-4.json @@ -0,0 +1,30 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "Actionable", + "statusText": "Status.actionable", + "eligibilityCohorts": [ + { + "cohortCode": "eli_291_cohort_4_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in eli_291_cohort_4" + } + ], + "suitabilityRules": [], + "actions": [ + { + "action_code": "", + "action_description": "", + "action_type": "", + "url_label": "", + "url_link": "" + } + ] + } + ] +} diff --git a/tests/e2e/data/s3/patient_s3_sample.json b/tests/e2e/data/s3/patient_s3_sample.json deleted file mode 100644 index 3e7b93748..000000000 --- a/tests/e2e/data/s3/patient_s3_sample.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "PATIENT": { - "NHS_NUMBER": "9876543210", - "SCENARIO": "SCN005", - "PERSON_ATTRIBUTES": { - "DATE_OF_BIRTH": "19880615", - "GENDER": "1", - "POSTCODE": "M1 1AA", - "POSTCODE_SECTOR": "M1", - "POSTCODE_OUTCODE": "1AA", - "MSOA": "E05000003", - "LSOA": "E01000003", - "GP_PRACTICE_CODE": "C98765", - "PCN": "PCN003", - "ICB": "ICB002", - "COMMISSIONING_REGION": "RegionZ", - "13Q_FLAG": "Y", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - }, - "TARGET_CONDITIONS": { - "COVID": { - "VALID_DOSES_COUNT": "2", - "INVALID_DOSES_COUNT": "0", - "LAST_SUCCESSFUL_DATE": "20241201", - "LAST_VALID_DOSE_DATE": "20241201", - "BOOKED_APPOINTMENT_DATE": "20250110", - "BOOKED_APPOINTMENT_PROVIDER": "Local Clinic", - "LAST_INVITE_DATE": "20241115", - "LAST_INVITE_STATUS": "Accepted", - "OPTOUT": "N" - }, - "FLU": { - "VALID_DOSES_COUNT": "1", - "INVALID_DOSES_COUNT": "0", - "LAST_SUCCESSFUL_DATE": "20241020", - "LAST_VALID_DOSE_DATE": "20241020", - "LAST_INVITE_DATE": "20240901", - "LAST_INVITE_STATUS": "Delivered", - "OPTOUT": "N" - }, - "RSV": { - "LAST_SUCCESSFUL_DATE": "20241105", - "LAST_VALID_DOSE_DATE": "20241105", - "OPTOUT": "N" - } - }, - "COHORT_MEMBERSHIP": [ - { - "cohort_label": "Trial_Group_A", - "date_joined": "20250210" - }, - { - "cohort_label": "Manchester_Residents_2024", - "date_joined": "20240701" - } - ] - } -} diff --git a/tests/e2e/tests/test_smoke_tests.py b/tests/e2e/tests/test_smoke_tests.py index 19579b1ad..8e798e9f8 100644 --- a/tests/e2e/tests/test_smoke_tests.py +++ b/tests/e2e/tests/test_smoke_tests.py @@ -1,3 +1,4 @@ +import http import os import pytest @@ -58,5 +59,5 @@ def test_check_for_missing_person(eligibility_client): response = eligibility_client.make_request(nhs_number, raise_on_error=False) - assert response["status_code"] == 404 + assert response["status_code"] == http.HTTPStatus.NOT_FOUND assert response["body"] == expected_body diff --git a/tests/e2e/tests/test_story_tests.py b/tests/e2e/tests/test_story_tests.py index d01299dc7..aab7c6f2e 100644 --- a/tests/e2e/tests/test_story_tests.py +++ b/tests/e2e/tests/test_story_tests.py @@ -1,3 +1,4 @@ +import http import os import pytest @@ -20,21 +21,19 @@ ] -@pytest.mark.storytest +@pytest.mark.storytest_all @pytest.mark.parametrize("filename, scenario", param_list, ids=id_list) def test_run_story_test_cases(filename, scenario, eligibility_client): - # get the nhs_number from the scenario nhs_number = scenario["nhs_number"] - # get the associated campaign config file from the scenario config_filename = scenario.get("config_filename", "") - # upload that config file to s3 if it is missing or has changed + request_headers = scenario.get("request_headers", {}) s3_manager.upload_if_missing_or_changed(os.path.abspath(os.path.join(config_path, config_filename))) actual_response = eligibility_client.make_request(nhs_number, strict_ssl=False) expected_response = all_expected_responses.get(filename).get("response_items", {}) # Assert and show details on failure - assert actual_response["status_code"] == 200 + assert actual_response["status_code"] == http.HTTPStatus.OK assert actual_response["body"] == expected_response, ( f"\nโŒ Mismatch in test: {filename}\n" f"NHS Number: {nhs_number}\n" diff --git a/tests/e2e/utils/data_loader.py b/tests/e2e/utils/data_loader.py index 02cb4487b..f1e472754 100644 --- a/tests/e2e/utils/data_loader.py +++ b/tests/e2e/utils/data_loader.py @@ -102,8 +102,9 @@ def load_all_test_scenarios(folder_path, config_folder_path="tests/e2e/data/conf raw_data = raw_json["data"] - config_filename = raw_json.get("config_filename") # Just the name - scenario_name = raw_json.get("scenario_name") # Just the name + config_filename = raw_json.get("config_filename") + scenario_name = raw_json.get("scenario_name") + request_headers = raw_json.get("request_headers") # Resolve placeholders with shared DTO resolved_data = resolve_placeholders_in_data(raw_data, dto, filename) @@ -116,6 +117,7 @@ def load_all_test_scenarios(folder_path, config_folder_path="tests/e2e/data/conf "dynamo_items": resolved_data, "nhs_number": nhs_number, "config_filename": config_filename, + "request_headers": request_headers, "scenario_name": scenario_name, } diff --git a/tests/e2e/utils/eligibilityApiClient.py b/tests/e2e/utils/eligibilityApiClient.py index a5ec98129..4614ef3bf 100644 --- a/tests/e2e/utils/eligibilityApiClient.py +++ b/tests/e2e/utils/eligibilityApiClient.py @@ -63,6 +63,46 @@ def _ensure_certs_present(self) -> None: f.write(cert_value) def make_request( + self, + nhs_number: str, + method: str = "GET", + payload: Optional[Union[Dict[str, Any], list]] = None, + headers: Optional[Dict[str, str]] = None, + strict_ssl: bool = False, + raise_on_error: bool = True, + ) -> Dict[str, Any]: + url = f"{self.api_url.rstrip('/')}/{nhs_number}" + cert = ( + str(self.cert_paths["client_cert"]), + str(self.cert_paths["private_key"]), + ) + verify: Union[bool, str] = str(self.cert_paths["ca_cert"]) if strict_ssl else False + + try: + response = requests.request( + method=method.upper(), + url=url, + cert=cert, + verify=verify, + json=payload, + headers=headers, + timeout=10, + ) + + if raise_on_error: + response.raise_for_status() + + return self._parse_response(response) + + except requests.exceptions.SSLError as ssl_err: + raise RuntimeError(f"SSL error during request: {ssl_err}") from ssl_err + except requests.exceptions.RequestException as req_err: + response = getattr(req_err, "response", None) + if isinstance(response, Response): + return self._parse_response(response) + raise RuntimeError(f"Request error: {req_err}") from req_err + + def make_request_old( self, nhs_number: str, method: str = "GET", From f530dcba964915a8f6d28bec92c6560e519871b0 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Tue, 1 Jul 2025 13:13:09 +0100 Subject: [PATCH 43/72] adding header functionality to the GET request --- .../storyTestConfigs/AUTO_RSV_ELI-216.json | 101 ++++++++++++++++++ .../storyTestData/AUTO_RSV_ELI-155.json | 2 +- .../storyTestData/AUTO_RSV_ELI-216-1.json | 48 +++++++++ .../storyTestData/AUTO_RSV_ELI-216-2.json | 48 +++++++++ .../storyTestData/AUTO_RSV_ELI-216-3.json | 48 +++++++++ .../storyTestData/AUTO_RSV_ELI-219-1.json | 2 +- .../storyTestResponses/AUTO_RSV_ELI-216.json | 30 ++++++ 7 files changed, 277 insertions(+), 2 deletions(-) create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-1.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-2.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-3.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216.json diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json new file mode 100644 index 000000000..b3395986e --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json @@ -0,0 +1,101 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "ELI-216 - NHS Number check (NHS login) - 9000000006", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "DEFAULT_ACTION", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "DEFAULT_ACTION", + "Version": "1", + "Name": "ELI-155 - Support Actions R Rules", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "M", + "IterationCohorts": [ + { + "CohortLabel": "eli_216_cohort", + "CohortGroup": "eli_216_cohort_group", + "PositiveDescription": "You are currently in eli_216_cohort", + "NegativeDescription": "You are not in eli_216_cohort", + "Priority": 0 + } + ], + "IterationRules": [ + { + "Type": "R", + "Name": "Actionable if future appointment is booked with ACC", + "Description": "Book An Appointment", + "Priority": 1000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D>=", + "Comparator": "0", + "CommsRouting": "ACTION_ONE" + }, + { + "Type": "R", + "Name": "Actionable if future appointment is booked with ACC", + "Description": "Book An Appointment", + "Priority": 1000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "is_not_null", + "Comparator": "", + "CommsRouting": "ACTION_ONE" + }, + { + "Type": "R", + "Name": "Actionable if future appointment is booked with ACC", + "Description": "Book An Appointment", + "Priority": 1000, + "AttributeLevel": "COHORT", + "Operator": "in", + "CommsRouting": "ACTION_ONE" + } + ], + "ActionsMapper": { + "ACTION_ONE": { + "ExternalRoutingCode": "ActionOneRoutingCode", + "ActionDescription": "ActionOneDescription", + "ActionType": "ActionOneActionType", + "UrlLink": "http://www.actiononeurl.com", + "UrlLabel": "ActionOneUrlLabel" + }, + "ACTION_TWO": { + "ExternalRoutingCode": "ActionTwoRoutingCode", + "ActionDescription": "ActionTwoDescription", + "ActionType": "ActionTwoActionType", + "UrlLink": "http://www.actiontwourl.com", + "UrlLabel": "ActionTwoUrlLabel" + }, + "DEFAULT_ACTION": { + "ExternalRoutingCode": "DefaultAction", + "ActionDescription": "DefaultActionDescription", + "ActionType": "DefaultActionType", + "UrlLink": "DefaultActionUrl", + "UrlLabel": "DefaultLabel" + } + } + } + ] + } +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json index 477c69982..949761440 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json @@ -1,7 +1,7 @@ { "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested actions (with booking)", "config_filename": "AUTO_RSV_ELI-155.json", - "request_headers": {"authenticated_nhs_number": "9000000001","includeActions": "N"}, + "request_headers": {"authenticated_nhs_number": "9000000001"}, "data": [ { "NHS_NUMBER": "9000000001", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-1.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-1.json new file mode 100644 index 000000000..60f0c706b --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-1.json @@ -0,0 +1,48 @@ +{ + "scenario_name": "ELI-216 - NHS Number check (NHS login) - Correct NHS_number", + "config_filename": "AUTO_RSV_ELI-216.json", + "request_headers": {"authenticated_nhs_number": "9000000006"}, + "data": [ + { + "NHS_NUMBER": "9000000006", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_216_cohort": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9000000006", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9000000006", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-2.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-2.json new file mode 100644 index 000000000..c0f878741 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-2.json @@ -0,0 +1,48 @@ +{ + "scenario_name": "ELI-216 - NHS Number check (NHS login) - Incorrect NHS_number", + "config_filename": "AUTO_RSV_ELI-216.json", + "request_headers": {"authenticated_nhs_number": "9000000001"}, + "data": [ + { + "NHS_NUMBER": "9000000006", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_216_cohort": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9000000006", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9000000006", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-3.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-3.json new file mode 100644 index 000000000..6de7ffeea --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-3.json @@ -0,0 +1,48 @@ +{ + "scenario_name": "ELI-216 - NHS Number check (NHS login) - No Header", + "config_filename": "AUTO_RSV_ELI-216.json", + "request_headers": {}, + "data": [ + { + "NHS_NUMBER": "9000000006", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_216_cohort": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9000000006", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9000000006", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json index 77276d502..13c517845 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json @@ -1,6 +1,6 @@ { "scenario_name": "ELI-219 - Fails and Rules and Returns Default", - "config_filename": "AUTO_RSV_ELI-219-1.json", + "config_filename": "AUTO_RSV_ELI-21-91.json", "data": [ { "NHS_NUMBER": "9000000002", diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216.json new file mode 100644 index 000000000..1e064316c --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216.json @@ -0,0 +1,30 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "Actionable", + "statusText": "Status.actionable", + "eligibilityCohorts": [ + { + "cohortCode": "eli_216_cohort_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in eli_216_cohort" + } + ], + "suitabilityRules": [], + "actions": [ + { + "action_type": "DefaultActionType", + "action_code": "DefaultAction", + "action_description": "DefaultActionDescription", + "url_label": "DefaultLabel", + "url_link": "DefaultActionUrl" + } + ] + } + ] +} From 2a4d1be86ab363f8b04ad3d77f43c1db9f96872d Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Tue, 1 Jul 2025 13:13:18 +0100 Subject: [PATCH 44/72] adding header functionality to the GET request --- tests/e2e/tests/test_error_scenario_tests.py | 24 ++++++++++++++++++++ tests/e2e/tests/test_smoke_tests.py | 4 +++- 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 tests/e2e/tests/test_error_scenario_tests.py diff --git a/tests/e2e/tests/test_error_scenario_tests.py b/tests/e2e/tests/test_error_scenario_tests.py new file mode 100644 index 000000000..e2929b80b --- /dev/null +++ b/tests/e2e/tests/test_error_scenario_tests.py @@ -0,0 +1,24 @@ +import http + +import pytest + + +@pytest.mark.smoketest +def test_check_for_missing_person(eligibility_client): + nhs_number = "1234567890" + + request_headers = {"authenticated_nhs_number": "1234567890"} + + expected_body = { + "issue": [{ + "code": "nhs-number-not-found", + "diagnostics": f'NHS Number "{nhs_number}" not found.', + "severity": "information" + }], + "resourceType": "OperationOutcome" + } + + response = eligibility_client.make_request(nhs_number, headers=request_headers, raise_on_error=False) + + assert response["status_code"] == http.HTTPStatus.NOT_FOUND + assert response["body"] == expected_body diff --git a/tests/e2e/tests/test_smoke_tests.py b/tests/e2e/tests/test_smoke_tests.py index 8e798e9f8..0df3c11eb 100644 --- a/tests/e2e/tests/test_smoke_tests.py +++ b/tests/e2e/tests/test_smoke_tests.py @@ -48,6 +48,8 @@ def test_run_smoke_case(filename, scenario, eligibility_client): def test_check_for_missing_person(eligibility_client): nhs_number = "1234567890" + request_headers = {"authenticated_nhs_number": "1234567890"}, + expected_body = { "issue": [{ "code": "nhs-number-not-found", @@ -57,7 +59,7 @@ def test_check_for_missing_person(eligibility_client): "resourceType": "OperationOutcome" } - response = eligibility_client.make_request(nhs_number, raise_on_error=False) + response = eligibility_client.make_request(nhs_number, headers= request_headers, raise_on_error=False) assert response["status_code"] == http.HTTPStatus.NOT_FOUND assert response["body"] == expected_body From ba5bc987713a1e6d1b3be36ae049194f3d93edf9 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Tue, 1 Jul 2025 21:48:40 +0100 Subject: [PATCH 45/72] adding new scenarios --- .../AUTO_RSV_REG_001.json | 207 ++++++++++++++++++ .../storyTestConfigs/AUTO_RSV_ELI-216.json | 9 - .../regressionTestData/AUTO_RSV_REG_001.json | 42 ++++ .../regressionTestData/AUTO_RSV_REG_002.json | 49 +++++ .../regressionTestData/AUTO_RSV_REG_003.json | 49 +++++ .../regressionTestData/AUTO_RSV_REG_010.json | 42 ++++ .../regressionTestData/AUTO_RSV_REG_011.json | 42 ++++ .../AUTO_RSV_ELI-216-2.json | 3 +- .../AUTO_RSV_ELI-216-3.json | 1 + .../AUTO_RSV_ELI-219-4.json | 1 + .../storyTestData/AUTO_RSV_ELI-155.json | 2 +- .../storyTestData/AUTO_RSV_ELI-216-1.json | 2 +- .../storyTestData/AUTO_RSV_ELI-219-1.json | 3 +- .../storyTestData/AUTO_RSV_ELI-219-2.json | 1 + .../storyTestData/AUTO_RSV_ELI-219-3.json | 1 + .../AUTO_RSV_REG_001.json} | 32 +-- .../AUTO_RSV_REG_002.json | 35 +++ .../AUTO_RSV_REG_003.json | 30 +++ .../AUTO_RSV_REG_010.json | 27 +++ .../AUTO_RSV_REG_011.json | 27 +++ .../AUTO_RSV_ELI-216-1.json | 30 +++ .../AUTO_RSV_ELI-216-2.json | 3 + .../AUTO_RSV_ELI-216-3.json | 3 + tests/e2e/tests/conftest.py | 5 +- tests/e2e/tests/test_config.py | 4 + tests/e2e/tests/test_error_scenario_tests.py | 64 +++++- tests/e2e/tests/test_regression_tests.py | 42 ++++ tests/e2e/tests/test_smoke_tests.py | 23 +- tests/e2e/tests/test_story_tests.py | 9 +- tests/e2e/utils/data_loader.py | 2 + tests/e2e/utils/dynamo_helper.py | 2 +- tests/e2e/utils/eligibilityApiClient.py | 37 ---- tests/e2e/utils/s3ConfigManager.py | 10 +- 33 files changed, 738 insertions(+), 101 deletions(-) create mode 100644 tests/e2e/data/configs/regressionTestConfigs/AUTO_RSV_REG_001.json create mode 100644 tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_001.json create mode 100644 tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_002.json create mode 100644 tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_003.json create mode 100644 tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_010.json create mode 100644 tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_011.json rename tests/e2e/data/dynamoDB/storyTestData/{ => .ignore_folder}/AUTO_RSV_ELI-216-2.json (92%) rename tests/e2e/data/dynamoDB/storyTestData/{ => .ignore_folder}/AUTO_RSV_ELI-216-3.json (97%) rename tests/e2e/data/dynamoDB/storyTestData/{ => .ignore_folder}/AUTO_RSV_ELI-219-4.json (95%) rename tests/e2e/data/responses/{storyTestResponses/AUTO_RSV_ELI-216.json => regressionTestResponses/AUTO_RSV_REG_001.json} (68%) create mode 100644 tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_002.json create mode 100644 tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_003.json create mode 100644 tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_010.json create mode 100644 tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_011.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-1.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-2.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-3.json create mode 100644 tests/e2e/tests/test_regression_tests.py diff --git a/tests/e2e/data/configs/regressionTestConfigs/AUTO_RSV_REG_001.json b/tests/e2e/data/configs/regressionTestConfigs/AUTO_RSV_REG_001.json new file mode 100644 index 000000000..861e0ae33 --- /dev/null +++ b/tests/e2e/data/configs/regressionTestConfigs/AUTO_RSV_REG_001.json @@ -0,0 +1,207 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "Automation RSV - Regression Test Config", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "DEFAULT_ACTION", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "DEFAULT_ACTION", + "Version": "1", + "Name": "Automation RSV - Regression Test Config Iteration", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "M", + "IterationCohorts": [ + { + "CohortLabel": "rsv_cohort_1", + "CohortGroup": "rsv_age_group", + "PositiveDescription": "You are in an age group 1.", + "NegativeDescription": "You are not in an age group 1.", + "Priority": 10 + }, + { + "CohortLabel": "rsv_cohort_2", + "CohortGroup": "rsv_age_group", + "PositiveDescription": "You are in an age group 2.", + "NegativeDescription": "You are not in an age group 2.", + "Priority": 0 + }, + { + "CohortLabel": "rsv_cohort_3", + "CohortGroup": "rsv_other_group", + "PositiveDescription": "You are in an another group.", + "NegativeDescription": "You are not in an other group.", + "Priority": 20 + }, + { + "CohortLabel": "elid_all_people", + "CohortGroup": "elid_all_people", + "PositiveDescription": "", + "NegativeDescription": "", + "Priority": 30 + }, + { + "CohortLabel": "no_group_description", + "CohortGroup": "", + "PositiveDescription": "", + "NegativeDescription": "", + "Priority": 40 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "Assure only already vaccinated taken from magic cohort", + "Description": "Exclude anyone who has NOT been given a dose of RSV Vaccination from the magic cohort", + "Operator": "is_empty", + "Comparator": "", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "elid_all_people", + "Priority": 100 + }, + { + "Type": "F", + "Name": "Under Age - Under 75 Years on day of execution", + "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", + "Priority": 120, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75" + }, + { + "Type": "F", + "Name": "Exclude Too OLD", + "Description": "Exclude anyone over 80", + "Priority": 130, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "<", + "Comparator": "-80" + }, + { + "Type": "S", + "Name": "AlreadyVaccinated", + "Description": "##You've had your RSV vaccination\nWe believe you had your vaccination on <>.", + "Priority": 500, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "Y>=", + "Comparator": "-25", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "NotAvailable", + "Description": "NotAvailable|Vaccinations are not currently available.", + "Priority": 510, + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "Operator": "=", + "Comparator": "SUPPRESSED_ICB" + }, + { + "Type": "R", + "Name": "Actionable Not Vaccinated", + "Description": "Book An Appointment", + "Priority": 1010, + "Operator": "is_empty", + "Comparator": "", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CommsRouting": "BOOK_NBS" + }, + { + "Type": "R", + "Name": "Actionable Not Vaccinated", + "Description": "Book An Appointment", + "Priority": 1010, + "Operator": "=", + "Comparator": "LS2", + "AttributeLevel": "PERSON", + "AttributeName": "POSTCODE_SECTOR", + "CommsRouting": "BOOK_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1020, + "Operator": ">=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1020, + "Operator": "=", + "Comparator": "NBS", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1030, + "Operator": ">=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "MANAGE_LOCAL" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1030, + "Operator": "!=", + "Comparator": "NBS", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", + "CommsRouting": "MANAGE_LOCAL" + } + ], + "ActionsMapper": { + "DEFAULT_ACTION": { + "ExternalRoutingCode": "DefaultAction", + "ActionDescription": "DefaultActionDescription", + "ActionType": "DefaultActionType", + "UrlLink": "DefaultActionUrl", + "UrlLabel": "DefaultLabel" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json index b3395986e..33cba2f1a 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json @@ -61,15 +61,6 @@ "Operator": "is_not_null", "Comparator": "", "CommsRouting": "ACTION_ONE" - }, - { - "Type": "R", - "Name": "Actionable if future appointment is booked with ACC", - "Description": "Book An Appointment", - "Priority": 1000, - "AttributeLevel": "COHORT", - "Operator": "in", - "CommsRouting": "ACTION_ONE" } ], "ActionsMapper": { diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_001.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_001.json new file mode 100644 index 000000000..b1f7a93e4 --- /dev/null +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_001.json @@ -0,0 +1,42 @@ +{ + "scenario_name": "RSV - Regression - Actionable, Single Eligible Cohort", + "request_headers": {"nhs-login-nhs-number": "1000000001"}, + "config_filename": "AUTO_RSV_REG_001.json", + "data": [ + { + "NHS_NUMBER": "1000000001", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_cohort_2": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "1000000001", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS2", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_002.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_002.json new file mode 100644 index 000000000..2d03e4cc1 --- /dev/null +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_002.json @@ -0,0 +1,49 @@ +{ + "scenario_name": "RSV - Regression - Actionable, Two Eligible Cohorts, Different Groups", + "request_headers": {"nhs-login-nhs-number": "1000000002"}, + "config_filename": "AUTO_RSV_REG_001.json", + "data": [ + { + "NHS_NUMBER": "1000000002", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_cohort_2": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + }, + "rsv_cohort_3": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "1000000002", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS2", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_003.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_003.json new file mode 100644 index 000000000..fa9b12fac --- /dev/null +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_003.json @@ -0,0 +1,49 @@ +{ + "scenario_name": "RSV - Regression - Actionable, Two Eligible Cohorts, Same Group", + "request_headers": {"nhs-login-nhs-number": "1000000003"}, + "config_filename": "AUTO_RSV_REG_001.json", + "data": [ + { + "NHS_NUMBER": "1000000003", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_cohort_2": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + }, + "rsv_cohort_1": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "1000000003", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS2", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_010.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_010.json new file mode 100644 index 000000000..265428f30 --- /dev/null +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_010.json @@ -0,0 +1,42 @@ +{ + "scenario_name": "RSV - Regression - Not Eligible, Not in Cohort", + "request_headers": {"nhs-login-nhs-number": "1000000010"}, + "config_filename": "AUTO_RSV_REG_001.json", + "data": [ + { + "NHS_NUMBER": "1000000010", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_cohort_22": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "1000000010", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS2", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_011.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_011.json new file mode 100644 index 000000000..7a7699f97 --- /dev/null +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_011.json @@ -0,0 +1,42 @@ +{ + "scenario_name": "RSV - Regression - Not Eligible, In Cohort, Filter Rule", + "request_headers": {"nhs-login-nhs-number": "1000000011"}, + "config_filename": "AUTO_RSV_REG_001.json", + "data": [ + { + "NHS_NUMBER": "1000000011", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_cohort_1": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "1000000011", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS2", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-2.json b/tests/e2e/data/dynamoDB/storyTestData/.ignore_folder/AUTO_RSV_ELI-216-2.json similarity index 92% rename from tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-2.json rename to tests/e2e/data/dynamoDB/storyTestData/.ignore_folder/AUTO_RSV_ELI-216-2.json index c0f878741..e6f6b01d0 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-2.json +++ b/tests/e2e/data/dynamoDB/storyTestData/.ignore_folder/AUTO_RSV_ELI-216-2.json @@ -1,7 +1,8 @@ { "scenario_name": "ELI-216 - NHS Number check (NHS login) - Incorrect NHS_number", "config_filename": "AUTO_RSV_ELI-216.json", - "request_headers": {"authenticated_nhs_number": "9000000001"}, + "request_headers": {"nhs-login-nhs-number": "9000000001"}, + "expected_response_code": 403, "data": [ { "NHS_NUMBER": "9000000006", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-3.json b/tests/e2e/data/dynamoDB/storyTestData/.ignore_folder/AUTO_RSV_ELI-216-3.json similarity index 97% rename from tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-3.json rename to tests/e2e/data/dynamoDB/storyTestData/.ignore_folder/AUTO_RSV_ELI-216-3.json index 6de7ffeea..15e70cdc5 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-3.json +++ b/tests/e2e/data/dynamoDB/storyTestData/.ignore_folder/AUTO_RSV_ELI-216-3.json @@ -2,6 +2,7 @@ "scenario_name": "ELI-216 - NHS Number check (NHS login) - No Header", "config_filename": "AUTO_RSV_ELI-216.json", "request_headers": {}, + "expected_response_code": 403, "data": [ { "NHS_NUMBER": "9000000006", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-4.json b/tests/e2e/data/dynamoDB/storyTestData/.ignore_folder/AUTO_RSV_ELI-219-4.json similarity index 95% rename from tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-4.json rename to tests/e2e/data/dynamoDB/storyTestData/.ignore_folder/AUTO_RSV_ELI-219-4.json index 83aba304f..37f291ca5 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-4.json +++ b/tests/e2e/data/dynamoDB/storyTestData/.ignore_folder/AUTO_RSV_ELI-219-4.json @@ -1,6 +1,7 @@ { "scenario_name": "ELI-219 - Testing the new requirement from Vita around empty actions ", "config_filename": "AUTO_RSV_ELI-219-4.json", + "request_headers": {"nhs-login-nhs-number": "9000000005"}, "data": [ { "NHS_NUMBER": "9000000005", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json index 949761440..998ef48a2 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json @@ -1,7 +1,7 @@ { "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested actions (with booking)", "config_filename": "AUTO_RSV_ELI-155.json", - "request_headers": {"authenticated_nhs_number": "9000000001"}, + "request_headers": {"nhs-login-nhs-number": "9000000001"}, "data": [ { "NHS_NUMBER": "9000000001", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-1.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-1.json index 60f0c706b..e2c7dd67c 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-1.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-1.json @@ -1,7 +1,7 @@ { "scenario_name": "ELI-216 - NHS Number check (NHS login) - Correct NHS_number", "config_filename": "AUTO_RSV_ELI-216.json", - "request_headers": {"authenticated_nhs_number": "9000000006"}, + "request_headers": {"nhs-login-nhs-number": "9000000006"}, "data": [ { "NHS_NUMBER": "9000000006", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json index 13c517845..2e30eabf2 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json @@ -1,6 +1,7 @@ { "scenario_name": "ELI-219 - Fails and Rules and Returns Default", - "config_filename": "AUTO_RSV_ELI-21-91.json", + "config_filename": "AUTO_RSV_ELI-219-1.json", + "request_headers": {"nhs-login-nhs-number": "9000000002"}, "data": [ { "NHS_NUMBER": "9000000002", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-2.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-2.json index 69ec4e273..cccfac6d1 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-2.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-2.json @@ -1,6 +1,7 @@ { "scenario_name": "ELI-219 - Returns the highest Priority Action", "config_filename": "AUTO_RSV_ELI-219-2.json", + "request_headers": {"nhs-login-nhs-number": "9000000003"}, "data": [ { "NHS_NUMBER": "9000000003", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-3.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-3.json index 952328aa2..f12db18d8 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-3.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-3.json @@ -1,6 +1,7 @@ { "scenario_name": "ELI-219 - Returns the highest Priority Action", "config_filename": "AUTO_RSV_ELI-219-3.json", + "request_headers": {"nhs-login-nhs-number": "9000000004"}, "data": [ { "NHS_NUMBER": "9000000004", diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216.json b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_001.json similarity index 68% rename from tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216.json rename to tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_001.json index 1e064316c..138a349a1 100644 --- a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216.json +++ b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_001.json @@ -1,30 +1,30 @@ { - "responseId": "<>", "meta": { - "lastUpdated": "<>" + "lastUpdated": "2025-07-01T19:52:24.529127+00:00" }, "processedSuggestions": [ { - "condition": "RSV", - "status": "Actionable", - "statusText": "Status.actionable", - "eligibilityCohorts": [ - { - "cohortCode": "eli_216_cohort_group", - "cohortStatus": "Actionable", - "cohortText": "You are currently in eli_216_cohort" - } - ], - "suitabilityRules": [], "actions": [ { - "action_type": "DefaultActionType", "action_code": "DefaultAction", "action_description": "DefaultActionDescription", + "action_type": "DefaultActionType", "url_label": "DefaultLabel", "url_link": "DefaultActionUrl" } - ] + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_group", + "cohortStatus": "Actionable", + "cohortText": "You are in an age group 2." + } + ], + "status": "Actionable", + "statusText": "Status.actionable", + "suitabilityRules": [] } - ] + ], + "responseId": "a53d2e37-7e25-4edd-99c1-9f487c95b347" } diff --git a/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_002.json b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_002.json new file mode 100644 index 000000000..cbdbadda3 --- /dev/null +++ b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_002.json @@ -0,0 +1,35 @@ +{ + "meta": { + "lastUpdated": "2025-07-01T19:57:39.626808+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "action_code": "DefaultAction", + "action_description": "DefaultActionDescription", + "action_type": "DefaultActionType", + "url_label": "DefaultLabel", + "url_link": "DefaultActionUrl" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_group", + "cohortStatus": "Actionable", + "cohortText": "You are in an age group 2." + }, + { + "cohortCode": "rsv_other_group", + "cohortStatus": "Actionable", + "cohortText": "You are in an another group." + } + ], + "status": "Actionable", + "statusText": "Status.actionable", + "suitabilityRules": [] + } + ], + "responseId": "c01bcba2-7ae3-4346-bb03-416dd1e307f8" +} diff --git a/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_003.json b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_003.json new file mode 100644 index 000000000..e7ef07bd5 --- /dev/null +++ b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_003.json @@ -0,0 +1,30 @@ +{ + "meta": { + "lastUpdated": "2025-07-01T19:57:39.626808+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "action_code": "DefaultAction", + "action_description": "DefaultActionDescription", + "action_type": "DefaultActionType", + "url_label": "DefaultLabel", + "url_link": "DefaultActionUrl" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_group", + "cohortStatus": "Actionable", + "cohortText": "You are in an age group 2." + } + ], + "status": "Actionable", + "statusText": "Status.actionable", + "suitabilityRules": [] + } + ], + "responseId": "c01bcba2-7ae3-4346-bb03-416dd1e307f8" +} diff --git a/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_010.json b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_010.json new file mode 100644 index 000000000..1030e0ffb --- /dev/null +++ b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_010.json @@ -0,0 +1,27 @@ +{ + "meta": { + "lastUpdated": "2025-07-01T20:20:47.764973+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not in an age group 2." + }, + { + "cohortCode": "rsv_other_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not in an other group." + } + ], + "status": "NotEligible", + "statusText": "Status.not_eligible", + "suitabilityRules": [] + } + ], + "responseId": "49db2d41-326f-45d4-afaf-0dd6897413af" +} diff --git a/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_011.json b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_011.json new file mode 100644 index 000000000..1030e0ffb --- /dev/null +++ b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_011.json @@ -0,0 +1,27 @@ +{ + "meta": { + "lastUpdated": "2025-07-01T20:20:47.764973+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not in an age group 2." + }, + { + "cohortCode": "rsv_other_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not in an other group." + } + ], + "status": "NotEligible", + "statusText": "Status.not_eligible", + "suitabilityRules": [] + } + ], + "responseId": "49db2d41-326f-45d4-afaf-0dd6897413af" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-1.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-1.json new file mode 100644 index 000000000..46781b583 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-1.json @@ -0,0 +1,30 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "Actionable", + "statusText": "Status.actionable", + "eligibilityCohorts": [ + { + "cohortCode": "eli_216_cohort_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in eli_216_cohort" + } + ], + "suitabilityRules": [], + "actions": [ + { + "action_code": "ActionOneRoutingCode", + "action_description": "ActionOneDescription", + "action_type": "ActionOneActionType", + "url_label": "ActionOneUrlLabel", + "url_link": "http://www.actiononeurl.com" + } + ] + } + ] +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-2.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-2.json new file mode 100644 index 000000000..d959499f3 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-2.json @@ -0,0 +1,3 @@ +{ + "response_items": "NHS number mismatch" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-3.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-3.json new file mode 100644 index 000000000..d959499f3 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-3.json @@ -0,0 +1,3 @@ +{ + "response_items": "NHS number mismatch" +} diff --git a/tests/e2e/tests/conftest.py b/tests/e2e/tests/conftest.py index 40549aef7..842407c9a 100644 --- a/tests/e2e/tests/conftest.py +++ b/tests/e2e/tests/conftest.py @@ -13,7 +13,7 @@ load_dotenv(dotenv_path=".env.local") # Constants -BASE_URL = os.getenv("BASE_URL", "https://sandbox.api.service.nhs.uk/eligibility-signposting-api") +BASE_URL = os.getenv("BASE_URL", "https://test.eligibility-signposting-api.nhs.uk/patient-check") API_KEY = os.getenv("API_KEY", "") VALID_NHS_NUMBER = os.getenv("VALID_NHS_NUMBER", "50000000004") DYNAMODB_TABLE_NAME = os.getenv("DYNAMODB_TABLE_NAME", "eligibility_data_store") @@ -84,5 +84,4 @@ def setup_dynamodb_data(request): @pytest.fixture(scope="session") def eligibility_client(): - base_url = "https://test.eligibility-signposting-api.nhs.uk/patient-check" - return EligibilityApiClient(base_url) + return EligibilityApiClient(BASE_URL) diff --git a/tests/e2e/tests/test_config.py b/tests/e2e/tests/test_config.py index 04d26bd92..34344e71b 100644 --- a/tests/e2e/tests/test_config.py +++ b/tests/e2e/tests/test_config.py @@ -10,3 +10,7 @@ STORY_TEST_DATA: str = "tests/e2e/data/dynamoDB/storyTestData/" STORY_TEST_RESPONSES: str = "tests/e2e/data/responses/storyTestResponses/" STORY_TEST_CONFIGS: str = "tests/e2e/data/configs/storyTestConfigs" +# Story Test Data Paths +REGRESSION_TEST_DATA: str = "tests/e2e/data/dynamoDB/regressionTestData/" +REGRESSION_RESPONSES: str = "tests/e2e/data/responses/regressionTestResponses/" +REGRESSION_CONFIGS: str = "tests/e2e/data/configs/regressionTestConfigs" diff --git a/tests/e2e/tests/test_error_scenario_tests.py b/tests/e2e/tests/test_error_scenario_tests.py index e2929b80b..b242fa25b 100644 --- a/tests/e2e/tests/test_error_scenario_tests.py +++ b/tests/e2e/tests/test_error_scenario_tests.py @@ -1,5 +1,4 @@ import http - import pytest @@ -7,7 +6,7 @@ def test_check_for_missing_person(eligibility_client): nhs_number = "1234567890" - request_headers = {"authenticated_nhs_number": "1234567890"} + request_headers = {"nhs-login-nhs-number": "1234567890"} expected_body = { "issue": [{ @@ -22,3 +21,64 @@ def test_check_for_missing_person(eligibility_client): assert response["status_code"] == http.HTTPStatus.NOT_FOUND assert response["body"] == expected_body + + + +@pytest.mark.smoketest +@pytest.mark.parametrize( + "scenario, nhs_number, request_headers, expected_status, expected_body", + [ + ( + "correct header - NHS number exists but not found in data", + "1234567890", + {"nhs-login-nhs-number": "1234567890"}, + http.HTTPStatus.NOT_FOUND, + { + "issue": [{ + "code": "nhs-number-not-found", + "diagnostics": 'NHS Number "1234567890" not found.', + "severity": "information" + }], + "resourceType": "OperationOutcome" + }, + ), + ( + "incorrect header - NHS number mismatch", + "1234567890", + {"nhs-login-nhs-number": "12345678900"}, + http.HTTPStatus.FORBIDDEN, + "NHS number mismatch", + ), + ( + "missing header - NHS number required", + "1234567890", + {}, + http.HTTPStatus.FORBIDDEN, + "NHS number mismatch", + ), + ], + ids=[ + "correct-header", + "incorrect-header", + "missing-header" + ] +) +def test_nhs_login_header_handling( + eligibility_client, + scenario, + nhs_number, + request_headers, + expected_status, + expected_body +): + response = eligibility_client.make_request( + nhs_number, + headers=request_headers, + raise_on_error=False + ) + + response_ststus = response["status_code"] + + assert response["status_code"] == expected_status, f"{scenario} failed on status code" + assert response["body"] == expected_body, f"{scenario} failed on response body" + diff --git a/tests/e2e/tests/test_regression_tests.py b/tests/e2e/tests/test_regression_tests.py new file mode 100644 index 000000000..0f8e2419f --- /dev/null +++ b/tests/e2e/tests/test_regression_tests.py @@ -0,0 +1,42 @@ +import http +import os + +import pytest + +from tests.e2e.tests.test_config import * +from tests.e2e.utils.data_loader import load_all_expected_responses, initialise_tests +from tests.e2e.utils.s3ConfigManager import S3ConfigManager + +# Update the below with the configuration values specified in test_config.py +all_data, dto = initialise_tests(REGRESSION_TEST_DATA) +all_expected_responses = load_all_expected_responses(REGRESSION_RESPONSES) +config_path = REGRESSION_CONFIGS + +s3_manager = S3ConfigManager(S3_BUCKET, S3_PREFIX) + +param_list = list(all_data.items()) +id_list = [ + f"{filename} - {scenario.get('scenario_name', 'No Scenario')}" + for filename, scenario in param_list +] + + +@pytest.mark.regressiontest +@pytest.mark.parametrize("filename, scenario", param_list, ids=id_list) +def test_run_regression_tests(filename, scenario, eligibility_client): + nhs_number = scenario["nhs_number"] + config_filename = scenario.get("config_filename", "") + request_headers = scenario.get("request_headers", {}) + s3_manager.upload_if_missing_or_changed(os.path.abspath(os.path.join(config_path, config_filename))) + + actual_response = eligibility_client.make_request(nhs_number, headers= request_headers, strict_ssl=False) + expected_response = all_expected_responses.get(filename).get("response_items", {}) + + # Assert and show details on failure + assert actual_response["status_code"] == http.HTTPStatus.OK + assert actual_response["body"] == expected_response, ( + f"\nโŒ Mismatch in test: {filename}\n" + f"NHS Number: {nhs_number}\n" + f"Expected: {expected_response}\n" + f"Actual: {actual_response}\n" + ) diff --git a/tests/e2e/tests/test_smoke_tests.py b/tests/e2e/tests/test_smoke_tests.py index 0df3c11eb..34888cf9e 100644 --- a/tests/e2e/tests/test_smoke_tests.py +++ b/tests/e2e/tests/test_smoke_tests.py @@ -35,31 +35,10 @@ def test_run_smoke_case(filename, scenario, eligibility_client): expected_response = all_expected_responses.get(filename).get("response_items", {}) # Assert and show details on failure - assert actual_response["status_code"] == 200 + assert actual_response["status_code"] == http.HTTPStatus.OK assert actual_response["body"] == expected_response, ( f"\nโŒ Mismatch in test: {filename}\n" f"NHS Number: {nhs_number}\n" f"Expected: {expected_response}\n" f"Actual: {actual_response}\n" ) - - -@pytest.mark.smoketest -def test_check_for_missing_person(eligibility_client): - nhs_number = "1234567890" - - request_headers = {"authenticated_nhs_number": "1234567890"}, - - expected_body = { - "issue": [{ - "code": "nhs-number-not-found", - "diagnostics": f'NHS Number "{nhs_number}" not found.', - "severity": "information" - }], - "resourceType": "OperationOutcome" - } - - response = eligibility_client.make_request(nhs_number, headers= request_headers, raise_on_error=False) - - assert response["status_code"] == http.HTTPStatus.NOT_FOUND - assert response["body"] == expected_body diff --git a/tests/e2e/tests/test_story_tests.py b/tests/e2e/tests/test_story_tests.py index aab7c6f2e..fb4e74e01 100644 --- a/tests/e2e/tests/test_story_tests.py +++ b/tests/e2e/tests/test_story_tests.py @@ -27,13 +27,18 @@ def test_run_story_test_cases(filename, scenario, eligibility_client): nhs_number = scenario["nhs_number"] config_filename = scenario.get("config_filename", "") request_headers = scenario.get("request_headers", {}) + expected_response_code = scenario.get("expected_response_code", None) s3_manager.upload_if_missing_or_changed(os.path.abspath(os.path.join(config_path, config_filename))) - actual_response = eligibility_client.make_request(nhs_number, strict_ssl=False) + actual_response = eligibility_client.make_request(nhs_number, headers=request_headers, strict_ssl=False) expected_response = all_expected_responses.get(filename).get("response_items", {}) # Assert and show details on failure - assert actual_response["status_code"] == http.HTTPStatus.OK + if expected_response_code: + assert actual_response["status_code"] == expected_response_code + else: + assert actual_response["status_code"] == http.HTTPStatus.OK + assert actual_response["body"] == expected_response, ( f"\nโŒ Mismatch in test: {filename}\n" f"NHS Number: {nhs_number}\n" diff --git a/tests/e2e/utils/data_loader.py b/tests/e2e/utils/data_loader.py index f1e472754..3f4a5c9a0 100644 --- a/tests/e2e/utils/data_loader.py +++ b/tests/e2e/utils/data_loader.py @@ -105,6 +105,7 @@ def load_all_test_scenarios(folder_path, config_folder_path="tests/e2e/data/conf config_filename = raw_json.get("config_filename") scenario_name = raw_json.get("scenario_name") request_headers = raw_json.get("request_headers") + expected_response_code = raw_json.get("expected_response_code") # Resolve placeholders with shared DTO resolved_data = resolve_placeholders_in_data(raw_data, dto, filename) @@ -117,6 +118,7 @@ def load_all_test_scenarios(folder_path, config_folder_path="tests/e2e/data/conf "dynamo_items": resolved_data, "nhs_number": nhs_number, "config_filename": config_filename, + "expected_response_code": expected_response_code, "request_headers": request_headers, "scenario_name": scenario_name, } diff --git a/tests/e2e/utils/dynamo_helper.py b/tests/e2e/utils/dynamo_helper.py index 2202cb85f..3dcc72886 100644 --- a/tests/e2e/utils/dynamo_helper.py +++ b/tests/e2e/utils/dynamo_helper.py @@ -50,7 +50,7 @@ def get_item(self, key: dict): def insert_into_dynamo(data): - print("Inserting into Dynamo (fake):", data) + print("Inserting into Dynamo:", data) table = DynamoDBHelper("eligibility-signposting-api-test-eligibility_datastore") for item in data: try: diff --git a/tests/e2e/utils/eligibilityApiClient.py b/tests/e2e/utils/eligibilityApiClient.py index 4614ef3bf..98e35f1b6 100644 --- a/tests/e2e/utils/eligibilityApiClient.py +++ b/tests/e2e/utils/eligibilityApiClient.py @@ -102,43 +102,6 @@ def make_request( return self._parse_response(response) raise RuntimeError(f"Request error: {req_err}") from req_err - def make_request_old( - self, - nhs_number: str, - method: str = "GET", - payload: Optional[Union[Dict[str, Any], list]] = None, - strict_ssl: bool = False, - raise_on_error: bool = True, - ) -> Dict[str, Any]: - url = f"{self.api_url.rstrip('/')}/{nhs_number}" - cert = ( - str(self.cert_paths["client_cert"]), - str(self.cert_paths["private_key"]), - ) - verify: Union[bool, str] = str(self.cert_paths["ca_cert"]) if strict_ssl else False - - try: - response = requests.request( - method=method.upper(), - url=url, - cert=cert, - verify=verify, - json=payload, - timeout=10, - ) - - if raise_on_error: - response.raise_for_status() - - return self._parse_response(response) - - except requests.exceptions.SSLError as ssl_err: - raise RuntimeError(f"SSL error during request: {ssl_err}") from ssl_err - except requests.exceptions.RequestException as req_err: - response = getattr(req_err, "response", None) - if isinstance(response, Response): - return self._parse_response(response) - raise RuntimeError(f"Request error: {req_err}") from req_err def _parse_response(self, response: Response) -> Dict[str, Any]: try: diff --git a/tests/e2e/utils/s3ConfigManager.py b/tests/e2e/utils/s3ConfigManager.py index 22d2df7d5..9ca6b5f9a 100644 --- a/tests/e2e/utils/s3ConfigManager.py +++ b/tests/e2e/utils/s3ConfigManager.py @@ -33,26 +33,26 @@ def upload_if_missing_or_changed(self, local_path: str) -> None: try: if self.config_exists_and_matches(local_path, s3_key): print( - f"โœ… Config '{filename}' already exists and matches in S3. " + f"\n๐Ÿ” Config '{filename}' already exists and matches in S3. " "Skipping upload." ) return print( - f"๐Ÿงน A different config exists under '{self.s3_prefix}/'. " + f"\n๐Ÿงน A different config exists under '{self.s3_prefix}/'. " "Deleting all existing files..." ) self._delete_all_in_prefix() except self.s3_client.exceptions.NoSuchKey: - print(f"๐Ÿ†• No config found under '{self.s3_prefix}/'. Proceeding to upload.") + print(f"\n๐Ÿ†• No config found under '{self.s3_prefix}/'. Proceeding to upload.") except botocore.exceptions.ClientError as error: if error.response.get("Error", {}).get("Code") == "NoSuchKey": - print(f"๐Ÿ†• No config found under '{self.s3_prefix}/'. Proceeding to upload.") + print(f"\n๐Ÿ†• No config found under '{self.s3_prefix}/'. Proceeding to upload.") else: raise print(f"โฌ†๏ธ Uploading new config '{filename}' to S3...") self.s3_client.upload_file(local_path, self.bucket_name, s3_key) - print(f"โœ… Uploaded to s3://{self.bucket_name}/{s3_key}") + print(f"๐Ÿ“„ Uploaded to s3://{self.bucket_name}/{s3_key}") def config_exists_and_matches(self, local_path: str, s3_key: str) -> bool: try: From f40bb3c5b3b9e4413921e2f7c82f52143c15592c Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Wed, 2 Jul 2025 15:57:44 +0100 Subject: [PATCH 46/72] updates to fix 90+ formatting and linting issues :) --- .../smokeTestData/AUTO_RSV_SB_001.json | 1 + .../smokeTestData/AUTO_RSV_SB_002.json | 1 + .../smokeTestData/AUTO_RSV_SB_003.json | 1 + tests/e2e/tests/conftest.py | 2 +- tests/e2e/tests/test_error_scenario_tests.py | 102 ++++++++---------- tests/e2e/tests/test_regression_tests.py | 27 +++-- tests/e2e/tests/test_smoke_tests.py | 31 +++--- tests/e2e/tests/test_story_tests.py | 25 ++--- tests/e2e/utils/data_loader.py | 53 ++++----- tests/e2e/utils/dynamo_helper.py | 29 ++--- ...ApiClient.py => eligibility_api_client.py} | 61 +++++------ tests/e2e/utils/placeholder_context.py | 2 +- tests/e2e/utils/placeholder_utils.py | 87 +++++++++------ ...3ConfigManager.py => s3_config_manager.py} | 53 ++++----- 14 files changed, 231 insertions(+), 244 deletions(-) rename tests/e2e/utils/{eligibilityApiClient.py => eligibility_api_client.py} (68%) rename tests/e2e/utils/{s3ConfigManager.py => s3_config_manager.py} (55%) diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json index 429c0c9da..d462e22a9 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json @@ -1,5 +1,6 @@ { "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested actions (with booking)", + "request_headers": {"nhs-login-nhs-number": "5000000001"}, "config_filename": "AUTO_RSV_SB_001.json", "data": [ { diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json index fa00e7ef8..6e2d77050 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json @@ -1,5 +1,6 @@ { "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested action (not booking)", + "request_headers": {"nhs-login-nhs-number": "5000000002"}, "config_filename": "AUTO_RSV_SB_001.json", "data": [ { diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json index dbece05d1..c5ab64bdb 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json @@ -1,5 +1,6 @@ { "scenario_name": "RSV - Actionable due to membership of an alternative Age Cohort incl. suggested action", + "request_headers": {"nhs-login-nhs-number": "5000000003"}, "config_filename": "AUTO_RSV_SB_001.json", "data": [ { diff --git a/tests/e2e/tests/conftest.py b/tests/e2e/tests/conftest.py index 842407c9a..2814eaad5 100644 --- a/tests/e2e/tests/conftest.py +++ b/tests/e2e/tests/conftest.py @@ -7,7 +7,7 @@ import pytest from dotenv import load_dotenv -from tests.e2e.utils.eligibilityApiClient import EligibilityApiClient +from tests.e2e.utils.eligibility_api_client import EligibilityApiClient # Load environment variables from .env.local load_dotenv(dotenv_path=".env.local") diff --git a/tests/e2e/tests/test_error_scenario_tests.py b/tests/e2e/tests/test_error_scenario_tests.py index b242fa25b..25956058f 100644 --- a/tests/e2e/tests/test_error_scenario_tests.py +++ b/tests/e2e/tests/test_error_scenario_tests.py @@ -1,4 +1,5 @@ import http + import pytest @@ -9,12 +10,14 @@ def test_check_for_missing_person(eligibility_client): request_headers = {"nhs-login-nhs-number": "1234567890"} expected_body = { - "issue": [{ - "code": "nhs-number-not-found", - "diagnostics": f'NHS Number "{nhs_number}" not found.', - "severity": "information" - }], - "resourceType": "OperationOutcome" + "issue": [ + { + "code": "nhs-number-not-found", + "diagnostics": f'NHS Number "{nhs_number}" not found.', + "severity": "information", + } + ], + "resourceType": "OperationOutcome", } response = eligibility_client.make_request(nhs_number, headers=request_headers, raise_on_error=False) @@ -23,62 +26,49 @@ def test_check_for_missing_person(eligibility_client): assert response["body"] == expected_body - @pytest.mark.smoketest @pytest.mark.parametrize( - "scenario, nhs_number, request_headers, expected_status, expected_body", + "test_case", [ - ( - "correct header - NHS number exists but not found in data", - "1234567890", - {"nhs-login-nhs-number": "1234567890"}, - http.HTTPStatus.NOT_FOUND, - { - "issue": [{ - "code": "nhs-number-not-found", - "diagnostics": 'NHS Number "1234567890" not found.', - "severity": "information" - }], - "resourceType": "OperationOutcome" + { + "scenario": "correct header - NHS number exists but not found in data", + "nhs_number": "1234567890", + "request_headers": {"nhs-login-nhs-number": "1234567890"}, + "expected_status": http.HTTPStatus.NOT_FOUND, + "expected_body": { + "issue": [ + { + "code": "nhs-number-not-found", + "diagnostics": 'NHS Number "1234567890" not found.', + "severity": "information", + } + ], + "resourceType": "OperationOutcome", }, - ), - ( - "incorrect header - NHS number mismatch", - "1234567890", - {"nhs-login-nhs-number": "12345678900"}, - http.HTTPStatus.FORBIDDEN, - "NHS number mismatch", - ), - ( - "missing header - NHS number required", - "1234567890", - {}, - http.HTTPStatus.FORBIDDEN, - "NHS number mismatch", - ), + }, + { + "scenario": "incorrect header - NHS number mismatch", + "nhs_number": "1234567890", + "request_headers": {"nhs-login-nhs-number": "12345678900"}, + "expected_status": http.HTTPStatus.FORBIDDEN, + "expected_body": "NHS number mismatch", + }, + { + "scenario": "missing header - NHS number required", + "nhs_number": "1234567890", + "request_headers": {}, + "expected_status": http.HTTPStatus.FORBIDDEN, + "expected_body": "NHS number mismatch", + }, ], - ids=[ - "correct-header", - "incorrect-header", - "missing-header" - ] + ids=["correct-header", "incorrect-header", "missing-header"], ) -def test_nhs_login_header_handling( - eligibility_client, - scenario, - nhs_number, - request_headers, - expected_status, - expected_body -): +def test_nhs_login_header_handling(eligibility_client, test_case): response = eligibility_client.make_request( - nhs_number, - headers=request_headers, - raise_on_error=False + test_case["nhs_number"], + headers=test_case["request_headers"], + raise_on_error=False, ) - response_ststus = response["status_code"] - - assert response["status_code"] == expected_status, f"{scenario} failed on status code" - assert response["body"] == expected_body, f"{scenario} failed on response body" - + assert response["status_code"] == test_case["expected_status"], f"{test_case['scenario']} failed on status code" + assert response["body"] == test_case["expected_body"], f"{test_case['scenario']} failed on response body" diff --git a/tests/e2e/tests/test_regression_tests.py b/tests/e2e/tests/test_regression_tests.py index 0f8e2419f..f6b062811 100644 --- a/tests/e2e/tests/test_regression_tests.py +++ b/tests/e2e/tests/test_regression_tests.py @@ -1,35 +1,32 @@ import http -import os +from pathlib import Path import pytest -from tests.e2e.tests.test_config import * -from tests.e2e.utils.data_loader import load_all_expected_responses, initialise_tests -from tests.e2e.utils.s3ConfigManager import S3ConfigManager +from tests.e2e.tests import test_config +from tests.e2e.utils.data_loader import initialise_tests, load_all_expected_responses +from tests.e2e.utils.s3_config_manager import S3ConfigManager # Update the below with the configuration values specified in test_config.py -all_data, dto = initialise_tests(REGRESSION_TEST_DATA) -all_expected_responses = load_all_expected_responses(REGRESSION_RESPONSES) -config_path = REGRESSION_CONFIGS +all_data, dto = initialise_tests(test_config.REGRESSION_TEST_DATA) +all_expected_responses = load_all_expected_responses(test_config.REGRESSION_RESPONSES) +config_path = test_config.REGRESSION_CONFIGS -s3_manager = S3ConfigManager(S3_BUCKET, S3_PREFIX) +s3_manager = S3ConfigManager(test_config.S3_BUCKET, test_config.S3_PREFIX) param_list = list(all_data.items()) -id_list = [ - f"{filename} - {scenario.get('scenario_name', 'No Scenario')}" - for filename, scenario in param_list -] +id_list = [f"{filename} - {scenario.get('scenario_name', 'No Scenario')}" for filename, scenario in param_list] @pytest.mark.regressiontest -@pytest.mark.parametrize("filename, scenario", param_list, ids=id_list) +@pytest.mark.parametrize(("filename", "scenario"), param_list, ids=id_list) def test_run_regression_tests(filename, scenario, eligibility_client): nhs_number = scenario["nhs_number"] config_filename = scenario.get("config_filename", "") request_headers = scenario.get("request_headers", {}) - s3_manager.upload_if_missing_or_changed(os.path.abspath(os.path.join(config_path, config_filename))) + s3_manager.upload_if_missing_or_changed((Path(config_path) / config_filename).resolve()) - actual_response = eligibility_client.make_request(nhs_number, headers= request_headers, strict_ssl=False) + actual_response = eligibility_client.make_request(nhs_number, headers=request_headers, strict_ssl=False) expected_response = all_expected_responses.get(filename).get("response_items", {}) # Assert and show details on failure diff --git a/tests/e2e/tests/test_smoke_tests.py b/tests/e2e/tests/test_smoke_tests.py index 34888cf9e..85f5e01d4 100644 --- a/tests/e2e/tests/test_smoke_tests.py +++ b/tests/e2e/tests/test_smoke_tests.py @@ -1,37 +1,32 @@ import http -import os +from pathlib import Path import pytest -from tests.e2e.tests.test_config import * -from tests.e2e.utils.data_loader import load_all_expected_responses, initialise_tests -from tests.e2e.utils.s3ConfigManager import S3ConfigManager +from tests.e2e.tests import test_config +from tests.e2e.utils.data_loader import initialise_tests, load_all_expected_responses +from tests.e2e.utils.s3_config_manager import S3ConfigManager # Update the below with the configuration values specified in test_config.py -all_data, dto = initialise_tests(SMOKE_TEST_DATA) -all_expected_responses = load_all_expected_responses(SMOKE_TEST_RESPONSES) -config_path = SMOKE_TEST_CONFIGS +all_data, dto = initialise_tests(test_config.SMOKE_TEST_DATA) +all_expected_responses = load_all_expected_responses(test_config.SMOKE_TEST_RESPONSES) +config_path = test_config.SMOKE_TEST_CONFIGS -s3_manager = S3ConfigManager(S3_BUCKET, S3_PREFIX) +s3_manager = S3ConfigManager(test_config.S3_BUCKET, test_config.S3_PREFIX) param_list = list(all_data.items()) -id_list = [ - f"{filename} - {scenario.get('scenario_name', 'No Scenario')}" - for filename, scenario in param_list -] +id_list = [f"{filename} - {scenario.get('scenario_name', 'No Scenario')}" for filename, scenario in param_list] @pytest.mark.smoketest -@pytest.mark.parametrize("filename, scenario", param_list, ids=id_list) +@pytest.mark.parametrize(("filename", "scenario"), param_list, ids=id_list) def test_run_smoke_case(filename, scenario, eligibility_client): - # get the nhs_number from the scenario nhs_number = scenario["nhs_number"] - # get the associated campaign config file from the scenario config_filename = scenario.get("config_filename", "") - # upload that config file to s3 if it is missing or has changed - s3_manager.upload_if_missing_or_changed(os.path.abspath(os.path.join(config_path, config_filename))) + request_headers = scenario.get("request_headers", {}) + s3_manager.upload_if_missing_or_changed((Path(config_path) / config_filename).resolve()) - actual_response = eligibility_client.make_request(nhs_number, strict_ssl=False) + actual_response = eligibility_client.make_request(nhs_number, headers=request_headers, strict_ssl=False) expected_response = all_expected_responses.get(filename).get("response_items", {}) # Assert and show details on failure diff --git a/tests/e2e/tests/test_story_tests.py b/tests/e2e/tests/test_story_tests.py index fb4e74e01..3e2938c6c 100644 --- a/tests/e2e/tests/test_story_tests.py +++ b/tests/e2e/tests/test_story_tests.py @@ -1,34 +1,31 @@ import http -import os +from pathlib import Path import pytest -from tests.e2e.tests.test_config import * -from tests.e2e.utils.data_loader import load_all_expected_responses, initialise_tests -from tests.e2e.utils.s3ConfigManager import S3ConfigManager +from tests.e2e.tests import test_config +from tests.e2e.utils.data_loader import initialise_tests, load_all_expected_responses +from tests.e2e.utils.s3_config_manager import S3ConfigManager # Update the below with the configuration values specified in test_config.py -all_data, dto = initialise_tests(STORY_TEST_DATA) -all_expected_responses = load_all_expected_responses(STORY_TEST_RESPONSES) -config_path = STORY_TEST_CONFIGS +all_data, dto = initialise_tests(test_config.STORY_TEST_DATA) +all_expected_responses = load_all_expected_responses(test_config.STORY_TEST_RESPONSES) +config_path = test_config.STORY_TEST_CONFIGS -s3_manager = S3ConfigManager(S3_BUCKET, S3_PREFIX) +s3_manager = S3ConfigManager(test_config.S3_BUCKET, test_config.S3_PREFIX) param_list = list(all_data.items()) -id_list = [ - f"{filename} - {scenario.get('scenario_name', 'No Scenario')}" - for filename, scenario in param_list -] +id_list = [f"{filename} - {scenario.get('scenario_name', 'No Scenario')}" for filename, scenario in param_list] @pytest.mark.storytest_all -@pytest.mark.parametrize("filename, scenario", param_list, ids=id_list) +@pytest.mark.parametrize(("filename", "scenario"), param_list, ids=id_list) def test_run_story_test_cases(filename, scenario, eligibility_client): nhs_number = scenario["nhs_number"] config_filename = scenario.get("config_filename", "") request_headers = scenario.get("request_headers", {}) expected_response_code = scenario.get("expected_response_code", None) - s3_manager.upload_if_missing_or_changed(os.path.abspath(os.path.join(config_path, config_filename))) + s3_manager.upload_if_missing_or_changed((Path(config_path) / config_filename).resolve()) actual_response = eligibility_client.make_request(nhs_number, headers=request_headers, strict_ssl=False) expected_response = all_expected_responses.get(filename).get("response_items", {}) diff --git a/tests/e2e/utils/data_loader.py b/tests/e2e/utils/data_loader.py index 3f4a5c9a0..8e7a9787c 100644 --- a/tests/e2e/utils/data_loader.py +++ b/tests/e2e/utils/data_loader.py @@ -1,13 +1,13 @@ -import os import json +from pathlib import Path from .dynamo_helper import insert_into_dynamo +from .placeholder_context import PlaceholderDTO, ResolvedPlaceholderContext from .placeholder_utils import resolve_placeholders -from .placeholder_context import ResolvedPlaceholderContext, PlaceholderDTO def initialise_tests(folder): - folder_path = os.path.abspath(folder) + folder_path = Path(folder).resolve() all_data, dto = load_all_test_scenarios(folder_path) # Insert to Dynamo (placeholder) @@ -20,17 +20,16 @@ def initialise_tests(folder): def resolve_placeholders_in_data(data, context, file_name): if isinstance(data, dict): return {k: resolve_placeholders_in_data(v, context, file_name) for k, v in data.items()} - elif isinstance(data, list): + if isinstance(data, list): return [resolve_placeholders_in_data(item, context, file_name) for item in data] - else: - return resolve_placeholders(data, context, file_name) + return resolve_placeholders(data, context, file_name) def load_test_scenario(file_path): - with open(file_path, "r") as f: + with Path.open(file_path) as f: raw_data = json.load(f) - file_name = os.path.basename(file_path) + file_name = Path(file_path).name context = ResolvedPlaceholderContext() resolved_data = resolve_placeholders_in_data(raw_data["data"], context, file_name) @@ -38,7 +37,7 @@ def load_test_scenario(file_path): "file": file_name, "scenario_name": raw_data.get("scenario_name"), "data": resolved_data, - "placeholders": context.all() # Now just placeholder โ†’ value + "placeholders": context.all(), # Now just placeholder โ†’ value } @@ -48,7 +47,7 @@ def find_nhs(obj): for k, v in obj.items(): if k.lower().replace("_", "") == "nhsnumber": return v - elif isinstance(v, (dict, list)): + if isinstance(v, (dict, list)): result = find_nhs(v) if result: return result @@ -66,38 +65,30 @@ def load_all_expected_responses(folder_path): all_data = {} dto = PlaceholderDTO() # Shared across all files - for filename in os.listdir(folder_path): - if not filename.endswith(".json"): + for path in Path(folder_path).iterdir(): + if path.suffix != ".json": continue - full_path = os.path.join(folder_path, filename) - - # Load JSON - with open(full_path, "r") as f: + with path.open() as f: raw_json = json.load(f) - resolved_data = resolve_placeholders_in_data(raw_json, dto, filename) - cleaned_data = clean_expected_response(resolved_data, ) + resolved_data = resolve_placeholders_in_data(raw_json, dto, path.name) + cleaned_data = clean_expected_response(resolved_data) - all_data[filename] = { - "response_items": cleaned_data - } + all_data[path.name] = {"response_items": cleaned_data} return all_data -def load_all_test_scenarios(folder_path, config_folder_path="tests/e2e/data/configs"): +def load_all_test_scenarios(folder_path): all_data = {} dto = PlaceholderDTO() # Shared across all files - for filename in os.listdir(folder_path): - if not filename.endswith(".json"): + for path in Path(folder_path).iterdir(): + if path.suffix != ".json": continue - full_path = os.path.join(folder_path, filename) - - # Load scenario JSON - with open(full_path, "r") as f: + with path.open() as f: raw_json = json.load(f) raw_data = raw_json["data"] @@ -108,13 +99,13 @@ def load_all_test_scenarios(folder_path, config_folder_path="tests/e2e/data/conf expected_response_code = raw_json.get("expected_response_code") # Resolve placeholders with shared DTO - resolved_data = resolve_placeholders_in_data(raw_data, dto, filename) + resolved_data = resolve_placeholders_in_data(raw_data, dto, path.name) # Extract NHS number nhs_number = extract_nhs_number_from_data(resolved_data) # Add resolved scenario - all_data[filename] = { + all_data[path.name] = { "dynamo_items": resolved_data, "nhs_number": nhs_number, "config_filename": config_filename, @@ -138,6 +129,6 @@ def _remove_volatile_fields(data, keys_to_remove): for key, value in data.items() if key not in keys_to_remove } - elif isinstance(data, list): + if isinstance(data, list): return [_remove_volatile_fields(item, keys_to_remove) for item in data] return data diff --git a/tests/e2e/utils/dynamo_helper.py b/tests/e2e/utils/dynamo_helper.py index 3dcc72886..5a749ed57 100644 --- a/tests/e2e/utils/dynamo_helper.py +++ b/tests/e2e/utils/dynamo_helper.py @@ -1,16 +1,18 @@ -import os -from dotenv import load_dotenv +import logging + import boto3 from botocore.exceptions import ClientError +from dotenv import load_dotenv # Load credentials from .env load_dotenv() +logger = logging.getLogger(__name__) class DynamoDBHelper: def __init__(self, table_name): # Create DynamoDB resource using credentials from env - self.dynamodb = boto3.resource('dynamodb') + self.dynamodb = boto3.resource("dynamodb") self.table = self.dynamodb.Table(table_name) def insert_item(self, item: dict): @@ -19,10 +21,11 @@ def insert_item(self, item: dict): """ try: response = self.table.put_item(Item=item) - return response except ClientError as e: - print(f"Failed to insert item: {e.response['Error']['Message']}") + logger.exception("Failed to insert item: %s", e.response["Error"]["Message"]) raise + else: + return response def insert_items(self, items: list): """ @@ -32,10 +35,11 @@ def insert_items(self, items: list): with self.table.batch_writer() as batch: for item in items: batch.put_item(Item=item) - print("Batch insert complete.") except ClientError as e: - print(f"Batch insert failed: {e.response['Error']['Message']}") + logger.exception("Batch insert failed: %s", e.response["Error"]["Message"]) raise + else: + logger.info("Batch insert complete.") def get_item(self, key: dict): """ @@ -43,18 +47,19 @@ def get_item(self, key: dict): """ try: response = self.table.get_item(Key=key) - return response.get("Item") except ClientError as e: - print(f"Failed to get item: {e.response['Error']['Message']}") + logger.exception("Failed to get item: %s", e.response["Error"]["Message"]) raise + else: + return response.get("Item") def insert_into_dynamo(data): - print("Inserting into Dynamo:", data) + logger.info("Inserting into Dynamo: %s", data) table = DynamoDBHelper("eligibility-signposting-api-test-eligibility_datastore") for item in data: try: table.insert_item(item) - print(f"โœ… Inserted: {item}") + logger.info("โœ… Inserted: %s", item) except ClientError as e: - print(f"โŒ Failed to insert {item}: {e.response['Error']['Message']}") + logger.exception("โŒ Failed to insert %s: %s", item, e.response["Error"]["Message"]) diff --git a/tests/e2e/utils/eligibilityApiClient.py b/tests/e2e/utils/eligibility_api_client.py similarity index 68% rename from tests/e2e/utils/eligibilityApiClient.py rename to tests/e2e/utils/eligibility_api_client.py index 98e35f1b6..d69be6bfb 100644 --- a/tests/e2e/utils/eligibilityApiClient.py +++ b/tests/e2e/utils/eligibility_api_client.py @@ -1,35 +1,35 @@ -import os import json +import os from pathlib import Path -from typing import Any, Dict, Optional, Union +from typing import Any import boto3 import requests -from dotenv import load_dotenv from botocore.exceptions import ClientError +from dotenv import load_dotenv from requests import Response class EligibilityApiClient: def __init__(self, api_url: str, cert_dir: str = "tests/e2e/certs") -> None: - load_dotenv(dotenv_path=os.path.join(os.path.dirname(__file__), "../.env")) + load_dotenv(dotenv_path=Path(__file__).resolve().parent / "../.env") self.api_url: str = api_url - self.region: Optional[str] = os.getenv("AWS_REGION") - self.aws_access_key_id: Optional[str] = os.getenv("AWS_ACCESS_KEY_ID") - self.aws_secret_access_key: Optional[str] = os.getenv("AWS_SECRET_ACCESS_KEY") - self.aws_session_token: Optional[str] = os.getenv("AWS_SESSION_TOKEN") + self.region: str | None = os.getenv("AWS_REGION") + self.aws_access_key_id: str | None = os.getenv("AWS_ACCESS_KEY_ID") + self.aws_secret_access_key: str | None = os.getenv("AWS_SECRET_ACCESS_KEY") + self.aws_session_token: str | None = os.getenv("AWS_SESSION_TOKEN") self.cert_dir: Path = Path(cert_dir) self.cert_dir.mkdir(parents=True, exist_ok=True) - self.cert_paths: Dict[str, Path] = { + self.cert_paths: dict[str, Path] = { "private_key": self.cert_dir / "api_private_key_cert.pem", "client_cert": self.cert_dir / "api_client_cert.pem", "ca_cert": self.cert_dir / "api_ca_cert.pem", } - self.ssm_params: Dict[str, str] = { + self.ssm_params: dict[str, str] = { "private_key": "/test/mtls/api_private_key_cert", "client_cert": "/test/mtls/api_client_cert", "ca_cert": "/test/mtls/api_ca_cert", @@ -37,7 +37,7 @@ def __init__(self, api_url: str, cert_dir: str = "tests/e2e/certs") -> None: self._ensure_certs_present() - def _get_ssm_parameter(self, param_name: str, decrypt: bool = True) -> str: + def _get_ssm_parameter(self, param_name: str, *, decrypt: bool = True) -> str: try: client = boto3.client( "ssm", @@ -49,7 +49,8 @@ def _get_ssm_parameter(self, param_name: str, decrypt: bool = True) -> str: response = client.get_parameter(Name=param_name, WithDecryption=decrypt) return response["Parameter"]["Value"] except ClientError as e: - raise RuntimeError(f"Error retrieving {param_name} from SSM: {e}") from e + msg = f"Error retrieving {param_name} from SSM: {e}" + raise RuntimeError(msg) from e def _ensure_certs_present(self) -> None: missing = [k for k, path in self.cert_paths.items() if not path.exists()] @@ -59,24 +60,25 @@ def _ensure_certs_present(self) -> None: for cert_type in missing: param_name = self.ssm_params[cert_type] cert_value = self._get_ssm_parameter(param_name) - with open(self.cert_paths[cert_type], "w", encoding="utf-8") as f: + with Path.open(self.cert_paths[cert_type], "w", encoding="utf-8") as f: f.write(cert_value) def make_request( - self, - nhs_number: str, - method: str = "GET", - payload: Optional[Union[Dict[str, Any], list]] = None, - headers: Optional[Dict[str, str]] = None, - strict_ssl: bool = False, - raise_on_error: bool = True, - ) -> Dict[str, Any]: + self, + nhs_number: str, + method: str = "GET", + payload: dict[str, Any] | list | None = None, + headers: dict[str, str] | None = None, + **options, + ) -> dict[str, Any]: + strict_ssl = options.get("strict_ssl", False) + raise_on_error = options.get("raise_on_error", True) url = f"{self.api_url.rstrip('/')}/{nhs_number}" cert = ( str(self.cert_paths["client_cert"]), str(self.cert_paths["private_key"]), ) - verify: Union[bool, str] = str(self.cert_paths["ca_cert"]) if strict_ssl else False + verify: bool | str = str(self.cert_paths["ca_cert"]) if strict_ssl else False try: response = requests.request( @@ -95,15 +97,16 @@ def make_request( return self._parse_response(response) except requests.exceptions.SSLError as ssl_err: - raise RuntimeError(f"SSL error during request: {ssl_err}") from ssl_err + msg = "SSL error during request: %s", ssl_err + raise RuntimeError(msg) from ssl_err except requests.exceptions.RequestException as req_err: response = getattr(req_err, "response", None) if isinstance(response, Response): return self._parse_response(response) - raise RuntimeError(f"Request error: {req_err}") from req_err - + msg = "Request error: %s", req_err + raise RuntimeError(msg) from req_err - def _parse_response(self, response: Response) -> Dict[str, Any]: + def _parse_response(self, response: Response) -> dict[str, Any]: try: data = response.json() cleaned = self._clean_response(data) @@ -121,15 +124,13 @@ def _clean_response(self, data: Any) -> Any: keys_to_ignore = ["responseId", "lastUpdated"] return self._remove_volatile_fields(data, keys_to_ignore) - def _remove_volatile_fields( - self, data: Union[Dict[str, Any], list, Any], keys_to_remove: list - ) -> Any: + def _remove_volatile_fields(self, data: dict[str, Any] | list | Any, keys_to_remove: list) -> Any: if isinstance(data, dict): return { key: self._remove_volatile_fields(value, keys_to_remove) for key, value in data.items() if key not in keys_to_remove } - elif isinstance(data, list): + if isinstance(data, list): return [self._remove_volatile_fields(item, keys_to_remove) for item in data] return data diff --git a/tests/e2e/utils/placeholder_context.py b/tests/e2e/utils/placeholder_context.py index 4809407d0..5d22c5534 100644 --- a/tests/e2e/utils/placeholder_context.py +++ b/tests/e2e/utils/placeholder_context.py @@ -2,7 +2,7 @@ class ResolvedPlaceholderContext: def __init__(self): self.values = {} - def add(self, placeholder: str, resolved_value: str, file_name: str = None): + def add(self, placeholder: str, resolved_value: str): self.values[placeholder] = resolved_value # Drop file_name key nesting def get(self, placeholder: str) -> str: diff --git a/tests/e2e/utils/placeholder_utils.py b/tests/e2e/utils/placeholder_utils.py index 5b106a63d..1ddf67f8f 100644 --- a/tests/e2e/utils/placeholder_utils.py +++ b/tests/e2e/utils/placeholder_utils.py @@ -1,7 +1,11 @@ +import logging import re -from datetime import datetime, timedelta -from dateutil.relativedelta import relativedelta from calendar import isleap +from datetime import UTC, datetime, timedelta + +from dateutil.relativedelta import relativedelta + +logger = logging.getLogger(__name__) def resolve_placeholders(value, context=None, file_name=None): @@ -10,50 +14,63 @@ def resolve_placeholders(value, context=None, file_name=None): match = re.search(r"<<(.*?)>>", value) if not match: - return value # No placeholder to resolve + return value placeholder = match.group(1) - parts = placeholder.split("_") try: - if placeholder in ["IGNORE_RESPONSE_ID", "IGNORE_DATE"]: - return value.replace(f"<<{placeholder}>>", placeholder) - elif len(parts) != 3 or parts[0] not in ["DATE", "RDATE", "IGNORE"]: - return value # Unrecognized format + resolved = _resolve_placeholder_value(placeholder) + if context: + context.add(placeholder, resolved, file_name) + return value.replace(f"<<{placeholder}>>", resolved) + except Exception: + logger.exception("[ERROR] Could not resolve placeholder %s:", placeholder) + return value - date_type, arg = parts[1], parts[2] - today = datetime.today() - if date_type == "AGE": - target_year = today.year - int(arg) - try: - result_date = today.replace(year=target_year) - except ValueError: - if today.month == 2 and today.day == 29 and not isleap(target_year): - result_date = datetime(target_year, 2, 28) - else: - raise +def _resolve_placeholder_value(placeholder: str) -> str: + placeholder_parts_length = 3 + valid_placeholder_types = ["DATE", "RDATE", "IGNORE"] + result = f"<<{placeholder}>>" # Default fallback - elif date_type == "DAY": - result_date = today + timedelta(days=int(arg)) + if placeholder in ["IGNORE_RESPONSE_ID", "IGNORE_DATE"]: + return placeholder - elif date_type == "MONTH": - result_date = today + relativedelta(months=int(arg)) + parts = placeholder.split("_") + if len(parts) != placeholder_parts_length or parts[0] not in valid_placeholder_types: + return result - elif date_type == "YEAR": - result_date = today + relativedelta(years=int(arg)) + today = datetime.now(UTC) + date_type, arg = parts[1], parts[2] - else: - return value + try: + if date_type == "AGE": + result = _resolve_age_placeholder(today, int(arg), parts[0]) + elif date_type == "DAY": + result = _format_date(today + timedelta(days=int(arg)), parts[0]) + elif date_type == "MONTH": + result = _format_date(today + relativedelta(months=int(arg)), parts[0]) + elif date_type == "YEAR": + result = _format_date(today + relativedelta(years=int(arg)), parts[0]) + except Exception: + logger.exception("Failed to resolve placeholder: %s", placeholder) + raise + return result - resolved = result_date.strftime("%Y%m%d") if parts[0] == "DATE" else result_date.strftime("%-d %B %Y") - if context: - context.add(placeholder, resolved, file_name) +def _resolve_age_placeholder(today: datetime, years_back: int, format_type: str) -> str: + target_year = today.year - years_back + february = 2 + leap_year_day = 29 + try: + result_date = today.replace(year=target_year) + except ValueError: + if today.month == february and today.day == leap_year_day and not isleap(target_year): + result_date = datetime(target_year, 2, 28, tzinfo=UTC) + else: + raise + return _format_date(result_date, format_type) - # Replace the single placeholder in the original string - return value.replace(f"<<{placeholder}>>", resolved) - except Exception as e: - print(f"[ERROR] Could not resolve placeholder '{placeholder}': {e}") - return value +def _format_date(date: datetime, format_type: str) -> str: + return date.strftime("%Y%m%d") if format_type == "DATE" else date.strftime("%-d %B %Y") diff --git a/tests/e2e/utils/s3ConfigManager.py b/tests/e2e/utils/s3_config_manager.py similarity index 55% rename from tests/e2e/utils/s3ConfigManager.py rename to tests/e2e/utils/s3_config_manager.py index 9ca6b5f9a..6edefe109 100644 --- a/tests/e2e/utils/s3ConfigManager.py +++ b/tests/e2e/utils/s3_config_manager.py @@ -1,12 +1,13 @@ -import os import hashlib +import logging +from pathlib import Path + import boto3 import botocore -from typing import Optional - from dotenv import load_dotenv load_dotenv() +logger = logging.getLogger(__name__) class S3ConfigManager: @@ -16,70 +17,60 @@ def __init__(self, bucket_name: str, s3_prefix: str = "") -> None: self.s3_client = boto3.client("s3") def _s3_key(self, filename: str) -> str: - return os.path.join(self.s3_prefix, filename) + return str(Path(self.s3_prefix) / filename) - def _calculate_file_hash(self, file_path: str) -> str: + def _calculate_file_hash(self, file_path: Path) -> str: """Return SHA256 hash of the given file.""" sha256 = hashlib.sha256() - with open(file_path, "rb") as file: + with file_path.open("rb") as file: for chunk in iter(lambda: file.read(4096), b""): sha256.update(chunk) return sha256.hexdigest() - def upload_if_missing_or_changed(self, local_path: str) -> None: - filename = os.path.basename(local_path) + def upload_if_missing_or_changed(self, local_path: Path) -> None: + filename = Path(local_path).name s3_key = self._s3_key(filename) try: if self.config_exists_and_matches(local_path, s3_key): - print( - f"\n๐Ÿ” Config '{filename}' already exists and matches in S3. " - "Skipping upload." - ) + logger.info("\n๐Ÿ” Config '%s' already exists and matches in S3. Skipping upload.", filename) return - print( - f"\n๐Ÿงน A different config exists under '{self.s3_prefix}/'. " - "Deleting all existing files..." - ) + logger.info("\n๐Ÿงน A different config exists under '%s/'. Deleting all existing files...", self.s3_prefix) self._delete_all_in_prefix() except self.s3_client.exceptions.NoSuchKey: - print(f"\n๐Ÿ†• No config found under '{self.s3_prefix}/'. Proceeding to upload.") + logger.info("\n๐Ÿ†• No config found under '%s/'. Proceeding to upload.", self.s3_prefix) except botocore.exceptions.ClientError as error: if error.response.get("Error", {}).get("Code") == "NoSuchKey": - print(f"\n๐Ÿ†• No config found under '{self.s3_prefix}/'. Proceeding to upload.") + logger.info("\n๐Ÿ†• No config found under '%s/'. Proceeding to upload.", self.s3_prefix) else: raise - print(f"โฌ†๏ธ Uploading new config '{filename}' to S3...") + logger.info("โฌ†๏ธ Uploading new config '%s' to S3...", filename) self.s3_client.upload_file(local_path, self.bucket_name, s3_key) - print(f"๐Ÿ“„ Uploaded to s3://{self.bucket_name}/{s3_key}") + logger.info("๐Ÿ“„ Uploaded to s3://%s/%s", self.bucket_name, s3_key) - def config_exists_and_matches(self, local_path: str, s3_key: str) -> bool: + def config_exists_and_matches(self, local_path: Path, s3_key: str) -> bool: try: s3_obj = self.s3_client.get_object(Bucket=self.bucket_name, Key=s3_key) s3_data = s3_obj["Body"].read() s3_hash = hashlib.sha256(s3_data).hexdigest() local_hash = self._calculate_file_hash(local_path) - return s3_hash == local_hash except self.s3_client.exceptions.NoSuchKey: return False except botocore.exceptions.ClientError as error: if error.response.get("Error", {}).get("Code") == "NoSuchKey": return False raise + else: + return s3_hash == local_hash def _delete_all_in_prefix(self) -> None: """Delete all S3 objects under the current prefix.""" - response = self.s3_client.list_objects_v2( - Bucket=self.bucket_name, Prefix=self.s3_prefix - ) + response = self.s3_client.list_objects_v2(Bucket=self.bucket_name, Prefix=self.s3_prefix) if "Contents" in response: to_delete = [{"Key": obj["Key"]} for obj in response["Contents"]] - self.s3_client.delete_objects( - Bucket=self.bucket_name, - Delete={"Objects": to_delete} - ) - print(f"๐Ÿ—‘๏ธ Deleted {len(to_delete)} file(s) under prefix '{self.s3_prefix}/'.") + self.s3_client.delete_objects(Bucket=self.bucket_name, Delete={"Objects": to_delete}) + logger.info("๐Ÿ—‘๏ธ Deleted %d file(s) under prefix '%s/'.", len(to_delete), self.s3_prefix) else: - print(f"๐Ÿ“ญ Nothing to delete under prefix '{self.s3_prefix}/'.") + logger.info("๐Ÿ“ญ Nothing to delete under prefix '%s/'.", self.s3_prefix) From 0f3d8fcda6e24e732092caf32e8481109fe19094 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 7 Jul 2025 10:03:23 +0100 Subject: [PATCH 47/72] updates --- pytest.ini | 4 +- .../integrationTestData/AUTO_RSV_INT_001.json | 44 +++++++++++++++++++ .../integrationTestData/AUTO_RSV_INT_002.json | 44 +++++++++++++++++++ .../integrationTestData/AUTO_RSV_INT_003.json | 44 +++++++++++++++++++ tests/e2e/features/conftest.py | 2 +- tests/e2e/tests/__init__.py | 2 + tests/e2e/tests/conftest.py | 2 +- .../tests/eligibility_signposting/conftest.py | 5 ++- .../test_eligibility_check.py | 3 +- .../test_eligibility_check_bdd.py | 28 ++++++------ tests/e2e/tests/test_config.py | 2 + tests/e2e/tests/test_regression_tests.py | 2 +- tests/e2e/tests/test_smoke_tests.py | 2 +- tests/e2e/tests/test_story_tests.py | 2 +- tests/e2e/utils/__init__.py | 2 + tests/e2e/utils/api_client.py | 3 +- tests/e2e/utils/placeholder_utils.py | 1 + tests/e2e/utils/s3_config_manager.py | 12 ++++- 18 files changed, 179 insertions(+), 25 deletions(-) create mode 100644 tests/e2e/data/dynamoDB/integrationTestData/AUTO_RSV_INT_001.json create mode 100644 tests/e2e/data/dynamoDB/integrationTestData/AUTO_RSV_INT_002.json create mode 100644 tests/e2e/data/dynamoDB/integrationTestData/AUTO_RSV_INT_003.json diff --git a/pytest.ini b/pytest.ini index 1a4189b55..fe523a190 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,7 +1,9 @@ [pytest] python_files = *_tests.py test_*.py norecursedirs = .venv .eggs build dist utils -addopts = --strict-markers +#addopts = --strict-markers markers = e2e: end to end tests smoketest: suitable to run against all environments even production + functionale2eregression: functional regression tests + sandboxtests: tests to replicate example sandbox responses agreed with vita diff --git a/tests/e2e/data/dynamoDB/integrationTestData/AUTO_RSV_INT_001.json b/tests/e2e/data/dynamoDB/integrationTestData/AUTO_RSV_INT_001.json new file mode 100644 index 000000000..45f2c6690 --- /dev/null +++ b/tests/e2e/data/dynamoDB/integrationTestData/AUTO_RSV_INT_001.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "RSV - Integration - Actionable due to membership of an Age Cohort including suggested national booking action (actions requested)", + "request_headers": { + "nhs-login-nhs-number": "9735548844" + }, + "config_filename": "AUTO_RSV_INT_001.json", + "data": [ + { + "NHS_NUMBER": "9735548844", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_75_rolling": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9735548844", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/integrationTestData/AUTO_RSV_INT_002.json b/tests/e2e/data/dynamoDB/integrationTestData/AUTO_RSV_INT_002.json new file mode 100644 index 000000000..fa1bee067 --- /dev/null +++ b/tests/e2e/data/dynamoDB/integrationTestData/AUTO_RSV_INT_002.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "RSV - Integration - Actionable due to membership of an Age Cohort including suggested action (not national booking)", + "request_headers": { + "nhs-login-nhs-number": "9735548852" + }, + "config_filename": "AUTO_RSV_INT_001.json", + "data": [ + { + "NHS_NUMBER": "9735548852", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_75_rolling": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9735548852", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "2", + "POSTCODE": "CB3 8DX", + "POSTCODE_SECTOR": "CB38", + "POSTCODE_OUTCODE": "CB3", + "MSOA": "E02007085", + "LSOA": "E01018223", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/integrationTestData/AUTO_RSV_INT_003.json b/tests/e2e/data/dynamoDB/integrationTestData/AUTO_RSV_INT_003.json new file mode 100644 index 000000000..ed8db868c --- /dev/null +++ b/tests/e2e/data/dynamoDB/integrationTestData/AUTO_RSV_INT_003.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "RSV - Integration - Actionable due to membership of an alternative Age Cohort including suggested action (not national booking)", + "request_headers": { + "nhs-login-nhs-number": "9735548860" + }, + "config_filename": "AUTO_RSV_INT_001.json", + "data": [ + { + "NHS_NUMBER": "9735548860", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_75to79_2024": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9735548860", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "2", + "POSTCODE": "CB3 8DX", + "POSTCODE_SECTOR": "CB38", + "POSTCODE_OUTCODE": "CB3", + "MSOA": "E02007085", + "LSOA": "E01018223", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/features/conftest.py b/tests/e2e/features/conftest.py index d309566c5..0ab0c0301 100644 --- a/tests/e2e/features/conftest.py +++ b/tests/e2e/features/conftest.py @@ -14,7 +14,7 @@ HTTP_STATUS_SERVER_ERROR = 500 -@pytest.fixture(scope="session", autouse=True) +@pytest.fixture(scope="session", autouse=False) def check_api_accessibility(): """Check if the API is accessible before running tests.""" try: diff --git a/tests/e2e/tests/__init__.py b/tests/e2e/tests/__init__.py index e69de29bb..a18a56d35 100644 --- a/tests/e2e/tests/__init__.py +++ b/tests/e2e/tests/__init__.py @@ -0,0 +1,2 @@ +from dotenv import load_dotenv +load_dotenv(dotenv_path=".env") diff --git a/tests/e2e/tests/conftest.py b/tests/e2e/tests/conftest.py index 2814eaad5..cb7c4d04d 100644 --- a/tests/e2e/tests/conftest.py +++ b/tests/e2e/tests/conftest.py @@ -10,7 +10,7 @@ from tests.e2e.utils.eligibility_api_client import EligibilityApiClient # Load environment variables from .env.local -load_dotenv(dotenv_path=".env.local") +load_dotenv(dotenv_path=".env") # Constants BASE_URL = os.getenv("BASE_URL", "https://test.eligibility-signposting-api.nhs.uk/patient-check") diff --git a/tests/e2e/tests/eligibility_signposting/conftest.py b/tests/e2e/tests/eligibility_signposting/conftest.py index c10b433d8..e9f3e570e 100644 --- a/tests/e2e/tests/eligibility_signposting/conftest.py +++ b/tests/e2e/tests/eligibility_signposting/conftest.py @@ -1,8 +1,9 @@ """Pytest fixtures for the Eligibility Signposting API tests.""" import pytest -from utils.api_client import ApiClient -from utils.config import INVALID_NHS_NUMBER, VALID_NHS_NUMBER + +from tests.e2e.utils.api_client import ApiClient +from tests.e2e.utils.config import INVALID_NHS_NUMBER, VALID_NHS_NUMBER @pytest.fixture diff --git a/tests/e2e/tests/eligibility_signposting/test_eligibility_check.py b/tests/e2e/tests/eligibility_signposting/test_eligibility_check.py index 76dbd8194..d30b2e68c 100644 --- a/tests/e2e/tests/eligibility_signposting/test_eligibility_check.py +++ b/tests/e2e/tests/eligibility_signposting/test_eligibility_check.py @@ -3,7 +3,8 @@ import jsonschema import pytest import requests -from utils.config import BASE_URL, ELIGIBILITY_CHECK_SCHEMA + +from tests.e2e.utils.config import BASE_URL, ELIGIBILITY_CHECK_SCHEMA # HTTP Status Code Constants HTTP_STATUS_OK = 200 diff --git a/tests/e2e/tests/eligibility_signposting/test_eligibility_check_bdd.py b/tests/e2e/tests/eligibility_signposting/test_eligibility_check_bdd.py index 3bb0360bd..49e893626 100644 --- a/tests/e2e/tests/eligibility_signposting/test_eligibility_check_bdd.py +++ b/tests/e2e/tests/eligibility_signposting/test_eligibility_check_bdd.py @@ -1,14 +1,14 @@ -import sys -from pathlib import Path - -import pytest -from behave import scenarios - -# Add the features directory to the Python path -sys.path.append(str(Path(__file__).parent.parent.parent)) - -# Mark all tests as BDD tests -pytestmark = [pytest.mark.bdd, pytest.mark.eligibility] - -# Load the scenarios from the feature file -scenarios("../../features/eligibility_check/eligibility_check.feature") +# import sys +# from pathlib import Path +# +# import pytest +# from behave import scenarios +# +# # Add the features directory to the Python path +# sys.path.append(str(Path(__file__).parent.parent.parent)) +# +# # Mark all tests as BDD tests +# pytestmark = [pytest.mark.bdd, pytest.mark.eligibility] +# +# # Load the scenarios from the feature file +# scenarios("../../features/eligibility_check/eligibility_check.feature") diff --git a/tests/e2e/tests/test_config.py b/tests/e2e/tests/test_config.py index 34344e71b..a7b43dcb3 100644 --- a/tests/e2e/tests/test_config.py +++ b/tests/e2e/tests/test_config.py @@ -14,3 +14,5 @@ REGRESSION_TEST_DATA: str = "tests/e2e/data/dynamoDB/regressionTestData/" REGRESSION_RESPONSES: str = "tests/e2e/data/responses/regressionTestResponses/" REGRESSION_CONFIGS: str = "tests/e2e/data/configs/regressionTestConfigs" + +INTEGRATION_TEST_DATA: str = "tests/e2e/data/dynamoDB/integrationTestData/" diff --git a/tests/e2e/tests/test_regression_tests.py b/tests/e2e/tests/test_regression_tests.py index f6b062811..ac7591f3f 100644 --- a/tests/e2e/tests/test_regression_tests.py +++ b/tests/e2e/tests/test_regression_tests.py @@ -18,7 +18,7 @@ id_list = [f"{filename} - {scenario.get('scenario_name', 'No Scenario')}" for filename, scenario in param_list] -@pytest.mark.regressiontest +@pytest.mark.functionale2eregression @pytest.mark.parametrize(("filename", "scenario"), param_list, ids=id_list) def test_run_regression_tests(filename, scenario, eligibility_client): nhs_number = scenario["nhs_number"] diff --git a/tests/e2e/tests/test_smoke_tests.py b/tests/e2e/tests/test_smoke_tests.py index 85f5e01d4..0b0c854b5 100644 --- a/tests/e2e/tests/test_smoke_tests.py +++ b/tests/e2e/tests/test_smoke_tests.py @@ -18,7 +18,7 @@ id_list = [f"{filename} - {scenario.get('scenario_name', 'No Scenario')}" for filename, scenario in param_list] -@pytest.mark.smoketest +@pytest.mark.sandboxtests @pytest.mark.parametrize(("filename", "scenario"), param_list, ids=id_list) def test_run_smoke_case(filename, scenario, eligibility_client): nhs_number = scenario["nhs_number"] diff --git a/tests/e2e/tests/test_story_tests.py b/tests/e2e/tests/test_story_tests.py index 3e2938c6c..1cfe44ff5 100644 --- a/tests/e2e/tests/test_story_tests.py +++ b/tests/e2e/tests/test_story_tests.py @@ -18,7 +18,7 @@ id_list = [f"{filename} - {scenario.get('scenario_name', 'No Scenario')}" for filename, scenario in param_list] -@pytest.mark.storytest_all +@pytest.mark.functionale2eregression @pytest.mark.parametrize(("filename", "scenario"), param_list, ids=id_list) def test_run_story_test_cases(filename, scenario, eligibility_client): nhs_number = scenario["nhs_number"] diff --git a/tests/e2e/utils/__init__.py b/tests/e2e/utils/__init__.py index e69de29bb..a18a56d35 100644 --- a/tests/e2e/utils/__init__.py +++ b/tests/e2e/utils/__init__.py @@ -0,0 +1,2 @@ +from dotenv import load_dotenv +load_dotenv(dotenv_path=".env") diff --git a/tests/e2e/utils/api_client.py b/tests/e2e/utils/api_client.py index 53c706168..facce8ae8 100644 --- a/tests/e2e/utils/api_client.py +++ b/tests/e2e/utils/api_client.py @@ -1,7 +1,8 @@ """API client module for making HTTP requests to the Eligibility Signposting API.""" import requests -from utils.config import API_KEY, BASE_URL + +from tests.e2e.utils.config import API_KEY, BASE_URL # Default timeout for API requests in seconds DEFAULT_TIMEOUT = 10 diff --git a/tests/e2e/utils/placeholder_utils.py b/tests/e2e/utils/placeholder_utils.py index 1ddf67f8f..fc0dca7c3 100644 --- a/tests/e2e/utils/placeholder_utils.py +++ b/tests/e2e/utils/placeholder_utils.py @@ -30,6 +30,7 @@ def resolve_placeholders(value, context=None, file_name=None): def _resolve_placeholder_value(placeholder: str) -> str: placeholder_parts_length = 3 + #RDATE valid_placeholder_types = ["DATE", "RDATE", "IGNORE"] result = f"<<{placeholder}>>" # Default fallback diff --git a/tests/e2e/utils/s3_config_manager.py b/tests/e2e/utils/s3_config_manager.py index 6edefe109..dd439516a 100644 --- a/tests/e2e/utils/s3_config_manager.py +++ b/tests/e2e/utils/s3_config_manager.py @@ -6,7 +6,7 @@ import botocore from dotenv import load_dotenv -load_dotenv() +load_dotenv(dotenv_path=".env") logger = logging.getLogger(__name__) @@ -16,6 +16,9 @@ def __init__(self, bucket_name: str, s3_prefix: str = "") -> None: self.s3_prefix: str = s3_prefix self.s3_client = boto3.client("s3") + def _s3_client(self): + return self.session.client("s3") + def _s3_key(self, filename: str) -> str: return str(Path(self.s3_prefix) / filename) @@ -50,6 +53,13 @@ def upload_if_missing_or_changed(self, local_path: Path) -> None: logger.info("๐Ÿ“„ Uploaded to s3://%s/%s", self.bucket_name, s3_key) def config_exists_and_matches(self, local_path: Path, s3_key: str) -> bool: + + session = boto3.Session() + credentials = session.get_credentials() + logger.info("AWS_ACCESS_KEY_ID = %s", credentials.access_key) + logger.info("AWS_SECRET_ACCESS_KEY = %s", credentials.secret_key) + logger.info("AWS_SESSION_TOKEN = %s", credentials.token) + try: s3_obj = self.s3_client.get_object(Bucket=self.bucket_name, Key=s3_key) s3_data = s3_obj["Body"].read() From 256affd396c8c5d5d9575d2b35ffe74362426b68 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 7 Jul 2025 10:04:41 +0100 Subject: [PATCH 48/72] updates --- tests/e2e/.env.example | 27 --- tests/e2e/.temp/global_config.json | 182 ------------------ .../responses/.temp/AUTO_RSV_SB_001_old.json | 30 --- 3 files changed, 239 deletions(-) delete mode 100644 tests/e2e/.env.example delete mode 100644 tests/e2e/.temp/global_config.json delete mode 100644 tests/e2e/data/responses/.temp/AUTO_RSV_SB_001_old.json diff --git a/tests/e2e/.env.example b/tests/e2e/.env.example deleted file mode 100644 index 988f9081c..000000000 --- a/tests/e2e/.env.example +++ /dev/null @@ -1,27 +0,0 @@ -# API Configuration -BASE_URL=https://sandbox.api.service.nhs.uk/eligibility-signposting-api -API_KEY=your_api_key_here - -# Test Data -VALID_NHS_NUMBER=50000000004 -INVALID_NHS_NUMBER=9876543210 - -# AWS -# AWS -ABORT_ON_AWS_FAILURE=false -AWS_REGION=eu-west-2 -AWS_ACCESS_KEY_ID=aws_access_key_id_token_here -AWS_SECRET_ACCESS_KEY=aws_secret_access_ey_token_here -AWS_SESSION_TOKEN=aws_session_token_here - -# DynamoDB -DYNAMODB_TABLE_NAME=eligibilty_data_store -DYNAMO_JSON_SOURCE_DIR=./data/dynamoDB/test_data.json - -# S3 -S3_BUCKET_NAME=your-bucket-name -S3_UPLOAD_DIR=qa/json -S3_JSON_SOURCE_DIR=./data/s3 - -# Cleanup -KEEP_SEED=false diff --git a/tests/e2e/.temp/global_config.json b/tests/e2e/.temp/global_config.json deleted file mode 100644 index 1286e8e19..000000000 --- a/tests/e2e/.temp/global_config.json +++ /dev/null @@ -1,182 +0,0 @@ -{ - "CampaignConfig": { - "ID": "<>", - "Version": 1, - "Name": "Automation RSV - Campaign 1", - "Type": "O", - "Target": "RSV", - "Manager": "adam.waller1@nhs.net", - "Approver": "adam.waller1@nhs.net", - "Reviewer": "adam.waller1@nhs.net", - "IterationFrequency": "X", - "IterationType": "A", - "IterationTime": "07:00:00", - "DefaultCommsRouting": "CONTACT_GP", - "StartDate": "20250601", - "EndDate": "20260601", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Iterations": [ - { - "ID": ",<>", - "DefaultCommsRouting": "CONTACT_GP", - "Version": 1, - "Name": "Automation RSV - Campaign 1 Iteration 1", - "IterationDate": "20250601", - "IterationNumber": 1, - "CommsType": "I", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Type": "A", - "IterationCohorts": [ - { - "CohortLabel": "rsv_75_rolling", - "CohortGroupCode": "rsv_age_range", - "CohortGroupDescriptionPositive": "You are currently aged 75 to 79", - "CohortGroupDescriptionNegative": "You are not currently aged 75 to 79", - "Priority": 0 - }, - { - "CohortLabel": "rsv_75to79_2024", - "CohortGroupCode": "rsv_catch_up_age_range", - "CohortGroupDescriptionPositive": "You turned 80 after 1 September 2024, so are eligible for the RSV vaccine until 31 August 2025", - "CohortGroupDescriptionNegative": "You did not turn 80 after 1 September 2024 and get vaccinated by 31 August 2025", - "Priority": 10 - }, - { - "CohortLabel": "elid_all_people", - "CohortGroupCode": "elid_all_people", - "CohortGroupDescriptionPositive": "Our records do not say why you are eligible", - "CohortGroupDescriptionNegative": "", - "Priority": 10 - } - ], - "IterationRules": [ - { - "Type": "F", - "Name": "Assure only already vaccinated taken from magic cohort", - "Description": "Exclude anyone who has NOT been given a dose of RSV Vaccination from the magic cohort", - "Operator": "is_empty", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "LAST_SUCCESSFUL_DATE", - "CohortLabel": "elid_all_people", - "Priority": 100 - }, - { - "Type": "F", - "Name": "Assure only already vaccinated taken from magic cohort", - "Description": "Exclude anyone who has NOT been given a dose of RSV within the last 25 years from the magic cohort", - "Operator": "Y<=", - "Comparator": "-25[[NVL:18000101]]", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "LAST_SUCCESSFUL_DATE", - "CohortLabel": "elid_all_people", - "Priority": 110 - }, - { - "Type": "F", - "Name": "Under Age - Under 75 Years on day of execution", - "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", - "Priority": 120, - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Operator": "Y>", - "Comparator": "-75" - }, - { - "Type": "F", - "Name": "Exclude Too OLD", - "Description": "Exclude anyone over 80", - "Priority": 130, - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Operator": "<", - "Comparator": "-80" - }, - { - "Type": "S", - "Name": "Dose Interval - Make NotActionable everyone who has been given a dose of RSV Vaccination in the last 25 years", - "Description": "AlreadyVaccinated|##You've had your RSV vaccination\\nBased on our records, you recently had this vaccination. You do not need to do anything.", - "Priority": 200, - "AttributeLevel": "TARGET", - "AttributeTarget": "RSV", - "AttributeName": "LAST_SUCCESSFUL_DATE", - "Operator": "Y>=", - "Comparator": "-25", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "Future Booking", - "Description": "AlreadyBooked|You are already Booked.", - "Priority": 200, - "AttributeLevel": "TARGET", - "AttributeTarget": "RSV", - "AttributeName": "BOOKED_DATE", - "Operator": "D>=", - "Comparator": "0" - }, - { - "Type": "R", - "Name": "Future Booking", - "Description": "AlreadyBooked|You are already Booked.", - "Priority": 1000, - "AttributeLevel": "TARGET", - "AttributeTarget": "RSV", - "AttributeName": "BOOKED_DATE", - "Operator": "D>=", - "Comparator": "0", - "CommsRouting": "AMEND_NBS" - } - ], - "ActionsMapper": { - "BOOK_NBS": { - "ExternalRoutingCode": "BookNBS", - "ActionDescription": "", - "ActionType": "ButtonAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Continue to booking" - }, - "AMEND_NBS": { - "ExternalRoutingCode": "AmendNBS", - "ActionDescription": "", - "ActionType": "ButtonAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Update or Cancel booking" - }, - "CONTACT_GP": { - "actionType": "InfoText", - "actionCode": "ContactGP", - "description": "Contact your GP surgery to get vaccinated", - "UrlLink": "", - "urlLabel": "" - } - } - } - ], - "AvailableActions": [ - { - "actionType": "ButtonAuthLink", - "actionCode": "BookNBS", - "description": "", - "urlLink": "http://www.nhs.uk/book-rsv", - "urlLabel": "Continue to booking" - }, - { - "actionType": "Link", - "actionCode": "FindWalkin", - "description": "Find a walk-in service for RSV vaccinations", - "urlLink": "www.england.nhs.uk/rsv-east", - "urlLabel": "" - }, - { - "actionType": "InfoText", - "actionCode": "ContactGP", - "description": "Contact your GP surgery to get vaccinated", - "urlLabel": "" - } - ] - } -} diff --git a/tests/e2e/data/responses/.temp/AUTO_RSV_SB_001_old.json b/tests/e2e/data/responses/.temp/AUTO_RSV_SB_001_old.json deleted file mode 100644 index 92056db6f..000000000 --- a/tests/e2e/data/responses/.temp/AUTO_RSV_SB_001_old.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "responseId": "<>", - "meta": { - "lastUpdated": "<>" - }, - "processedSuggestions": [ - { - "condition": "RSV", - "status": "Actionable", - "statusText": "You should have the RSV vaccine", - "eligibilityCohorts": [ - { - "cohortCode": "rsv_age_rolling", - "cohortStatus": "Actionable", - "cohortText": "You are aged 75 to 79 years old." - } - ], - "suitabilityRules": [], - "actions": [ - { - "actionType": "ButtonWithAuthLink", - "actionCode": "BookNBS", - "description": "", - "urlLink": "http://www.nhs.uk/book-rsv", - "urlLabel": "Continue to booking" - } - ] - } - ] -} From ae22f6ce65412aa9fe53d31dea0d67087a9da660 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Tue, 8 Jul 2025 16:10:29 +0100 Subject: [PATCH 49/72] tidying of data files and responses and some refactoring. --- .../AUTO_RSV_REG_001.json | 2 +- .../.temp/AUTO_RSV_SB_001.json | 282 ++++++++++++++++++ .../.temp/AUTO_RSV_SB_008.json | 122 ++++++++ .../smokeTestConfigs/AUTO_RSV_SB_001.json | 84 ++++-- .../smokeTestConfigs/AUTO_RSV_SB_008.json | 235 +++++++++++++++ .../storyTestConfigs/AUTO_RSV_ELI-216.json | 6 +- .../storyTestConfigs/AUTO_RSV_ELI-219-1.json | 4 +- .../storyTestConfigs/AUTO_RSV_ELI-219-2.json | 2 +- .../storyTestConfigs/AUTO_RSV_ELI-219-3.json | 2 +- .../storyTestConfigs/AUTO_RSV_ELI-219-4.json | 4 +- .../smokeTestData/AUTO_RSV_SB_001.json | 4 +- .../smokeTestData/AUTO_RSV_SB_002.json | 4 +- .../smokeTestData/AUTO_RSV_SB_003.json | 4 +- .../smokeTestData/AUTO_RSV_SB_004.json | 3 + .../smokeTestData/AUTO_RSV_SB_005.json | 3 + .../smokeTestData/AUTO_RSV_SB_006.json | 6 +- .../smokeTestData/AUTO_RSV_SB_007.json | 5 +- .../smokeTestData/AUTO_RSV_SB_008.json | 5 +- .../smokeTestData/AUTO_RSV_SB_009.json | 7 +- .../smokeTestData/AUTO_RSV_SB_010.json | 7 +- .../smokeTestData/AUTO_RSV_SB_011.json | 3 + .../smokeTestData/AUTO_RSV_SB_012.json | 3 + .../smokeTestData/AUTO_RSV_SB_013.json | 3 + .../smokeTestData/AUTO_RSV_SB_014.json | 3 + .../AUTO_RSV_ELI-219-4.json | 0 .../AUTO_RSV_REG_001.json | 10 +- .../AUTO_RSV_REG_002.json | 10 +- .../AUTO_RSV_REG_003.json | 10 +- .../smokeTestResponses/AUTO_RSV_SB_001.json | 11 +- .../smokeTestResponses/AUTO_RSV_SB_002.json | 10 +- .../smokeTestResponses/AUTO_RSV_SB_003.json | 10 +- .../smokeTestResponses/AUTO_RSV_SB_004.json | 4 +- .../smokeTestResponses/AUTO_RSV_SB_005.json | 6 +- .../smokeTestResponses/AUTO_RSV_SB_006.json | 6 +- .../smokeTestResponses/AUTO_RSV_SB_007.json | 6 +- .../smokeTestResponses/AUTO_RSV_SB_009.json | 6 +- .../smokeTestResponses/AUTO_RSV_SB_010.json | 6 +- .../smokeTestResponses/AUTO_RSV_SB_011.json | 6 +- .../smokeTestResponses/AUTO_RSV_SB_012.json | 2 +- .../smokeTestResponses/AUTO_RSV_SB_013.json | 2 +- .../smokeTestResponses/AUTO_RSV_SB_014.json | 6 +- .../storyTestResponses/AUTO_RSV_ELI-155.json | 10 +- .../AUTO_RSV_ELI-216-1.json | 10 +- .../AUTO_RSV_ELI-219-1.json | 10 +- .../AUTO_RSV_ELI-219-2.json | 10 +- .../AUTO_RSV_ELI-219-3.json | 10 +- .../AUTO_RSV_ELI-219-4.json | 10 +- tests/e2e/tests/__init__.py | 2 - tests/e2e/tests/conftest.py | 2 +- tests/e2e/tests/test_config.py | 24 +- tests/e2e/tests/test_regression_tests.py | 6 +- tests/e2e/tests/test_smoke_tests.py | 6 +- tests/e2e/tests/test_story_tests.py | 14 +- tests/e2e/utils/__init__.py | 2 - tests/e2e/utils/dynamo_helper.py | 3 +- tests/e2e/utils/placeholder_utils.py | 2 +- tests/e2e/utils/s3_config_manager.py | 10 +- 57 files changed, 878 insertions(+), 167 deletions(-) create mode 100644 tests/e2e/data/configs/smokeTestConfigs/.temp/AUTO_RSV_SB_001.json create mode 100644 tests/e2e/data/configs/smokeTestConfigs/.temp/AUTO_RSV_SB_008.json create mode 100644 tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_008.json rename tests/e2e/data/dynamoDB/storyTestData/{.ignore_folder => }/AUTO_RSV_ELI-219-4.json (100%) diff --git a/tests/e2e/data/configs/regressionTestConfigs/AUTO_RSV_REG_001.json b/tests/e2e/data/configs/regressionTestConfigs/AUTO_RSV_REG_001.json index 861e0ae33..769de7fed 100644 --- a/tests/e2e/data/configs/regressionTestConfigs/AUTO_RSV_REG_001.json +++ b/tests/e2e/data/configs/regressionTestConfigs/AUTO_RSV_REG_001.json @@ -197,7 +197,7 @@ "ExternalRoutingCode": "DefaultAction", "ActionDescription": "DefaultActionDescription", "ActionType": "DefaultActionType", - "UrlLink": "DefaultActionUrl", + "UrlLink": "https://www.defaultaction.com", "UrlLabel": "DefaultLabel" } } diff --git a/tests/e2e/data/configs/smokeTestConfigs/.temp/AUTO_RSV_SB_001.json b/tests/e2e/data/configs/smokeTestConfigs/.temp/AUTO_RSV_SB_001.json new file mode 100644 index 000000000..862ced3f0 --- /dev/null +++ b/tests/e2e/data/configs/smokeTestConfigs/.temp/AUTO_RSV_SB_001.json @@ -0,0 +1,282 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "Automation RSV - Smoke Test Config", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "BOOK_LOCAL", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "BOOK_LOCAL", + "Version": "1", + "Name": "Automation RSV - Smoke Test Config Iteration", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "M", + "IterationCohorts": [ + { + "CohortLabel": "rsv_75_rolling", + "CohortGroup": "rsv_age_rolling", + "PositiveDescription": "are aged 75 to 79 years old.", + "NegativeDescription": "are not aged 75 to 79 years old.", + "Priority": 0 + }, + { + "CohortLabel": "rsv_75to79_2024", + "CohortGroup": "rsv_age_catchup", + "PositiveDescription": "turned 80 between 2nd September 2024 and 31st August 2025", + "NegativeDescription": "did not turn 80 after 1 September 2024 and get vaccinated by 31 August 2025", + "Priority": 10 + }, + { + "CohortLabel": "elid_all_people", + "CohortGroup": "elid_all_people", + "PositiveDescription": "", + "NegativeDescription": "", + "Priority": 20 + }, + { + "CohortLabel": "no_group_description", + "CohortGroup": "", + "PositiveDescription": "", + "NegativeDescription": "", + "Priority": 30 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "Assure only already vaccinated taken from magic cohort", + "Description": "Exclude anyone who has NOT been given a dose of RSV Vaccination from the magic cohort", + "Operator": "is_empty", + "Comparator": "", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "elid_all_people", + "Priority": 100 + }, + { + "Type": "F", + "Name": "Under Age - Under 75 Years on day of execution", + "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", + "Priority": 120, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75" + }, + { + "Type": "F", + "Name": "Exclude Too OLD", + "Description": "Exclude anyone over 80", + "Priority": 130, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "<", + "Comparator": "-80" + }, + { + "Type": "S", + "Name": "AlreadyVaccinated", + "Description": "##You've had your RSV vaccination\nWe believe you had your vaccination on <>.", + "Priority": 550, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "Y>=", + "Comparator": "-25", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "NotAvailable", + "Description": "NotAvailable|Vaccinations are not currently available.", + "Priority": 510, + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "Operator": "=", + "Comparator": "SUPPRESSED_ICB", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "NotYetDue", + "Description": "NotYetDue|Your next dose is not yet due.", + "Priority": 520, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "=", + "Comparator": "<>", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "TooClose", + "Description": "TooClose|Your previous vaccination was less than 91 days ago.", + "Priority": 530, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "D>", + "Comparator": "<>", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "TooClose", + "Description": "TooClose|Your previous vaccination was less than 91 days ago.", + "Priority": 530, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "D<", + "Comparator": "<>", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "OtherSetting", + "Description": "OtherSetting|## Getting the vaccine\nOur record show you're living in a setting where care is provided.\nIf you think you should have the RSV vaccine, speak to a member of staff where you live.", + "Priority": 540, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y" + }, + { + "Type": "R", + "Name": "Actionable Not Vaccinated", + "Description": "Book An Appointment", + "Priority": 1010, + "Operator": "is_empty", + "Comparator": "", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CommsRouting": "BOOK_NBS" + }, + { + "Type": "R", + "Name": "Actionable Not Vaccinated", + "Description": "Book An Appointment", + "Priority": 1010, + "Operator": "=", + "Comparator": "LS2", + "AttributeLevel": "PERSON", + "AttributeName": "POSTCODE_SECTOR", + "CommsRouting": "BOOK_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1020, + "Operator": ">=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1020, + "Operator": "=", + "Comparator": "NBS", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1030, + "Operator": ">=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "MANAGE_LOCAL" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1030, + "Operator": "!=", + "Comparator": "NBS", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", + "CommsRouting": "MANAGE_LOCAL" + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + }, + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Manage your appointment" + }, + "CONTACT_GP": { + "ExternalRoutingCode": "ContactGP", + "ActionDescription": "Contact your GP", + "ActionType": "InfoText", + "UrlLink": "", + "UrlLabel": "" + }, + "BOOK_LOCAL": { + "ExternalRoutingCode": "BookLocal", + "ActionDescription": "##Getting the vaccine\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "ActionType": "InfoText", + "UrlLink": "", + "UrlLabel": "" + }, + "MANAGE_LOCAL": { + "ExternalRoutingCode": "ManageLocal", + "ActionDescription": "##You have an RSV vaccination appointment\nContact your healthcare provider to change or cancel your appointment.", + "ActionType": "CardWithText", + "UrlLink": "", + "UrlLabel": "" + }, + "CHECK_CORRECT": { + "ExternalRoutingCode": "CheckCorrect", + "ActionDescription": "##If you think this is incorrect\\nIf you have not had this vaccination and you think you should, speak to your healthcare professional.", + "ActionType": "InfoText", + "UrlLink": "", + "UrlLabel": "" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/smokeTestConfigs/.temp/AUTO_RSV_SB_008.json b/tests/e2e/data/configs/smokeTestConfigs/.temp/AUTO_RSV_SB_008.json new file mode 100644 index 000000000..41a8d3415 --- /dev/null +++ b/tests/e2e/data/configs/smokeTestConfigs/.temp/AUTO_RSV_SB_008.json @@ -0,0 +1,122 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "Automation RSV - Smoke Test Config", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "BOOK_LOCAL", + "StartDate": "20260601", + "EndDate": "20270601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "BOOK_LOCAL", + "Version": "1", + "Name": "Automation RSV - Smoke Test Config Iteration", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "M", + "IterationCohorts": [ + { + "CohortLabel": "rsv_75_rolling", + "CohortGroup": "rsv_age_rolling", + "PositiveDescription": "are aged 75 to 79 years old.", + "NegativeDescription": "are not aged 75 to 79 years old.", + "Priority": 0 + }, + { + "CohortLabel": "rsv_75to79_2024", + "CohortGroup": "rsv_age_catchup", + "PositiveDescription": "turned 80 between 2nd September 2024 and 31st August 2025", + "NegativeDescription": "did not turn 80 after 1 September 2024 and get vaccinated by 31 August 2025", + "Priority": 10 + }, + { + "CohortLabel": "elid_all_people", + "CohortGroup": "elid_all_people", + "PositiveDescription": "", + "NegativeDescription": "", + "Priority": 20 + }, + { + "CohortLabel": "no_group_description", + "CohortGroup": "", + "PositiveDescription": "", + "NegativeDescription": "", + "Priority": 30 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "Assure only already vaccinated taken from magic cohort", + "Description": "Exclude anyone who has NOT been given a dose of RSV Vaccination from the magic cohort", + "Operator": "is_empty", + "Comparator": "", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "elid_all_people", + "Priority": 100 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + }, + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Manage your appointment" + }, + "CONTACT_GP": { + "ExternalRoutingCode": "ContactGP", + "ActionDescription": "Contact your GP", + "ActionType": "InfoText", + "UrlLink": "", + "UrlLabel": "" + }, + "BOOK_LOCAL": { + "ExternalRoutingCode": "BookLocal", + "ActionDescription": "##Getting the vaccine\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "ActionType": "InfoText", + "UrlLink": "", + "UrlLabel": "" + }, + "MANAGE_LOCAL": { + "ExternalRoutingCode": "ManageLocal", + "ActionDescription": "##You have an RSV vaccination appointment\nContact your healthcare provider to change or cancel your appointment.", + "ActionType": "CardWithText", + "UrlLink": "", + "UrlLabel": "" + }, + "CHECK_CORRECT": { + "ExternalRoutingCode": "CheckCorrect", + "ActionDescription": "##If you think this is incorrect\\nIf you have not had this vaccination and you think you should, speak to your healthcare professional.", + "ActionType": "InfoText", + "UrlLink": "", + "UrlLabel": "" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_001.json b/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_001.json index bf9ed746c..f313f5691 100644 --- a/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_001.json +++ b/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_001.json @@ -27,20 +27,20 @@ "CommsType": "I", "ApprovalMinimum": 0, "ApprovalMaximum": 0, - "Type": "M", + "Type": "O", "IterationCohorts": [ { "CohortLabel": "rsv_75_rolling", "CohortGroup": "rsv_age_rolling", - "PositiveDescription": "You are aged 75 to 79 years old.", - "NegativeDescription": "You are not aged 75 to 79 years old.", + "PositiveDescription": "are aged 75 to 79 years old.", + "NegativeDescription": "are not aged 75 to 79 years old.", "Priority": 0 }, { "CohortLabel": "rsv_75to79_2024", "CohortGroup": "rsv_age_catchup", - "PositiveDescription": "You turned 80 between 2nd September 2024 and 31st August 2025", - "NegativeDescription": "You did not turn 80 after 1 September 2024 and get vaccinated by 31 August 2025", + "PositiveDescription": "turned 80 between 2nd September 2024 and 31st August 2025", + "NegativeDescription": "did not turn 80 between 2nd September 2024 and 31st August 2025", "Priority": 10 }, { @@ -79,7 +79,19 @@ "AttributeLevel": "PERSON", "AttributeName": "DATE_OF_BIRTH", "Operator": "Y>", - "Comparator": "-75" + "Comparator": "-75", + "CohortLabel": "rsv_75to79_2024" + }, + { + "Type": "F", + "Name": "Under Age - Under 75 Years on day of execution", + "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", + "Priority": 125, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75", + "CohortLabel": "rsv_75_rolling" }, { "Type": "F", @@ -95,7 +107,7 @@ "Type": "S", "Name": "AlreadyVaccinated", "Description": "##You've had your RSV vaccination\nWe believe you had your vaccination on <>.", - "Priority": 500, + "Priority": 550, "AttributeLevel": "TARGET", "AttributeTarget": "RSV", "AttributeName": "LAST_SUCCESSFUL_DATE", @@ -113,6 +125,40 @@ "Operator": "=", "Comparator": "SUPPRESSED_ICB" }, + { + "Type": "S", + "Name": "NotYetDue", + "Description": "NotYetDue|Your next dose is not yet due.", + "Priority": 520, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "=", + "Comparator": "20250326", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "TooClose", + "Description": "TooClose|Your previous vaccination was less than 91 days ago.", + "Priority": 530, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "=", + "Comparator": "20250327", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "OtherSetting", + "Description": "OtherSetting|## Getting the vaccine\n\nOur record show you're living in a setting where care is provided.\n\nIf you think you should have the RSV vaccine, speak to a member of staff where you live.", + "Priority": 540, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y" + }, { "Type": "R", "Name": "Actionable Not Vaccinated", @@ -203,30 +249,24 @@ "CONTACT_GP": { "ExternalRoutingCode": "ContactGP", "ActionDescription": "Contact your GP", - "ActionType": "InfoText", - "UrlLink": "", - "UrlLabel": "" + "ActionType": "InfoText" }, - "BOOK_LOCAL": { + "BOOK_LOCAL": { "ExternalRoutingCode": "BookLocal", "ActionDescription": "##Getting the vaccine\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", - "ActionType": "InfoText", - "UrlLink": "", - "UrlLabel": "" + "ActionType": "InfoText" }, - "MANAGE_LOCAL": { + "MANAGE_LOCAL": { "ExternalRoutingCode": "ManageLocal", "ActionDescription": "##You have an RSV vaccination appointment\nContact your healthcare provider to change or cancel your appointment.", - "ActionType": "CardWithText", - "UrlLink": "", - "UrlLabel": "" + "ActionType": "CardWithText" + }, - "CHECK_CORRECT": { + "CHECK_CORRECT": { "ExternalRoutingCode": "CheckCorrect", "ActionDescription": "##If you think this is incorrect\\nIf you have not had this vaccination and you think you should, speak to your healthcare professional.", - "ActionType": "InfoText", - "UrlLink": "", - "UrlLabel": "" + "ActionType": "InfoText" + } } } diff --git a/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_008.json b/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_008.json new file mode 100644 index 000000000..8e75aedf9 --- /dev/null +++ b/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_008.json @@ -0,0 +1,235 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "Automation RSV - Smoke Test Config", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "BOOK_LOCAL", + "StartDate": "20260601", + "EndDate": "20270601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "BOOK_LOCAL", + "Version": "1", + "Name": "Automation RSV - Smoke Test Config Iteration", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_75_rolling", + "CohortGroup": "rsv_age_rolling", + "PositiveDescription": "are aged 75 to 79 years old.", + "NegativeDescription": "are not aged 75 to 79 years old.", + "Priority": 0 + }, + { + "CohortLabel": "rsv_75to79_2024", + "CohortGroup": "rsv_age_catchup", + "PositiveDescription": "turned 80 between 2nd September 2024 and 31st August 2025", + "NegativeDescription": "did not turn 80 after 1 September 2024 and get vaccinated by 31 August 2025", + "Priority": 10 + }, + { + "CohortLabel": "elid_all_people", + "CohortGroup": "elid_all_people", + "PositiveDescription": "", + "NegativeDescription": "", + "Priority": 20 + }, + { + "CohortLabel": "no_group_description", + "CohortGroup": "", + "PositiveDescription": "", + "NegativeDescription": "", + "Priority": 30 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "Assure only already vaccinated taken from magic cohort", + "Description": "Exclude anyone who has NOT been given a dose of RSV Vaccination from the magic cohort", + "Operator": "is_empty", + "Comparator": "", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "elid_all_people", + "Priority": 100 + }, + { + "Type": "F", + "Name": "Under Age - Under 75 Years on day of execution", + "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", + "Priority": 120, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75" + }, + { + "Type": "F", + "Name": "Exclude Too OLD", + "Description": "Exclude anyone over 80", + "Priority": 130, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "<", + "Comparator": "-80" + }, + { + "Type": "S", + "Name": "AlreadyVaccinated", + "Description": "##You've had your RSV vaccination\nWe believe you had your vaccination on <>.", + "Priority": 500, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "Y>=", + "Comparator": "-25", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "NotAvailable", + "Description": "NotAvailable|Vaccinations are not currently available.", + "Priority": 510, + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "Operator": "=", + "Comparator": "SUPPRESSED_ICB" + }, + { + "Type": "R", + "Name": "Actionable Not Vaccinated", + "Description": "Book An Appointment", + "Priority": 1010, + "Operator": "is_empty", + "Comparator": "", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CommsRouting": "BOOK_NBS" + }, + { + "Type": "R", + "Name": "Actionable Not Vaccinated", + "Description": "Book An Appointment", + "Priority": 1010, + "Operator": "=", + "Comparator": "LS2", + "AttributeLevel": "PERSON", + "AttributeName": "POSTCODE_SECTOR", + "CommsRouting": "BOOK_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1020, + "Operator": ">=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1020, + "Operator": "=", + "Comparator": "NBS", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1030, + "Operator": ">=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "MANAGE_LOCAL" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1030, + "Operator": "!=", + "Comparator": "NBS", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", + "CommsRouting": "MANAGE_LOCAL" + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + }, + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Manage your appointment" + }, + "CONTACT_GP": { + "ExternalRoutingCode": "ContactGP", + "ActionDescription": "Contact your GP", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "BOOK_LOCAL": { + "ExternalRoutingCode": "BookLocal", + "ActionDescription": "##Getting the vaccine\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "MANAGE_LOCAL": { + "ExternalRoutingCode": "ManageLocal", + "ActionDescription": "##You have an RSV vaccination appointment\nContact your healthcare provider to change or cancel your appointment.", + "ActionType": "CardWithText", + "UrlLink": null, + "UrlLabel": "" + }, + "CHECK_CORRECT": { + "ExternalRoutingCode": "CheckCorrect", + "ActionDescription": "##If you think this is incorrect\\nIf you have not had this vaccination and you think you should, speak to your healthcare professional.", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json index 33cba2f1a..be3e30eb1 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json @@ -68,21 +68,21 @@ "ExternalRoutingCode": "ActionOneRoutingCode", "ActionDescription": "ActionOneDescription", "ActionType": "ActionOneActionType", - "UrlLink": "http://www.actiononeurl.com", + "UrlLink": "http://www.actiononeurl.com/", "UrlLabel": "ActionOneUrlLabel" }, "ACTION_TWO": { "ExternalRoutingCode": "ActionTwoRoutingCode", "ActionDescription": "ActionTwoDescription", "ActionType": "ActionTwoActionType", - "UrlLink": "http://www.actiontwourl.com", + "UrlLink": "http://www.actiontwourl.com/", "UrlLabel": "ActionTwoUrlLabel" }, "DEFAULT_ACTION": { "ExternalRoutingCode": "DefaultAction", "ActionDescription": "DefaultActionDescription", "ActionType": "DefaultActionType", - "UrlLink": "DefaultActionUrl", + "UrlLink": "http://www.defaultActionUrl.com/", "UrlLabel": "DefaultLabel" } } diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-1.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-1.json index b8303b6a5..48bd7919e 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-1.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-1.json @@ -92,8 +92,8 @@ "ExternalRoutingCode": "DefaultAction", "ActionDescription": "DefaultActionDescription", "ActionType": "DefaultActionType", - "UrlLink": "DefaultActionUrl", - "UrlLabel": "DefaultLabel" + "UrlLink": "https://www.defaultactionurl.com/", + "UrlLabel": "DefaultActionLabel" } } } diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-2.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-2.json index 6ed902f21..29ebbd38c 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-2.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-2.json @@ -99,7 +99,7 @@ "ExternalRoutingCode": "DefaultAction", "ActionDescription": "DefaultActionDescription", "ActionType": "DefaultActionType", - "UrlLink": "DefaultActionUrl", + "UrlLink": "https://www.defaultactionurl.com", "UrlLabel": "DefaultLabel" } } diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-3.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-3.json index f274ade2b..f5466dbfc 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-3.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-3.json @@ -89,7 +89,7 @@ "ExternalRoutingCode": "DefaultAction", "ActionDescription": "DefaultActionDescription", "ActionType": "DefaultActionType", - "UrlLink": "DefaultActionUrl", + "UrlLink": "https://www.defaultactionurl.com", "UrlLabel": "DefaultLabel" } } diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-4.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-4.json index 884bb6c9e..f70026d7c 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-4.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-4.json @@ -56,7 +56,7 @@ "ExternalRoutingCode": "", "ActionDescription": "", "ActionType": "", - "UrlLink": "", + "UrlLink": null, "UrlLabel": "" }, "ACTION_TWO": { @@ -77,7 +77,7 @@ "ExternalRoutingCode": "DefaultAction", "ActionDescription": "DefaultActionDescription", "ActionType": "DefaultActionType", - "UrlLink": "DefaultActionUrl", + "UrlLink": "https://www.defaultactionurl.com", "UrlLabel": "DefaultLabel" } } diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json index d462e22a9..e8cba4cf0 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json @@ -1,6 +1,8 @@ { "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested actions (with booking)", - "request_headers": {"nhs-login-nhs-number": "5000000001"}, + "request_headers": { + "nhs-login-nhs-number": "5000000001" + }, "config_filename": "AUTO_RSV_SB_001.json", "data": [ { diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json index 6e2d77050..f6bda7144 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json @@ -1,6 +1,8 @@ { "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested action (not booking)", - "request_headers": {"nhs-login-nhs-number": "5000000002"}, + "request_headers": { + "nhs-login-nhs-number": "5000000002" + }, "config_filename": "AUTO_RSV_SB_001.json", "data": [ { diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json index c5ab64bdb..fca3a419b 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json @@ -1,6 +1,8 @@ { "scenario_name": "RSV - Actionable due to membership of an alternative Age Cohort incl. suggested action", - "request_headers": {"nhs-login-nhs-number": "5000000003"}, + "request_headers": { + "nhs-login-nhs-number": "5000000003" + }, "config_filename": "AUTO_RSV_SB_001.json", "data": [ { diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_004.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_004.json index ceae22035..5339dfac6 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_004.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_004.json @@ -1,5 +1,8 @@ { "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested action (existing national booking)", + "request_headers": { + "nhs-login-nhs-number": "5000000004" + }, "config_filename": "AUTO_RSV_SB_001.json", "data": [ { diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_005.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_005.json index e71b7c0c7..d4f4265ef 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_005.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_005.json @@ -1,5 +1,8 @@ { "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested actions (with local booking)", + "request_headers": { + "nhs-login-nhs-number": "5000000005" + }, "config_filename": "AUTO_RSV_SB_001.json", "data": [ { diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_006.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_006.json index 0a38a4223..ba55e3091 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_006.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_006.json @@ -1,6 +1,10 @@ { "scenario_name": "RSV - Not Actionable despite membership of an Age Cohort, already vaccinated", + "request_headers": { + "nhs-login-nhs-number": "5000000006" + }, "config_filename": "AUTO_RSV_SB_001.json", + "notes": "actions need updating in the response when the functionality is delivered to provide actions for not_actionable responses", "data": [ { "NHS_NUMBER": "5000000006", @@ -8,7 +12,7 @@ "COHORT_MAP": { "cohorts": { "M": { - "rsv_75_rolling": { + "no_group_description": { "M": { "dateJoined": { "S": "20230515" diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_007.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_007.json index e132f791a..ac8cb72bc 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_007.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_007.json @@ -1,5 +1,8 @@ { - "scenario_name": "RSV - Not Actionable, membership of Age Cohort, no available vaccinations (not available type 1)", + "scenario_name": "RSV - Not Actionable despite to membership of an Age Cohort with reasoning of no available vaccinations (not available type 1)", + "request_headers": { + "nhs-login-nhs-number": "5000000007" + }, "config_filename": "AUTO_RSV_SB_001.json", "data": [ { diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_008.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_008.json index 9b9e6187a..8aa367705 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_008.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_008.json @@ -1,6 +1,9 @@ { "scenario_name": "RSV - No RSV response as no active campaign (not available type 2)", - "config_filename": "AUTO_RSV_SB_001.json", + "request_headers": { + "nhs-login-nhs-number": "5000000008" + }, + "config_filename": "AUTO_RSV_SB_008.json", "data": [ { "NHS_NUMBER": "5000000008", diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_009.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_009.json index 19fc51cfb..aa661f5a1 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_009.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_009.json @@ -1,5 +1,8 @@ { - "scenario_name": "RSV - Not Actionable, membership of Age Cohort, dose not yet due", + "scenario_name": "RSV - Not Actionable despite to membership of an Age Cohort with reasoning of dose not yet due", + "request_headers": { + "nhs-login-nhs-number": "5000000009" + }, "config_filename": "AUTO_RSV_SB_001.json", "data": [ { @@ -40,7 +43,7 @@ { "NHS_NUMBER": "5000000009", "ATTRIBUTE_TYPE": "RSV", - "LAST_SUCCESSFUL_DATE": "<>" + "LAST_SUCCESSFUL_DATE": "20250326" } ] } diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json index febabd9de..0bd87ebce 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json @@ -1,5 +1,8 @@ { - "scenario_name": "RSV - Not Actionable, membership of Age Cohort, dose not far enough apart", + "scenario_name": "RSV - Not Actionable despite to membership of an Age Cohort with reasoning of dose not far enough apart", + "request_headers": { + "nhs-login-nhs-number": "5000000010" + }, "config_filename": "AUTO_RSV_SB_001.json", "data": [ { @@ -40,7 +43,7 @@ { "NHS_NUMBER": "5000000010", "ATTRIBUTE_TYPE": "RSV", - "LAST_SUCCESSFUL_DATE": "<>" + "LAST_SUCCESSFUL_DATE": "20250327" } ] } diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_011.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_011.json index 4f245bf8c..46f43270a 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_011.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_011.json @@ -1,5 +1,8 @@ { "scenario_name": "RSV - Not Actionable despite to membership of an Age Cohort with reasoning of vaccination given in other setting (e.g. care home)", + "request_headers": { + "nhs-login-nhs-number": "5000000011" + }, "config_filename": "AUTO_RSV_SB_001.json", "data": [ { diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_012.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_012.json index 3323bd404..1c2d8a4a6 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_012.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_012.json @@ -1,5 +1,8 @@ { "scenario_name": "RSV - Not Actionable despite no cohort membership with reasoning of already vaccinated (type 1 includes unknown cohort)", + "request_headers": { + "nhs-login-nhs-number": "5000000012" + }, "config_filename": "AUTO_RSV_SB_001.json", "data": [ { diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_013.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_013.json index e84b7fa90..4f48a06bb 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_013.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_013.json @@ -1,5 +1,8 @@ { "scenario_name": "RSV - Not Actionable despite no cohort membership with reasoning of already vaccinated (type 2 includes no cohorts)", + "request_headers": { + "nhs-login-nhs-number": "5000000013" + }, "config_filename": "AUTO_RSV_SB_001.json", "data": [ { diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_014.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_014.json index 7861cab99..f6945cb28 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_014.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_014.json @@ -1,5 +1,8 @@ { "scenario_name": "RSV - Not Eligible", + "request_headers": { + "nhs-login-nhs-number": "5000000014" + }, "config_filename": "AUTO_RSV_SB_001.json", "data": [ { diff --git a/tests/e2e/data/dynamoDB/storyTestData/.ignore_folder/AUTO_RSV_ELI-219-4.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-4.json similarity index 100% rename from tests/e2e/data/dynamoDB/storyTestData/.ignore_folder/AUTO_RSV_ELI-219-4.json rename to tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-4.json diff --git a/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_001.json b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_001.json index 138a349a1..b86ea8cab 100644 --- a/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_001.json +++ b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_001.json @@ -6,11 +6,11 @@ { "actions": [ { - "action_code": "DefaultAction", - "action_description": "DefaultActionDescription", - "action_type": "DefaultActionType", - "url_label": "DefaultLabel", - "url_link": "DefaultActionUrl" + "actionCode": "DefaultAction", + "description": "DefaultActionDescription", + "actionType": "DefaultActionType", + "urlLabel": "DefaultLabel", + "urlLink": "https://www.defaultaction.com/" } ], "condition": "RSV", diff --git a/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_002.json b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_002.json index cbdbadda3..dd59901fb 100644 --- a/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_002.json +++ b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_002.json @@ -6,11 +6,11 @@ { "actions": [ { - "action_code": "DefaultAction", - "action_description": "DefaultActionDescription", - "action_type": "DefaultActionType", - "url_label": "DefaultLabel", - "url_link": "DefaultActionUrl" + "actionCode": "DefaultAction", + "description": "DefaultActionDescription", + "actionType": "DefaultActionType", + "urlLabel": "DefaultLabel", + "urlLink": "https://www.defaultaction.com/" } ], "condition": "RSV", diff --git a/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_003.json b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_003.json index e7ef07bd5..da6a4fabb 100644 --- a/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_003.json +++ b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_003.json @@ -6,11 +6,11 @@ { "actions": [ { - "action_code": "DefaultAction", - "action_description": "DefaultActionDescription", - "action_type": "DefaultActionType", - "url_label": "DefaultLabel", - "url_link": "DefaultActionUrl" + "actionCode": "DefaultAction", + "description": "DefaultActionDescription", + "actionType": "DefaultActionType", + "urlLabel": "DefaultLabel", + "urlLink": "https://www.defaultaction.com/" } ], "condition": "RSV", diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_001.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_001.json index 0c28e2dd1..926afef2f 100644 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_001.json +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_001.json @@ -12,16 +12,17 @@ { "cohortCode": "rsv_age_rolling", "cohortStatus": "Actionable", - "cohortText": "You are aged 75 to 79 years old." + "cohortText": "are aged 75 to 79 years old." } ], "suitabilityRules": [], "actions": [ { - "action_type": "ButtonWithAuthLink", - "action_code": "BookNBS", - "url_link": "http://www.nhs.uk/book-rsv", - "url_label": "Continue to booking" + "actionType": "ButtonWithAuthLink", + "actionCode": "BookNBS", + "description": "", + "urlLink": "http://www.nhs.uk/book-rsv", + "urlLabel": "Continue to booking" } ] } diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_002.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_002.json index d51710fd9..a3c9c415b 100644 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_002.json +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_002.json @@ -11,16 +11,18 @@ "eligibilityCohorts": [ { "cohortCode": "rsv_age_rolling", - "cohortText": "You are aged 75 to 79 years old.", + "cohortText": "are aged 75 to 79 years old.", "cohortStatus": "Actionable" } ], "suitabilityRules": [], "actions": [ { - "action_type": "InfoText", - "action_code": "BookLocal", - "action_description": "##Getting the vaccine\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination." + "actionType": "InfoText", + "actionCode": "BookLocal", + "description": "##Getting the vaccine\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "urlLink": "", + "urlLabel": "" } ] } diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_003.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_003.json index d8388e6d6..6f544c105 100644 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_003.json +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_003.json @@ -11,16 +11,18 @@ "eligibilityCohorts": [ { "cohortCode": "rsv_age_catchup", - "cohortText": "You turned 80 between 2nd September 2024 and 31st August 2025", + "cohortText": "turned 80 between 2nd September 2024 and 31st August 2025", "cohortStatus": "Actionable" } ], "suitabilityRules": [], "actions": [ { - "action_type": "InfoText", - "action_code": "BookLocal", - "action_description": "##Getting the vaccine\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination." + "actionType": "InfoText", + "actionCode": "BookLocal", + "description": "##Getting the vaccine\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "urlLink": "", + "urlLabel": "" } ] } diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_004.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_004.json index ed4663779..251e4577a 100644 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_004.json +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_004.json @@ -7,12 +7,12 @@ { "condition": "RSV", "status": "Actionable", - "statusText": "You should have the RSV vaccine", + "statusText": "Status.actionable", "eligibilityCohorts": [], "suitabilityRules": [], "actions": [ { - "actionType": "CardWithAuthButton", + "actionType": "ButtonWithAuthLink", "actionCode": "AmendNBS", "description": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", "urlLink": "http://www.nhs.uk/book-rsv", diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_005.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_005.json index 687b05405..9d7eafb4a 100644 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_005.json +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_005.json @@ -7,14 +7,16 @@ { "condition": "RSV", "status": "Actionable", - "statusText": "You should have the RSV vaccine", + "statusText": "Status.actionable", "eligibilityCohorts": [], "suitabilityRules": [], "actions": [ { "actionType": "CardWithText", "actionCode": "ManageLocal", - "description": "##You have an RSV vaccination appointment\nContact your healthcare provider to change or cancel your appointment." + "description": "##You have an RSV vaccination appointment\nContact your healthcare provider to change or cancel your appointment.", + "urlLink": "", + "urlLabel": "" } ] } diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_006.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_006.json index ad80f0baa..47f03f7c7 100644 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_006.json +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_006.json @@ -7,13 +7,15 @@ { "condition": "RSV", "status": "NotActionable", - "statusText": "You should have the RSV vaccine", + "statusText": "Status.not_actionable", "eligibilityCohorts": [], "suitabilityRules": [ { "ruleType": "S", "ruleCode": "AlreadyVaccinated", - "ruleText": "##You've had your RSV vaccination\nWe believe you had your vaccination on <>." + "ruleText": "##You've had your RSV vaccination\nWe believe you had your vaccination on <>.", + "urlLink": "", + "urlLabel": "" } ], "actions": [] diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_007.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_007.json index d555621c0..9ce1a0826 100644 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_007.json +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_007.json @@ -7,18 +7,18 @@ { "condition": "RSV", "status": "NotActionable", - "statusText": "You should have the RSV vaccine", + "statusText": "Status.not_actionable", "eligibilityCohorts": [ { "cohortCode": "rsv_age_rolling", "cohortStatus": "NotActionable", - "cohortText": "You are aged 75 to 79 years old." + "cohortText": "are aged 75 to 79 years old." } ], "suitabilityRules": [ { "ruleCode": "NotAvailable", - "ruleText": "Vaccinations are not currently available", + "ruleText": "NotAvailable|Vaccinations are not currently available.", "ruleType": "S" } ], diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_009.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_009.json index 65ecaf7a5..ff3f2f443 100644 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_009.json +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_009.json @@ -7,11 +7,11 @@ { "condition": "RSV", "status": "NotActionable", - "statusText": "You should have the RSV vaccine", + "statusText": "Status.not_actionable", "eligibilityCohorts": [ { "cohortCode": "rsv_age_rolling", - "cohortText": "You are aged 75 to 79 years old.", + "cohortText": "are aged 75 to 79 years old.", "cohortStatus": "NotActionable" } ], @@ -19,7 +19,7 @@ { "ruleType": "S", "ruleCode": "NotYetDue", - "ruleText": "Your next dose is not yet due." + "ruleText": "NotYetDue|Your next dose is not yet due." } ], "actions": [] diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_010.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_010.json index 0b9dc7258..ab8170724 100644 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_010.json +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_010.json @@ -7,11 +7,11 @@ { "condition": "RSV", "status": "NotActionable", - "statusText": "You should have the RSV vaccine", + "statusText": "Status.not_actionable", "eligibilityCohorts": [ { "cohortCode": "rsv_age_rolling", - "cohortText": "You are aged 75 to 79 years old.", + "cohortText": "are aged 75 to 79 years old.", "cohortStatus": "NotActionable" } ], @@ -19,7 +19,7 @@ { "ruleType": "S", "ruleCode": "TooClose", - "ruleText": "Your previous vaccination was less than 91 days ago." + "ruleText": "TooClose|Your previous vaccination was less than 91 days ago." } ], "actions": [] diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_011.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_011.json index 3070205d0..9518db805 100644 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_011.json +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_011.json @@ -7,11 +7,11 @@ { "condition": "RSV", "status": "NotActionable", - "statusText": "You should have the RSV vaccine", + "statusText": "Status.not_actionable", "eligibilityCohorts": [ { "cohortCode": "rsv_age_rolling", - "cohortText": "You are aged 75 to 79 years old.", + "cohortText": "are aged 75 to 79 years old.", "cohortStatus": "NotActionable" } ], @@ -19,7 +19,7 @@ { "ruleType": "S", "ruleCode": "OtherSetting", - "ruleText": "##Getting the vaccine\nOur record show you're living in a setting where care is provided.\nIf you think you should have the RSV vaccine, speak to a member of staff where you live." + "ruleText": "OtherSetting|## Getting the vaccine\n\nOur record show you're living in a setting where care is provided.\n\nIf you think you should have the RSV vaccine, speak to a member of staff where you live." } ], "actions": [] diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_012.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_012.json index 4996198d2..280b5732b 100644 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_012.json +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_012.json @@ -7,7 +7,7 @@ { "condition": "RSV", "status": "NotActionable", - "statusText": "You should have the RSV vaccine", + "statusText": "Status.not_actionable", "eligibilityCohorts": [ { "cohortCode": "unknown_cohort_membership", diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_013.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_013.json index 3413b999b..28cb06b60 100644 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_013.json +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_013.json @@ -7,7 +7,7 @@ { "condition": "RSV", "status": "NotActionable", - "statusText": "You should have the RSV vaccine", + "statusText": "Status.not_actionable", "eligibilityCohorts": [], "suitabilityRules": [ { diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_014.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_014.json index 3858968e4..3c2e410c5 100644 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_014.json +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_014.json @@ -7,16 +7,16 @@ { "condition": "RSV", "status": "NotEligible", - "statusText": "We do not believe you should have this vaccine", + "statusText": "Status.not_eligible", "eligibilityCohorts": [ { "cohortCode": "rsv_age_rolling", - "cohortText": "You are not aged 75 to 79 years old.", + "cohortText": "are not aged 75 to 79 years old.", "cohortStatus": "NotEligible" }, { "cohortCode": "rsv_age_catchup", - "cohortText": "You did not turn 80 between 2nd September 2024 and 31st August 2025", + "cohortText": "did not turn 80 between 2nd September 2024 and 31st August 2025", "cohortStatus": "NotEligible" } ], diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-155.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-155.json index ffba018dc..6cbc2e600 100644 --- a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-155.json +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-155.json @@ -18,11 +18,11 @@ "suitabilityRules": [], "actions": [ { - "action_code": "AmendNBS", - "action_description": "##You have an RSV vaccination appointment\\nYou can view, change or cancel your appointment below.", - "action_type": "ButtonWithAuthLink", - "url_link": "http://www.nhs.uk/book-rsv", - "url_label": "Manage your appointment" + "actionCode": "AmendNBS", + "description": "##You have an RSV vaccination appointment\\nYou can view, change or cancel your appointment below.", + "actionType": "ButtonWithAuthLink", + "urlLink": "http://www.nhs.uk/book-rsv", + "urlLabel": "Manage your appointment" } ] } diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-1.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-1.json index 46781b583..2da2d6781 100644 --- a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-1.json +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-1.json @@ -18,11 +18,11 @@ "suitabilityRules": [], "actions": [ { - "action_code": "ActionOneRoutingCode", - "action_description": "ActionOneDescription", - "action_type": "ActionOneActionType", - "url_label": "ActionOneUrlLabel", - "url_link": "http://www.actiononeurl.com" + "actionCode": "ActionOneRoutingCode", + "description": "ActionOneDescription", + "actionType": "ActionOneActionType", + "urlLabel": "ActionOneUrlLabel", + "urlLink": "http://www.actiononeurl.com/" } ] } diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-1.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-1.json index f6cc88345..8dc271345 100644 --- a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-1.json +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-1.json @@ -18,11 +18,11 @@ "suitabilityRules": [], "actions": [ { - "action_type": "DefaultActionType", - "action_code": "DefaultAction", - "action_description": "DefaultActionDescription", - "url_label": "DefaultLabel", - "url_link": "DefaultActionUrl" + "actionType": "DefaultActionType", + "actionCode": "DefaultAction", + "description": "DefaultActionDescription", + "urlLabel": "DefaultActionLabel", + "urlLink": "https://www.defaultactionurl.com/" } ] } diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-2.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-2.json index 82ecaecde..e9b897771 100644 --- a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-2.json +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-2.json @@ -18,11 +18,11 @@ "suitabilityRules": [], "actions": [ { - "action_type": "ActionTwoActionType", - "action_code": "ActionTwoRoutingCode", - "action_description": "ActionTwoDescription", - "url_link": "http://www.actiontwourl.com", - "url_label": "ActionTwoUrlLabel" + "actionType": "ActionTwoActionType", + "actionCode": "ActionTwoRoutingCode", + "description": "ActionTwoDescription", + "urlLink": "http://www.actiontwourl.com/", + "urlLabel": "ActionTwoUrlLabel" } ] } diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-3.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-3.json index 4df9ef7ef..be5a4a540 100644 --- a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-3.json +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-3.json @@ -18,11 +18,11 @@ "suitabilityRules": [], "actions": [ { - "action_code": "ActionOneRoutingCode", - "action_description": "ActionOneDescription", - "action_type": "ActionOneActionType", - "url_label": "ActionOneUrlLabel", - "url_link": "http://www.actiononeurl.com" + "actionCode": "ActionOneRoutingCode", + "description": "ActionOneDescription", + "actionType": "ActionOneActionType", + "urlLabel": "ActionOneUrlLabel", + "urlLink": "http://www.actiononeurl.com/" } ] } diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-4.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-4.json index 472dda0c6..913fab564 100644 --- a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-4.json +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-4.json @@ -18,11 +18,11 @@ "suitabilityRules": [], "actions": [ { - "action_code": "", - "action_description": "", - "action_type": "", - "url_label": "", - "url_link": "" + "actionCode": "", + "description": "", + "actionType": "", + "urlLabel": "", + "urlLink": "" } ] } diff --git a/tests/e2e/tests/__init__.py b/tests/e2e/tests/__init__.py index a18a56d35..e69de29bb 100644 --- a/tests/e2e/tests/__init__.py +++ b/tests/e2e/tests/__init__.py @@ -1,2 +0,0 @@ -from dotenv import load_dotenv -load_dotenv(dotenv_path=".env") diff --git a/tests/e2e/tests/conftest.py b/tests/e2e/tests/conftest.py index cb7c4d04d..d2b5075be 100644 --- a/tests/e2e/tests/conftest.py +++ b/tests/e2e/tests/conftest.py @@ -84,4 +84,4 @@ def setup_dynamodb_data(request): @pytest.fixture(scope="session") def eligibility_client(): - return EligibilityApiClient(BASE_URL) + return EligibilityApiClient(BASE_URL, cert_dir="certs") diff --git a/tests/e2e/tests/test_config.py b/tests/e2e/tests/test_config.py index a7b43dcb3..ce906601e 100644 --- a/tests/e2e/tests/test_config.py +++ b/tests/e2e/tests/test_config.py @@ -1,18 +1,14 @@ -# S3 Configuration -S3_BUCKET: str = "eligibility-signposting-api-test-eli-rules" -S3_PREFIX: str = "manual-uploads" - # Smoke Test Data Paths -SMOKE_TEST_DATA: str = "tests/e2e/data/dynamoDB/smokeTestData/" -SMOKE_TEST_RESPONSES: str = "tests/e2e/data/responses/smokeTestResponses/" -SMOKE_TEST_CONFIGS: str = "tests/e2e/data/configs/smokeTestConfigs" +SMOKE_TEST_DATA: str = "data/dynamoDB/smokeTestData/" +SMOKE_TEST_RESPONSES: str = "data/responses/smokeTestResponses/" +SMOKE_TEST_CONFIGS: str = "data/configs/smokeTestConfigs" # Story Test Data Paths -STORY_TEST_DATA: str = "tests/e2e/data/dynamoDB/storyTestData/" -STORY_TEST_RESPONSES: str = "tests/e2e/data/responses/storyTestResponses/" -STORY_TEST_CONFIGS: str = "tests/e2e/data/configs/storyTestConfigs" +STORY_TEST_DATA: str = "data/dynamoDB/storyTestData/" +STORY_TEST_RESPONSES: str = "data/responses/storyTestResponses/" +STORY_TEST_CONFIGS: str = "data/configs/storyTestConfigs" # Story Test Data Paths -REGRESSION_TEST_DATA: str = "tests/e2e/data/dynamoDB/regressionTestData/" -REGRESSION_RESPONSES: str = "tests/e2e/data/responses/regressionTestResponses/" -REGRESSION_CONFIGS: str = "tests/e2e/data/configs/regressionTestConfigs" +REGRESSION_TEST_DATA: str = "data/dynamoDB/regressionTestData/" +REGRESSION_RESPONSES: str = "data/responses/regressionTestResponses/" +REGRESSION_CONFIGS: str = "data/configs/regressionTestConfigs" -INTEGRATION_TEST_DATA: str = "tests/e2e/data/dynamoDB/integrationTestData/" +INTEGRATION_TEST_DATA: str = "data/dynamoDB/integrationTestData/" diff --git a/tests/e2e/tests/test_regression_tests.py b/tests/e2e/tests/test_regression_tests.py index ac7591f3f..0937df7e7 100644 --- a/tests/e2e/tests/test_regression_tests.py +++ b/tests/e2e/tests/test_regression_tests.py @@ -5,15 +5,13 @@ from tests.e2e.tests import test_config from tests.e2e.utils.data_loader import initialise_tests, load_all_expected_responses -from tests.e2e.utils.s3_config_manager import S3ConfigManager +from tests.e2e.utils.s3_config_manager import upload_config_to_s3 # Update the below with the configuration values specified in test_config.py all_data, dto = initialise_tests(test_config.REGRESSION_TEST_DATA) all_expected_responses = load_all_expected_responses(test_config.REGRESSION_RESPONSES) config_path = test_config.REGRESSION_CONFIGS -s3_manager = S3ConfigManager(test_config.S3_BUCKET, test_config.S3_PREFIX) - param_list = list(all_data.items()) id_list = [f"{filename} - {scenario.get('scenario_name', 'No Scenario')}" for filename, scenario in param_list] @@ -24,7 +22,7 @@ def test_run_regression_tests(filename, scenario, eligibility_client): nhs_number = scenario["nhs_number"] config_filename = scenario.get("config_filename", "") request_headers = scenario.get("request_headers", {}) - s3_manager.upload_if_missing_or_changed((Path(config_path) / config_filename).resolve()) + upload_config_to_s3((Path(config_path) / config_filename).resolve()) actual_response = eligibility_client.make_request(nhs_number, headers=request_headers, strict_ssl=False) expected_response = all_expected_responses.get(filename).get("response_items", {}) diff --git a/tests/e2e/tests/test_smoke_tests.py b/tests/e2e/tests/test_smoke_tests.py index 0b0c854b5..3322d2100 100644 --- a/tests/e2e/tests/test_smoke_tests.py +++ b/tests/e2e/tests/test_smoke_tests.py @@ -5,15 +5,13 @@ from tests.e2e.tests import test_config from tests.e2e.utils.data_loader import initialise_tests, load_all_expected_responses -from tests.e2e.utils.s3_config_manager import S3ConfigManager +from tests.e2e.utils.s3_config_manager import upload_config_to_s3 # Update the below with the configuration values specified in test_config.py all_data, dto = initialise_tests(test_config.SMOKE_TEST_DATA) all_expected_responses = load_all_expected_responses(test_config.SMOKE_TEST_RESPONSES) config_path = test_config.SMOKE_TEST_CONFIGS -s3_manager = S3ConfigManager(test_config.S3_BUCKET, test_config.S3_PREFIX) - param_list = list(all_data.items()) id_list = [f"{filename} - {scenario.get('scenario_name', 'No Scenario')}" for filename, scenario in param_list] @@ -24,7 +22,7 @@ def test_run_smoke_case(filename, scenario, eligibility_client): nhs_number = scenario["nhs_number"] config_filename = scenario.get("config_filename", "") request_headers = scenario.get("request_headers", {}) - s3_manager.upload_if_missing_or_changed((Path(config_path) / config_filename).resolve()) + upload_config_to_s3((Path(config_path) / config_filename).resolve()) actual_response = eligibility_client.make_request(nhs_number, headers=request_headers, strict_ssl=False) expected_response = all_expected_responses.get(filename).get("response_items", {}) diff --git a/tests/e2e/tests/test_story_tests.py b/tests/e2e/tests/test_story_tests.py index 1cfe44ff5..dbada8232 100644 --- a/tests/e2e/tests/test_story_tests.py +++ b/tests/e2e/tests/test_story_tests.py @@ -5,15 +5,13 @@ from tests.e2e.tests import test_config from tests.e2e.utils.data_loader import initialise_tests, load_all_expected_responses -from tests.e2e.utils.s3_config_manager import S3ConfigManager +from tests.e2e.utils.s3_config_manager import upload_config_to_s3 # Update the below with the configuration values specified in test_config.py all_data, dto = initialise_tests(test_config.STORY_TEST_DATA) all_expected_responses = load_all_expected_responses(test_config.STORY_TEST_RESPONSES) config_path = test_config.STORY_TEST_CONFIGS -s3_manager = S3ConfigManager(test_config.S3_BUCKET, test_config.S3_PREFIX) - param_list = list(all_data.items()) id_list = [f"{filename} - {scenario.get('scenario_name', 'No Scenario')}" for filename, scenario in param_list] @@ -24,18 +22,12 @@ def test_run_story_test_cases(filename, scenario, eligibility_client): nhs_number = scenario["nhs_number"] config_filename = scenario.get("config_filename", "") request_headers = scenario.get("request_headers", {}) - expected_response_code = scenario.get("expected_response_code", None) - s3_manager.upload_if_missing_or_changed((Path(config_path) / config_filename).resolve()) + upload_config_to_s3((Path(config_path) / config_filename).resolve()) actual_response = eligibility_client.make_request(nhs_number, headers=request_headers, strict_ssl=False) expected_response = all_expected_responses.get(filename).get("response_items", {}) - # Assert and show details on failure - if expected_response_code: - assert actual_response["status_code"] == expected_response_code - else: - assert actual_response["status_code"] == http.HTTPStatus.OK - + assert actual_response["status_code"] == http.HTTPStatus.OK assert actual_response["body"] == expected_response, ( f"\nโŒ Mismatch in test: {filename}\n" f"NHS Number: {nhs_number}\n" diff --git a/tests/e2e/utils/__init__.py b/tests/e2e/utils/__init__.py index a18a56d35..e69de29bb 100644 --- a/tests/e2e/utils/__init__.py +++ b/tests/e2e/utils/__init__.py @@ -1,2 +0,0 @@ -from dotenv import load_dotenv -load_dotenv(dotenv_path=".env") diff --git a/tests/e2e/utils/dynamo_helper.py b/tests/e2e/utils/dynamo_helper.py index 5a749ed57..142fd796f 100644 --- a/tests/e2e/utils/dynamo_helper.py +++ b/tests/e2e/utils/dynamo_helper.py @@ -1,4 +1,5 @@ import logging +import os import boto3 from botocore.exceptions import ClientError @@ -56,7 +57,7 @@ def get_item(self, key: dict): def insert_into_dynamo(data): logger.info("Inserting into Dynamo: %s", data) - table = DynamoDBHelper("eligibility-signposting-api-test-eligibility_datastore") + table = DynamoDBHelper(os.getenv("DYNAMODB_TABLE_NAME")) for item in data: try: table.insert_item(item) diff --git a/tests/e2e/utils/placeholder_utils.py b/tests/e2e/utils/placeholder_utils.py index fc0dca7c3..36a16c273 100644 --- a/tests/e2e/utils/placeholder_utils.py +++ b/tests/e2e/utils/placeholder_utils.py @@ -30,7 +30,7 @@ def resolve_placeholders(value, context=None, file_name=None): def _resolve_placeholder_value(placeholder: str) -> str: placeholder_parts_length = 3 - #RDATE + # RDATE valid_placeholder_types = ["DATE", "RDATE", "IGNORE"] result = f"<<{placeholder}>>" # Default fallback diff --git a/tests/e2e/utils/s3_config_manager.py b/tests/e2e/utils/s3_config_manager.py index dd439516a..f4bfd804e 100644 --- a/tests/e2e/utils/s3_config_manager.py +++ b/tests/e2e/utils/s3_config_manager.py @@ -1,5 +1,6 @@ import hashlib import logging +import os from pathlib import Path import boto3 @@ -16,9 +17,6 @@ def __init__(self, bucket_name: str, s3_prefix: str = "") -> None: self.s3_prefix: str = s3_prefix self.s3_client = boto3.client("s3") - def _s3_client(self): - return self.session.client("s3") - def _s3_key(self, filename: str) -> str: return str(Path(self.s3_prefix) / filename) @@ -53,7 +51,6 @@ def upload_if_missing_or_changed(self, local_path: Path) -> None: logger.info("๐Ÿ“„ Uploaded to s3://%s/%s", self.bucket_name, s3_key) def config_exists_and_matches(self, local_path: Path, s3_key: str) -> bool: - session = boto3.Session() credentials = session.get_credentials() logger.info("AWS_ACCESS_KEY_ID = %s", credentials.access_key) @@ -84,3 +81,8 @@ def _delete_all_in_prefix(self) -> None: logger.info("๐Ÿ—‘๏ธ Deleted %d file(s) under prefix '%s/'.", len(to_delete), self.s3_prefix) else: logger.info("๐Ÿ“ญ Nothing to delete under prefix '%s/'.", self.s3_prefix) + + +def upload_config_to_s3(local_path: Path) -> None: + s3_connection = S3ConfigManager(os.getenv("S3_BUCKET_NAME"), os.getenv("S3_PREFIX")) + s3_connection.upload_if_missing_or_changed(local_path) From ec47e9fe7055c5954677c0f06ae0e715e1876edf Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Thu, 10 Jul 2025 10:11:49 +0100 Subject: [PATCH 50/72] addition of more data files and the functionality to include query_params --- .../AUTO_RSV_REG_001.json | 80 +++++++++++++++-- .../storyTestConfigs/AUTO_RSV_ELI-317-1.json | 85 ++++++++++++++++++ ...ELI-219-4.json => AUTO_RSV_ELI-317-2.json} | 12 +-- .../storyTestConfigs/AUTO_RSV_ELI-317-3.json | 87 +++++++++++++++++++ .../storyTestConfigs/AUTO_RSV_ELI-317-4.json | 87 +++++++++++++++++++ .../regressionTestData/AUTO_RSV_REG_012.json | 25 ++++++ .../regressionTestData/AUTO_RSV_REG_013.json | 30 +++++++ .../storyTestData/AUTO_RSV_ELI-216-1.json | 8 +- .../storyTestData/AUTO_RSV_ELI-219-1.json | 10 +-- .../storyTestData/AUTO_RSV_ELI-219-2.json | 6 +- .../storyTestData/AUTO_RSV_ELI-219-3.json | 8 +- .../storyTestData/AUTO_RSV_ELI-317-1.json | 49 +++++++++++ .../storyTestData/AUTO_RSV_ELI-317-2.json | 49 +++++++++++ ...ELI-219-4.json => AUTO_RSV_ELI-317-3.json} | 15 ++-- .../storyTestData/AUTO_RSV_ELI-317-4.json | 43 +++++++++ .../AUTO_RSV_REG_012.json | 27 ++++++ .../AUTO_RSV_REG_013.json | 22 +++++ ...ELI-219-4.json => AUTO_RSV_ELI-317-1.json} | 4 +- .../AUTO_RSV_ELI-317-2.json | 30 +++++++ .../AUTO_RSV_ELI-317-3.json | 21 +++++ .../AUTO_RSV_ELI-317-4.json | 22 +++++ tests/e2e/tests/test_smoke_tests.py | 3 +- tests/e2e/tests/test_story_tests.py | 3 +- tests/e2e/utils/data_loader.py | 2 + tests/e2e/utils/eligibility_api_client.py | 2 + 25 files changed, 691 insertions(+), 39 deletions(-) create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-1.json rename tests/e2e/data/configs/storyTestConfigs/{AUTO_RSV_ELI-219-4.json => AUTO_RSV_ELI-317-2.json} (87%) create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-3.json create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-4.json create mode 100644 tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_012.json create mode 100644 tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_013.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-1.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-2.json rename tests/e2e/data/dynamoDB/storyTestData/{AUTO_RSV_ELI-219-4.json => AUTO_RSV_ELI-317-3.json} (72%) create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-4.json create mode 100644 tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_012.json create mode 100644 tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_013.json rename tests/e2e/data/responses/storyTestResponses/{AUTO_RSV_ELI-219-4.json => AUTO_RSV_ELI-317-1.json} (82%) create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-2.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-3.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-4.json diff --git a/tests/e2e/data/configs/regressionTestConfigs/AUTO_RSV_REG_001.json b/tests/e2e/data/configs/regressionTestConfigs/AUTO_RSV_REG_001.json index 769de7fed..d874cbbfa 100644 --- a/tests/e2e/data/configs/regressionTestConfigs/AUTO_RSV_REG_001.json +++ b/tests/e2e/data/configs/regressionTestConfigs/AUTO_RSV_REG_001.json @@ -1,6 +1,6 @@ { "CampaignConfig": { - "ID": "<>", + "ID": "Automation RSV - Regression Test Config - Camp ID", "Version": "1", "Name": "Automation RSV - Regression Test Config", "Type": "V", @@ -18,7 +18,7 @@ "ApprovalMaximum": 0, "Iterations": [ { - "ID": ",<>", + "ID": "Automation RSV - Regression Test Config - It ID", "DefaultCommsRouting": "DEFAULT_ACTION", "Version": "1", "Name": "Automation RSV - Regression Test Config Iteration", @@ -82,21 +82,89 @@ "Type": "F", "Name": "Under Age - Under 75 Years on day of execution", "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", - "Priority": 120, + "Priority": 110, "AttributeLevel": "PERSON", "AttributeName": "DATE_OF_BIRTH", "Operator": "Y>", - "Comparator": "-75" + "Comparator": "-75", + "CohortLabel": "rsv_cohort_1" }, { "Type": "F", "Name": "Exclude Too OLD", "Description": "Exclude anyone over 80", - "Priority": 130, + "Priority": 111, "AttributeLevel": "PERSON", "AttributeName": "DATE_OF_BIRTH", "Operator": "<", - "Comparator": "-80" + "Comparator": "-80", + "CohortLabel": "rsv_cohort_1" + }, + { + "Type": "F", + "Name": "Under Age - Under 75 Years on day of execution", + "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", + "Priority": 112, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75", + "CohortLabel": "rsv_cohort_2" + }, + { + "Type": "F", + "Name": "Exclude Too OLD", + "Description": "Exclude anyone over 80", + "Priority": 113, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "<", + "Comparator": "-80", + "CohortLabel": "rsv_cohort_2" + }, + { + "Type": "F", + "Name": "Under Age - Under 75 Years on day of execution", + "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", + "Priority": 114, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75", + "CohortLabel": "rsv_cohort_3" + }, + { + "Type": "F", + "Name": "Exclude Too OLD", + "Description": "Exclude anyone over 80", + "Priority": 115, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "<", + "Comparator": "-80", + "CohortLabel": "rsv_cohort_3" + }, + { + "Type": "F", + "Name": "Under Age - Under 75 Years on day of execution", + "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", + "Priority": 116, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75", + "CohortLabel": "no_group_description" + }, + { + "Type": "F", + "Name": "Exclude Too OLD", + "Description": "Exclude anyone over 80", + "Priority": 117, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "<", + "Comparator": "-80", + "CohortLabel": "no_group_description" }, { "Type": "S", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-1.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-1.json new file mode 100644 index 000000000..7289f389e --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-1.json @@ -0,0 +1,85 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "ELI-317-1 - Empty Actions - 9000000005", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "DEFAULT_ACTION", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "DEFAULT_ACTION", + "Version": "1", + "Name": "ELI-317 - Empty Actions - 9000000005", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "M", + "IterationCohorts": [ + { + "CohortLabel": "eli_317_cohort_1", + "CohortGroup": "eli_317_cohort_1_group", + "PositiveDescription": "You are currently in eli_317_cohort_1", + "NegativeDescription": "You are not in eli_317_cohort_1", + "Priority": 1 + } + ], + "IterationRules": [ + { + "Type": "R", + "Name": "Actionable if future appointment is booked", + "Description": "Book An Appointment", + "Priority": 1000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D>=", + "Comparator": "0", + "CommsRouting": "EMPTY_ACTION" + } + ], + "ActionsMapper": { + "EMPTY_ACTION": { + "ExternalRoutingCode": "", + "ActionDescription": "", + "ActionType": "" + }, + "ACTION_TWO": { + "ExternalRoutingCode": "ActionTwoRoutingCode", + "ActionDescription": "ActionTwoDescription", + "ActionType": "ActionTwoActionType", + "UrlLink": "http://www.actiontwourl.com", + "UrlLabel": "ActionTwoUrlLabel" + }, + "ACTION_THREE": { + "ExternalRoutingCode": "ActionThreeRoutingCode", + "ActionDescription": "ActionThreeDescription", + "ActionType": "ActionThreeActionType", + "UrlLink": "http://www.actionthreeurl.com", + "UrlLabel": "ActionThreeUrlLabel" + }, + "DEFAULT_ACTION": { + "ExternalRoutingCode": "DefaultAction", + "ActionDescription": "DefaultActionDescription", + "ActionType": "DefaultActionType", + "UrlLink": "https://www.defaultactionurl.com", + "UrlLabel": "DefaultLabel" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-4.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-2.json similarity index 87% rename from tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-4.json rename to tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-2.json index f70026d7c..265be4bca 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-4.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-2.json @@ -2,7 +2,7 @@ "CampaignConfig": { "ID": "<>", "Version": "1", - "Name": "ELI-219 - Empty Actions - 9000000005", + "Name": "ELI-317 - Empty Actions - 9000000006", "Type": "V", "Target": "RSV", "Manager": "person1@nhs.net", @@ -21,7 +21,7 @@ "ID": ",<>", "DefaultCommsRouting": "DEFAULT_ACTION", "Version": "1", - "Name": "ELI-219 - Support Actions R Rules - 9000000004", + "Name": "ELI-317 - Empty Actions - 9000000006", "IterationDate": "20250601", "IterationNumber": 1, "CommsType": "I", @@ -30,10 +30,10 @@ "Type": "M", "IterationCohorts": [ { - "CohortLabel": "eli_291_cohort_4", - "CohortGroup": "eli_291_cohort_4_group", - "PositiveDescription": "You are currently in eli_291_cohort_4", - "NegativeDescription": "You are not in eli_291_cohort_4", + "CohortLabel": "eli_317_cohort_2", + "CohortGroup": "eli_317_cohort_2_group", + "PositiveDescription": "You are currently in eli_317_cohort_2", + "NegativeDescription": "You are not in eli_317_cohort_2", "Priority": 1 } ], diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-3.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-3.json new file mode 100644 index 000000000..6f333034a --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-3.json @@ -0,0 +1,87 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "ELI-317 - includeActions=N - 9000000008", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "DEFAULT_ACTION", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "DEFAULT_ACTION", + "Version": "1", + "Name": "ELI-317 - Empty Actions - 9000000006", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "M", + "IterationCohorts": [ + { + "CohortLabel": "eli_317_cohort_3", + "CohortGroup": "eli_317_cohort_3_group", + "PositiveDescription": "You are currently in eli_317_cohort_3", + "NegativeDescription": "You are not in eli_317_cohort_3", + "Priority": 1 + } + ], + "IterationRules": [ + { + "Type": "R", + "Name": "Actionable if future appointment is booked", + "Description": "Book An Appointment", + "Priority": 1000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D>=", + "Comparator": "0", + "CommsRouting": "ACTION_TWO" + } + ], + "ActionsMapper": { + "EMPTY_ACTION": { + "ExternalRoutingCode": "", + "ActionDescription": "", + "ActionType": "", + "UrlLink": null, + "UrlLabel": "" + }, + "ACTION_TWO": { + "ExternalRoutingCode": "ActionTwoRoutingCode", + "ActionDescription": "ActionTwoDescription", + "ActionType": "ActionTwoActionType", + "UrlLink": "http://www.actiontwourl.com", + "UrlLabel": "ActionTwoUrlLabel" + }, + "ACTION_THREE": { + "ExternalRoutingCode": "ActionThreeRoutingCode", + "ActionDescription": "ActionThreeDescription", + "ActionType": "ActionThreeActionType", + "UrlLink": "http://www.actionthreeurl.com", + "UrlLabel": "ActionThreeUrlLabel" + }, + "DEFAULT_ACTION": { + "ExternalRoutingCode": "DefaultAction", + "ActionDescription": "DefaultActionDescription", + "ActionType": "DefaultActionType", + "UrlLink": "https://www.defaultactionurl.com", + "UrlLabel": "DefaultLabel" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-4.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-4.json new file mode 100644 index 000000000..29dc386e4 --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-4.json @@ -0,0 +1,87 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "ELI-317 - includeActions=N - 9000000009", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "", + "Version": "1", + "Name": "ELI-317 - Empty Actions - 9000000006", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "M", + "IterationCohorts": [ + { + "CohortLabel": "eli_317_cohort_4", + "CohortGroup": "eli_317_cohort_4_group", + "PositiveDescription": "You are currently in eli_317_cohort_4", + "NegativeDescription": "You are not in eli_317_cohort_4", + "Priority": 1 + } + ], + "IterationRules": [ + { + "Type": "R", + "Name": "Actionable if future appointment is booked", + "Description": "Book An Appointment", + "Priority": 1000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D>=", + "Comparator": "0", + "CommsRouting": "ACTION_TWO" + } + ], + "ActionsMapper": { + "EMPTY_ACTION": { + "ExternalRoutingCode": "", + "ActionDescription": "", + "ActionType": "", + "UrlLink": null, + "UrlLabel": "" + }, + "ACTION_TWO": { + "ExternalRoutingCode": "ActionTwoRoutingCode", + "ActionDescription": "ActionTwoDescription", + "ActionType": "ActionTwoActionType", + "UrlLink": "http://www.actiontwourl.com", + "UrlLabel": "ActionTwoUrlLabel" + }, + "ACTION_THREE": { + "ExternalRoutingCode": "ActionThreeRoutingCode", + "ActionDescription": "ActionThreeDescription", + "ActionType": "ActionThreeActionType", + "UrlLink": "http://www.actionthreeurl.com", + "UrlLabel": "ActionThreeUrlLabel" + }, + "DEFAULT_ACTION": { + "ExternalRoutingCode": "DefaultAction", + "ActionDescription": "DefaultActionDescription", + "ActionType": "DefaultActionType", + "UrlLink": "https://www.defaultactionurl.com", + "UrlLabel": "DefaultLabel" + } + } + } + ] + } +} diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_012.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_012.json new file mode 100644 index 000000000..ce717d981 --- /dev/null +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_012.json @@ -0,0 +1,25 @@ +{ + "scenario_name": "RSV - Regression - Not Eligible, In Magic Cohort, Filtered out Rule", + "request_headers": {"nhs-login-nhs-number": "1000000012"}, + "config_filename": "AUTO_RSV_REG_001.json", + "data": [ + { + "NHS_NUMBER": "1000000012", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS2", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_013.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_013.json new file mode 100644 index 000000000..d949d5f99 --- /dev/null +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_013.json @@ -0,0 +1,30 @@ +{ + "scenario_name": "RSV - Regression - Eligible, In Magic Cohort", + "request_headers": {"nhs-login-nhs-number": "1000000013"}, + "config_filename": "AUTO_RSV_REG_001.json", + "data": [ + { + "NHS_NUMBER": "1000000013", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS2", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "1000000013", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-1.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-1.json index e2c7dd67c..c22fd88cf 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-1.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-1.json @@ -1,10 +1,10 @@ { "scenario_name": "ELI-216 - NHS Number check (NHS login) - Correct NHS_number", "config_filename": "AUTO_RSV_ELI-216.json", - "request_headers": {"nhs-login-nhs-number": "9000000006"}, + "request_headers": {"nhs-login-nhs-number": "9000000002"}, "data": [ { - "NHS_NUMBER": "9000000006", + "NHS_NUMBER": "9000000002", "ATTRIBUTE_TYPE": "COHORTS", "COHORT_MAP": { "cohorts": { @@ -21,7 +21,7 @@ } }, { - "NHS_NUMBER": "9000000006", + "NHS_NUMBER": "9000000002", "ATTRIBUTE_TYPE": "PERSON", "DATE_OF_BIRTH": "<>", "GENDER": "0", @@ -39,7 +39,7 @@ "DE_FLAG": "N" }, { - "NHS_NUMBER": "9000000006", + "NHS_NUMBER": "9000000002", "ATTRIBUTE_TYPE": "RSV", "BOOKED_APPOINTMENT_DATE": "<>", "LAST_SUCCESSFUL_DATE": "<>" diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json index 2e30eabf2..0f19367dd 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json @@ -1,10 +1,10 @@ { - "scenario_name": "ELI-219 - Fails and Rules and Returns Default", + "scenario_name": "ELI-219 - 1 - Fails and Rules and Returns Default", "config_filename": "AUTO_RSV_ELI-219-1.json", - "request_headers": {"nhs-login-nhs-number": "9000000002"}, + "request_headers": {"nhs-login-nhs-number": "9000000003"}, "data": [ { - "NHS_NUMBER": "9000000002", + "NHS_NUMBER": "9000000003", "ATTRIBUTE_TYPE": "COHORTS", "COHORT_MAP": { "cohorts": { @@ -21,7 +21,7 @@ } }, { - "NHS_NUMBER": "9000000002", + "NHS_NUMBER": "9000000003", "ATTRIBUTE_TYPE": "PERSON", "DATE_OF_BIRTH": "<>", "GENDER": "0", @@ -39,7 +39,7 @@ "DE_FLAG": "N" }, { - "NHS_NUMBER": "9000000002", + "NHS_NUMBER": "9000000003", "ATTRIBUTE_TYPE": "RSV", "BOOKED_APPOINTMENT_DATE": "<>", "LAST_SUCCESSFUL_DATE": "<>" diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-2.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-2.json index cccfac6d1..5bb96e394 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-2.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-2.json @@ -1,10 +1,10 @@ { "scenario_name": "ELI-219 - Returns the highest Priority Action", "config_filename": "AUTO_RSV_ELI-219-2.json", - "request_headers": {"nhs-login-nhs-number": "9000000003"}, + "request_headers": {"nhs-login-nhs-number": "9000000004"}, "data": [ { - "NHS_NUMBER": "9000000003", + "NHS_NUMBER": "9000000004", "ATTRIBUTE_TYPE": "COHORTS", "COHORT_MAP": { "cohorts": { @@ -21,7 +21,7 @@ } }, { - "NHS_NUMBER": "9000000003", + "NHS_NUMBER": "9000000004", "ATTRIBUTE_TYPE": "PERSON", "DATE_OF_BIRTH": "<>", "GENDER": "0", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-3.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-3.json index f12db18d8..7b164516a 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-3.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-3.json @@ -1,10 +1,10 @@ { "scenario_name": "ELI-219 - Returns the highest Priority Action", "config_filename": "AUTO_RSV_ELI-219-3.json", - "request_headers": {"nhs-login-nhs-number": "9000000004"}, + "request_headers": {"nhs-login-nhs-number": "9000000005"}, "data": [ { - "NHS_NUMBER": "9000000004", + "NHS_NUMBER": "9000000005", "ATTRIBUTE_TYPE": "COHORTS", "COHORT_MAP": { "cohorts": { @@ -21,7 +21,7 @@ } }, { - "NHS_NUMBER": "9000000004", + "NHS_NUMBER": "9000000005", "ATTRIBUTE_TYPE": "PERSON", "DATE_OF_BIRTH": "<>", "GENDER": "0", @@ -39,7 +39,7 @@ "DE_FLAG": "N" }, { - "NHS_NUMBER": "9000000004", + "NHS_NUMBER": "9000000005", "ATTRIBUTE_TYPE": "RSV", "BOOKED_APPOINTMENT_DATE": "<>", "LAST_SUCCESSFUL_DATE": "<>" diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-1.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-1.json new file mode 100644 index 000000000..e87a4697d --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-1.json @@ -0,0 +1,49 @@ +{ + "scenario_name": "ELI-317 - Testing the new requirement from Vita around empty actions ", + "config_filename": "AUTO_RSV_ELI-317-1.json", + "request_headers": {"nhs-login-nhs-number": "9000000006"}, + "query_params": {"includeActions": "Y"}, + "data": [ + { + "NHS_NUMBER": "9000000006", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_317_cohort_1": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9000000006", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9000000006", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-2.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-2.json new file mode 100644 index 000000000..6713f19fa --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-2.json @@ -0,0 +1,49 @@ +{ + "scenario_name": "ELI-317 - Testing the new requirement from Vita around empty actions ", + "config_filename": "AUTO_RSV_ELI-317-2.json", + "request_headers": {"nhs-login-nhs-number": "9000000007"}, + "query_params": {"includeActions": "Y"}, + "data": [ + { + "NHS_NUMBER": "9000000007", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_317_cohort_2": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9000000007", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9000000007", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-4.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-3.json similarity index 72% rename from tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-4.json rename to tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-3.json index 37f291ca5..4649a3f3c 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-4.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-3.json @@ -1,15 +1,16 @@ { - "scenario_name": "ELI-219 - Testing the new requirement from Vita around empty actions ", - "config_filename": "AUTO_RSV_ELI-219-4.json", - "request_headers": {"nhs-login-nhs-number": "9000000005"}, + "scenario_name": "ELI-317 - includeActions=N ", + "config_filename": "AUTO_RSV_ELI-317-3.json", + "request_headers": {"nhs-login-nhs-number": "9000000008"}, + "query_params": {"includeActions": "N"}, "data": [ { - "NHS_NUMBER": "9000000005", + "NHS_NUMBER": "9000000008", "ATTRIBUTE_TYPE": "COHORTS", "COHORT_MAP": { "cohorts": { "M": { - "eli_291_cohort_4": { + "eli_317_cohort_3": { "M": { "dateJoined": { "S": "20230515" @@ -21,7 +22,7 @@ } }, { - "NHS_NUMBER": "9000000005", + "NHS_NUMBER": "9000000008", "ATTRIBUTE_TYPE": "PERSON", "DATE_OF_BIRTH": "<>", "GENDER": "0", @@ -39,7 +40,7 @@ "DE_FLAG": "N" }, { - "NHS_NUMBER": "9000000005", + "NHS_NUMBER": "9000000008", "ATTRIBUTE_TYPE": "RSV", "BOOKED_APPOINTMENT_DATE": "<>", "LAST_SUCCESSFUL_DATE": "<>" diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-4.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-4.json new file mode 100644 index 000000000..a0f385278 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-4.json @@ -0,0 +1,43 @@ +{ + "scenario_name": "ELI-317 - includeActions=Y, but no actions in response ", + "config_filename": "AUTO_RSV_ELI-317-4.json", + "request_headers": {"nhs-login-nhs-number": "9000000009"}, + "query_params": {"includeActions": "Y"}, + "data": [ + { + "NHS_NUMBER": "9000000009", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_317_cohort_4": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9000000009", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_012.json b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_012.json new file mode 100644 index 000000000..1030e0ffb --- /dev/null +++ b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_012.json @@ -0,0 +1,27 @@ +{ + "meta": { + "lastUpdated": "2025-07-01T20:20:47.764973+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not in an age group 2." + }, + { + "cohortCode": "rsv_other_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not in an other group." + } + ], + "status": "NotEligible", + "statusText": "Status.not_eligible", + "suitabilityRules": [] + } + ], + "responseId": "49db2d41-326f-45d4-afaf-0dd6897413af" +} diff --git a/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_013.json b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_013.json new file mode 100644 index 000000000..6fa6dbca1 --- /dev/null +++ b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_013.json @@ -0,0 +1,22 @@ +{ + "meta": { + "lastUpdated": "2025-07-09T15:16:43.877615+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [], + "status": "NotActionable", + "statusText": "Status.not_actionable", + "suitabilityRules": [ + { + "ruleCode": "AlreadyVaccinated", + "ruleText": "##You've had your RSV vaccination\nWe believe you had your vaccination on <>.", + "ruleType": "S" + } + ] + } + ], + "responseId": "c064aa23-730c-454a-831e-e7299007307d" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-4.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-1.json similarity index 82% rename from tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-4.json rename to tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-1.json index 913fab564..c2ac8c709 100644 --- a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-4.json +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-1.json @@ -10,9 +10,9 @@ "statusText": "Status.actionable", "eligibilityCohorts": [ { - "cohortCode": "eli_291_cohort_4_group", + "cohortCode": "eli_317_cohort_1_group", "cohortStatus": "Actionable", - "cohortText": "You are currently in eli_291_cohort_4" + "cohortText": "You are currently in eli_317_cohort_1" } ], "suitabilityRules": [], diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-2.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-2.json new file mode 100644 index 000000000..12c33e6ea --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-2.json @@ -0,0 +1,30 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "Actionable", + "statusText": "Status.actionable", + "eligibilityCohorts": [ + { + "cohortCode": "eli_317_cohort_2_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in eli_317_cohort_2" + } + ], + "suitabilityRules": [], + "actions": [ + { + "actionCode": "", + "description": "", + "actionType": "", + "urlLabel": "", + "urlLink": "" + } + ] + } + ] +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-3.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-3.json new file mode 100644 index 000000000..c53b450a0 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-3.json @@ -0,0 +1,21 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "Actionable", + "statusText": "Status.actionable", + "eligibilityCohorts": [ + { + "cohortCode": "eli_317_cohort_3_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in eli_317_cohort_3" + } + ], + "suitabilityRules": [] + } + ] +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-4.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-4.json new file mode 100644 index 000000000..cd61107d7 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-4.json @@ -0,0 +1,22 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "status": "Actionable", + "statusText": "Status.actionable", + "eligibilityCohorts": [ + { + "cohortCode": "eli_317_cohort_4_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in eli_317_cohort_4" + } + ], + "suitabilityRules": [], + "actions": [] + } + ] +} diff --git a/tests/e2e/tests/test_smoke_tests.py b/tests/e2e/tests/test_smoke_tests.py index 3322d2100..46127dc05 100644 --- a/tests/e2e/tests/test_smoke_tests.py +++ b/tests/e2e/tests/test_smoke_tests.py @@ -22,9 +22,10 @@ def test_run_smoke_case(filename, scenario, eligibility_client): nhs_number = scenario["nhs_number"] config_filename = scenario.get("config_filename", "") request_headers = scenario.get("request_headers", {}) + query_params = scenario.get("query_params", {}) upload_config_to_s3((Path(config_path) / config_filename).resolve()) - actual_response = eligibility_client.make_request(nhs_number, headers=request_headers, strict_ssl=False) + actual_response = eligibility_client.make_request(nhs_number, headers=request_headers, query_params= query_params, strict_ssl=False) expected_response = all_expected_responses.get(filename).get("response_items", {}) # Assert and show details on failure diff --git a/tests/e2e/tests/test_story_tests.py b/tests/e2e/tests/test_story_tests.py index dbada8232..13fba6085 100644 --- a/tests/e2e/tests/test_story_tests.py +++ b/tests/e2e/tests/test_story_tests.py @@ -22,9 +22,10 @@ def test_run_story_test_cases(filename, scenario, eligibility_client): nhs_number = scenario["nhs_number"] config_filename = scenario.get("config_filename", "") request_headers = scenario.get("request_headers", {}) + query_params = scenario.get("query_params", {}) upload_config_to_s3((Path(config_path) / config_filename).resolve()) - actual_response = eligibility_client.make_request(nhs_number, headers=request_headers, strict_ssl=False) + actual_response = eligibility_client.make_request(nhs_number, headers=request_headers, query_params= query_params, strict_ssl=False) expected_response = all_expected_responses.get(filename).get("response_items", {}) assert actual_response["status_code"] == http.HTTPStatus.OK diff --git a/tests/e2e/utils/data_loader.py b/tests/e2e/utils/data_loader.py index 8e7a9787c..98b3c4f51 100644 --- a/tests/e2e/utils/data_loader.py +++ b/tests/e2e/utils/data_loader.py @@ -97,6 +97,7 @@ def load_all_test_scenarios(folder_path): scenario_name = raw_json.get("scenario_name") request_headers = raw_json.get("request_headers") expected_response_code = raw_json.get("expected_response_code") + query_params = raw_json.get("query_params") # Resolve placeholders with shared DTO resolved_data = resolve_placeholders_in_data(raw_data, dto, path.name) @@ -111,6 +112,7 @@ def load_all_test_scenarios(folder_path): "config_filename": config_filename, "expected_response_code": expected_response_code, "request_headers": request_headers, + "query_params": query_params, "scenario_name": scenario_name, } diff --git a/tests/e2e/utils/eligibility_api_client.py b/tests/e2e/utils/eligibility_api_client.py index d69be6bfb..994ff3784 100644 --- a/tests/e2e/utils/eligibility_api_client.py +++ b/tests/e2e/utils/eligibility_api_client.py @@ -69,6 +69,7 @@ def make_request( method: str = "GET", payload: dict[str, Any] | list | None = None, headers: dict[str, str] | None = None, + query_params: dict[str, Any] | None = None, **options, ) -> dict[str, Any]: strict_ssl = options.get("strict_ssl", False) @@ -88,6 +89,7 @@ def make_request( verify=verify, json=payload, headers=headers, + params=query_params, timeout=10, ) From e255cbe0ded2f683aeaa69ce3c3cb61d7feecb89 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 14 Jul 2025 15:34:00 +0100 Subject: [PATCH 51/72] addition of more data files and clean up of how to deal with dynamic data --- .../AUTO_RSV_REG_001.json | 9 +- .../storyTestConfigs/AUTO_RSV_ELI-317-5.json | 85 ++++++++++++++ .../storyTestData/AUTO_RSV_ELI-219-2.json | 2 +- .../storyTestData/AUTO_RSV_ELI-317-5.json | 43 +++++++ .../AUTO_RSV_ELI-317-5.json | 44 +++++++ tests/e2e/tests/test_config.py | 5 + tests/e2e/tests/test_error_scenario_tests.py | 108 +++++++++++++++--- tests/e2e/tests/test_regression_tests.py | 2 +- tests/e2e/tests/test_smoke_tests.py | 6 +- tests/e2e/tests/test_story_tests.py | 6 +- .../utils/{data_loader.py => data_helper.py} | 28 +++-- tests/e2e/utils/eligibility_api_client.py | 21 +--- 12 files changed, 309 insertions(+), 50 deletions(-) create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-5.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-5.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-5.json rename tests/e2e/utils/{data_loader.py => data_helper.py} (81%) diff --git a/tests/e2e/data/configs/regressionTestConfigs/AUTO_RSV_REG_001.json b/tests/e2e/data/configs/regressionTestConfigs/AUTO_RSV_REG_001.json index d874cbbfa..0daa66961 100644 --- a/tests/e2e/data/configs/regressionTestConfigs/AUTO_RSV_REG_001.json +++ b/tests/e2e/data/configs/regressionTestConfigs/AUTO_RSV_REG_001.json @@ -251,7 +251,7 @@ "Type": "R", "Name": "Actionable Future Booked Appointment", "Description": "Actionable Future Booked Appointment", - "Priority": 1030, + "Priority": 1050, "Operator": "!=", "Comparator": "NBS", "AttributeTarget": "RSV", @@ -267,6 +267,13 @@ "ActionType": "DefaultActionType", "UrlLink": "https://www.defaultaction.com", "UrlLabel": "DefaultLabel" + }, + "ANOTHER_ACTION": { + "ExternalRoutingCode": "AnotherAction", + "ActionDescription": "AnotherActionDescription", + "ActionType": "AnotherActionType", + "UrlLink": "https://www.anoteraction.com", + "UrlLabel": "AnotherLabel" } } } diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-5.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-5.json new file mode 100644 index 000000000..4fd9772c1 --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-5.json @@ -0,0 +1,85 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "ELI-317 - Multiple Actions - 9000000010", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "DEFAULT_ACTION", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "DEFAULT_ACTION", + "Version": "1", + "Name": "ELI-317 - Multiple Actions - 9000000010", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "eli_317_cohort_5", + "CohortGroup": "eli_317_cohort_5_group", + "PositiveDescription": "You are currently in eli_317_cohort_5", + "NegativeDescription": "You are not in eli_317_cohort_5", + "Priority": 1 + } + ], + "IterationRules": [ + { + "Type": "R", + "Name": "Actionable if future appointment is booked", + "Description": "Book An Appointment", + "Priority": 1000, + "AttributeLevel": "PERSON", + "AttributeName": "DE_FLAG", + "Operator": "=", + "Comparator": "Y", + "CommsRouting": "ACTION_ONE|ACTION_TWO|ACTION_THREE" + } + ], + "ActionsMapper": { + "ACTION_ONE": { + "ExternalRoutingCode": "ActionOneRoutingCode", + "ActionDescription": "ActionOneDescription", + "ActionType": "ActionOneActionType", + "UrlLink": "http://www.actiononeurl.com", + "UrlLabel": "ActiononeUrlLabel" + },"ACTION_TWO": { + "ExternalRoutingCode": "ActionTwoRoutingCode", + "ActionDescription": "ActionTwoDescription", + "ActionType": "ActionTwoActionType", + "UrlLink": "http://www.actiontwourl.com", + "UrlLabel": "ActionTwoUrlLabel" + }, + "ACTION_THREE": { + "ExternalRoutingCode": "ActionThreeRoutingCode", + "ActionDescription": "ActionThreeDescription", + "ActionType": "ActionThreeActionType", + "UrlLink": "http://www.actionthreeurl.com", + "UrlLabel": "ActionThreeUrlLabel" + }, + "DEFAULT_ACTION": { + "ExternalRoutingCode": "DefaultAction", + "ActionDescription": "DefaultActionDescription", + "ActionType": "DefaultActionType", + "UrlLink": "https://www.defaultactionurl.com", + "UrlLabel": "DefaultLabel" + } + } + } + ] + } +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-2.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-2.json index 5bb96e394..a107cad92 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-2.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-2.json @@ -39,7 +39,7 @@ "DE_FLAG": "N" }, { - "NHS_NUMBER": "9000000002", + "NHS_NUMBER": "9000000004", "ATTRIBUTE_TYPE": "RSV", "BOOKED_APPOINTMENT_DATE": "<>", "LAST_SUCCESSFUL_DATE": "<>" diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-5.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-5.json new file mode 100644 index 000000000..28f2d97bd --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-5.json @@ -0,0 +1,43 @@ +{ + "scenario_name": "ELI-317 - Multiple actions", + "config_filename": "AUTO_RSV_ELI-317-5.json", + "request_headers": {"nhs-login-nhs-number": "9000000010"}, + "query_params": {"includeActions": "Y"}, + "data": [ + { + "NHS_NUMBER": "9000000010", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_317_cohort_5": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9000000010", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "Y" + } + ] +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-5.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-5.json new file mode 100644 index 000000000..5a1a15b91 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-5.json @@ -0,0 +1,44 @@ +{ + "responseId": "<>", + "meta": { + "lastUpdated": "<>" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "ActionOneRoutingCode", + "actionType": "ActionOneActionType", + "description": "ActionOneDescription", + "urlLabel": "ActiononeUrlLabel", + "urlLink": "http://www.actiononeurl.com/" + }, + { + "actionCode": "ActionTwoRoutingCode", + "actionType": "ActionTwoActionType", + "description": "ActionTwoDescription", + "urlLabel": "ActionTwoUrlLabel", + "urlLink": "http://www.actiontwourl.com/" + }, + { + "actionCode": "ActionThreeRoutingCode", + "actionType": "ActionThreeActionType", + "description": "ActionThreeDescription", + "urlLabel": "ActionThreeUrlLabel", + "urlLink": "http://www.actionthreeurl.com/" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_317_cohort_5_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in eli_317_cohort_5" + } + ], + "status": "Actionable", + "statusText": "Status.actionable", + "suitabilityRules": [] + } + ] +} diff --git a/tests/e2e/tests/test_config.py b/tests/e2e/tests/test_config.py index ce906601e..cab8a01e9 100644 --- a/tests/e2e/tests/test_config.py +++ b/tests/e2e/tests/test_config.py @@ -11,4 +11,9 @@ REGRESSION_RESPONSES: str = "data/responses/regressionTestResponses/" REGRESSION_CONFIGS: str = "data/configs/regressionTestConfigs" +# Story Test Data Paths +IN_PROGRESS_TEST_DATA: str = "data/dynamoDB/inProgressTestData/" +IN_PROGRESS_RESPONSES: str = "data/responses/inProgressTestResponses/" +IN_PROGRESS_CONFIGS: str = "data/configs/inProgressTestConfigs" + INTEGRATION_TEST_DATA: str = "data/dynamoDB/integrationTestData/" diff --git a/tests/e2e/tests/test_error_scenario_tests.py b/tests/e2e/tests/test_error_scenario_tests.py index 25956058f..f59685dce 100644 --- a/tests/e2e/tests/test_error_scenario_tests.py +++ b/tests/e2e/tests/test_error_scenario_tests.py @@ -2,28 +2,47 @@ import pytest +from tests.e2e.utils.data_helper import clean_responses + +volatile_fields = ["lastUpdated", "id"] + @pytest.mark.smoketest def test_check_for_missing_person(eligibility_client): - nhs_number = "1234567890" + nhs_number = "9934567890" - request_headers = {"nhs-login-nhs-number": "1234567890"} + request_headers = {"nhs-login-nhs-number": "9934567890"} expected_body = { + "resourceType": "OperationOutcome", + "id": "", + 'meta': {'lastUpdated': ''}, "issue": [ { - "code": "nhs-number-not-found", - "diagnostics": f'NHS Number "{nhs_number}" not found.', - "severity": "information", + "severity": "error", + "code": "processing", + "details": { + "coding": [ + { + "system": "https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1", + "code": "REFERENCE_NOT_FOUND", + "display": "The given NHS number was not found in our datasets. " + "This could be because the number is incorrect or some other reason " + "we cannot process that number.", + } + ] + }, + "diagnostics": "NHS Number '9934567890' was not recognised by the Eligibility Signposting API", + "location": ["parameters/id"], } ], - "resourceType": "OperationOutcome", } response = eligibility_client.make_request(nhs_number, headers=request_headers, raise_on_error=False) assert response["status_code"] == http.HTTPStatus.NOT_FOUND assert response["body"] == expected_body + assert response["headers"].get("Content-Type".lower()) == "application/fhir+json" @pytest.mark.smoketest @@ -32,33 +51,89 @@ def test_check_for_missing_person(eligibility_client): [ { "scenario": "correct header - NHS number exists but not found in data", - "nhs_number": "1234567890", - "request_headers": {"nhs-login-nhs-number": "1234567890"}, + "nhs_number": "9934567890", + "request_headers": {"nhs-login-nhs-number": "9934567890"}, "expected_status": http.HTTPStatus.NOT_FOUND, "expected_body": { + "resourceType": "OperationOutcome", + "id": "", + "meta": {"lastUpdated": ""}, "issue": [ { - "code": "nhs-number-not-found", - "diagnostics": 'NHS Number "1234567890" not found.', - "severity": "information", + "severity": "error", + "code": "processing", + "details": { + "coding": [ + { + "system": "https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1", + "code": "REFERENCE_NOT_FOUND", + "display": "The given NHS number was not found in our datasets. " + "This could be because the number is incorrect or some other reason we " + "cannot process that number.", + } + ] + }, + "diagnostics": "NHS Number '9934567890' was not recognised by the Eligibility Signposting API", + "location": ["parameters/id"], } ], - "resourceType": "OperationOutcome", }, }, { "scenario": "incorrect header - NHS number mismatch", - "nhs_number": "1234567890", - "request_headers": {"nhs-login-nhs-number": "12345678900"}, + "nhs_number": "9934567890", + "request_headers": {"nhs-login-nhs-number": "99345678900"}, "expected_status": http.HTTPStatus.FORBIDDEN, - "expected_body": "NHS number mismatch", + "expected_body": { + "resourceType": "OperationOutcome", + "id": "", + "meta": {"lastUpdated": ""}, + "issue": [ + { + "severity": "error", + "code": "forbidden", + "details": { + "coding": [ + { + "system": "https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1", + "code": "INVALID_NHS_NUMBER", + "display": "The provided NHS number does not match the record.", + } + ] + }, + "diagnostics": "NHS Number 9934567890 does not match the header NHS Number 99345678900", + "location": ["parameters/id"], + } + ], + }, }, { "scenario": "missing header - NHS number required", "nhs_number": "1234567890", "request_headers": {}, "expected_status": http.HTTPStatus.FORBIDDEN, - "expected_body": "NHS number mismatch", + "expected_body": { + "resourceType": "OperationOutcome", + "id": "", + "meta": {"lastUpdated": ""}, + "issue": [ + { + "severity": "error", + "code": "forbidden", + "details": { + "coding": [ + { + "system": "https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1", + "code": "INVALID_NHS_NUMBER", + "display": "The provided NHS number does not match the record.", + } + ] + }, + "diagnostics": "NHS Number 1234567890 does not match the header NHS Number ", + "location": ["parameters/id"], + } + ], + }, }, ], ids=["correct-header", "incorrect-header", "missing-header"], @@ -72,3 +147,4 @@ def test_nhs_login_header_handling(eligibility_client, test_case): assert response["status_code"] == test_case["expected_status"], f"{test_case['scenario']} failed on status code" assert response["body"] == test_case["expected_body"], f"{test_case['scenario']} failed on response body" + assert response["headers"].get("Content-Type".lower()) == "application/fhir+json" diff --git a/tests/e2e/tests/test_regression_tests.py b/tests/e2e/tests/test_regression_tests.py index 0937df7e7..2a98dd39f 100644 --- a/tests/e2e/tests/test_regression_tests.py +++ b/tests/e2e/tests/test_regression_tests.py @@ -4,7 +4,7 @@ import pytest from tests.e2e.tests import test_config -from tests.e2e.utils.data_loader import initialise_tests, load_all_expected_responses +from tests.e2e.utils.data_helper import initialise_tests, load_all_expected_responses from tests.e2e.utils.s3_config_manager import upload_config_to_s3 # Update the below with the configuration values specified in test_config.py diff --git a/tests/e2e/tests/test_smoke_tests.py b/tests/e2e/tests/test_smoke_tests.py index 46127dc05..920a93b81 100644 --- a/tests/e2e/tests/test_smoke_tests.py +++ b/tests/e2e/tests/test_smoke_tests.py @@ -4,7 +4,7 @@ import pytest from tests.e2e.tests import test_config -from tests.e2e.utils.data_loader import initialise_tests, load_all_expected_responses +from tests.e2e.utils.data_helper import initialise_tests, load_all_expected_responses from tests.e2e.utils.s3_config_manager import upload_config_to_s3 # Update the below with the configuration values specified in test_config.py @@ -25,7 +25,9 @@ def test_run_smoke_case(filename, scenario, eligibility_client): query_params = scenario.get("query_params", {}) upload_config_to_s3((Path(config_path) / config_filename).resolve()) - actual_response = eligibility_client.make_request(nhs_number, headers=request_headers, query_params= query_params, strict_ssl=False) + actual_response = eligibility_client.make_request( + nhs_number, headers=request_headers, query_params=query_params, strict_ssl=False + ) expected_response = all_expected_responses.get(filename).get("response_items", {}) # Assert and show details on failure diff --git a/tests/e2e/tests/test_story_tests.py b/tests/e2e/tests/test_story_tests.py index 13fba6085..205a80a20 100644 --- a/tests/e2e/tests/test_story_tests.py +++ b/tests/e2e/tests/test_story_tests.py @@ -4,7 +4,7 @@ import pytest from tests.e2e.tests import test_config -from tests.e2e.utils.data_loader import initialise_tests, load_all_expected_responses +from tests.e2e.utils.data_helper import initialise_tests, load_all_expected_responses from tests.e2e.utils.s3_config_manager import upload_config_to_s3 # Update the below with the configuration values specified in test_config.py @@ -25,7 +25,9 @@ def test_run_story_test_cases(filename, scenario, eligibility_client): query_params = scenario.get("query_params", {}) upload_config_to_s3((Path(config_path) / config_filename).resolve()) - actual_response = eligibility_client.make_request(nhs_number, headers=request_headers, query_params= query_params, strict_ssl=False) + actual_response = eligibility_client.make_request( + nhs_number, headers=request_headers, query_params=query_params, strict_ssl=False + ) expected_response = all_expected_responses.get(filename).get("response_items", {}) assert actual_response["status_code"] == http.HTTPStatus.OK diff --git a/tests/e2e/utils/data_loader.py b/tests/e2e/utils/data_helper.py similarity index 81% rename from tests/e2e/utils/data_loader.py rename to tests/e2e/utils/data_helper.py index 98b3c4f51..a6bcc217c 100644 --- a/tests/e2e/utils/data_loader.py +++ b/tests/e2e/utils/data_helper.py @@ -5,6 +5,8 @@ from .placeholder_context import PlaceholderDTO, ResolvedPlaceholderContext from .placeholder_utils import resolve_placeholders +keys_to_ignore = ["responseId", "lastUpdated", "id"] + def initialise_tests(folder): folder_path = Path(folder).resolve() @@ -73,7 +75,7 @@ def load_all_expected_responses(folder_path): raw_json = json.load(f) resolved_data = resolve_placeholders_in_data(raw_json, dto, path.name) - cleaned_data = clean_expected_response(resolved_data) + cleaned_data = clean_responses(data=resolved_data, ignore_keys=keys_to_ignore) all_data[path.name] = {"response_items": cleaned_data} @@ -119,18 +121,22 @@ def load_all_test_scenarios(folder_path): return all_data, dto -def clean_expected_response(data: dict) -> dict: - keys_to_ignore = ["responseId", "lastUpdated"] - return _remove_volatile_fields(data, keys_to_ignore) +def clean_responses(data: dict, ignore_keys: list) -> dict: + return _mask_volatile_fields(data, ignore_keys) -def _remove_volatile_fields(data, keys_to_remove): +def _mask_volatile_fields(data, keys_to_mask, placeholder=""): if isinstance(data, dict): - return { - key: _remove_volatile_fields(value, keys_to_remove) - for key, value in data.items() - if key not in keys_to_remove - } + result = {} + for key, value in data.items(): + # Always recurse first + if isinstance(value, (dict, list)): + masked_value = _mask_volatile_fields(value, keys_to_mask, placeholder) + else: + masked_value = value + # Then decide if this key should be masked + result[key] = placeholder if key in keys_to_mask else masked_value + return result if isinstance(data, list): - return [_remove_volatile_fields(item, keys_to_remove) for item in data] + return [_mask_volatile_fields(item, keys_to_mask, placeholder) for item in data] return data diff --git a/tests/e2e/utils/eligibility_api_client.py b/tests/e2e/utils/eligibility_api_client.py index 994ff3784..6d57dbadc 100644 --- a/tests/e2e/utils/eligibility_api_client.py +++ b/tests/e2e/utils/eligibility_api_client.py @@ -9,6 +9,10 @@ from dotenv import load_dotenv from requests import Response +from tests.e2e.utils.data_helper import clean_responses + +ignore_keys = ["lastUpdated", "responseId", "id"] + class EligibilityApiClient: def __init__(self, api_url: str, cert_dir: str = "tests/e2e/certs") -> None: @@ -111,7 +115,7 @@ def make_request( def _parse_response(self, response: Response) -> dict[str, Any]: try: data = response.json() - cleaned = self._clean_response(data) + cleaned = clean_responses(data=data, ignore_keys=ignore_keys) except json.JSONDecodeError: cleaned = response.text @@ -121,18 +125,3 @@ def _parse_response(self, response: Response) -> dict[str, Any]: "body": cleaned, "ok": response.ok, } - - def _clean_response(self, data: Any) -> Any: - keys_to_ignore = ["responseId", "lastUpdated"] - return self._remove_volatile_fields(data, keys_to_ignore) - - def _remove_volatile_fields(self, data: dict[str, Any] | list | Any, keys_to_remove: list) -> Any: - if isinstance(data, dict): - return { - key: self._remove_volatile_fields(value, keys_to_remove) - for key, value in data.items() - if key not in keys_to_remove - } - if isinstance(data, list): - return [self._remove_volatile_fields(item, keys_to_remove) for item in data] - return data From 4262306c56f424f721b4d5a3b8e655a94c3ac25b Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Tue, 15 Jul 2025 21:34:01 +0100 Subject: [PATCH 52/72] additional error scenarios and lots more data changes and refactoring --- .../smokeTestConfigs/AUTO_RSV_SB_001.json | 540 +++++++++--------- .../AUTO_RSV_ELI-320-COVID.json | 65 +++ .../AUTO_RSV_ELI-320-MMR.json | 66 +++ .../AUTO_RSV_ELI-320-RSV.json | 67 +++ .../AUTO_RSV_ELI-320-10.json | 74 +++ .../AUTO_RSV_ELI-320-11.json | 74 +++ .../AUTO_RSV_ELI-320-6.json | 73 +++ .../AUTO_RSV_ELI-320-7.json | 73 +++ .../AUTO_RSV_ELI-320-8.json | 73 +++ .../regressionTestData/AUTO_RSV_REG_001.json | 2 +- .../regressionTestData/AUTO_RSV_REG_002.json | 2 +- .../regressionTestData/AUTO_RSV_REG_003.json | 2 +- .../regressionTestData/AUTO_RSV_REG_010.json | 2 +- .../regressionTestData/AUTO_RSV_REG_011.json | 2 +- .../regressionTestData/AUTO_RSV_REG_012.json | 2 +- .../regressionTestData/AUTO_RSV_REG_013.json | 2 +- .../smokeTestData/AUTO_RSV_SB_001.json | 4 +- .../smokeTestData/AUTO_RSV_SB_002.json | 4 +- .../smokeTestData/AUTO_RSV_SB_003.json | 4 +- .../smokeTestData/AUTO_RSV_SB_004.json | 4 +- .../smokeTestData/AUTO_RSV_SB_005.json | 4 +- .../smokeTestData/AUTO_RSV_SB_006.json | 4 +- .../smokeTestData/AUTO_RSV_SB_007.json | 4 +- .../smokeTestData/AUTO_RSV_SB_008.json | 4 +- .../smokeTestData/AUTO_RSV_SB_009.json | 4 +- .../smokeTestData/AUTO_RSV_SB_010.json | 4 +- .../smokeTestData/AUTO_RSV_SB_011.json | 4 +- .../smokeTestData/AUTO_RSV_SB_012.json | 4 +- .../smokeTestData/AUTO_RSV_SB_013.json | 4 +- .../smokeTestData/AUTO_RSV_SB_014.json | 4 +- .../storyTestData/AUTO_RSV_ELI-155.json | 8 +- .../storyTestData/AUTO_RSV_ELI-216-1.json | 8 +- .../storyTestData/AUTO_RSV_ELI-219-1.json | 8 +- .../storyTestData/AUTO_RSV_ELI-219-2.json | 8 +- .../storyTestData/AUTO_RSV_ELI-219-3.json | 8 +- .../storyTestData/AUTO_RSV_ELI-317-1.json | 12 +- .../storyTestData/AUTO_RSV_ELI-317-2.json | 12 +- .../storyTestData/AUTO_RSV_ELI-317-3.json | 12 +- .../storyTestData/AUTO_RSV_ELI-317-4.json | 12 +- .../storyTestData/AUTO_RSV_ELI-317-5.json | 12 +- .../storyTestData/AUTO_RSV_ELI-320-1.json | 63 ++ .../storyTestData/AUTO_RSV_ELI-320-2.json | 66 +++ .../storyTestData/AUTO_RSV_ELI-320-3.json | 66 +++ .../storyTestData/AUTO_RSV_ELI-320-4.json | 66 +++ .../storyTestData/AUTO_RSV_ELI-320-5.json | 66 +++ .../AUTO_RSV_ELI-320-10.json | 28 + .../AUTO_RSV_ELI-320-11.json | 7 + .../AUTO_RSV_ELI-320-6.json | 78 +++ .../AUTO_RSV_ELI-320-8.json | 44 ++ .../AUTO_RSV_ELI-320-9.json | 42 ++ .../AUTO_RSV_REG_001.json | 2 +- .../AUTO_RSV_REG_002.json | 2 +- .../AUTO_RSV_REG_003.json | 2 +- .../AUTO_RSV_REG_010.json | 2 +- .../AUTO_RSV_REG_011.json | 2 +- .../AUTO_RSV_REG_012.json | 2 +- .../AUTO_RSV_REG_013.json | 2 +- .../smokeTestResponses/AUTO_RSV_SB_001.json | 2 +- .../smokeTestResponses/AUTO_RSV_SB_002.json | 2 +- .../smokeTestResponses/AUTO_RSV_SB_003.json | 2 +- .../smokeTestResponses/AUTO_RSV_SB_004.json | 2 +- .../smokeTestResponses/AUTO_RSV_SB_005.json | 2 +- .../smokeTestResponses/AUTO_RSV_SB_006.json | 2 +- .../smokeTestResponses/AUTO_RSV_SB_007.json | 2 +- .../smokeTestResponses/AUTO_RSV_SB_009.json | 2 +- .../smokeTestResponses/AUTO_RSV_SB_010.json | 2 +- .../smokeTestResponses/AUTO_RSV_SB_011.json | 2 +- .../smokeTestResponses/AUTO_RSV_SB_012.json | 2 +- .../smokeTestResponses/AUTO_RSV_SB_013.json | 2 +- .../smokeTestResponses/AUTO_RSV_SB_014.json | 2 +- .../storyTestResponses/AUTO_RSV_ELI-155.json | 2 +- .../AUTO_RSV_ELI-216-1.json | 2 +- .../AUTO_RSV_ELI-219-1.json | 2 +- .../AUTO_RSV_ELI-219-2.json | 2 +- .../AUTO_RSV_ELI-219-3.json | 2 +- .../AUTO_RSV_ELI-317-1.json | 2 +- .../AUTO_RSV_ELI-317-2.json | 2 +- .../AUTO_RSV_ELI-317-3.json | 2 +- .../AUTO_RSV_ELI-317-4.json | 2 +- .../AUTO_RSV_ELI-317-5.json | 2 +- .../AUTO_RSV_ELI-320-1.json | 64 +++ .../AUTO_RSV_ELI-320-2.json | 64 +++ .../AUTO_RSV_ELI-320-3.json | 28 + .../AUTO_RSV_ELI-320-4.json | 22 + .../AUTO_RSV_ELI-320-5.json | 44 ++ tests/e2e/tests/conftest.py | 18 + .../test_eligibility_check_bdd.py | 14 - tests/e2e/tests/test_error_scenario_tests.py | 118 +++- tests/e2e/tests/test_regression_tests.py | 9 +- tests/e2e/tests/test_smoke_tests.py | 10 +- tests/e2e/tests/test_story_tests.py | 12 +- tests/e2e/utils/data_helper.py | 4 +- tests/e2e/utils/s3_config_manager.py | 51 +- 93 files changed, 1927 insertions(+), 392 deletions(-) create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-COVID.json create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-MMR.json create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-RSV.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-320-10.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-320-11.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-320-6.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-320-7.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-320-8.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-1.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-2.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-3.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-4.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-5.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-320-10.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-320-11.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-320-6.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-320-8.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-320-9.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-1.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-2.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-3.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-4.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-5.json delete mode 100644 tests/e2e/tests/eligibility_signposting/test_eligibility_check_bdd.py diff --git a/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_001.json b/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_001.json index f313f5691..9e7a48d01 100644 --- a/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_001.json +++ b/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_001.json @@ -1,275 +1,275 @@ -{ - "CampaignConfig": { - "ID": "<>", - "Version": "1", - "Name": "Automation RSV - Smoke Test Config", - "Type": "V", - "Target": "RSV", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", - "IterationFrequency": "X", - "IterationType": "O", - "IterationTime": "07:00:00", - "DefaultCommsRouting": "BOOK_LOCAL", - "StartDate": "20250601", - "EndDate": "20260601", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Iterations": [ - { - "ID": ",<>", - "DefaultCommsRouting": "BOOK_LOCAL", - "Version": "1", - "Name": "Automation RSV - Smoke Test Config Iteration", - "IterationDate": "20250601", - "IterationNumber": 1, - "CommsType": "I", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Type": "O", - "IterationCohorts": [ - { - "CohortLabel": "rsv_75_rolling", - "CohortGroup": "rsv_age_rolling", - "PositiveDescription": "are aged 75 to 79 years old.", - "NegativeDescription": "are not aged 75 to 79 years old.", - "Priority": 0 - }, - { - "CohortLabel": "rsv_75to79_2024", - "CohortGroup": "rsv_age_catchup", - "PositiveDescription": "turned 80 between 2nd September 2024 and 31st August 2025", - "NegativeDescription": "did not turn 80 between 2nd September 2024 and 31st August 2025", - "Priority": 10 - }, - { - "CohortLabel": "elid_all_people", - "CohortGroup": "elid_all_people", - "PositiveDescription": "", - "NegativeDescription": "", - "Priority": 20 - }, - { - "CohortLabel": "no_group_description", - "CohortGroup": "", - "PositiveDescription": "", - "NegativeDescription": "", - "Priority": 30 - } - ], - "IterationRules": [ - { - "Type": "F", - "Name": "Assure only already vaccinated taken from magic cohort", - "Description": "Exclude anyone who has NOT been given a dose of RSV Vaccination from the magic cohort", - "Operator": "is_empty", - "Comparator": "", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "LAST_SUCCESSFUL_DATE", - "CohortLabel": "elid_all_people", - "Priority": 100 - }, - { - "Type": "F", - "Name": "Under Age - Under 75 Years on day of execution", - "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", - "Priority": 120, - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Operator": "Y>", - "Comparator": "-75", - "CohortLabel": "rsv_75to79_2024" - }, - { - "Type": "F", - "Name": "Under Age - Under 75 Years on day of execution", - "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", - "Priority": 125, - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Operator": "Y>", - "Comparator": "-75", - "CohortLabel": "rsv_75_rolling" - }, - { - "Type": "F", - "Name": "Exclude Too OLD", - "Description": "Exclude anyone over 80", - "Priority": 130, - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Operator": "<", - "Comparator": "-80" - }, - { - "Type": "S", - "Name": "AlreadyVaccinated", - "Description": "##You've had your RSV vaccination\nWe believe you had your vaccination on <>.", - "Priority": 550, - "AttributeLevel": "TARGET", - "AttributeTarget": "RSV", - "AttributeName": "LAST_SUCCESSFUL_DATE", - "Operator": "Y>=", - "Comparator": "-25", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "NotAvailable", - "Description": "NotAvailable|Vaccinations are not currently available.", - "Priority": 510, - "AttributeLevel": "PERSON", - "AttributeName": "ICB", - "Operator": "=", - "Comparator": "SUPPRESSED_ICB" - }, - { - "Type": "S", - "Name": "NotYetDue", - "Description": "NotYetDue|Your next dose is not yet due.", - "Priority": 520, - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "LAST_SUCCESSFUL_DATE", - "Operator": "=", - "Comparator": "20250326", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "TooClose", - "Description": "TooClose|Your previous vaccination was less than 91 days ago.", - "Priority": 530, - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "LAST_SUCCESSFUL_DATE", - "Operator": "=", - "Comparator": "20250327", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "OtherSetting", - "Description": "OtherSetting|## Getting the vaccine\n\nOur record show you're living in a setting where care is provided.\n\nIf you think you should have the RSV vaccine, speak to a member of staff where you live.", - "Priority": 540, - "AttributeLevel": "PERSON", - "AttributeName": "CARE_HOME_FLAG", - "Operator": "=", - "Comparator": "Y" - }, - { - "Type": "R", - "Name": "Actionable Not Vaccinated", - "Description": "Book An Appointment", - "Priority": 1010, - "Operator": "is_empty", - "Comparator": "", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "LAST_SUCCESSFUL_DATE", - "CommsRouting": "BOOK_NBS" - }, - { - "Type": "R", - "Name": "Actionable Not Vaccinated", - "Description": "Book An Appointment", - "Priority": 1010, - "Operator": "=", - "Comparator": "LS2", - "AttributeLevel": "PERSON", - "AttributeName": "POSTCODE_SECTOR", - "CommsRouting": "BOOK_NBS" - }, - { - "Type": "R", - "Name": "Actionable Future Booked Appointment", - "Description": "Actionable Future Booked Appointment", - "Priority": 1020, - "Operator": ">=", - "Comparator": "0", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_DATE", - "CommsRouting": "AMEND_NBS" - }, - { - "Type": "R", - "Name": "Actionable Future Booked Appointment", - "Description": "Actionable Future Booked Appointment", - "Priority": 1020, - "Operator": "=", - "Comparator": "NBS", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", - "CommsRouting": "AMEND_NBS" - }, - { - "Type": "R", - "Name": "Actionable Future Booked Appointment", - "Description": "Actionable Future Booked Appointment", - "Priority": 1030, - "Operator": ">=", - "Comparator": "0", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_DATE", - "CommsRouting": "MANAGE_LOCAL" - }, - { - "Type": "R", - "Name": "Actionable Future Booked Appointment", - "Description": "Actionable Future Booked Appointment", - "Priority": 1030, - "Operator": "!=", - "Comparator": "NBS", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", - "CommsRouting": "MANAGE_LOCAL" - } - ], - "ActionsMapper": { - "BOOK_NBS": { - "ExternalRoutingCode": "BookNBS", - "ActionDescription": "", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Continue to booking" - }, - "AMEND_NBS": { - "ExternalRoutingCode": "AmendNBS", - "ActionDescription": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Manage your appointment" - }, - "CONTACT_GP": { - "ExternalRoutingCode": "ContactGP", - "ActionDescription": "Contact your GP", - "ActionType": "InfoText" - }, - "BOOK_LOCAL": { - "ExternalRoutingCode": "BookLocal", - "ActionDescription": "##Getting the vaccine\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", - "ActionType": "InfoText" - }, - "MANAGE_LOCAL": { - "ExternalRoutingCode": "ManageLocal", - "ActionDescription": "##You have an RSV vaccination appointment\nContact your healthcare provider to change or cancel your appointment.", - "ActionType": "CardWithText" + { + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "Automation RSV - Smoke Test Config", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "BOOK_LOCAL", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "BOOK_LOCAL", + "Version": "1", + "Name": "Automation RSV - Smoke Test Config Iteration", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_75_rolling", + "CohortGroup": "rsv_age_rolling", + "PositiveDescription": "are aged 75 to 79 years old.", + "NegativeDescription": "are not aged 75 to 79 years old.", + "Priority": 0 + }, + { + "CohortLabel": "rsv_75to79_2024", + "CohortGroup": "rsv_age_catchup", + "PositiveDescription": "turned 80 between 2nd September 2024 and 31st August 2025", + "NegativeDescription": "did not turn 80 between 2nd September 2024 and 31st August 2025", + "Priority": 10 + }, + { + "CohortLabel": "elid_all_people", + "CohortGroup": "elid_all_people", + "PositiveDescription": "", + "NegativeDescription": "", + "Priority": 20 + }, + { + "CohortLabel": "no_group_description", + "CohortGroup": "", + "PositiveDescription": "", + "NegativeDescription": "", + "Priority": 30 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "Assure only already vaccinated taken from magic cohort", + "Description": "Exclude anyone who has NOT been given a dose of RSV Vaccination from the magic cohort", + "Operator": "is_empty", + "Comparator": "", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "elid_all_people", + "Priority": 100 + }, + { + "Type": "F", + "Name": "Under Age - Under 75 Years on day of execution", + "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", + "Priority": 120, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75", + "CohortLabel": "rsv_75to79_2024" + }, + { + "Type": "F", + "Name": "Under Age - Under 75 Years on day of execution", + "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", + "Priority": 125, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75", + "CohortLabel": "rsv_75_rolling" + }, + { + "Type": "F", + "Name": "Exclude Too OLD", + "Description": "Exclude anyone over 80", + "Priority": 130, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "<", + "Comparator": "-80" + }, + { + "Type": "S", + "Name": "AlreadyVaccinated", + "Description": "##You've had your RSV vaccination\nWe believe you had your vaccination on <>.", + "Priority": 550, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "Y>=", + "Comparator": "-25", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "NotAvailable", + "Description": "NotAvailable|Vaccinations are not currently available.", + "Priority": 510, + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "Operator": "=", + "Comparator": "SUPPRESSED_ICB" + }, + { + "Type": "S", + "Name": "NotYetDue", + "Description": "NotYetDue|Your next dose is not yet due.", + "Priority": 520, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "=", + "Comparator": "20250326", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "TooClose", + "Description": "TooClose|Your previous vaccination was less than 91 days ago.", + "Priority": 530, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "=", + "Comparator": "20250327", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "OtherSetting", + "Description": "OtherSetting|## Getting the vaccine\n\nOur record show you're living in a setting where care is provided.\n\nIf you think you should have the RSV vaccine, speak to a member of staff where you live.", + "Priority": 540, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y" + }, + { + "Type": "R", + "Name": "Actionable Not Vaccinated", + "Description": "Book An Appointment", + "Priority": 1010, + "Operator": "is_empty", + "Comparator": "", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CommsRouting": "BOOK_NBS" + }, + { + "Type": "R", + "Name": "Actionable Not Vaccinated", + "Description": "Book An Appointment", + "Priority": 1010, + "Operator": "=", + "Comparator": "LS2", + "AttributeLevel": "PERSON", + "AttributeName": "POSTCODE_SECTOR", + "CommsRouting": "BOOK_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1020, + "Operator": ">=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1020, + "Operator": "=", + "Comparator": "NBS", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1030, + "Operator": ">=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "MANAGE_LOCAL" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1030, + "Operator": "!=", + "Comparator": "NBS", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", + "CommsRouting": "MANAGE_LOCAL" + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + }, + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Manage your appointment" + }, + "CONTACT_GP": { + "ExternalRoutingCode": "ContactGP", + "ActionDescription": "Contact your GP", + "ActionType": "InfoText" + }, + "BOOK_LOCAL": { + "ExternalRoutingCode": "BookLocal", + "ActionDescription": "##Getting the vaccine\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "ActionType": "InfoText" + }, + "MANAGE_LOCAL": { + "ExternalRoutingCode": "ManageLocal", + "ActionDescription": "##You have an RSV vaccination appointment\nContact your healthcare provider to change or cancel your appointment.", + "ActionType": "CardWithText" - }, - "CHECK_CORRECT": { - "ExternalRoutingCode": "CheckCorrect", - "ActionDescription": "##If you think this is incorrect\\nIf you have not had this vaccination and you think you should, speak to your healthcare professional.", - "ActionType": "InfoText" + }, + "CHECK_CORRECT": { + "ExternalRoutingCode": "CheckCorrect", + "ActionDescription": "##If you think this is incorrect\\nIf you have not had this vaccination and you think you should, speak to your healthcare professional.", + "ActionType": "InfoText" + } } } - } - ] + ] + } } -} diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-COVID.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-COVID.json new file mode 100644 index 000000000..e284c5077 --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-COVID.json @@ -0,0 +1,65 @@ +{ + "CampaignConfig": { + "ID": "CC1234", + "Version": "1", + "Name": "ELI-320 - COVID Config", + "Type": "V", + "Target": "COVID", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "CONTACT_GP", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": "IC1234", + "DefaultCommsRouting": "CONTACT_GP", + "Version": "1", + "Name": "ELI-320 - COVID Iteration Config", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "covid_cohort", + "CohortGroup": "covid_cohort_group", + "PositiveDescription": "You are currently in a covid cohort", + "NegativeDescription": "You are not currently in a covid cohort", + "Priority": 0 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "AlreadyVaccinated", + "Description": "##You've had your COVID vaccination\nWe believe you already had your COVID vaccination.", + "Priority": 1000, + "AttributeLevel": "TARGET", + "AttributeTarget": "COVID", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "D<=", + "Comparator": "0" + } + ], + "ActionsMapper": { + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "##You have an COVID vaccination appointment\nYou can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-covid", + "UrlLabel": "Manage your appointment" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-MMR.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-MMR.json new file mode 100644 index 000000000..659b1f3e1 --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-MMR.json @@ -0,0 +1,66 @@ +{ + "CampaignConfig": { + "ID": "CC8901", + "Version": "1", + "Name": "ELI-320 - MMR Config", + "Type": "V", + "Target": "MMR", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "CONTACT_GP", + "StartDate": "20250612", + "EndDate": "20260713", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": "IC8901", + "DefaultCommsRouting": "CONTACT_GP", + "Version": "1", + "Name": "ELI-320 - MMR Iteration Config", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "mmr_cohort", + "CohortGroup": "mmr_cohort_group", + "PositiveDescription": "You are currently in a mmr cohort", + "NegativeDescription": "You are not currently in an mmr cohort", + "Priority": 0 + } + ], + "IterationRules": [ + { + "Type": "R", + "Name": "Actionable if future appointment is booked", + "Description": "Book An Appointment", + "Priority": 1000, + "AttributeLevel": "TARGET", + "AttributeTarget": "MMR", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D>=", + "Comparator": "0", + "CommsRouting": "AMEND_NBS" + } + ], + "ActionsMapper": { + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "##You have an MMR vaccination appointment\nYou can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/amend-mmr", + "UrlLabel": "Manage your appointment" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-RSV.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-RSV.json new file mode 100644 index 000000000..4995bcd12 --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-RSV.json @@ -0,0 +1,67 @@ +{ + "CampaignConfig": { + "ID": "CC5678", + "Version": "1", + "Name": "ELI-320 - RSV Config", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "AmendNBS", + "StartDate": "20250612", + "EndDate": "20260713", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": "IC5678", + "DefaultCommsRouting": "AmendNBS", + "Version": "1", + "Name": "ELI-320 - RSV Iteration Config", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_cohort", + "CohortGroup": "rsv_cohort_group", + "PositiveDescription": "You are currently in an RSV cohort", + "NegativeDescription": "You are not currently in an RSV cohort", + "Priority": 0 + } + ], + "IterationRules": [ + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D>=", + "Comparator": "0", + "CommsRouting": "AMEND_NBS" + + } + ], + "ActionsMapper": { + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Manage your appointment" + } + } + } + ] + } +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-320-10.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-320-10.json new file mode 100644 index 000000000..12a5face0 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-320-10.json @@ -0,0 +1,74 @@ +{ + "scenario_name": "ELI-320 - Multiple Category Campaigns - Category=VACCINATIONS Condition=covid", + "request_headers": { + "nhs-login-nhs-number": "9900032010" + }, + "query_params": { + "category": "VACCINATIONS", + "conditions": "covid" + }, + "config_filenames": ["AUTO_RSV_ELI-320-COVID.json","AUTO_RSV_ELI-320-MMR.json", "AUTO_RSV_ELI-320-SCREENING-1.json", "AUTO_RSV_ELI-320-SCREENING-2.json"], + "data": [ + { + "NHS_NUMBER": "9900032010", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + }, + "covid_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + }, + "FLU_screening_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900032010", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900032010", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + }, + { + "NHS_NUMBER": "9900032010", + "ATTRIBUTE_TYPE": "COVID", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-320-11.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-320-11.json new file mode 100644 index 000000000..159ba5cc3 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-320-11.json @@ -0,0 +1,74 @@ +{ + "scenario_name": "ELI-320 - Multiple Category Campaigns - Condition not active", + "request_headers": { + "nhs-login-nhs-number": "9900032011" + }, + "query_params": { + "category": "VACCINATIONS", + "conditions": "HPV" + }, + "config_filenames": ["AUTO_RSV_ELI-320-COVID.json","AUTO_RSV_ELI-320-MMR.json", "AUTO_RSV_ELI-320-SCREENING-1.json", "AUTO_RSV_ELI-320-SCREENING-2.json"], + "data": [ + { + "NHS_NUMBER": "9900032011", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + }, + "covid_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + }, + "FLU_screening_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900032011", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900032011", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + }, + { + "NHS_NUMBER": "9900032011", + "ATTRIBUTE_TYPE": "COVID", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-320-6.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-320-6.json new file mode 100644 index 000000000..4bde84657 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-320-6.json @@ -0,0 +1,73 @@ +{ + "scenario_name": "ELI-320 - Multiple Category Campaigns - Category missing", + "request_headers": { + "nhs-login-nhs-number": "9990032060" + }, + "query_params": { + "category": null + }, + "config_filenames": ["AUTO_RSV_ELI-320-COVID.json","AUTO_RSV_ELI-320-MMR.json", "AUTO_RSV_ELI-320-SCREENING-1.json", "AUTO_RSV_ELI-320-SCREENING-2.json"], + "data": [ + { + "NHS_NUMBER": "9990032060", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + }, + "covid_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + }, + "FLU_screening_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9990032060", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9990032060", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + }, + { + "NHS_NUMBER": "9990032060", + "ATTRIBUTE_TYPE": "COVID", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-320-7.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-320-7.json new file mode 100644 index 000000000..705196839 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-320-7.json @@ -0,0 +1,73 @@ +{ + "scenario_name": "ELI-320 - Multiple Category Campaigns - Category=ALL", + "request_headers": { + "nhs-login-nhs-number": "9990032070" + }, + "query_params": { + "category": "all" + }, + "config_filenames": ["AUTO_RSV_ELI-320-COVID.json","AUTO_RSV_ELI-320-MMR.json", "AUTO_RSV_ELI-320-SCREENING-1.json", "AUTO_RSV_ELI-320-SCREENING-2.json"], + "data": [ + { + "NHS_NUMBER": "9990032070", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + }, + "covid_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + }, + "FLU_screening_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9990032070", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9990032070", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + }, + { + "NHS_NUMBER": "9990032070", + "ATTRIBUTE_TYPE": "COVID", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-320-8.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-320-8.json new file mode 100644 index 000000000..79bf028b9 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-320-8.json @@ -0,0 +1,73 @@ +{ + "scenario_name": "ELI-320 - Multiple Category Campaigns - Category=screening", + "request_headers": { + "nhs-login-nhs-number": "9990032080" + }, + "query_params": { + "category": "screening" + }, + "config_filenames": ["AUTO_RSV_ELI-320-COVID.json","AUTO_RSV_ELI-320-MMR.json", "AUTO_RSV_ELI-320-SCREENING-1.json", "AUTO_RSV_ELI-320-SCREENING-2.json"], + "data": [ + { + "NHS_NUMBER": "9990032080", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + }, + "covid_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + }, + "FLU_screening_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9990032080", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9990032080", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + }, + { + "NHS_NUMBER": "9990032080", + "ATTRIBUTE_TYPE": "COVID", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_001.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_001.json index b1f7a93e4..7c43889f0 100644 --- a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_001.json +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_001.json @@ -1,7 +1,7 @@ { "scenario_name": "RSV - Regression - Actionable, Single Eligible Cohort", "request_headers": {"nhs-login-nhs-number": "1000000001"}, - "config_filename": "AUTO_RSV_REG_001.json", + "config_filenames": ["AUTO_RSV_REG_001.json"], "data": [ { "NHS_NUMBER": "1000000001", diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_002.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_002.json index 2d03e4cc1..97292ccaa 100644 --- a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_002.json +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_002.json @@ -1,7 +1,7 @@ { "scenario_name": "RSV - Regression - Actionable, Two Eligible Cohorts, Different Groups", "request_headers": {"nhs-login-nhs-number": "1000000002"}, - "config_filename": "AUTO_RSV_REG_001.json", + "config_filenames": ["AUTO_RSV_REG_001.json"], "data": [ { "NHS_NUMBER": "1000000002", diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_003.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_003.json index fa9b12fac..307422476 100644 --- a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_003.json +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_003.json @@ -1,7 +1,7 @@ { "scenario_name": "RSV - Regression - Actionable, Two Eligible Cohorts, Same Group", "request_headers": {"nhs-login-nhs-number": "1000000003"}, - "config_filename": "AUTO_RSV_REG_001.json", + "config_filenames": ["AUTO_RSV_REG_001.json"], "data": [ { "NHS_NUMBER": "1000000003", diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_010.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_010.json index 265428f30..676db5821 100644 --- a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_010.json +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_010.json @@ -1,7 +1,7 @@ { "scenario_name": "RSV - Regression - Not Eligible, Not in Cohort", "request_headers": {"nhs-login-nhs-number": "1000000010"}, - "config_filename": "AUTO_RSV_REG_001.json", + "config_filenames": ["AUTO_RSV_REG_001.json"], "data": [ { "NHS_NUMBER": "1000000010", diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_011.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_011.json index 7a7699f97..9de307d9c 100644 --- a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_011.json +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_011.json @@ -1,7 +1,7 @@ { "scenario_name": "RSV - Regression - Not Eligible, In Cohort, Filter Rule", "request_headers": {"nhs-login-nhs-number": "1000000011"}, - "config_filename": "AUTO_RSV_REG_001.json", + "config_filenames": ["AUTO_RSV_REG_001.json"], "data": [ { "NHS_NUMBER": "1000000011", diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_012.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_012.json index ce717d981..90ad0dc3f 100644 --- a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_012.json +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_012.json @@ -1,7 +1,7 @@ { "scenario_name": "RSV - Regression - Not Eligible, In Magic Cohort, Filtered out Rule", "request_headers": {"nhs-login-nhs-number": "1000000012"}, - "config_filename": "AUTO_RSV_REG_001.json", + "config_filenames": ["AUTO_RSV_REG_001.json"], "data": [ { "NHS_NUMBER": "1000000012", diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_013.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_013.json index d949d5f99..18442da51 100644 --- a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_013.json +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_013.json @@ -1,7 +1,7 @@ { "scenario_name": "RSV - Regression - Eligible, In Magic Cohort", "request_headers": {"nhs-login-nhs-number": "1000000013"}, - "config_filename": "AUTO_RSV_REG_001.json", + "config_filenames": ["AUTO_RSV_REG_001.json"], "data": [ { "NHS_NUMBER": "1000000013", diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json index e8cba4cf0..91780aeb4 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "5000000001" }, - "config_filename": "AUTO_RSV_SB_001.json", + "config_filenames": [ + "AUTO_RSV_SB_001.json" + ], "data": [ { "NHS_NUMBER": "5000000001", diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json index f6bda7144..b38fc046b 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "5000000002" }, - "config_filename": "AUTO_RSV_SB_001.json", + "config_filenames": [ + "AUTO_RSV_SB_001.json" + ], "data": [ { "NHS_NUMBER": "5000000002", diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json index fca3a419b..4df520b9e 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "5000000003" }, - "config_filename": "AUTO_RSV_SB_001.json", + "config_filenames": [ + "AUTO_RSV_SB_001.json" + ], "data": [ { "NHS_NUMBER": "5000000003", diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_004.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_004.json index 5339dfac6..11ed624db 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_004.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_004.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "5000000004" }, - "config_filename": "AUTO_RSV_SB_001.json", + "config_filenames": [ + "AUTO_RSV_SB_001.json" + ], "data": [ { "NHS_NUMBER": "5000000004", diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_005.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_005.json index d4f4265ef..cc2fbc347 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_005.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_005.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "5000000005" }, - "config_filename": "AUTO_RSV_SB_001.json", + "config_filenames": [ + "AUTO_RSV_SB_001.json" + ], "data": [ { "NHS_NUMBER": "5000000005", diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_006.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_006.json index ba55e3091..11300ffa9 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_006.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_006.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "5000000006" }, - "config_filename": "AUTO_RSV_SB_001.json", + "config_filenames": [ + "AUTO_RSV_SB_001.json" + ], "notes": "actions need updating in the response when the functionality is delivered to provide actions for not_actionable responses", "data": [ { diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_007.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_007.json index ac8cb72bc..3d348c282 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_007.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_007.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "5000000007" }, - "config_filename": "AUTO_RSV_SB_001.json", + "config_filenames": [ + "AUTO_RSV_SB_001.json" + ], "data": [ { "NHS_NUMBER": "5000000007", diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_008.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_008.json index 8aa367705..351533a4d 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_008.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_008.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "5000000008" }, - "config_filename": "AUTO_RSV_SB_008.json", + "config_filenames": [ + "AUTO_RSV_SB_008.json" + ], "data": [ { "NHS_NUMBER": "5000000008", diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_009.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_009.json index aa661f5a1..6c602f330 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_009.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_009.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "5000000009" }, - "config_filename": "AUTO_RSV_SB_001.json", + "config_filenames": [ + "AUTO_RSV_SB_001.json" + ], "data": [ { "NHS_NUMBER": "5000000009", diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json index 0bd87ebce..299a00d66 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "5000000010" }, - "config_filename": "AUTO_RSV_SB_001.json", + "config_filenames": [ + "AUTO_RSV_SB_001.json" + ], "data": [ { "NHS_NUMBER": "5000000010", diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_011.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_011.json index 46f43270a..bfe3e02b5 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_011.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_011.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "5000000011" }, - "config_filename": "AUTO_RSV_SB_001.json", + "config_filenames": [ + "AUTO_RSV_SB_001.json" + ], "data": [ { "NHS_NUMBER": "5000000011", diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_012.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_012.json index 1c2d8a4a6..10802907e 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_012.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_012.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "5000000012" }, - "config_filename": "AUTO_RSV_SB_001.json", + "config_filenames": [ + "AUTO_RSV_SB_001.json" + ], "data": [ { "NHS_NUMBER": "5000000012", diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_013.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_013.json index 4f48a06bb..bb7c22fdb 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_013.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_013.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "5000000013" }, - "config_filename": "AUTO_RSV_SB_001.json", + "config_filenames": [ + "AUTO_RSV_SB_001.json" + ], "data": [ { "NHS_NUMBER": "5000000013", diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_014.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_014.json index f6945cb28..1c26c5414 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_014.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_014.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "5000000014" }, - "config_filename": "AUTO_RSV_SB_001.json", + "config_filenames": [ + "AUTO_RSV_SB_001.json" + ], "data": [ { "NHS_NUMBER": "5000000014", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json index 998ef48a2..eb10bcb57 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json @@ -1,7 +1,11 @@ { "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested actions (with booking)", - "config_filename": "AUTO_RSV_ELI-155.json", - "request_headers": {"nhs-login-nhs-number": "9000000001"}, + "config_filenames": [ + "AUTO_RSV_ELI-155.json" + ], + "request_headers": { + "nhs-login-nhs-number": "9000000001" + }, "data": [ { "NHS_NUMBER": "9000000001", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-1.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-1.json index c22fd88cf..dc0b7f0d9 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-1.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-1.json @@ -1,7 +1,11 @@ { "scenario_name": "ELI-216 - NHS Number check (NHS login) - Correct NHS_number", - "config_filename": "AUTO_RSV_ELI-216.json", - "request_headers": {"nhs-login-nhs-number": "9000000002"}, + "config_filenames": [ + "AUTO_RSV_ELI-216.json" + ], + "request_headers": { + "nhs-login-nhs-number": "9000000002" + }, "data": [ { "NHS_NUMBER": "9000000002", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json index 0f19367dd..1bae1fccf 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json @@ -1,7 +1,11 @@ { "scenario_name": "ELI-219 - 1 - Fails and Rules and Returns Default", - "config_filename": "AUTO_RSV_ELI-219-1.json", - "request_headers": {"nhs-login-nhs-number": "9000000003"}, + "config_filenames": [ + "AUTO_RSV_ELI-219-1.json" + ], + "request_headers": { + "nhs-login-nhs-number": "9000000003" + }, "data": [ { "NHS_NUMBER": "9000000003", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-2.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-2.json index a107cad92..64994bddd 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-2.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-2.json @@ -1,7 +1,11 @@ { "scenario_name": "ELI-219 - Returns the highest Priority Action", - "config_filename": "AUTO_RSV_ELI-219-2.json", - "request_headers": {"nhs-login-nhs-number": "9000000004"}, + "config_filenames": [ + "AUTO_RSV_ELI-219-2.json" + ], + "request_headers": { + "nhs-login-nhs-number": "9000000004" + }, "data": [ { "NHS_NUMBER": "9000000004", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-3.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-3.json index 7b164516a..ca47b2124 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-3.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-3.json @@ -1,7 +1,11 @@ { "scenario_name": "ELI-219 - Returns the highest Priority Action", - "config_filename": "AUTO_RSV_ELI-219-3.json", - "request_headers": {"nhs-login-nhs-number": "9000000005"}, + "config_filenames": [ + "AUTO_RSV_ELI-219-3.json" + ], + "request_headers": { + "nhs-login-nhs-number": "9000000005" + }, "data": [ { "NHS_NUMBER": "9000000005", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-1.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-1.json index e87a4697d..6655ff443 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-1.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-1.json @@ -1,8 +1,14 @@ { "scenario_name": "ELI-317 - Testing the new requirement from Vita around empty actions ", - "config_filename": "AUTO_RSV_ELI-317-1.json", - "request_headers": {"nhs-login-nhs-number": "9000000006"}, - "query_params": {"includeActions": "Y"}, + "config_filenames": [ + "AUTO_RSV_ELI-317-1.json" + ], + "request_headers": { + "nhs-login-nhs-number": "9000000006" + }, + "query_params": { + "includeActions": "Y" + }, "data": [ { "NHS_NUMBER": "9000000006", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-2.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-2.json index 6713f19fa..219afa8d8 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-2.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-2.json @@ -1,8 +1,14 @@ { "scenario_name": "ELI-317 - Testing the new requirement from Vita around empty actions ", - "config_filename": "AUTO_RSV_ELI-317-2.json", - "request_headers": {"nhs-login-nhs-number": "9000000007"}, - "query_params": {"includeActions": "Y"}, + "config_filenames": [ + "AUTO_RSV_ELI-317-2.json" + ], + "request_headers": { + "nhs-login-nhs-number": "9000000007" + }, + "query_params": { + "includeActions": "Y" + }, "data": [ { "NHS_NUMBER": "9000000007", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-3.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-3.json index 4649a3f3c..fe0b15dcb 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-3.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-3.json @@ -1,8 +1,14 @@ { "scenario_name": "ELI-317 - includeActions=N ", - "config_filename": "AUTO_RSV_ELI-317-3.json", - "request_headers": {"nhs-login-nhs-number": "9000000008"}, - "query_params": {"includeActions": "N"}, + "config_filenames": [ + "AUTO_RSV_ELI-317-3.json" + ], + "request_headers": { + "nhs-login-nhs-number": "9000000008" + }, + "query_params": { + "includeActions": "N" + }, "data": [ { "NHS_NUMBER": "9000000008", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-4.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-4.json index a0f385278..7c6e9db5a 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-4.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-4.json @@ -1,8 +1,14 @@ { "scenario_name": "ELI-317 - includeActions=Y, but no actions in response ", - "config_filename": "AUTO_RSV_ELI-317-4.json", - "request_headers": {"nhs-login-nhs-number": "9000000009"}, - "query_params": {"includeActions": "Y"}, + "config_filenames": [ + "AUTO_RSV_ELI-317-4.json" + ], + "request_headers": { + "nhs-login-nhs-number": "9000000009" + }, + "query_params": { + "includeActions": "Y" + }, "data": [ { "NHS_NUMBER": "9000000009", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-5.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-5.json index 28f2d97bd..64faef323 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-5.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-5.json @@ -1,8 +1,14 @@ { "scenario_name": "ELI-317 - Multiple actions", - "config_filename": "AUTO_RSV_ELI-317-5.json", - "request_headers": {"nhs-login-nhs-number": "9000000010"}, - "query_params": {"includeActions": "Y"}, + "config_filenames": [ + "AUTO_RSV_ELI-317-5.json" + ], + "request_headers": { + "nhs-login-nhs-number": "9000000010" + }, + "query_params": { + "includeActions": "Y" + }, "data": [ { "NHS_NUMBER": "9000000010", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-1.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-1.json new file mode 100644 index 000000000..0a985164f --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-1.json @@ -0,0 +1,63 @@ +{ + "scenario_name": "ELI-320 - Multiple Condition Campaigns - Condition Query - None", + "request_headers": { + "nhs-login-nhs-number": "9990032010" + }, + "config_filenames": ["AUTO_RSV_ELI-320-COVID.json","AUTO_RSV_ELI-320-RSV.json", "AUTO_RSV_ELI-320-MMR.json"], + "data": [ + { + "NHS_NUMBER": "9990032010", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + }, + "covid_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9990032010", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9990032010", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + }, + { + "NHS_NUMBER": "9990032010", + "ATTRIBUTE_TYPE": "COVID", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-2.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-2.json new file mode 100644 index 000000000..b856294f5 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-2.json @@ -0,0 +1,66 @@ +{ + "scenario_name": "ELI-320 - Multiple Condition Campaigns - Condition Query - ALL", + "request_headers": { + "nhs-login-nhs-number": "9990032020" + }, + "query_params": { + "conditions": "ALL" + }, + "config_filenames": ["AUTO_RSV_ELI-320-COVID.json","AUTO_RSV_ELI-320-RSV.json", "AUTO_RSV_ELI-320-MMR.json"], + "data": [ + { + "NHS_NUMBER": "9990032020", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + }, + "covid_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9990032020", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9990032020", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + }, + { + "NHS_NUMBER": "9990032020", + "ATTRIBUTE_TYPE": "COVID", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-3.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-3.json new file mode 100644 index 000000000..ca015864e --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-3.json @@ -0,0 +1,66 @@ +{ + "scenario_name": "ELI-320 - Multiple Condition Campaigns - Condition Query - Covid", + "request_headers": { + "nhs-login-nhs-number": "9990032030" + }, + "query_params": { + "conditions": "covid" + }, + "config_filenames": ["AUTO_RSV_ELI-320-COVID.json","AUTO_RSV_ELI-320-RSV.json", "AUTO_RSV_ELI-320-MMR.json"], + "data": [ + { + "NHS_NUMBER": "9990032030", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + }, + "covid_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9990032030", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9990032030", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + }, + { + "NHS_NUMBER": "9990032030", + "ATTRIBUTE_TYPE": "COVID", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-4.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-4.json new file mode 100644 index 000000000..76d0dab2c --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-4.json @@ -0,0 +1,66 @@ +{ + "scenario_name": "ELI-320 - Multiple Condition Campaigns - Condition Query - MMR", + "request_headers": { + "nhs-login-nhs-number": "9990032040" + }, + "query_params": { + "conditions": "MMR" + }, + "config_filenames": ["AUTO_RSV_ELI-320-COVID.json","AUTO_RSV_ELI-320-RSV.json", "AUTO_RSV_ELI-320-MMR.json"], + "data": [ + { + "NHS_NUMBER": "9990032040", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + }, + "covid_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9990032040", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9990032040", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + }, + { + "NHS_NUMBER": "9990032040", + "ATTRIBUTE_TYPE": "COVID", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-5.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-5.json new file mode 100644 index 000000000..a9861ed29 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-5.json @@ -0,0 +1,66 @@ +{ + "scenario_name": "ELI-320 - Multiple Condition Campaigns - Condition Query - RSV, MMR", + "request_headers": { + "nhs-login-nhs-number": "9990032050" + }, + "query_params": { + "conditions": "RSV,MMR" + }, + "config_filenames": ["AUTO_RSV_ELI-320-COVID.json","AUTO_RSV_ELI-320-RSV.json", "AUTO_RSV_ELI-320-MMR.json"], + "data": [ + { + "NHS_NUMBER": "9990032050", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + }, + "covid_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9990032050", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9990032050", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + }, + { + "NHS_NUMBER": "9990032050", + "ATTRIBUTE_TYPE": "COVID", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-320-10.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-320-10.json new file mode 100644 index 000000000..8b30871c9 --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-320-10.json @@ -0,0 +1,28 @@ +{ + "meta": { + "lastUpdated": "2025-07-15T14:52:52.785698+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "COVID", + "eligibilityCohorts": [ + { + "cohortCode": "covid_cohort_group", + "cohortStatus": "NotActionable", + "cohortText": "You are currently in a covid cohort" + } + ], + "status": "NotActionable", + "statusText": "You should have the COVID vaccine", + "suitabilityRules": [ + { + "ruleCode": "AlreadyVaccinated", + "ruleText": "##You've had your COVID vaccination\nWe believe you already had your COVID vaccination.", + "ruleType": "S" + } + ] + } + ], + "responseId": "5c8d1cb3-8326-40b1-93ad-1b7fa24c2595" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-320-11.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-320-11.json new file mode 100644 index 000000000..9f936928c --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-320-11.json @@ -0,0 +1,7 @@ +{ + "meta": { + "lastUpdated": "2025-07-15T14:52:52.785698+00:00" + }, + "processedSuggestions": [], + "responseId": "5c8d1cb3-8326-40b1-93ad-1b7fa24c2595" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-320-6.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-320-6.json new file mode 100644 index 000000000..a3dc37c19 --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-320-6.json @@ -0,0 +1,78 @@ +{ + "meta": { + "lastUpdated": "2025-07-15T13:05:26.936847+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "COVID", + "eligibilityCohorts": [ + { + "cohortCode": "covid_cohort_group", + "cohortStatus": "NotActionable", + "cohortText": "You are currently in a covid cohort" + } + ], + "status": "NotActionable", + "statusText": "You should have the COVID vaccine", + "suitabilityRules": [ + { + "ruleCode": "AlreadyVaccinated", + "ruleText": "##You've had your COVID vaccination\nWe believe you already had your COVID vaccination.", + "ruleType": "S" + } + ] + }, + { + "actions": [ + { + "actionCode": "AmendNBS", + "actionType": "ButtonWithAuthLink", + "description": "##You have an flu screening appointment\nYou can view, change or cancel your appointment below.", + "urlLabel": "Manage your appointment", + "urlLink": "http://www.nhs.uk/book-bs" + } + ], + "condition": "FLU", + "eligibilityCohorts": [ + { + "cohortCode": "FLU_screening_cohort_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in an flu SCREENING cohort" + } + ], + "status": "Actionable", + "statusText": "You should have the FLU vaccine", + "suitabilityRules": [] + }, + { + "actions": [], + "condition": "MMR", + "eligibilityCohorts": [ + { + "cohortCode": "mmr_cohort_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not currently in an mmr cohort" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + }, + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_screening_cohort_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not currently in an RSV SCREENING cohort" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "2e40b120-6f59-4418-9c80-363137e6386e" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-320-8.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-320-8.json new file mode 100644 index 000000000..bff98f1db --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-320-8.json @@ -0,0 +1,44 @@ +{ + "meta": { + "lastUpdated": "2025-07-15T14:52:52.785698+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "AmendNBS", + "actionType": "ButtonWithAuthLink", + "description": "##You have an flu screening appointment\nYou can view, change or cancel your appointment below.", + "urlLabel": "Manage your appointment", + "urlLink": "http://www.nhs.uk/book-bs" + } + ], + "condition": "FLU", + "eligibilityCohorts": [ + { + "cohortCode": "FLU_screening_cohort_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in an flu SCREENING cohort" + } + ], + "status": "Actionable", + "statusText": "You should have the FLU vaccine", + "suitabilityRules": [] + }, + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_screening_cohort_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not currently in an RSV SCREENING cohort" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "5c8d1cb3-8326-40b1-93ad-1b7fa24c2595" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-320-9.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-320-9.json new file mode 100644 index 000000000..45c8d1200 --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-320-9.json @@ -0,0 +1,42 @@ +{ + "meta": { + "lastUpdated": "2025-07-15T14:52:52.785698+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "COVID", + "eligibilityCohorts": [ + { + "cohortCode": "covid_cohort_group", + "cohortStatus": "NotActionable", + "cohortText": "You are currently in a covid cohort" + } + ], + "status": "NotActionable", + "statusText": "You should have the COVID vaccine", + "suitabilityRules": [ + { + "ruleCode": "AlreadyVaccinated", + "ruleText": "##You've had your COVID vaccination\nWe believe you already had your COVID vaccination.", + "ruleType": "S" + } + ] + }, + { + "actions": [], + "condition": "MMR", + "eligibilityCohorts": [ + { + "cohortCode": "mmr_cohort_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not currently in an mmr cohort" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "5c8d1cb3-8326-40b1-93ad-1b7fa24c2595" +} diff --git a/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_001.json b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_001.json index b86ea8cab..e3a66ecda 100644 --- a/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_001.json +++ b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_001.json @@ -22,7 +22,7 @@ } ], "status": "Actionable", - "statusText": "Status.actionable", + "statusText": "You should have the RSV vaccine", "suitabilityRules": [] } ], diff --git a/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_002.json b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_002.json index dd59901fb..331ba4e36 100644 --- a/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_002.json +++ b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_002.json @@ -27,7 +27,7 @@ } ], "status": "Actionable", - "statusText": "Status.actionable", + "statusText": "You should have the RSV vaccine", "suitabilityRules": [] } ], diff --git a/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_003.json b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_003.json index da6a4fabb..e1b80269b 100644 --- a/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_003.json +++ b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_003.json @@ -22,7 +22,7 @@ } ], "status": "Actionable", - "statusText": "Status.actionable", + "statusText": "You should have the RSV vaccine", "suitabilityRules": [] } ], diff --git a/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_010.json b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_010.json index 1030e0ffb..0fdf41d8f 100644 --- a/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_010.json +++ b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_010.json @@ -19,7 +19,7 @@ } ], "status": "NotEligible", - "statusText": "Status.not_eligible", + "statusText": "We do not believe you can have it", "suitabilityRules": [] } ], diff --git a/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_011.json b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_011.json index 1030e0ffb..0fdf41d8f 100644 --- a/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_011.json +++ b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_011.json @@ -19,7 +19,7 @@ } ], "status": "NotEligible", - "statusText": "Status.not_eligible", + "statusText": "We do not believe you can have it", "suitabilityRules": [] } ], diff --git a/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_012.json b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_012.json index 1030e0ffb..0fdf41d8f 100644 --- a/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_012.json +++ b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_012.json @@ -19,7 +19,7 @@ } ], "status": "NotEligible", - "statusText": "Status.not_eligible", + "statusText": "We do not believe you can have it", "suitabilityRules": [] } ], diff --git a/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_013.json b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_013.json index 6fa6dbca1..0f2cac110 100644 --- a/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_013.json +++ b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_013.json @@ -8,7 +8,7 @@ "condition": "RSV", "eligibilityCohorts": [], "status": "NotActionable", - "statusText": "Status.not_actionable", + "statusText": "You should have the RSV vaccine", "suitabilityRules": [ { "ruleCode": "AlreadyVaccinated", diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_001.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_001.json index 926afef2f..4a6253f15 100644 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_001.json +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_001.json @@ -7,7 +7,7 @@ { "condition": "RSV", "status": "Actionable", - "statusText": "Status.actionable", + "statusText": "You should have the RSV vaccine", "eligibilityCohorts": [ { "cohortCode": "rsv_age_rolling", diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_002.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_002.json index a3c9c415b..53b88e29c 100644 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_002.json +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_002.json @@ -7,7 +7,7 @@ { "condition": "RSV", "status": "Actionable", - "statusText": "Status.actionable", + "statusText": "You should have the RSV vaccine", "eligibilityCohorts": [ { "cohortCode": "rsv_age_rolling", diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_003.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_003.json index 6f544c105..c02961ada 100644 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_003.json +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_003.json @@ -7,7 +7,7 @@ { "condition": "RSV", "status": "Actionable", - "statusText": "Status.actionable", + "statusText": "You should have the RSV vaccine", "eligibilityCohorts": [ { "cohortCode": "rsv_age_catchup", diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_004.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_004.json index 251e4577a..9f1a7ba65 100644 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_004.json +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_004.json @@ -7,7 +7,7 @@ { "condition": "RSV", "status": "Actionable", - "statusText": "Status.actionable", + "statusText": "You should have the RSV vaccine", "eligibilityCohorts": [], "suitabilityRules": [], "actions": [ diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_005.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_005.json index 9d7eafb4a..7522fb0f2 100644 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_005.json +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_005.json @@ -7,7 +7,7 @@ { "condition": "RSV", "status": "Actionable", - "statusText": "Status.actionable", + "statusText": "You should have the RSV vaccine", "eligibilityCohorts": [], "suitabilityRules": [], "actions": [ diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_006.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_006.json index 47f03f7c7..b52ea12f7 100644 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_006.json +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_006.json @@ -7,7 +7,7 @@ { "condition": "RSV", "status": "NotActionable", - "statusText": "Status.not_actionable", + "statusText": "You should have the RSV vaccine", "eligibilityCohorts": [], "suitabilityRules": [ { diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_007.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_007.json index 9ce1a0826..df94725a6 100644 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_007.json +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_007.json @@ -7,7 +7,7 @@ { "condition": "RSV", "status": "NotActionable", - "statusText": "Status.not_actionable", + "statusText": "You should have the RSV vaccine", "eligibilityCohorts": [ { "cohortCode": "rsv_age_rolling", diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_009.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_009.json index ff3f2f443..f3821a053 100644 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_009.json +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_009.json @@ -7,7 +7,7 @@ { "condition": "RSV", "status": "NotActionable", - "statusText": "Status.not_actionable", + "statusText": "You should have the RSV vaccine", "eligibilityCohorts": [ { "cohortCode": "rsv_age_rolling", diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_010.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_010.json index ab8170724..8993f41d8 100644 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_010.json +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_010.json @@ -7,7 +7,7 @@ { "condition": "RSV", "status": "NotActionable", - "statusText": "Status.not_actionable", + "statusText": "You should have the RSV vaccine", "eligibilityCohorts": [ { "cohortCode": "rsv_age_rolling", diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_011.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_011.json index 9518db805..509cd71b7 100644 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_011.json +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_011.json @@ -7,7 +7,7 @@ { "condition": "RSV", "status": "NotActionable", - "statusText": "Status.not_actionable", + "statusText": "You should have the RSV vaccine", "eligibilityCohorts": [ { "cohortCode": "rsv_age_rolling", diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_012.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_012.json index 280b5732b..4996198d2 100644 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_012.json +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_012.json @@ -7,7 +7,7 @@ { "condition": "RSV", "status": "NotActionable", - "statusText": "Status.not_actionable", + "statusText": "You should have the RSV vaccine", "eligibilityCohorts": [ { "cohortCode": "unknown_cohort_membership", diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_013.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_013.json index 28cb06b60..3413b999b 100644 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_013.json +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_013.json @@ -7,7 +7,7 @@ { "condition": "RSV", "status": "NotActionable", - "statusText": "Status.not_actionable", + "statusText": "You should have the RSV vaccine", "eligibilityCohorts": [], "suitabilityRules": [ { diff --git a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_014.json b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_014.json index 3c2e410c5..29ba244d7 100644 --- a/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_014.json +++ b/tests/e2e/data/responses/smokeTestResponses/AUTO_RSV_SB_014.json @@ -7,7 +7,7 @@ { "condition": "RSV", "status": "NotEligible", - "statusText": "Status.not_eligible", + "statusText": "We do not believe you can have it", "eligibilityCohorts": [ { "cohortCode": "rsv_age_rolling", diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-155.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-155.json index 6cbc2e600..6cb46fb63 100644 --- a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-155.json +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-155.json @@ -7,7 +7,7 @@ { "condition": "RSV", "status": "Actionable", - "statusText": "Status.actionable", + "statusText": "You should have the RSV vaccine", "eligibilityCohorts": [ { "cohortCode": "eli_155_in_group", diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-1.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-1.json index 2da2d6781..b1c43974f 100644 --- a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-1.json +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-1.json @@ -7,7 +7,7 @@ { "condition": "RSV", "status": "Actionable", - "statusText": "Status.actionable", + "statusText": "You should have the RSV vaccine", "eligibilityCohorts": [ { "cohortCode": "eli_216_cohort_group", diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-1.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-1.json index 8dc271345..754b65de9 100644 --- a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-1.json +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-1.json @@ -7,7 +7,7 @@ { "condition": "RSV", "status": "Actionable", - "statusText": "Status.actionable", + "statusText": "You should have the RSV vaccine", "eligibilityCohorts": [ { "cohortCode": "eli_291_cohort_1_group", diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-2.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-2.json index e9b897771..8d29be962 100644 --- a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-2.json +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-2.json @@ -7,7 +7,7 @@ { "condition": "RSV", "status": "Actionable", - "statusText": "Status.actionable", + "statusText": "You should have the RSV vaccine", "eligibilityCohorts": [ { "cohortCode": "eli_291_cohort_2_group", diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-3.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-3.json index be5a4a540..5657c9940 100644 --- a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-3.json +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-219-3.json @@ -7,7 +7,7 @@ { "condition": "RSV", "status": "Actionable", - "statusText": "Status.actionable", + "statusText": "You should have the RSV vaccine", "eligibilityCohorts": [ { "cohortCode": "eli_291_cohort_3_group", diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-1.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-1.json index c2ac8c709..2d56cbf08 100644 --- a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-1.json +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-1.json @@ -7,7 +7,7 @@ { "condition": "RSV", "status": "Actionable", - "statusText": "Status.actionable", + "statusText": "You should have the RSV vaccine", "eligibilityCohorts": [ { "cohortCode": "eli_317_cohort_1_group", diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-2.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-2.json index 12c33e6ea..d832085ad 100644 --- a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-2.json +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-2.json @@ -7,7 +7,7 @@ { "condition": "RSV", "status": "Actionable", - "statusText": "Status.actionable", + "statusText": "You should have the RSV vaccine", "eligibilityCohorts": [ { "cohortCode": "eli_317_cohort_2_group", diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-3.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-3.json index c53b450a0..ebca2e682 100644 --- a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-3.json +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-3.json @@ -7,7 +7,7 @@ { "condition": "RSV", "status": "Actionable", - "statusText": "Status.actionable", + "statusText": "You should have the RSV vaccine", "eligibilityCohorts": [ { "cohortCode": "eli_317_cohort_3_group", diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-4.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-4.json index cd61107d7..d053d6e8e 100644 --- a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-4.json +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-4.json @@ -7,7 +7,7 @@ { "condition": "RSV", "status": "Actionable", - "statusText": "Status.actionable", + "statusText": "You should have the RSV vaccine", "eligibilityCohorts": [ { "cohortCode": "eli_317_cohort_4_group", diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-5.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-5.json index 5a1a15b91..c9e5aa9b2 100644 --- a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-5.json +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-317-5.json @@ -37,7 +37,7 @@ } ], "status": "Actionable", - "statusText": "Status.actionable", + "statusText": "You should have the RSV vaccine", "suitabilityRules": [] } ] diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-1.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-1.json new file mode 100644 index 000000000..306ee5b66 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-1.json @@ -0,0 +1,64 @@ +{ + "meta": { + "lastUpdated": "2025-07-14T20:34:22.263070+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "COVID", + "eligibilityCohorts": [ + { + "cohortCode": "covid_cohort_group", + "cohortStatus": "NotActionable", + "cohortText": "You are currently in a covid cohort" + } + ], + "status": "NotActionable", + "statusText": "You should have the COVID vaccine", + "suitabilityRules": [ + { + "ruleCode": "AlreadyVaccinated", + "ruleText": "##You've had your COVID vaccination\nWe believe you already had your COVID vaccination.", + "ruleType": "S" + } + ] + }, + { + "actions": [], + "condition": "MMR", + "eligibilityCohorts": [ + { + "cohortCode": "mmr_cohort_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not currently in an mmr cohort" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + }, + { + "actions": [ + { + "actionCode": "AmendNBS", + "actionType": "ButtonWithAuthLink", + "description": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", + "urlLabel": "Manage your appointment", + "urlLink": "http://www.nhs.uk/book-rsv" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_cohort_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in an RSV cohort" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "d5255fb5-ec34-46c0-8177-d54c3bcbb5d1" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-2.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-2.json new file mode 100644 index 000000000..306ee5b66 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-2.json @@ -0,0 +1,64 @@ +{ + "meta": { + "lastUpdated": "2025-07-14T20:34:22.263070+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "COVID", + "eligibilityCohorts": [ + { + "cohortCode": "covid_cohort_group", + "cohortStatus": "NotActionable", + "cohortText": "You are currently in a covid cohort" + } + ], + "status": "NotActionable", + "statusText": "You should have the COVID vaccine", + "suitabilityRules": [ + { + "ruleCode": "AlreadyVaccinated", + "ruleText": "##You've had your COVID vaccination\nWe believe you already had your COVID vaccination.", + "ruleType": "S" + } + ] + }, + { + "actions": [], + "condition": "MMR", + "eligibilityCohorts": [ + { + "cohortCode": "mmr_cohort_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not currently in an mmr cohort" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + }, + { + "actions": [ + { + "actionCode": "AmendNBS", + "actionType": "ButtonWithAuthLink", + "description": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", + "urlLabel": "Manage your appointment", + "urlLink": "http://www.nhs.uk/book-rsv" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_cohort_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in an RSV cohort" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "d5255fb5-ec34-46c0-8177-d54c3bcbb5d1" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-3.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-3.json new file mode 100644 index 000000000..658cd5c04 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-3.json @@ -0,0 +1,28 @@ +{ + "meta": { + "lastUpdated": "2025-07-14T20:34:22.263070+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "COVID", + "eligibilityCohorts": [ + { + "cohortCode": "covid_cohort_group", + "cohortStatus": "NotActionable", + "cohortText": "You are currently in a covid cohort" + } + ], + "status": "NotActionable", + "statusText": "You should have the COVID vaccine", + "suitabilityRules": [ + { + "ruleCode": "AlreadyVaccinated", + "ruleText": "##You've had your COVID vaccination\nWe believe you already had your COVID vaccination.", + "ruleType": "S" + } + ] + } + ], + "responseId": "d5255fb5-ec34-46c0-8177-d54c3bcbb5d1" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-4.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-4.json new file mode 100644 index 000000000..ec373e2a9 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-4.json @@ -0,0 +1,22 @@ +{ + "meta": { + "lastUpdated": "2025-07-14T20:34:22.263070+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "MMR", + "eligibilityCohorts": [ + { + "cohortCode": "mmr_cohort_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not currently in an mmr cohort" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "d5255fb5-ec34-46c0-8177-d54c3bcbb5d1" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-5.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-5.json new file mode 100644 index 000000000..7fb1ab894 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-5.json @@ -0,0 +1,44 @@ +{ + "meta": { + "lastUpdated": "2025-07-14T20:34:22.263070+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "MMR", + "eligibilityCohorts": [ + { + "cohortCode": "mmr_cohort_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not currently in an mmr cohort" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + }, + { + "actions": [ + { + "actionCode": "AmendNBS", + "actionType": "ButtonWithAuthLink", + "description": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", + "urlLabel": "Manage your appointment", + "urlLink": "http://www.nhs.uk/book-rsv" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_cohort_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in an RSV cohort" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "d5255fb5-ec34-46c0-8177-d54c3bcbb5d1" +} diff --git a/tests/e2e/tests/conftest.py b/tests/e2e/tests/conftest.py index d2b5075be..975ab8b20 100644 --- a/tests/e2e/tests/conftest.py +++ b/tests/e2e/tests/conftest.py @@ -8,6 +8,7 @@ from dotenv import load_dotenv from tests.e2e.utils.eligibility_api_client import EligibilityApiClient +from tests.e2e.utils.s3_config_manager import upload_configs_to_s3 # Load environment variables from .env.local load_dotenv(dotenv_path=".env") @@ -85,3 +86,20 @@ def setup_dynamodb_data(request): @pytest.fixture(scope="session") def eligibility_client(): return EligibilityApiClient(BASE_URL, cert_dir="certs") + + +@pytest.fixture +def get_scenario_params(request): + _ = request + + def _setup(scenario, config_path): + nhs_number = scenario["nhs_number"] + config_filenames = scenario.get("config_filenames", []) + request_headers = scenario.get("request_headers", {}) + query_params = scenario.get("query_params", {}) + + upload_configs_to_s3(config_filenames, config_path) + + return nhs_number, config_filenames, request_headers, query_params + + return _setup diff --git a/tests/e2e/tests/eligibility_signposting/test_eligibility_check_bdd.py b/tests/e2e/tests/eligibility_signposting/test_eligibility_check_bdd.py deleted file mode 100644 index 49e893626..000000000 --- a/tests/e2e/tests/eligibility_signposting/test_eligibility_check_bdd.py +++ /dev/null @@ -1,14 +0,0 @@ -# import sys -# from pathlib import Path -# -# import pytest -# from behave import scenarios -# -# # Add the features directory to the Python path -# sys.path.append(str(Path(__file__).parent.parent.parent)) -# -# # Mark all tests as BDD tests -# pytestmark = [pytest.mark.bdd, pytest.mark.eligibility] -# -# # Load the scenarios from the feature file -# scenarios("../../features/eligibility_check/eligibility_check.feature") diff --git a/tests/e2e/tests/test_error_scenario_tests.py b/tests/e2e/tests/test_error_scenario_tests.py index f59685dce..4e29894e8 100644 --- a/tests/e2e/tests/test_error_scenario_tests.py +++ b/tests/e2e/tests/test_error_scenario_tests.py @@ -2,9 +2,7 @@ import pytest -from tests.e2e.utils.data_helper import clean_responses - -volatile_fields = ["lastUpdated", "id"] +from tests.e2e.utils.s3_config_manager import delete_all_configs_from_s3 @pytest.mark.smoketest @@ -16,7 +14,7 @@ def test_check_for_missing_person(eligibility_client): expected_body = { "resourceType": "OperationOutcome", "id": "", - 'meta': {'lastUpdated': ''}, + "meta": {"lastUpdated": ""}, "issue": [ { "severity": "error", @@ -148,3 +146,115 @@ def test_nhs_login_header_handling(eligibility_client, test_case): assert response["status_code"] == test_case["expected_status"], f"{test_case['scenario']} failed on status code" assert response["body"] == test_case["expected_body"], f"{test_case['scenario']} failed on response body" assert response["headers"].get("Content-Type".lower()) == "application/fhir+json" + + +@pytest.mark.parametrize( + "test_case", + [ + { + "scenario": "invalid conditions - use special character in conditions", + "nhs_number": "9990032010", + "request_headers": {"nhs-login-nhs-number": "9990032010"}, + "query_params": {"conditions": "covid-rsv"}, + "expected_status": http.HTTPStatus.BAD_REQUEST, + "expected_body": { + "id": "", + "issue": [ + { + "code": "value", + "details": { + "coding": [ + { + "code": "INVALID_PARAMETER", + "display": "The given conditions were not in the expected format.", + "system": "https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1", + } + ] + }, + "diagnostics": "covid-rsv should be a single or comma separated list of condition strings " + "with no other punctuation or special characters", + "location": ["parameters/conditions"], + "severity": "error", + } + ], + "meta": {"lastUpdated": ""}, + "resourceType": "OperationOutcome", + }, + }, + { + "scenario": "unknown-category - misspelt category", + "nhs_number": "9990032010", + "request_headers": {"nhs-login-nhs-number": "9990032010"}, + "query_params": {"category": "VACCINATIONSS"}, + "expected_status": http.HTTPStatus.UNPROCESSABLE_ENTITY, + "expected_body": { + "id": "", + "issue": [ + { + "code": "value", + "details": { + "coding": [ + { + "code": "INVALID_PARAMETER", + "display": "The supplied category was not recognised by the API.", + "system": "https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1", + } + ] + }, + "diagnostics": "VACCINATIONSS is not a category that is supported by the API", + "location": ["parameters/category"], + "severity": "error", + } + ], + "meta": {"lastUpdated": ""}, + "resourceType": "OperationOutcome", + }, + }, + ], + ids=["invalid-conditions", "unknown-category"], +) +def test_query_param_errors(eligibility_client, test_case): + response = eligibility_client.make_request( + test_case["nhs_number"], + headers=test_case["request_headers"], + query_params=test_case["query_params"], + raise_on_error=False, + ) + + assert response["status_code"] == test_case["expected_status"], f"{test_case['scenario']} failed on status code" + assert response["body"] == test_case["expected_body"], f"{test_case['scenario']} failed on response body" + assert response["headers"].get("Content-Type".lower()) == "application/fhir+json" + + +def test_no_config_error(eligibility_client): + expected_response = { + "id": "", + "issue": [ + { + "code": "processing", + "details": { + "coding": [ + { + "code": "INTERNAL_SERVER_ERROR", + "display": "An unexpected internal server error occurred.", + "system": "https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1", + } + ] + }, + "diagnostics": "An unexpected error occurred.", + "severity": "error", + } + ], + "meta": {"lastUpdated": ""}, + "resourceType": "OperationOutcome", + } + + delete_all_configs_from_s3() + + response = eligibility_client.make_request( + nhs_number="9990032010", headers={"nhs-login-nhs-number": "9990032010"}, raise_on_error=False + ) + + assert response["status_code"] == http.HTTPStatus.INTERNAL_SERVER_ERROR + assert response["body"] == expected_response + assert response["headers"].get("Content-Type".lower()) == "application/fhir+json" diff --git a/tests/e2e/tests/test_regression_tests.py b/tests/e2e/tests/test_regression_tests.py index 2a98dd39f..a9fa5a211 100644 --- a/tests/e2e/tests/test_regression_tests.py +++ b/tests/e2e/tests/test_regression_tests.py @@ -1,11 +1,9 @@ import http -from pathlib import Path import pytest from tests.e2e.tests import test_config from tests.e2e.utils.data_helper import initialise_tests, load_all_expected_responses -from tests.e2e.utils.s3_config_manager import upload_config_to_s3 # Update the below with the configuration values specified in test_config.py all_data, dto = initialise_tests(test_config.REGRESSION_TEST_DATA) @@ -18,11 +16,8 @@ @pytest.mark.functionale2eregression @pytest.mark.parametrize(("filename", "scenario"), param_list, ids=id_list) -def test_run_regression_tests(filename, scenario, eligibility_client): - nhs_number = scenario["nhs_number"] - config_filename = scenario.get("config_filename", "") - request_headers = scenario.get("request_headers", {}) - upload_config_to_s3((Path(config_path) / config_filename).resolve()) +def test_run_regression_tests(filename, scenario, eligibility_client, get_scenario_params): + nhs_number, config_filenames, request_headers, query_params = get_scenario_params(scenario, config_path) actual_response = eligibility_client.make_request(nhs_number, headers=request_headers, strict_ssl=False) expected_response = all_expected_responses.get(filename).get("response_items", {}) diff --git a/tests/e2e/tests/test_smoke_tests.py b/tests/e2e/tests/test_smoke_tests.py index 920a93b81..f99fd3265 100644 --- a/tests/e2e/tests/test_smoke_tests.py +++ b/tests/e2e/tests/test_smoke_tests.py @@ -1,11 +1,9 @@ import http -from pathlib import Path import pytest from tests.e2e.tests import test_config from tests.e2e.utils.data_helper import initialise_tests, load_all_expected_responses -from tests.e2e.utils.s3_config_manager import upload_config_to_s3 # Update the below with the configuration values specified in test_config.py all_data, dto = initialise_tests(test_config.SMOKE_TEST_DATA) @@ -18,12 +16,8 @@ @pytest.mark.sandboxtests @pytest.mark.parametrize(("filename", "scenario"), param_list, ids=id_list) -def test_run_smoke_case(filename, scenario, eligibility_client): - nhs_number = scenario["nhs_number"] - config_filename = scenario.get("config_filename", "") - request_headers = scenario.get("request_headers", {}) - query_params = scenario.get("query_params", {}) - upload_config_to_s3((Path(config_path) / config_filename).resolve()) +def test_run_smoke_case(filename, scenario, eligibility_client, get_scenario_params): + nhs_number, config_filenames, request_headers, query_params = get_scenario_params(scenario, config_path) actual_response = eligibility_client.make_request( nhs_number, headers=request_headers, query_params=query_params, strict_ssl=False diff --git a/tests/e2e/tests/test_story_tests.py b/tests/e2e/tests/test_story_tests.py index 205a80a20..a3e328dba 100644 --- a/tests/e2e/tests/test_story_tests.py +++ b/tests/e2e/tests/test_story_tests.py @@ -1,11 +1,9 @@ import http -from pathlib import Path import pytest from tests.e2e.tests import test_config from tests.e2e.utils.data_helper import initialise_tests, load_all_expected_responses -from tests.e2e.utils.s3_config_manager import upload_config_to_s3 # Update the below with the configuration values specified in test_config.py all_data, dto = initialise_tests(test_config.STORY_TEST_DATA) @@ -18,15 +16,11 @@ @pytest.mark.functionale2eregression @pytest.mark.parametrize(("filename", "scenario"), param_list, ids=id_list) -def test_run_story_test_cases(filename, scenario, eligibility_client): - nhs_number = scenario["nhs_number"] - config_filename = scenario.get("config_filename", "") - request_headers = scenario.get("request_headers", {}) - query_params = scenario.get("query_params", {}) - upload_config_to_s3((Path(config_path) / config_filename).resolve()) +def test_run_story_test_cases(filename, scenario, eligibility_client, get_scenario_params): + nhs_number, config_filenames, request_headers, query_params = get_scenario_params(scenario, config_path) actual_response = eligibility_client.make_request( - nhs_number, headers=request_headers, query_params=query_params, strict_ssl=False + nhs_number=nhs_number, headers=request_headers, query_params=query_params, strict_ssl=False ) expected_response = all_expected_responses.get(filename).get("response_items", {}) diff --git a/tests/e2e/utils/data_helper.py b/tests/e2e/utils/data_helper.py index a6bcc217c..d07fa2ffa 100644 --- a/tests/e2e/utils/data_helper.py +++ b/tests/e2e/utils/data_helper.py @@ -95,7 +95,7 @@ def load_all_test_scenarios(folder_path): raw_data = raw_json["data"] - config_filename = raw_json.get("config_filename") + config_filenames = raw_json.get("config_filenames") scenario_name = raw_json.get("scenario_name") request_headers = raw_json.get("request_headers") expected_response_code = raw_json.get("expected_response_code") @@ -111,7 +111,7 @@ def load_all_test_scenarios(folder_path): all_data[path.name] = { "dynamo_items": resolved_data, "nhs_number": nhs_number, - "config_filename": config_filename, + "config_filenames": config_filenames, "expected_response_code": expected_response_code, "request_headers": request_headers, "query_params": query_params, diff --git a/tests/e2e/utils/s3_config_manager.py b/tests/e2e/utils/s3_config_manager.py index f4bfd804e..cf1c6a1fa 100644 --- a/tests/e2e/utils/s3_config_manager.py +++ b/tests/e2e/utils/s3_config_manager.py @@ -37,7 +37,7 @@ def upload_if_missing_or_changed(self, local_path: Path) -> None: logger.info("\n๐Ÿ” Config '%s' already exists and matches in S3. Skipping upload.", filename) return logger.info("\n๐Ÿงน A different config exists under '%s/'. Deleting all existing files...", self.s3_prefix) - self._delete_all_in_prefix() + self.delete_all_in_prefix() except self.s3_client.exceptions.NoSuchKey: logger.info("\n๐Ÿ†• No config found under '%s/'. Proceeding to upload.", self.s3_prefix) except botocore.exceptions.ClientError as error: @@ -71,7 +71,7 @@ def config_exists_and_matches(self, local_path: Path, s3_key: str) -> bool: else: return s3_hash == local_hash - def _delete_all_in_prefix(self) -> None: + def delete_all_in_prefix(self) -> None: """Delete all S3 objects under the current prefix.""" response = self.s3_client.list_objects_v2(Bucket=self.bucket_name, Prefix=self.s3_prefix) @@ -82,7 +82,54 @@ def _delete_all_in_prefix(self) -> None: else: logger.info("๐Ÿ“ญ Nothing to delete under prefix '%s/'.", self.s3_prefix) + def upload_all_configs(self, local_paths: list[Path]) -> None: + desired_filenames = [p.name for p in local_paths] + desired_keys = {self._s3_key(name) for name in desired_filenames} + + existing_keys = self._list_existing_keys() + + # Delete only unwanted ones (not in desired_keys) + keys_to_delete = [key for key in existing_keys if key not in desired_keys] + if keys_to_delete: + self._delete_keys(keys_to_delete) + + for path in local_paths: + filename = path.name + s3_key = self._s3_key(filename) + + if self.config_exists_and_matches(path, s3_key): + logger.info("โœ… Config '%s' is unchanged in S3. Skipping upload.", filename) + else: + logger.info("โฌ†๏ธ Uploading config '%s' to S3...", filename) + self.s3_client.upload_file(path, self.bucket_name, s3_key) + logger.info("๐Ÿ“„ Uploaded to s3://%s/%s", self.bucket_name, s3_key) + + def _list_existing_keys(self) -> list[str]: + """List all object keys under the current S3 prefix.""" + response = self.s3_client.list_objects_v2(Bucket=self.bucket_name, Prefix=self.s3_prefix) + return [obj["Key"] for obj in response.get("Contents", [])] + + def _delete_keys(self, keys: list[str]) -> None: + """Delete specific keys from the S3 bucket.""" + self.s3_client.delete_objects( + Bucket=self.bucket_name, + Delete={"Objects": [{"Key": key} for key in keys]}, + ) + logger.info("๐Ÿ—‘๏ธ Deleted %d obsolete file(s): %s", len(keys), keys) + def upload_config_to_s3(local_path: Path) -> None: s3_connection = S3ConfigManager(os.getenv("S3_BUCKET_NAME"), os.getenv("S3_PREFIX")) s3_connection.upload_if_missing_or_changed(local_path) + + +def upload_configs_to_s3(config_filenames: list[str], config_path: str | Path) -> None: + config_path = Path(config_path) + local_paths = [config_path / f for f in config_filenames] + s3_connection = S3ConfigManager(os.getenv("S3_BUCKET_NAME"), os.getenv("S3_PREFIX")) + s3_connection.upload_all_configs(local_paths) + + +def delete_all_configs_from_s3() -> None: + s3_connection = S3ConfigManager(os.getenv("S3_BUCKET_NAME"), os.getenv("S3_PREFIX")) + s3_connection.delete_all_in_prefix() From cf41fff5b3cd464d54d84ad9d3daeed8d1f91fa9 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Tue, 15 Jul 2025 22:24:27 +0100 Subject: [PATCH 53/72] eli-320 cases added --- .../AUTO_RSV_ELI-320-SCREENING-1.json | 67 ++++++++++++++++ .../AUTO_RSV_ELI-320-SCREENING-2.json | 67 ++++++++++++++++ .../AUTO_RSV_ELI-320-10.json | 0 .../AUTO_RSV_ELI-320-11.json | 0 .../AUTO_RSV_ELI-320-6.json | 0 .../AUTO_RSV_ELI-320-7.json | 0 .../AUTO_RSV_ELI-320-8.json | 0 .../storyTestData/AUTO_RSV_ELI-320-9.json | 73 +++++++++++++++++ .../AUTO_RSV_ELI-320-10.json | 0 .../AUTO_RSV_ELI-320-11.json | 0 .../AUTO_RSV_ELI-320-6.json | 0 .../AUTO_RSV_ELI-320-7.json | 78 +++++++++++++++++++ .../AUTO_RSV_ELI-320-8.json | 0 .../AUTO_RSV_ELI-320-9.json | 0 14 files changed, 285 insertions(+) create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-SCREENING-1.json create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-SCREENING-2.json rename tests/e2e/data/dynamoDB/{inProgressTestData => storyTestData}/AUTO_RSV_ELI-320-10.json (100%) rename tests/e2e/data/dynamoDB/{inProgressTestData => storyTestData}/AUTO_RSV_ELI-320-11.json (100%) rename tests/e2e/data/dynamoDB/{inProgressTestData => storyTestData}/AUTO_RSV_ELI-320-6.json (100%) rename tests/e2e/data/dynamoDB/{inProgressTestData => storyTestData}/AUTO_RSV_ELI-320-7.json (100%) rename tests/e2e/data/dynamoDB/{inProgressTestData => storyTestData}/AUTO_RSV_ELI-320-8.json (100%) create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-9.json rename tests/e2e/data/responses/{inProgressTestResponses => storyTestResponses}/AUTO_RSV_ELI-320-10.json (100%) rename tests/e2e/data/responses/{inProgressTestResponses => storyTestResponses}/AUTO_RSV_ELI-320-11.json (100%) rename tests/e2e/data/responses/{inProgressTestResponses => storyTestResponses}/AUTO_RSV_ELI-320-6.json (100%) create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-7.json rename tests/e2e/data/responses/{inProgressTestResponses => storyTestResponses}/AUTO_RSV_ELI-320-8.json (100%) rename tests/e2e/data/responses/{inProgressTestResponses => storyTestResponses}/AUTO_RSV_ELI-320-9.json (100%) diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-SCREENING-1.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-SCREENING-1.json new file mode 100644 index 000000000..d66ae00fa --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-SCREENING-1.json @@ -0,0 +1,67 @@ +{ + "CampaignConfig": { + "ID": "CC56789", + "Version": "1", + "Name": "ELI-320 - FLU SCREENING Config", + "Type": "S", + "Target": "FLU", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "AmendNBS", + "StartDate": "20250612", + "EndDate": "20260713", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": "IC56789", + "DefaultCommsRouting": "AmendNBS", + "Version": "1", + "Name": "ELI-320 - FLU Screening Iteration Config", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "FLU_screening_cohort", + "CohortGroup": "FLU_screening_cohort_group", + "PositiveDescription": "You are currently in an flu SCREENING cohort", + "NegativeDescription": "You are not currently in an flu SCREENING cohort", + "Priority": 0 + } + ], + "IterationRules": [ + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D>=", + "Comparator": "0", + "CommsRouting": "AMEND_NBS" + + } + ], + "ActionsMapper": { + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "##You have an flu screening appointment\nYou can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-bs", + "UrlLabel": "Manage your appointment" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-SCREENING-2.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-SCREENING-2.json new file mode 100644 index 000000000..9058c4cae --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-SCREENING-2.json @@ -0,0 +1,67 @@ +{ + "CampaignConfig": { + "ID": "CC567891", + "Version": "1", + "Name": "ELI-320 - RSV SCREENING Config", + "Type": "S", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "AmendNBS", + "StartDate": "20250612", + "EndDate": "20260713", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": "IC567891", + "DefaultCommsRouting": "AmendNBS", + "Version": "1", + "Name": "ELI-320 - RSV SCREENING Iteration Config", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_screening_cohort", + "CohortGroup": "rsv_screening_cohort_group", + "PositiveDescription": "You are currently in an RSV SCREENING cohort", + "NegativeDescription": "You are not currently in an RSV SCREENING cohort", + "Priority": 0 + } + ], + "IterationRules": [ + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D>=", + "Comparator": "0", + "CommsRouting": "AMEND_NBS" + + } + ], + "ActionsMapper": { + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "##You have an bowel screening appointment\nYou can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-bs", + "UrlLabel": "Manage your appointment" + } + } + } + ] + } +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-320-10.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-10.json similarity index 100% rename from tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-320-10.json rename to tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-10.json diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-320-11.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-11.json similarity index 100% rename from tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-320-11.json rename to tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-11.json diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-320-6.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-6.json similarity index 100% rename from tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-320-6.json rename to tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-6.json diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-320-7.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-7.json similarity index 100% rename from tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-320-7.json rename to tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-7.json diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-320-8.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-8.json similarity index 100% rename from tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-320-8.json rename to tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-8.json diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-9.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-9.json new file mode 100644 index 000000000..369f12b29 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-9.json @@ -0,0 +1,73 @@ +{ + "scenario_name": "ELI-320 - Multiple Category Campaigns - Category=VACCINATIONS", + "request_headers": { + "nhs-login-nhs-number": "9900032090" + }, + "query_params": { + "category": "VACCINATIONS" + }, + "config_filenames": ["AUTO_RSV_ELI-320-COVID.json","AUTO_RSV_ELI-320-MMR.json", "AUTO_RSV_ELI-320-SCREENING-1.json", "AUTO_RSV_ELI-320-SCREENING-2.json"], + "data": [ + { + "NHS_NUMBER": "9900032090", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "rsv_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + }, + "covid_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + }, + "FLU_screening_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900032090", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900032090", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + }, + { + "NHS_NUMBER": "9900032090", + "ATTRIBUTE_TYPE": "COVID", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-320-10.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-10.json similarity index 100% rename from tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-320-10.json rename to tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-10.json diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-320-11.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-11.json similarity index 100% rename from tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-320-11.json rename to tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-11.json diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-320-6.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-6.json similarity index 100% rename from tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-320-6.json rename to tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-6.json diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-7.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-7.json new file mode 100644 index 000000000..c57312555 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-7.json @@ -0,0 +1,78 @@ +{ + "meta": { + "lastUpdated": "2025-07-15T14:52:52.785698+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "COVID", + "eligibilityCohorts": [ + { + "cohortCode": "covid_cohort_group", + "cohortStatus": "NotActionable", + "cohortText": "You are currently in a covid cohort" + } + ], + "status": "NotActionable", + "statusText": "You should have the COVID vaccine", + "suitabilityRules": [ + { + "ruleCode": "AlreadyVaccinated", + "ruleText": "##You've had your COVID vaccination\nWe believe you already had your COVID vaccination.", + "ruleType": "S" + } + ] + }, + { + "actions": [ + { + "actionCode": "AmendNBS", + "actionType": "ButtonWithAuthLink", + "description": "##You have an flu screening appointment\nYou can view, change or cancel your appointment below.", + "urlLabel": "Manage your appointment", + "urlLink": "http://www.nhs.uk/book-bs" + } + ], + "condition": "FLU", + "eligibilityCohorts": [ + { + "cohortCode": "FLU_screening_cohort_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in an flu SCREENING cohort" + } + ], + "status": "Actionable", + "statusText": "You should have the FLU vaccine", + "suitabilityRules": [] + }, + { + "actions": [], + "condition": "MMR", + "eligibilityCohorts": [ + { + "cohortCode": "mmr_cohort_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not currently in an mmr cohort" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + }, + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_screening_cohort_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not currently in an RSV SCREENING cohort" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "5c8d1cb3-8326-40b1-93ad-1b7fa24c2595" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-320-8.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-8.json similarity index 100% rename from tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-320-8.json rename to tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-8.json diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-320-9.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-9.json similarity index 100% rename from tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-320-9.json rename to tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-9.json From d1700ada078352c65e5f3a76221a3a878ff27fa6 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Fri, 18 Jul 2025 17:17:52 +0100 Subject: [PATCH 54/72] eli-295 cases added and some refactoring --- .../AUTO_RSV_REG_001.json | 16 + .../smokeTestConfigs/AUTO_RSV_SB_001.json | 544 +++++++++--------- .../smokeTestConfigs/AUTO_RSV_SB_008.json | 2 + .../storyTestConfigs/AUTO_RSV_ELI-155.json | 4 +- .../storyTestConfigs/AUTO_RSV_ELI-216.json | 2 + .../storyTestConfigs/AUTO_RSV_ELI-219-1.json | 2 + .../storyTestConfigs/AUTO_RSV_ELI-219-2.json | 2 + .../storyTestConfigs/AUTO_RSV_ELI-219-3.json | 2 + .../storyTestConfigs/AUTO_RSV_ELI-295-1.json | 123 ++++ .../storyTestConfigs/AUTO_RSV_ELI-295-2.json | 145 +++++ .../storyTestConfigs/AUTO_RSV_ELI-295-3.json | 145 +++++ .../storyTestConfigs/AUTO_RSV_ELI-295-4.json | 167 ++++++ .../storyTestConfigs/AUTO_RSV_ELI-317-1.json | 2 + .../storyTestConfigs/AUTO_RSV_ELI-317-2.json | 2 + .../storyTestConfigs/AUTO_RSV_ELI-317-3.json | 2 + .../storyTestConfigs/AUTO_RSV_ELI-317-4.json | 2 + .../storyTestConfigs/AUTO_RSV_ELI-317-5.json | 2 + .../AUTO_RSV_ELI-320-COVID.json | 2 + .../AUTO_RSV_ELI-320-MMR.json | 2 + .../AUTO_RSV_ELI-320-RSV.json | 2 + .../AUTO_RSV_ELI-320-SCREENING-1.json | 2 + .../AUTO_RSV_ELI-320-SCREENING-2.json | 2 + .../regressionTestData/AUTO_RSV_REG_050.json | 30 + .../storyTestData/AUTO_RSV_ELI_295-01.json | 44 ++ .../storyTestData/AUTO_RSV_ELI_295-02.json | 44 ++ .../storyTestData/AUTO_RSV_ELI_295-03.json | 44 ++ .../storyTestData/AUTO_RSV_ELI_295-04.json | 45 ++ .../storyTestData/AUTO_RSV_ELI_295-05.json | 44 ++ .../storyTestData/AUTO_RSV_ELI_295-06.json | 44 ++ .../storyTestData/AUTO_RSV_ELI_295-07.json | 44 ++ .../storyTestData/AUTO_RSV_ELI_295-08.json | 44 ++ .../storyTestData/AUTO_RSV_ELI_295-09.json | 51 ++ .../storyTestData/AUTO_RSV_ELI_295-10.json | 47 ++ .../storyTestData/AUTO_RSV_ELI_295-11.json | 49 ++ .../storyTestData/AUTO_RSV_ELI_295-12.json | 54 ++ .../storyTestData/AUTO_RSV_ELI_295-13.json | 44 ++ .../storyTestData/AUTO_RSV_ELI_295-14.json | 44 ++ .../storyTestData/AUTO_RSV_ELI_295-15.json | 51 ++ .../storyTestData/AUTO_RSV_ELI_295-16.json | 44 ++ .../storyTestData/AUTO_RSV_ELI_295-17.json | 44 ++ .../AUTO_RSV_ELI_295-01.json | 22 + .../AUTO_RSV_ELI_295-02.json | 28 + .../AUTO_RSV_ELI_295-03.json | 23 + .../AUTO_RSV_ELI_295-04.json | 30 + .../AUTO_RSV_ELI_295-05.json | 36 ++ .../AUTO_RSV_ELI_295-06.json | 30 + .../AUTO_RSV_ELI_295-07.json | 30 + .../AUTO_RSV_ELI_295-08.json | 36 ++ .../AUTO_RSV_ELI_295-09.json | 30 + .../AUTO_RSV_ELI_295-10.json | 21 + .../AUTO_RSV_ELI_295-11.json | 27 + .../AUTO_RSV_ELI_295-12.json | 21 + .../AUTO_RSV_ELI_295-13.json | 37 ++ .../AUTO_RSV_ELI_295-14.json | 43 ++ .../AUTO_RSV_ELI_295-15.json | 37 ++ .../AUTO_RSV_ELI_295-16.json | 30 + .../AUTO_RSV_ELI_295-17.json | 36 ++ .../tests/eligibility_signposting/__init__.py | 0 .../tests/eligibility_signposting/conftest.py | 36 -- .../test_eligibility_check.py | 166 ------ tests/e2e/tests/test_error_scenario_tests.py | 7 +- tests/e2e/utils/api_client.py | 64 --- tests/e2e/utils/config.py | 146 ----- tests/e2e/utils/data_helper.py | 19 + 64 files changed, 2252 insertions(+), 688 deletions(-) create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-1.json create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-2.json create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-3.json create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-4.json create mode 100644 tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_050.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-01.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-02.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-03.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-04.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-05.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-06.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-07.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-08.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-09.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-10.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-11.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-12.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-13.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-14.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-15.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-16.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-17.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-01.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-02.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-03.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-04.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-05.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-06.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-07.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-08.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-09.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-10.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-11.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-12.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-13.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-14.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-15.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-16.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-17.json delete mode 100644 tests/e2e/tests/eligibility_signposting/__init__.py delete mode 100644 tests/e2e/tests/eligibility_signposting/conftest.py delete mode 100644 tests/e2e/tests/eligibility_signposting/test_eligibility_check.py delete mode 100644 tests/e2e/utils/api_client.py delete mode 100644 tests/e2e/utils/config.py diff --git a/tests/e2e/data/configs/regressionTestConfigs/AUTO_RSV_REG_001.json b/tests/e2e/data/configs/regressionTestConfigs/AUTO_RSV_REG_001.json index 0daa66961..c65ce9e8c 100644 --- a/tests/e2e/data/configs/regressionTestConfigs/AUTO_RSV_REG_001.json +++ b/tests/e2e/data/configs/regressionTestConfigs/AUTO_RSV_REG_001.json @@ -25,6 +25,8 @@ "IterationDate": "20250601", "IterationNumber": 1, "CommsType": "I", + "DefaultNotEligibleRouting": "", + "DefaultNotActionableRouting": "", "ApprovalMinimum": 0, "ApprovalMaximum": 0, "Type": "M", @@ -274,6 +276,20 @@ "ActionType": "AnotherActionType", "UrlLink": "https://www.anoteraction.com", "UrlLabel": "AnotherLabel" + }, + "DEFAULT_X": { + "ExternalRoutingCode": "DefaultX", + "ActionDescription": "DefaultXDescription", + "ActionType": "DefaultXType", + "UrlLink": "https://www.defaultxaction.com", + "UrlLabel": "DefaultXLabel" + }, + "DEFAULT_Y": { + "ExternalRoutingCode": "DefaultY", + "ActionDescription": "DefaultYDescription", + "ActionType": "DefaultYType", + "UrlLink": "https://www.defaultyaction.com", + "UrlLabel": "DefaultYLabel" } } } diff --git a/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_001.json b/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_001.json index 9e7a48d01..7a5366d27 100644 --- a/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_001.json +++ b/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_001.json @@ -1,275 +1,275 @@ - { - "CampaignConfig": { - "ID": "<>", - "Version": "1", - "Name": "Automation RSV - Smoke Test Config", - "Type": "V", - "Target": "RSV", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", - "IterationFrequency": "X", - "IterationType": "O", - "IterationTime": "07:00:00", - "DefaultCommsRouting": "BOOK_LOCAL", - "StartDate": "20250601", - "EndDate": "20260601", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Iterations": [ - { - "ID": ",<>", - "DefaultCommsRouting": "BOOK_LOCAL", - "Version": "1", - "Name": "Automation RSV - Smoke Test Config Iteration", - "IterationDate": "20250601", - "IterationNumber": 1, - "CommsType": "I", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Type": "O", - "IterationCohorts": [ - { - "CohortLabel": "rsv_75_rolling", - "CohortGroup": "rsv_age_rolling", - "PositiveDescription": "are aged 75 to 79 years old.", - "NegativeDescription": "are not aged 75 to 79 years old.", - "Priority": 0 - }, - { - "CohortLabel": "rsv_75to79_2024", - "CohortGroup": "rsv_age_catchup", - "PositiveDescription": "turned 80 between 2nd September 2024 and 31st August 2025", - "NegativeDescription": "did not turn 80 between 2nd September 2024 and 31st August 2025", - "Priority": 10 - }, - { - "CohortLabel": "elid_all_people", - "CohortGroup": "elid_all_people", - "PositiveDescription": "", - "NegativeDescription": "", - "Priority": 20 - }, - { - "CohortLabel": "no_group_description", - "CohortGroup": "", - "PositiveDescription": "", - "NegativeDescription": "", - "Priority": 30 - } - ], - "IterationRules": [ - { - "Type": "F", - "Name": "Assure only already vaccinated taken from magic cohort", - "Description": "Exclude anyone who has NOT been given a dose of RSV Vaccination from the magic cohort", - "Operator": "is_empty", - "Comparator": "", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "LAST_SUCCESSFUL_DATE", - "CohortLabel": "elid_all_people", - "Priority": 100 - }, - { - "Type": "F", - "Name": "Under Age - Under 75 Years on day of execution", - "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", - "Priority": 120, - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Operator": "Y>", - "Comparator": "-75", - "CohortLabel": "rsv_75to79_2024" - }, - { - "Type": "F", - "Name": "Under Age - Under 75 Years on day of execution", - "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", - "Priority": 125, - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Operator": "Y>", - "Comparator": "-75", - "CohortLabel": "rsv_75_rolling" - }, - { - "Type": "F", - "Name": "Exclude Too OLD", - "Description": "Exclude anyone over 80", - "Priority": 130, - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Operator": "<", - "Comparator": "-80" - }, - { - "Type": "S", - "Name": "AlreadyVaccinated", - "Description": "##You've had your RSV vaccination\nWe believe you had your vaccination on <>.", - "Priority": 550, - "AttributeLevel": "TARGET", - "AttributeTarget": "RSV", - "AttributeName": "LAST_SUCCESSFUL_DATE", - "Operator": "Y>=", - "Comparator": "-25", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "NotAvailable", - "Description": "NotAvailable|Vaccinations are not currently available.", - "Priority": 510, - "AttributeLevel": "PERSON", - "AttributeName": "ICB", - "Operator": "=", - "Comparator": "SUPPRESSED_ICB" - }, - { - "Type": "S", - "Name": "NotYetDue", - "Description": "NotYetDue|Your next dose is not yet due.", - "Priority": 520, - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "LAST_SUCCESSFUL_DATE", - "Operator": "=", - "Comparator": "20250326", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "TooClose", - "Description": "TooClose|Your previous vaccination was less than 91 days ago.", - "Priority": 530, - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "LAST_SUCCESSFUL_DATE", - "Operator": "=", - "Comparator": "20250327", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "OtherSetting", - "Description": "OtherSetting|## Getting the vaccine\n\nOur record show you're living in a setting where care is provided.\n\nIf you think you should have the RSV vaccine, speak to a member of staff where you live.", - "Priority": 540, - "AttributeLevel": "PERSON", - "AttributeName": "CARE_HOME_FLAG", - "Operator": "=", - "Comparator": "Y" - }, - { - "Type": "R", - "Name": "Actionable Not Vaccinated", - "Description": "Book An Appointment", - "Priority": 1010, - "Operator": "is_empty", - "Comparator": "", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "LAST_SUCCESSFUL_DATE", - "CommsRouting": "BOOK_NBS" - }, - { - "Type": "R", - "Name": "Actionable Not Vaccinated", - "Description": "Book An Appointment", - "Priority": 1010, - "Operator": "=", - "Comparator": "LS2", - "AttributeLevel": "PERSON", - "AttributeName": "POSTCODE_SECTOR", - "CommsRouting": "BOOK_NBS" - }, - { - "Type": "R", - "Name": "Actionable Future Booked Appointment", - "Description": "Actionable Future Booked Appointment", - "Priority": 1020, - "Operator": ">=", - "Comparator": "0", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_DATE", - "CommsRouting": "AMEND_NBS" - }, - { - "Type": "R", - "Name": "Actionable Future Booked Appointment", - "Description": "Actionable Future Booked Appointment", - "Priority": 1020, - "Operator": "=", - "Comparator": "NBS", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", - "CommsRouting": "AMEND_NBS" - }, - { - "Type": "R", - "Name": "Actionable Future Booked Appointment", - "Description": "Actionable Future Booked Appointment", - "Priority": 1030, - "Operator": ">=", - "Comparator": "0", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_DATE", - "CommsRouting": "MANAGE_LOCAL" - }, - { - "Type": "R", - "Name": "Actionable Future Booked Appointment", - "Description": "Actionable Future Booked Appointment", - "Priority": 1030, - "Operator": "!=", - "Comparator": "NBS", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", - "CommsRouting": "MANAGE_LOCAL" - } - ], - "ActionsMapper": { - "BOOK_NBS": { - "ExternalRoutingCode": "BookNBS", - "ActionDescription": "", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Continue to booking" - }, - "AMEND_NBS": { - "ExternalRoutingCode": "AmendNBS", - "ActionDescription": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Manage your appointment" - }, - "CONTACT_GP": { - "ExternalRoutingCode": "ContactGP", - "ActionDescription": "Contact your GP", - "ActionType": "InfoText" - }, - "BOOK_LOCAL": { - "ExternalRoutingCode": "BookLocal", - "ActionDescription": "##Getting the vaccine\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", - "ActionType": "InfoText" - }, - "MANAGE_LOCAL": { - "ExternalRoutingCode": "ManageLocal", - "ActionDescription": "##You have an RSV vaccination appointment\nContact your healthcare provider to change or cancel your appointment.", - "ActionType": "CardWithText" - - }, - "CHECK_CORRECT": { - "ExternalRoutingCode": "CheckCorrect", - "ActionDescription": "##If you think this is incorrect\\nIf you have not had this vaccination and you think you should, speak to your healthcare professional.", - "ActionType": "InfoText" - - } +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "Automation RSV - Smoke Test Config", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "BOOK_LOCAL", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "BOOK_LOCAL", + "Version": "1", + "Name": "Automation RSV - Smoke Test Config Iteration", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "DefaultNotEligibleRouting": "", + "DefaultNotActionableRouting": "", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_75_rolling", + "CohortGroup": "rsv_age_rolling", + "PositiveDescription": "are aged 75 to 79 years old.", + "NegativeDescription": "are not aged 75 to 79 years old.", + "Priority": 0 + }, + { + "CohortLabel": "rsv_75to79_2024", + "CohortGroup": "rsv_age_catchup", + "PositiveDescription": "turned 80 between 2nd September 2024 and 31st August 2025", + "NegativeDescription": "did not turn 80 between 2nd September 2024 and 31st August 2025", + "Priority": 10 + }, + { + "CohortLabel": "elid_all_people", + "CohortGroup": "elid_all_people", + "PositiveDescription": "", + "NegativeDescription": "", + "Priority": 20 + }, + { + "CohortLabel": "no_group_description", + "CohortGroup": "", + "PositiveDescription": "", + "NegativeDescription": "", + "Priority": 30 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "Assure only already vaccinated taken from magic cohort", + "Description": "Exclude anyone who has NOT been given a dose of RSV Vaccination from the magic cohort", + "Operator": "is_empty", + "Comparator": "", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "elid_all_people", + "Priority": 100 + }, + { + "Type": "F", + "Name": "Under Age - Under 75 Years on day of execution", + "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", + "Priority": 120, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75", + "CohortLabel": "rsv_75to79_2024" + }, + { + "Type": "F", + "Name": "Under Age - Under 75 Years on day of execution", + "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", + "Priority": 125, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75", + "CohortLabel": "rsv_75_rolling" + }, + { + "Type": "F", + "Name": "Exclude Too OLD", + "Description": "Exclude anyone over 80", + "Priority": 130, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "<", + "Comparator": "-80" + }, + { + "Type": "S", + "Name": "AlreadyVaccinated", + "Description": "##You've had your RSV vaccination\nWe believe you had your vaccination on <>.", + "Priority": 550, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "Y>=", + "Comparator": "-25", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "NotAvailable", + "Description": "NotAvailable|Vaccinations are not currently available.", + "Priority": 510, + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "Operator": "=", + "Comparator": "SUPPRESSED_ICB" + }, + { + "Type": "S", + "Name": "NotYetDue", + "Description": "NotYetDue|Your next dose is not yet due.", + "Priority": 520, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "=", + "Comparator": "20250326", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "TooClose", + "Description": "TooClose|Your previous vaccination was less than 91 days ago.", + "Priority": 530, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "=", + "Comparator": "20250327", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "OtherSetting", + "Description": "OtherSetting|## Getting the vaccine\n\nOur record show you're living in a setting where care is provided.\n\nIf you think you should have the RSV vaccine, speak to a member of staff where you live.", + "Priority": 540, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y" + }, + { + "Type": "R", + "Name": "Actionable Not Vaccinated", + "Description": "Book An Appointment", + "Priority": 1010, + "Operator": "is_empty", + "Comparator": "", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CommsRouting": "BOOK_NBS" + }, + { + "Type": "R", + "Name": "Actionable Not Vaccinated", + "Description": "Book An Appointment", + "Priority": 1010, + "Operator": "=", + "Comparator": "LS2", + "AttributeLevel": "PERSON", + "AttributeName": "POSTCODE_SECTOR", + "CommsRouting": "BOOK_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1020, + "Operator": ">=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1020, + "Operator": "=", + "Comparator": "NBS", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1030, + "Operator": ">=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "MANAGE_LOCAL" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1030, + "Operator": "!=", + "Comparator": "NBS", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", + "CommsRouting": "MANAGE_LOCAL" + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + }, + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Manage your appointment" + }, + "CONTACT_GP": { + "ExternalRoutingCode": "ContactGP", + "ActionDescription": "Contact your GP", + "ActionType": "InfoText" + }, + "BOOK_LOCAL": { + "ExternalRoutingCode": "BookLocal", + "ActionDescription": "##Getting the vaccine\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "ActionType": "InfoText" + }, + "MANAGE_LOCAL": { + "ExternalRoutingCode": "ManageLocal", + "ActionDescription": "##You have an RSV vaccination appointment\nContact your healthcare provider to change or cancel your appointment.", + "ActionType": "CardWithText" + }, + "CHECK_CORRECT": { + "ExternalRoutingCode": "CheckCorrect", + "ActionDescription": "##If you think this is incorrect\nIf you have not had this vaccination and you think you should, speak to your healthcare professional.", + "ActionType": "InfoText" } } - ] - } + } + ] } +} diff --git a/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_008.json b/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_008.json index 8e75aedf9..a0dffe1e7 100644 --- a/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_008.json +++ b/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_008.json @@ -25,6 +25,8 @@ "IterationDate": "20250601", "IterationNumber": 1, "CommsType": "I", + "DefaultNotEligibleRouting": "", + "DefaultNotActionableRouting": "", "ApprovalMinimum": 0, "ApprovalMaximum": 0, "Type": "O", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-155.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-155.json index adf1635f5..59ff46862 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-155.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-155.json @@ -25,6 +25,8 @@ "IterationDate": "20250601", "IterationNumber": 1, "CommsType": "I", + "DefaultNotEligibleRouting": "", + "DefaultNotActionableRouting": "", "ApprovalMinimum": 0, "ApprovalMaximum": 0, "Type": "M", @@ -41,7 +43,7 @@ "CohortGroup": "eli_155_out_group", "PositiveDescription": "You are currently in eli_155_out", "NegativeDescription": "You are not currently in eli_155_out", - "Priority": 0 + "Priority": 1 } ], "IterationRules": [ diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json index be3e30eb1..ae164ce7e 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json @@ -25,6 +25,8 @@ "IterationDate": "20250601", "IterationNumber": 1, "CommsType": "I", + "DefaultNotEligibleRouting": "", + "DefaultNotActionableRouting": "", "ApprovalMinimum": 0, "ApprovalMaximum": 0, "Type": "M", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-1.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-1.json index 48bd7919e..49101eb29 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-1.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-1.json @@ -25,6 +25,8 @@ "IterationDate": "20250601", "IterationNumber": 1, "CommsType": "I", + "DefaultNotEligibleRouting": "", + "DefaultNotActionableRouting": "", "ApprovalMinimum": 0, "ApprovalMaximum": 0, "Type": "M", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-2.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-2.json index 29ebbd38c..9cc24bf42 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-2.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-2.json @@ -25,6 +25,8 @@ "IterationDate": "20250601", "IterationNumber": 1, "CommsType": "I", + "DefaultNotEligibleRouting": "", + "DefaultNotActionableRouting": "", "ApprovalMinimum": 0, "ApprovalMaximum": 0, "Type": "M", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-3.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-3.json index f5466dbfc..b2f044036 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-3.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-3.json @@ -25,6 +25,8 @@ "IterationDate": "20250601", "IterationNumber": 1, "CommsType": "I", + "DefaultNotEligibleRouting": "", + "DefaultNotActionableRouting": "", "ApprovalMinimum": 0, "ApprovalMaximum": 0, "Type": "M", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-1.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-1.json new file mode 100644 index 000000000..5a4ee801c --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-1.json @@ -0,0 +1,123 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "ELI-295 - include generic text for Not Eligible (X/Y Rules)", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "CONTACT_GP", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "DEFAULT_A", + "DefaultNotEligibleRouting": "", + "DefaultNotActionableRouting": "", + "Version": "1", + "Name": "ELI-295 - Iteration", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "M", + "IterationCohorts": [ + { + "CohortLabel": "eli_295_cohort", + "CohortGroup": "eli_295_group", + "PositiveDescription": "You are currently in eli_295", + "NegativeDescription": "You are not currently in eli_295", + "Priority": 0 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "Test F Rule", + "Description": "Test F Rule", + "Priority": 100, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75" + }, + { + "Type": "S", + "Name": "Test S Rule", + "Description": "Test S Rule", + "Priority": 500, + "AttributeLevel": "PERSON", + "AttributeName": "NHS_NUMBER", + "Operator": "=", + "Comparator": "9900029502" + }, + { + "Type": "R", + "Name": "Actionable if future appointment is booked", + "Description": "Book An Appointment", + "Priority": 1000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D>=", + "Comparator": "0", + "CommsRouting": "AMEND_NBS" + } + ], + "ActionsMapper": { + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "##You have an RSV vaccination appointment\\nYou can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Manage your appointment" + }, + "DEFAULT_A": { + "ExternalRoutingCode": "DefaultA", + "ActionDescription": "DefaultADescription", + "ActionType": "DefaultAType", + "UrlLink": "https://www.defaultaaction.com", + "UrlLabel": "DefaultALabel" + }, + "DEFAULT_X": { + "ExternalRoutingCode": "DefaultX", + "ActionDescription": "DefaultXDescription", + "ActionType": "DefaultXType", + "UrlLink": "https://www.defaultxaction.com", + "UrlLabel": "DefaultXLabel" + }, + "OTHER_X": { + "ExternalRoutingCode": "OtherX", + "ActionDescription": "OtherXDescription", + "ActionType": "OtherXType", + "UrlLink": "https://www.otherxaction.com", + "UrlLabel": "OtherXLabel" + }, + "OTHER_Y": { + "ExternalRoutingCode": "OtherY", + "ActionDescription": "OtherYDescription", + "ActionType": "OtherYType", + "UrlLink": "https://www.otheryaction.com", + "UrlLabel": "OtherYLabel" + }, + "DEFAULT_Y": { + "ExternalRoutingCode": "DefaultY", + "ActionDescription": "DefaultYDescription", + "ActionType": "DefaultYType", + "UrlLink": "https://www.defaultyaction.com", + "UrlLabel": "DefaultYLabel" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-2.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-2.json new file mode 100644 index 000000000..9496a4989 --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-2.json @@ -0,0 +1,145 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "2", + "Name": "ELI-295 - include generic text for Not Eligible (X/Y Rules)", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "CONTACT_GP", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "", + "DefaultNotEligibleRouting": "DEFAULT_X", + "DefaultNotActionableRouting": "DEFAULT_Y", + "Version": "1", + "Name": "ELI-295 - Iteration", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "M", + "IterationCohorts": [ + { + "CohortLabel": "eli_295_cohort", + "CohortGroup": "eli_295_group", + "PositiveDescription": "You are currently in eli_295", + "NegativeDescription": "You are not currently in eli_295", + "Priority": 0 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "Test F Rule", + "Description": "Test F Rule", + "Priority": 100, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75" + }, + { + "Type": "S", + "Name": "Test S Rule", + "Description": "Test S Rule", + "Priority": 500, + "AttributeLevel": "PERSON", + "AttributeName": "NHS_NUMBER", + "Operator": "in", + "Comparator": "9900029502,9900029505,9900029508,9900029511" + }, + { + "Type": "R", + "Name": "Actionable if future appointment is booked", + "Description": "Book An Appointment", + "Priority": 1000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D>=", + "Comparator": "0", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "X", + "Name": "Test X Rule", + "Description": "Test X Rule", + "Priority": 2000, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75", + "CommsRouting": "OTHER_X" + }, + { + "Type": "Y", + "Name": "Test Y Rule", + "Description": "Test Y Rule", + "Priority": 3000, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y<", + "Comparator": "-79", + "CommsRouting": "OTHER_Y" + } + ], + "ActionsMapper": { + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "##You have an RSV vaccination appointment\\nYou can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Manage your appointment" + }, + "DEFAULT_A": { + "ExternalRoutingCode": "DefaultA", + "ActionDescription": "DefaultADescription", + "ActionType": "DefaultAType", + "UrlLink": "https://www.defaultaaction.com", + "UrlLabel": "DefaultALabel" + }, + "DEFAULT_X": { + "ExternalRoutingCode": "DefaultX", + "ActionDescription": "DefaultXDescription", + "ActionType": "DefaultXType", + "UrlLink": "https://www.defaultxaction.com", + "UrlLabel": "DefaultXLabel" + }, + "OTHER_X": { + "ExternalRoutingCode": "OtherX", + "ActionDescription": "OtherXDescription", + "ActionType": "OtherXType", + "UrlLink": "https://www.otherxaction.com", + "UrlLabel": "OtherXLabel" + }, + "OTHER_Y": { + "ExternalRoutingCode": "OtherY", + "ActionDescription": "OtherYDescription", + "ActionType": "OtherYType", + "UrlLink": "https://www.otheryaction.com", + "UrlLabel": "OtherYLabel" + }, + "DEFAULT_Y": { + "ExternalRoutingCode": "DefaultY", + "ActionDescription": "DefaultYDescription", + "ActionType": "DefaultYType", + "UrlLink": "https://www.defaultyaction.com", + "UrlLabel": "DefaultYLabel" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-3.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-3.json new file mode 100644 index 000000000..349db97af --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-3.json @@ -0,0 +1,145 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "ELI-295 - include generic text for Not Eligible (X/Y Rules)", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "CONTACT_GP", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "", + "DefaultNotEligibleRouting": "DEFAULT_X", + "DefaultNotActionableRouting": "DEFAULT_Y", + "Version": "1", + "Name": "ELI-295 - Iteration", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "M", + "IterationCohorts": [ + { + "CohortLabel": "eli_295_cohort", + "CohortGroup": "eli_295_group", + "PositiveDescription": "You are currently in eli_295", + "NegativeDescription": "You are not currently in eli_295", + "Priority": 0 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "Test F Rule", + "Description": "Test F Rule", + "Priority": 100, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75" + }, + { + "Type": "S", + "Name": "Test S Rule", + "Description": "Test S Rule", + "Priority": 500, + "AttributeLevel": "PERSON", + "AttributeName": "NHS_NUMBER", + "Operator": "in", + "Comparator": "9900029502,9900029505,9900029508,9900029511,9900029514" + }, + { + "Type": "R", + "Name": "Actionable if future appointment is booked", + "Description": "Book An Appointment", + "Priority": 1000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D>=", + "Comparator": "0", + "CommsRouting": "AMEND_NBS|DEFAULT_A" + }, + { + "Type": "X", + "Name": "Test X Rule", + "Description": "Test X Rule", + "Priority": 2000, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75", + "CommsRouting": "DEFAULT_X|OTHER_X" + }, + { + "Type": "Y", + "Name": "Test Y Rule", + "Description": "Test Y Rule", + "Priority": 3000, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y<", + "Comparator": "-79", + "CommsRouting": "DEFAULT_Y|OTHER_Y" + } + ], + "ActionsMapper": { + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "##You have an RSV vaccination appointment\\nYou can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Manage your appointment" + }, + "DEFAULT_A": { + "ExternalRoutingCode": "DefaultA", + "ActionDescription": "DefaultADescription", + "ActionType": "DefaultAType", + "UrlLink": "https://www.defaultaaction.com", + "UrlLabel": "DefaultALabel" + }, + "DEFAULT_X": { + "ExternalRoutingCode": "DefaultX", + "ActionDescription": "DefaultXDescription", + "ActionType": "DefaultXType", + "UrlLink": "https://www.defaultxaction.com", + "UrlLabel": "DefaultXLabel" + }, + "OTHER_X": { + "ExternalRoutingCode": "OtherX", + "ActionDescription": "OtherXDescription", + "ActionType": "OtherXType", + "UrlLink": "https://www.otherxaction.com", + "UrlLabel": "OtherXLabel" + }, + "OTHER_Y": { + "ExternalRoutingCode": "OtherY", + "ActionDescription": "OtherYDescription", + "ActionType": "OtherYType", + "UrlLink": "https://www.otheryaction.com", + "UrlLabel": "OtherYLabel" + }, + "DEFAULT_Y": { + "ExternalRoutingCode": "DefaultY", + "ActionDescription": "DefaultYDescription", + "ActionType": "DefaultYType", + "UrlLink": "https://www.defaultyaction.com", + "UrlLabel": "DefaultYLabel" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-4.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-4.json new file mode 100644 index 000000000..fcb5bc463 --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-4.json @@ -0,0 +1,167 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "ELI-295 - include generic text for Not Eligible (X/Y Rules)", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "CONTACT_GP", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "", + "DefaultNotEligibleRouting": "DEFAULT_X", + "DefaultNotActionableRouting": "DEFAULT_Y", + "Version": "1", + "Name": "ELI-295 - Iteration", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "M", + "IterationCohorts": [ + { + "CohortLabel": "eli_295_cohort", + "CohortGroup": "eli_295_group", + "PositiveDescription": "You are currently in eli_295", + "NegativeDescription": "You are not currently in eli_295", + "Priority": 0 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "Test F Rule", + "Description": "Test F Rule", + "Priority": 100, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75" + }, + { + "Type": "S", + "Name": "Test S Rule", + "Description": "Test S Rule", + "Priority": 500, + "AttributeLevel": "PERSON", + "AttributeName": "NHS_NUMBER", + "Operator": "in", + "Comparator": "9900029517" + }, + { + "Type": "R", + "Name": "Actionable if future appointment is booked", + "Description": "Book An Appointment", + "Priority": 1000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D>=", + "Comparator": "0", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "X", + "Name": "Test X Rule", + "Description": "Test X Rule", + "Priority": 2000, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75", + "CommsRouting": "LOWER_X" + }, + { + "Type": "X", + "Name": "Test X Rule", + "Description": "Test X Rule", + "Priority": 1999, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75", + "CommsRouting": "HIGHER_X" + }, + { + "Type": "Y", + "Name": "Test Y Rule", + "Description": "Test Y Rule", + "Priority": 3000, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y<", + "Comparator": "-79", + "CommsRouting": "LOWER_Y" + }, + { + "Type": "Y", + "Name": "Test Y Rule", + "Description": "Test Y Rule", + "Priority": 2999, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y<", + "Comparator": "-79", + "CommsRouting": "HIGHER_Y" + } + ], + "ActionsMapper": { + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "##You have an RSV vaccination appointment\\nYou can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Manage your appointment" + }, + "DEFAULT_A": { + "ExternalRoutingCode": "DefaultA", + "ActionDescription": "DefaultADescription", + "ActionType": "DefaultAType", + "UrlLink": "https://www.defaultaaction.com", + "UrlLabel": "DefaultALabel" + }, + "HIGHER_X": { + "ExternalRoutingCode": "HigherX", + "ActionDescription": "HigherXDescription", + "ActionType": "HigherXType", + "UrlLink": "https://www.higherxaction.com", + "UrlLabel": "HigherXLabel" + }, + "LOWER_X": { + "ExternalRoutingCode": "LowerX", + "ActionDescription": "LowerXDescription", + "ActionType": "LowerXType", + "UrlLink": "https://www.lowerxaction.com", + "UrlLabel": "LowerXLabel" + }, + "LOWER_Y": { + "ExternalRoutingCode": "LowerY", + "ActionDescription": "LowerYDescription", + "ActionType": "LowerYType", + "UrlLink": "https://www.loweryaction.com", + "UrlLabel": "LowerYLabel" + }, + "HIGHER_Y": { + "ExternalRoutingCode": "HigherY", + "ActionDescription": "HigherYDescription", + "ActionType": "HigherYType", + "UrlLink": "https://www.higheryaction.com", + "UrlLabel": "HigherYLabel" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-1.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-1.json index 7289f389e..beb979f0f 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-1.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-1.json @@ -25,6 +25,8 @@ "IterationDate": "20250601", "IterationNumber": 1, "CommsType": "I", + "DefaultNotEligibleRouting": "", + "DefaultNotActionableRouting": "", "ApprovalMinimum": 0, "ApprovalMaximum": 0, "Type": "M", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-2.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-2.json index 265be4bca..a6496f087 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-2.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-2.json @@ -25,6 +25,8 @@ "IterationDate": "20250601", "IterationNumber": 1, "CommsType": "I", + "DefaultNotEligibleRouting": "", + "DefaultNotActionableRouting": "", "ApprovalMinimum": 0, "ApprovalMaximum": 0, "Type": "M", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-3.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-3.json index 6f333034a..fc3724e47 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-3.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-3.json @@ -25,6 +25,8 @@ "IterationDate": "20250601", "IterationNumber": 1, "CommsType": "I", + "DefaultNotEligibleRouting": "", + "DefaultNotActionableRouting": "", "ApprovalMinimum": 0, "ApprovalMaximum": 0, "Type": "M", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-4.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-4.json index 29dc386e4..e5ec359c4 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-4.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-4.json @@ -25,6 +25,8 @@ "IterationDate": "20250601", "IterationNumber": 1, "CommsType": "I", + "DefaultNotEligibleRouting": "", + "DefaultNotActionableRouting": "", "ApprovalMinimum": 0, "ApprovalMaximum": 0, "Type": "M", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-5.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-5.json index 4fd9772c1..50c3bb0d1 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-5.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-5.json @@ -25,6 +25,8 @@ "IterationDate": "20250601", "IterationNumber": 1, "CommsType": "I", + "DefaultNotEligibleRouting": "", + "DefaultNotActionableRouting": "", "ApprovalMinimum": 0, "ApprovalMaximum": 0, "Type": "O", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-COVID.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-COVID.json index e284c5077..78bcae7e8 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-COVID.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-COVID.json @@ -25,6 +25,8 @@ "IterationDate": "20250601", "IterationNumber": 1, "CommsType": "I", + "DefaultNotEligibleRouting": "", + "DefaultNotActionableRouting": "", "ApprovalMinimum": 0, "ApprovalMaximum": 0, "Type": "O", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-MMR.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-MMR.json index 659b1f3e1..920f0a30d 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-MMR.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-MMR.json @@ -25,6 +25,8 @@ "IterationDate": "20250601", "IterationNumber": 1, "CommsType": "I", + "DefaultNotEligibleRouting": "", + "DefaultNotActionableRouting": "", "ApprovalMinimum": 0, "ApprovalMaximum": 0, "Type": "O", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-RSV.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-RSV.json index 4995bcd12..c6d20c966 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-RSV.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-RSV.json @@ -25,6 +25,8 @@ "IterationDate": "20250601", "IterationNumber": 1, "CommsType": "I", + "DefaultNotEligibleRouting": "", + "DefaultNotActionableRouting": "", "ApprovalMinimum": 0, "ApprovalMaximum": 0, "Type": "O", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-SCREENING-1.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-SCREENING-1.json index d66ae00fa..9048c1c86 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-SCREENING-1.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-SCREENING-1.json @@ -25,6 +25,8 @@ "IterationDate": "20250601", "IterationNumber": 1, "CommsType": "I", + "DefaultNotEligibleRouting": "", + "DefaultNotActionableRouting": "", "ApprovalMinimum": 0, "ApprovalMaximum": 0, "Type": "O", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-SCREENING-2.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-SCREENING-2.json index 9058c4cae..00ad0bfc6 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-SCREENING-2.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-SCREENING-2.json @@ -25,6 +25,8 @@ "IterationDate": "20250601", "IterationNumber": 1, "CommsType": "I", + "DefaultNotEligibleRouting": "", + "DefaultNotActionableRouting": "", "ApprovalMinimum": 0, "ApprovalMaximum": 0, "Type": "O", diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_050.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_050.json new file mode 100644 index 000000000..d99d67dca --- /dev/null +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_050.json @@ -0,0 +1,30 @@ +{ + "scenario_name": "RSV - Regression - NotActionable, Rule Stop Suppression", + "request_headers": {"nhs-login-nhs-number": "1000000050"}, + "config_filenames": ["AUTO_RSV_REG_001.json"], + "data": [ + { + "NHS_NUMBER": "1000000050", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS2", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "1000000050", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-01.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-01.json new file mode 100644 index 000000000..d87fc3925 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-01.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "ELI-295 - Not Eligible - Blank (\"\") Default X Rule - No Actions", + "request_headers": { + "nhs-login-nhs-number": "9900029501" + }, + "config_filenames": ["AUTO_RSV_ELI-295-1.json"], + "data": [ + { + "NHS_NUMBER": "9900029501", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_295_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029501", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-02.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-02.json new file mode 100644 index 000000000..3e91f83a1 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-02.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "ELI-295 - Not Actionable - Blank (\"\") Default Y Rule - No Actions", + "request_headers": { + "nhs-login-nhs-number": "9900029502" + }, + "config_filenames": ["AUTO_RSV_ELI-295-1.json"], + "data": [ + { + "NHS_NUMBER": "9900029502", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_295_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029502", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-03.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-03.json new file mode 100644 index 000000000..01369d3ac --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-03.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "ELI-295 - Actionable - Blank (\"\") Default R Rule - No Actions", + "request_headers": { + "nhs-login-nhs-number": "9900029503" + }, + "config_filenames": ["AUTO_RSV_ELI-295-2.json"], + "data": [ + { + "NHS_NUMBER": "9900029503", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_295_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029503", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-04.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-04.json new file mode 100644 index 000000000..3095869b1 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-04.json @@ -0,0 +1,45 @@ +{ + "scenario_name": "ELI-295 - Not Eligible -Trigger the Default X Rule", + "note": "triggers the default due to being 75", + "request_headers": { + "nhs-login-nhs-number": "9900029504" + }, + "config_filenames": ["AUTO_RSV_ELI-295-2.json"], + "data": [ + { + "NHS_NUMBER": "9900029504", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "other_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029504", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-05.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-05.json new file mode 100644 index 000000000..8728d9259 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-05.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "ELI-295 - Not Actionable -Trigger the Default Y Rule", + "request_headers": { + "nhs-login-nhs-number": "9900029505" + }, + "config_filenames": ["AUTO_RSV_ELI-295-2.json"], + "data": [ + { + "NHS_NUMBER": "9900029505", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_295_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029505", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-06.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-06.json new file mode 100644 index 000000000..0aaac4f16 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-06.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "ELI-295 - Actionable -Trigger the Default R Rule", + "request_headers": { + "nhs-login-nhs-number": "9900029506" + }, + "config_filenames": ["AUTO_RSV_ELI-295-1.json"], + "data": [ + { + "NHS_NUMBER": "9900029506", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_295_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029506", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-07.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-07.json new file mode 100644 index 000000000..4df6ccfb3 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-07.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "ELI-295 - NotEligible -Trigger an X Rule", + "request_headers": { + "nhs-login-nhs-number": "9900029507" + }, + "config_filenames": ["AUTO_RSV_ELI-295-2.json"], + "data": [ + { + "NHS_NUMBER": "9900029507", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_295_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029507", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-08.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-08.json new file mode 100644 index 000000000..77db115aa --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-08.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "ELI-295 - NotActionable -Trigger the Y Rule", + "request_headers": { + "nhs-login-nhs-number": "9900029508" + }, + "config_filenames": ["AUTO_RSV_ELI-295-2.json"], + "data": [ + { + "NHS_NUMBER": "9900029508", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_295_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029508", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-09.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-09.json new file mode 100644 index 000000000..73fa55e70 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-09.json @@ -0,0 +1,51 @@ +{ + "scenario_name": "ELI-295 - Actionable -Trigger the R Rule", + "request_headers": { + "nhs-login-nhs-number": "9900029509" + }, + "config_filenames": [ + "AUTO_RSV_ELI-295-2.json" + ], + "data": [ + { + "NHS_NUMBER": "9900029509", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_295_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029509", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900029509", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-10.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-10.json new file mode 100644 index 000000000..e333f6595 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-10.json @@ -0,0 +1,47 @@ +{ + "scenario_name": "ELI-295 - NotEligible -Trigger an X Rule - includeActions N", + "request_headers": { + "nhs-login-nhs-number": "9900029510" + }, + "query_params": { + "includeActions": "N" + }, + "config_filenames": ["AUTO_RSV_ELI-295-2.json"], + "data": [ + { + "NHS_NUMBER": "9900029510", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_295_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029510", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-11.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-11.json new file mode 100644 index 000000000..bb14c5273 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-11.json @@ -0,0 +1,49 @@ +{ + "scenario_name": "ELI-295 - NotActionable -Trigger the Y Rule - includeActions N", + "request_headers": { + "nhs-login-nhs-number": "9900029511" + }, + "query_params": { + "includeActions": "N" + }, + "config_filenames": [ + "AUTO_RSV_ELI-295-2.json" + ], + "data": [ + { + "NHS_NUMBER": "9900029511", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_295_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029511", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-12.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-12.json new file mode 100644 index 000000000..72b2d3e85 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-12.json @@ -0,0 +1,54 @@ +{ + "scenario_name": "ELI-295 - Actionable -Trigger the R Rule - includeActions N", + "request_headers": { + "nhs-login-nhs-number": "9900029512" + }, + "query_params": { + "includeActions": "N" + }, + "config_filenames": [ + "AUTO_RSV_ELI-295-2.json" + ], + "data": [ + { + "NHS_NUMBER": "9900029512", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_295_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029512", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900029512", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-13.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-13.json new file mode 100644 index 000000000..780af1bf8 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-13.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "ELI-295 - NotEligible -Trigger an X Rule - Mulitple Actions", + "request_headers": { + "nhs-login-nhs-number": "9900029513" + }, + "config_filenames": ["AUTO_RSV_ELI-295-3.json"], + "data": [ + { + "NHS_NUMBER": "9900029513", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_295_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029513", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-14.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-14.json new file mode 100644 index 000000000..3942e298d --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-14.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "ELI-295 - NotActionable -Trigger the Y Rule - Mulitple Actions", + "request_headers": { + "nhs-login-nhs-number": "9900029514" + }, + "config_filenames": ["AUTO_RSV_ELI-295-3.json"], + "data": [ + { + "NHS_NUMBER": "9900029514", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_295_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029514", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-15.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-15.json new file mode 100644 index 000000000..8e6190e47 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-15.json @@ -0,0 +1,51 @@ +{ + "scenario_name": "ELI-295 - Actionable -Trigger the R Rule - Mulitple Actions", + "request_headers": { + "nhs-login-nhs-number": "9900029515" + }, + "config_filenames": [ + "AUTO_RSV_ELI-295-3.json" + ], + "data": [ + { + "NHS_NUMBER": "9900029515", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_295_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029515", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900029515", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-16.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-16.json new file mode 100644 index 000000000..7a220f9a5 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-16.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "ELI-295 - NotEligible -Trigger an highest prio X Rule", + "request_headers": { + "nhs-login-nhs-number": "9900029516" + }, + "config_filenames": ["AUTO_RSV_ELI-295-4.json"], + "data": [ + { + "NHS_NUMBER": "9900029516", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_295_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029516", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-17.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-17.json new file mode 100644 index 000000000..6d13a98db --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-17.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "ELI-295 - NotActionable -Trigger the highest prio Y Rule", + "request_headers": { + "nhs-login-nhs-number": "9900029517" + }, + "config_filenames": ["AUTO_RSV_ELI-295-4.json"], + "data": [ + { + "NHS_NUMBER": "9900029517", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_295_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029517", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-01.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-01.json new file mode 100644 index 000000000..b0fa0933e --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-01.json @@ -0,0 +1,22 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T13:44:33.140529+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not currently in eli_295" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "fc6a6876-0ad8-45fb-85b3-9db42116d61b" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-02.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-02.json new file mode 100644 index 000000000..6ef823be4 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-02.json @@ -0,0 +1,28 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T13:54:13.465387+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "NotActionable", + "cohortText": "You are currently in eli_295" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "Test S Rule", + "ruleText": "Test S Rule", + "ruleType": "S" + } + ] + } + ], + "responseId": "81213bbe-8a5d-47be-8868-eb03bb37bb49" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-03.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-03.json new file mode 100644 index 000000000..0d4be9f79 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-03.json @@ -0,0 +1,23 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T13:54:13.465387+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in eli_295" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + ] + } + ], + "responseId": "81213bbe-8a5d-47be-8868-eb03bb37bb49" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-04.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-04.json new file mode 100644 index 000000000..24103b438 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-04.json @@ -0,0 +1,30 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T13:44:33.140529+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "DefaultX", + "actionType": "DefaultXType", + "description": "DefaultXDescription", + "urlLabel": "DefaultXLabel", + "urlLink": "https://www.defaultxaction.com/" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not currently in eli_295" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "fc6a6876-0ad8-45fb-85b3-9db42116d61b" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-05.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-05.json new file mode 100644 index 000000000..02a3d5f8f --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-05.json @@ -0,0 +1,36 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T13:54:13.465387+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "DefaultY", + "actionType": "DefaultYType", + "description": "DefaultYDescription", + "urlLabel": "DefaultYLabel", + "urlLink": "https://www.defaultyaction.com/" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "NotActionable", + "cohortText": "You are currently in eli_295" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "Test S Rule", + "ruleText": "Test S Rule", + "ruleType": "S" + } + ] + } + ], + "responseId": "81213bbe-8a5d-47be-8868-eb03bb37bb49" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-06.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-06.json new file mode 100644 index 000000000..2dcd834ed --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-06.json @@ -0,0 +1,30 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T13:54:13.465387+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "DefaultA", + "actionType": "DefaultAType", + "description": "DefaultADescription", + "urlLabel": "DefaultALabel", + "urlLink": "https://www.defaultaaction.com/" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in eli_295" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "81213bbe-8a5d-47be-8868-eb03bb37bb49" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-07.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-07.json new file mode 100644 index 000000000..3eeb4318e --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-07.json @@ -0,0 +1,30 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T14:29:05.049816+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "OtherX", + "actionType": "OtherXType", + "description": "OtherXDescription", + "urlLabel": "OtherXLabel", + "urlLink": "https://www.otherxaction.com/" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not currently in eli_295" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "5a263695-083e-4218-bbdf-969427913aa7" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-08.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-08.json new file mode 100644 index 000000000..7fee15448 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-08.json @@ -0,0 +1,36 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T14:33:55.562448+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "OtherY", + "actionType": "OtherYType", + "description": "OtherYDescription", + "urlLabel": "OtherYLabel", + "urlLink": "https://www.otheryaction.com/" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "NotActionable", + "cohortText": "You are currently in eli_295" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "Test S Rule", + "ruleText": "Test S Rule", + "ruleType": "S" + } + ] + } + ], + "responseId": "7e7578a6-cddf-4d9e-8552-9d5502bb4d4c" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-09.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-09.json new file mode 100644 index 000000000..3e391df5c --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-09.json @@ -0,0 +1,30 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T14:46:44.985757+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "AmendNBS", + "actionType": "ButtonWithAuthLink", + "description": "##You have an RSV vaccination appointment\\nYou can view, change or cancel your appointment below.", + "urlLabel": "Manage your appointment", + "urlLink": "http://www.nhs.uk/book-rsv" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in eli_295" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "65e95ff6-11e4-45e7-8844-d2242a422b88" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-10.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-10.json new file mode 100644 index 000000000..68f3ce5cb --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-10.json @@ -0,0 +1,21 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T14:29:05.049816+00:00" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not currently in eli_295" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "5a263695-083e-4218-bbdf-969427913aa7" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-11.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-11.json new file mode 100644 index 000000000..810c543d1 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-11.json @@ -0,0 +1,27 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T14:33:55.562448+00:00" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "NotActionable", + "cohortText": "You are currently in eli_295" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "Test S Rule", + "ruleText": "Test S Rule", + "ruleType": "S" + } + ] + } + ], + "responseId": "7e7578a6-cddf-4d9e-8552-9d5502bb4d4c" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-12.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-12.json new file mode 100644 index 000000000..a4f3e6b98 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-12.json @@ -0,0 +1,21 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T14:46:44.985757+00:00" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in eli_295" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "65e95ff6-11e4-45e7-8844-d2242a422b88" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-13.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-13.json new file mode 100644 index 000000000..2b95f2473 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-13.json @@ -0,0 +1,37 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T15:34:56.695680+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "DefaultX", + "actionType": "DefaultXType", + "description": "DefaultXDescription", + "urlLabel": "DefaultXLabel", + "urlLink": "https://www.defaultxaction.com/" + }, + { + "actionCode": "OtherX", + "actionType": "OtherXType", + "description": "OtherXDescription", + "urlLabel": "OtherXLabel", + "urlLink": "https://www.otherxaction.com/" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not currently in eli_295" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "6dbcf04d-9ae6-415d-b54f-6677c175dc6b" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-14.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-14.json new file mode 100644 index 000000000..a215f55e0 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-14.json @@ -0,0 +1,43 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T15:33:08.084080+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "DefaultY", + "actionType": "DefaultYType", + "description": "DefaultYDescription", + "urlLabel": "DefaultYLabel", + "urlLink": "https://www.defaultyaction.com/" + }, + { + "actionCode": "OtherY", + "actionType": "OtherYType", + "description": "OtherYDescription", + "urlLabel": "OtherYLabel", + "urlLink": "https://www.otheryaction.com/" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "NotActionable", + "cohortText": "You are currently in eli_295" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "Test S Rule", + "ruleText": "Test S Rule", + "ruleType": "S" + } + ] + } + ], + "responseId": "299ff75e-4d02-4f23-81ca-6bfbf1e075a5" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-15.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-15.json new file mode 100644 index 000000000..a436c06b1 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-15.json @@ -0,0 +1,37 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T15:31:37.704738+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "AmendNBS", + "actionType": "ButtonWithAuthLink", + "description": "##You have an RSV vaccination appointment\\nYou can view, change or cancel your appointment below.", + "urlLabel": "Manage your appointment", + "urlLink": "http://www.nhs.uk/book-rsv" + }, + { + "actionCode": "DefaultA", + "actionType": "DefaultAType", + "description": "DefaultADescription", + "urlLabel": "DefaultALabel", + "urlLink": "https://www.defaultaaction.com/" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in eli_295" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "65479e78-ee1d-4690-b29e-d75af4273d18" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-16.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-16.json new file mode 100644 index 000000000..1841f2b43 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-16.json @@ -0,0 +1,30 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T16:14:39.133500+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "HigherX", + "actionType": "HigherXType", + "description": "HigherXDescription", + "urlLabel": "HigherXLabel", + "urlLink": "https://www.higherxaction.com/" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not currently in eli_295" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "906fdcee-63f4-4585-a133-a3ee87c48d24" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-17.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-17.json new file mode 100644 index 000000000..2be0d2099 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-17.json @@ -0,0 +1,36 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T16:15:15.028078+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "HigherY", + "actionType": "HigherYType", + "description": "HigherYDescription", + "urlLabel": "HigherYLabel", + "urlLink": "https://www.higheryaction.com/" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "NotActionable", + "cohortText": "You are currently in eli_295" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "Test S Rule", + "ruleText": "Test S Rule", + "ruleType": "S" + } + ] + } + ], + "responseId": "33b7a6a5-3961-4e73-ad90-89dc5d61b219" +} diff --git a/tests/e2e/tests/eligibility_signposting/__init__.py b/tests/e2e/tests/eligibility_signposting/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/e2e/tests/eligibility_signposting/conftest.py b/tests/e2e/tests/eligibility_signposting/conftest.py deleted file mode 100644 index e9f3e570e..000000000 --- a/tests/e2e/tests/eligibility_signposting/conftest.py +++ /dev/null @@ -1,36 +0,0 @@ -"""Pytest fixtures for the Eligibility Signposting API tests.""" - -import pytest - -from tests.e2e.utils.api_client import ApiClient -from tests.e2e.utils.config import INVALID_NHS_NUMBER, VALID_NHS_NUMBER - - -@pytest.fixture -def api_client(): - """Create and return an instance of the API client. - - Returns: - ApiClient: Instance of the API client. - """ - return ApiClient() - - -@pytest.fixture -def valid_nhs_number(): - """Return a valid NHS number for testing. - - Returns: - str: Valid NHS number. - """ - return VALID_NHS_NUMBER - - -@pytest.fixture -def invalid_nhs_number(): - """Return an invalid NHS number for testing. - - Returns: - str: Invalid NHS number. - """ - return INVALID_NHS_NUMBER diff --git a/tests/e2e/tests/eligibility_signposting/test_eligibility_check.py b/tests/e2e/tests/eligibility_signposting/test_eligibility_check.py deleted file mode 100644 index d30b2e68c..000000000 --- a/tests/e2e/tests/eligibility_signposting/test_eligibility_check.py +++ /dev/null @@ -1,166 +0,0 @@ -"""Tests for the Eligibility Check endpoint.""" - -import jsonschema -import pytest -import requests - -from tests.e2e.utils.config import BASE_URL, ELIGIBILITY_CHECK_SCHEMA - -# HTTP Status Code Constants -HTTP_STATUS_OK = 200 -HTTP_STATUS_BAD_REQUEST = 400 -HTTP_STATUS_NOT_FOUND = 404 -HTTP_STATUS_SERVER_ERROR = 500 - - -# Check if the API is accessible -def is_api_accessible(): - """Check if the API is accessible. - - Returns: - bool: True if the API is accessible, False otherwise. - """ - try: - response = requests.get(f"{BASE_URL}/eligibility-check", timeout=5) - except requests.RequestException: - return False - else: - return response.status_code != HTTP_STATUS_NOT_FOUND - - -@pytest.mark.eligibility -class TestEligibilityCheck: - """Test suite for the Eligibility Check endpoint.""" - - @pytest.mark.smoke - def test_eligibility_check_success(self, api_client, valid_nhs_number): - """Test that the eligibility check endpoint returns a successful response. - - Args: - api_client: API client fixture. - valid_nhs_number: Valid NHS number fixture. - """ - # Make the API call - response = api_client.get_eligibility_check(valid_nhs_number) - - # Assert status code is 2xx - error_msg = f"Expected 2xx status code, got {response.status_code}" - assert HTTP_STATUS_OK <= response.status_code < HTTP_STATUS_BAD_REQUEST, error_msg - - # Assert content type is application/json - assert "application/json" in response.headers.get("Content-Type", ""), "Content-Type is not application/json" - - # Assert response has JSON body - response_json = response.json() - assert response_json, "Response does not have a JSON body" - - # Validate response against schema - try: - jsonschema.validate(instance=response_json, schema=ELIGIBILITY_CHECK_SCHEMA) - except jsonschema.exceptions.ValidationError as e: - pytest.fail(f"Response does not match schema: {e}") - - @pytest.mark.smoke - def test_eligibility_check_response_structure(self, api_client, valid_nhs_number): - """Test that the eligibility check response has the expected structure. - - Args: - api_client: API client fixture. - valid_nhs_number: Valid NHS number fixture. - """ - # Make the API call - response = api_client.get_eligibility_check(valid_nhs_number) - - # Assert status code is 2xx - error_msg = f"Expected 2xx status code, got {response.status_code}" - assert HTTP_STATUS_OK <= response.status_code < HTTP_STATUS_BAD_REQUEST, error_msg - - # Get response JSON - response_json = response.json() - - # Check for required fields - assert "responseId" in response_json, "Response does not contain responseId" - assert "meta" in response_json, "Response does not contain meta" - assert "processedSuggestions" in response_json, "Response does not contain processedSuggestions" - - # Check meta structure - assert "lastUpdated" in response_json["meta"], "Meta does not contain lastUpdated" - - # Check processedSuggestions structure if any exist - if response_json["processedSuggestions"]: - suggestion = response_json["processedSuggestions"][0] - assert "condition" in suggestion, "Suggestion does not contain condition" - assert "status" in suggestion, "Suggestion does not contain status" - assert suggestion["status"] in ["NotEligible", "NotActionable", "Actionable"], "Invalid status value" - - def test_eligibility_check_invalid_nhs_number(self, api_client, invalid_nhs_number): - """Test the behavior when an invalid NHS number is provided. - - Args: - api_client: API client fixture. - invalid_nhs_number: Invalid NHS number fixture. - """ - # Make the API call - response = api_client.get_eligibility_check(invalid_nhs_number) - - # Check the response - this could be a 4xx error or a specific response format - # depending on how the API handles invalid NHS numbers - if HTTP_STATUS_BAD_REQUEST <= response.status_code < HTTP_STATUS_SERVER_ERROR: - # If API returns an error code for invalid NHS numbers - assert True, "API correctly returned an error for invalid NHS number" - else: - # If API returns a success code but with specific content - response_json = response.json() - # Check if the response indicates no eligibility or an error message - # This will depend on the specific API behavior - assert response_json, "Response should contain data even for invalid NHS number" - - @pytest.mark.parametrize( - "query_param", - [ - "", # Missing NHS number - "patient=", # Empty NHS number - ], - ) - def test_eligibility_check_empty_parameters(self, api_client, query_param): - """Test the behavior when empty parameters are provided. - - Args: - api_client: API client fixture. - query_param: Query parameter to test. - """ - # Construct the endpoint with the query parameter - endpoint = f"/eligibility-check?{query_param}" if query_param else "/eligibility-check" - - # Make the API call - response = api_client.get(endpoint) - - # Check the response - expecting a 4xx error for invalid parameters - error_msg = f"Expected 4xx status code, got {response.status_code}" - assert HTTP_STATUS_BAD_REQUEST <= response.status_code < HTTP_STATUS_SERVER_ERROR, error_msg - - @pytest.mark.parametrize( - "query_param", - [ - "patient=ABC", # Non-numeric NHS number - ], - ) - def test_eligibility_check_invalid_parameters(self, api_client, query_param): - """Test the behavior when invalid parameters are provided. - - Args: - api_client: API client fixture. - query_param: Query parameter to test. - """ - # Construct the endpoint with the query parameter - endpoint = f"/eligibility-check?{query_param}" if query_param else "/eligibility-check" - - # Make the API call - response = api_client.get(endpoint) - - # We're now expecting a 2xx response even with invalid parameters - # The API handles invalid parameters gracefully - - # Assert status code is 2xx - error_msg = f"Expected 2xx status code, got {response.status_code}" - assert HTTP_STATUS_OK <= response.status_code < HTTP_STATUS_BAD_REQUEST, error_msg diff --git a/tests/e2e/tests/test_error_scenario_tests.py b/tests/e2e/tests/test_error_scenario_tests.py index 4e29894e8..13237767f 100644 --- a/tests/e2e/tests/test_error_scenario_tests.py +++ b/tests/e2e/tests/test_error_scenario_tests.py @@ -4,7 +4,7 @@ from tests.e2e.utils.s3_config_manager import delete_all_configs_from_s3 - +@pytest.mark.errorscenarios @pytest.mark.smoketest def test_check_for_missing_person(eligibility_client): nhs_number = "9934567890" @@ -43,7 +43,7 @@ def test_check_for_missing_person(eligibility_client): assert response["headers"].get("Content-Type".lower()) == "application/fhir+json" -@pytest.mark.smoketest +@pytest.mark.errorscenarios @pytest.mark.parametrize( "test_case", [ @@ -148,6 +148,7 @@ def test_nhs_login_header_handling(eligibility_client, test_case): assert response["headers"].get("Content-Type".lower()) == "application/fhir+json" +@pytest.mark.errorscenarios @pytest.mark.parametrize( "test_case", [ @@ -225,7 +226,7 @@ def test_query_param_errors(eligibility_client, test_case): assert response["body"] == test_case["expected_body"], f"{test_case['scenario']} failed on response body" assert response["headers"].get("Content-Type".lower()) == "application/fhir+json" - +@pytest.mark.errorscenarios def test_no_config_error(eligibility_client): expected_response = { "id": "", diff --git a/tests/e2e/utils/api_client.py b/tests/e2e/utils/api_client.py deleted file mode 100644 index facce8ae8..000000000 --- a/tests/e2e/utils/api_client.py +++ /dev/null @@ -1,64 +0,0 @@ -"""API client module for making HTTP requests to the Eligibility Signposting API.""" - -import requests - -from tests.e2e.utils.config import API_KEY, BASE_URL - -# Default timeout for API requests in seconds -DEFAULT_TIMEOUT = 10 - - -class ApiClient: - """API client for making HTTP requests to the Eligibility Signposting API.""" - - def __init__(self, base_url=BASE_URL, api_key=API_KEY): - """Initialize the API client with base URL and API key. - - Args: - base_url (str, optional): Base URL for the API. Defaults to BASE_URL from config. - api_key (str, optional): API key for authentication. Defaults to API_KEY from config. - """ - self.base_url = base_url - self.api_key = api_key - self.headers = {"Accept": "application/json", "apikey": self.api_key} - - def get_eligibility_check(self, nhs_number): - """Make a GET request to the eligibility-check endpoint. - - Args: - nhs_number (str): NHS number to check eligibility for. - - Returns: - requests.Response: Response object from the API. - """ - url = f"{BASE_URL}/patient-check/{nhs_number}" - params = {"patient": nhs_number} - - return requests.get(url, headers=self.headers, params=params, timeout=DEFAULT_TIMEOUT) - - def get(self, endpoint, params=None): - """Make a generic GET request to the API. - - Args: - endpoint (str): API endpoint to call. - params (dict, optional): Query parameters. Defaults to None. - - Returns: - requests.Response: Response object from the API. - """ - url = f"{self.base_url}{endpoint}" - return requests.get(url, headers=self.headers, params=params, timeout=DEFAULT_TIMEOUT) - - def post(self, endpoint, data=None, json=None): - """Make a generic POST request to the API. - - Args: - endpoint (str): API endpoint to call. - data (dict, optional): Form data. Defaults to None. - json (dict, optional): JSON data. Defaults to None. - - Returns: - requests.Response: Response object from the API. - """ - url = f"{self.base_url}{endpoint}" - return requests.post(url, headers=self.headers, data=data, json=json, timeout=DEFAULT_TIMEOUT) diff --git a/tests/e2e/utils/config.py b/tests/e2e/utils/config.py deleted file mode 100644 index b0666ea7e..000000000 --- a/tests/e2e/utils/config.py +++ /dev/null @@ -1,146 +0,0 @@ -"""Configuration module for the test framework.""" - -import os - -from dotenv import load_dotenv - -# Load environment variables from .env file -load_dotenv() - -# API Configuration -BASE_URL = os.getenv("BASE_URL", "https://sandbox.api.service.nhs.uk/eligibility-signposting-api") -API_KEY = os.getenv("API_KEY", "srgedsrgveg") - -# Test Data -VALID_NHS_NUMBER = os.getenv("VALID_NHS_NUMBER", "50000000004") -INVALID_NHS_NUMBER = os.getenv("INVALID_NHS_NUMBER", "9876543210") - -# API Endpoints -ELIGIBILITY_CHECK_ENDPOINT = "/eligibility-check" - -# Response Schema -ELIGIBILITY_CHECK_SCHEMA = { - "type": "object", - "properties": { - "responseId": { - "type": "string", - "description": "GUID assigned when the decisioning evaluation is carried out.", - }, - "meta": { - "type": "object", - "properties": { - "lastUpdated": { - "type": "string", - "description": "Timestamp of when the decisioning evaluation is carried out.", - } - }, - }, - "processedSuggestions": { - "type": "array", - "description": "List of suggestions the person is eligible for.", - "items": { - "type": "object", - "properties": { - "condition": { - "type": "string", - "description": ( - "String representing the vaccine target disease, screening target or other scenario " - "requiring decision based suggestions, that this suggestion relates to" - ), - }, - "status": { - "type": "string", - "description": ( - "String representing an overall summary of the persons status for this processedSuggestion" - ), - "enum": ["NotEligible", "NotActionable", "Actionable"], - }, - "statusText": {"type": "string"}, - "eligibilityCohorts": { - "type": "array", - "description": "Cohorts that drove the eligibility status returned.", - "items": { - "type": "object", - "properties": { - "cohortCode": { - "type": "string", - "description": ( - "Machine readable code signifying the cohort/cohort group that lead to a " - "person's eligibility for this suggestion" - ), - }, - "cohortText": { - "type": "string", - "description": ( - "Human readable (render-able) text describing the meaning of a cohort/" - "cohort group that lead to a person's eligibility for this suggestion" - ), - }, - "cohortStatus": { - "type": "string", - "description": ( - "String representing the persons status for this processedSuggestion " - "in respect of this particular cohort or cohort group" - ), - "enum": ["NotEligible", "NotActionable", "Actionable"], - }, - }, - }, - }, - "suitablityRules": { - "type": "array", - "description": ( - "Reasons that the eligibility status was changed from the base eligibility " - "to result in its status to not be eligible or to be acted on" - ), - "items": { - "type": "object", - "properties": { - "ruleType": { - "type": "string", - "description": ( - "The type of a rule that triggered to amend the status of the suggestion" - ), - "enum": ["F", "S"], - }, - "ruleCode": { - "type": "string", - "description": ( - "Machine readable code signifying a rule that amended the status " - "of the suggestion" - ), - }, - "ruleText": { - "type": "string", - "description": ( - "Human readable (render-able) text describing a rule that amended " - "the status of the suggestion" - ), - }, - }, - }, - }, - "actions": { - "type": "array", - "description": "List of actions to be shown to the person.", - "items": { - "type": "object", - "properties": { - "actionType": { - "type": "string", - "description": "Type of action to render. E.g. A primary button, a link, text etc", - }, - "actionCode": { - "type": "string", - "description": "Code representing the action to be taken", - }, - "description": {"type": "string", "description": "A brief description of the step."}, - "urlLink": {"type": "string", "description": "URL to invoke if action selected."}, - }, - }, - }, - }, - }, - }, - }, -} diff --git a/tests/e2e/utils/data_helper.py b/tests/e2e/utils/data_helper.py index d07fa2ffa..338d24345 100644 --- a/tests/e2e/utils/data_helper.py +++ b/tests/e2e/utils/data_helper.py @@ -121,6 +121,25 @@ def load_all_test_scenarios(folder_path): return all_data, dto +def load_data_items_to_dynamo(folder_path): + dto = PlaceholderDTO() # Shared across all files + + for path in Path(folder_path).iterdir(): + if path.suffix != ".json": + continue + + with path.open() as f: + raw_json = json.load(f) + + raw_data = raw_json["data"] + + # Resolve placeholders with shared DTO + resolved_data = resolve_placeholders_in_data(raw_data, dto, path.name) + + # Insert immediately + insert_into_dynamo(resolved_data) + + def clean_responses(data: dict, ignore_keys: list) -> dict: return _mask_volatile_fields(data, ignore_keys) From 2aa891c1ca4720b9f42a9b42b3ccefcb93e8ef9a Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 21 Jul 2025 09:33:24 +0100 Subject: [PATCH 55/72] eli-222 cases --- .../storyTestConfigs/AUTO_RSV_ELI-222.json | 185 ++++++++++++++++++ .../storyTestData/AUTO_RSV_ELI-222-1.json | 50 +++++ .../storyTestData/AUTO_RSV_ELI-222-2.json | 50 +++++ .../storyTestData/AUTO_RSV_ELI-222-3.json | 34 ++++ .../storyTestData/AUTO_RSV_ELI-222-4.json | 49 +++++ .../storyTestData/AUTO_RSV_ELI-222-5.json | 51 +++++ .../storyTestData/AUTO_RSV_ELI-222-6.json | 44 +++++ .../AUTO_RSV_ELI-222-1.json | 24 +++ .../AUTO_RSV_ELI-222-2.json | 27 +++ .../AUTO_RSV_ELI-222-3.json | 22 +++ .../AUTO_RSV_ELI-222-4.json | 22 +++ .../AUTO_RSV_ELI-222-5.json | 22 +++ .../AUTO_RSV_ELI-222-6.json | 27 +++ 13 files changed, 607 insertions(+) create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-222.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-1.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-2.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-3.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-4.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-5.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-6.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-222-1.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-222-2.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-222-3.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-222-4.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-222-5.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-222-6.json diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-222.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-222.json new file mode 100644 index 000000000..0950f974f --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-222.json @@ -0,0 +1,185 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "ELI-222 - Implement magic cohort", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "CONTACT_GP", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "CONTACT_GP", + "Version": "1", + "Name": "ELI-222 - Implement magic cohort", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "DefaultNotEligibleRouting": "", + "DefaultNotActionableRouting": "", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "M", + "IterationCohorts": [ + { + "CohortLabel": "rsv_75to79", + "CohortGroup": "rsv_age", + "PositiveDescription": "are aged 75 to 79 years old.", + "NegativeDescription": "are not aged 75 to 79 years old.", + "Priority": 0 + }, + { + "CohortLabel": "rsv_80_since_02_Sept_2024", + "CohortGroup": "rsv_age_catchup", + "PositiveDescription": "turned 80 after 1st September 2024", + "NegativeDescription": "did not turn 80 after 1 September 2024", + "Priority": 10 + }, + { + "CohortLabel": "elid_all_people", + "CohortGroup": "magic_cohort", + "PositiveDescription": "", + "NegativeDescription": "", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "Assure only already vaccinated or those with a future booking are included in magic cohort", + "Description": "Exclude anyone who has NOT been given a dose of RSV Vaccination from the magic cohort", + "Operator": "is_empty", + "Comparator": "", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "elid_all_people", + "Priority": 100 + }, + { + "Type": "F", + "Name": "Assure only already vaccinated or those with a future booking are included in magic cohort", + "Description": "Exclude anyone from the magic cohort who does not have a future booking", + "Operator": "D<", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CohortLabel": "elid_all_people", + "Priority": 100 + }, + { + "Type": "F", + "Name": "Assure only already vaccinated or those with a future booking are included in magic cohort", + "Description": "Exclude anyone from the magic cohort who does not have a future booking", + "Operator": "is_empty", + "Comparator": "", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CohortLabel": "elid_all_people", + "Priority": 110 + }, + { + "Type": "F", + "Name": "Assure only already vaccinated or those with a future booking are included in magic cohort", + "Description": "Exclude anyone who has NOT been given a dose of RSV Vaccination from the magic cohort", + "Operator": "is_empty", + "Comparator": "", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "elid_all_people", + "Priority": 110 + }, + { + "Type": "S", + "Name": "Already Vaccinated", + "Description": "##You've had your RSV vaccination\nWe believe you had your vaccination.", + "Priority": 200, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "Y>=", + "Comparator": "-25", + "RuleStop": "Y" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1030, + "Operator": ">=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1030, + "Operator": "=", + "Comparator": "NBS", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1040, + "Operator": ">=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "MANAGE_LOCAL" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Appointment", + "Description": "Actionable Future Booked Appointment", + "Priority": 1040, + "Operator": "!=", + "Comparator": "NBS", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", + "CommsRouting": "MANAGE_LOCAL" + } + ], + "ActionsMapper": { + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "##You have an RSV vaccination appointment\\nYou can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Manage your appointment" + }, + "MANAGE_LOCAL": { + "ExternalRoutingCode": "ManageLocal", + "ActionDescription": "##You have an RSV vaccination appointment\nContact your healthcare provider to change or cancel your appointment.", + "ActionType": "CardWithText", + "UrlLink": null, + "UrlLabel": "" + } + } + } + ] + } +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-1.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-1.json new file mode 100644 index 000000000..d90ae3396 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-1.json @@ -0,0 +1,50 @@ +{ + "scenario_name": "RSV - Magic Cohort Has Future booking No Vaccination", + "request_headers": { + "nhs-login-nhs-number": "9900002221" + }, + "config_filenames": ["AUTO_RSV_ELI-222.json"], + "data": [ + { + "NHS_NUMBER": "9900002221", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "other_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900002221", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900002221", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-2.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-2.json new file mode 100644 index 000000000..40b51ee18 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-2.json @@ -0,0 +1,50 @@ +{ + "scenario_name": "RSV - Magic Cohort Has Past booking No Vaccination", + "request_headers": { + "nhs-login-nhs-number": "9900002222" + }, + "config_filenames": ["AUTO_RSV_ELI-222.json"], + "data": [ + { + "NHS_NUMBER": "9900002222", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "other_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900002222", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900002222", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-3.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-3.json new file mode 100644 index 000000000..0d69913fb --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-3.json @@ -0,0 +1,34 @@ +{ + "scenario_name": "RSV - Magic Cohort Has a Past RSV Vaccination - Future Booking", + "request_headers": { + "nhs-login-nhs-number": "9900002223" + }, + "config_filenames": ["AUTO_RSV_ELI-222.json"], + "data": [ + { + "NHS_NUMBER": "9900002223", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900002223", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": "<>", + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "ACC" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-4.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-4.json new file mode 100644 index 000000000..58e307ada --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-4.json @@ -0,0 +1,49 @@ +{ + "scenario_name": "RSV - Magic Cohort Has a Past RSV Vaccination - No Booking", + "request_headers": { + "nhs-login-nhs-number": "9900002224" + }, + "config_filenames": ["AUTO_RSV_ELI-222.json"], + "data": [ + { + "NHS_NUMBER": "9900002224", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "other_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900002224", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900002224", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-5.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-5.json new file mode 100644 index 000000000..f072103d9 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-5.json @@ -0,0 +1,51 @@ +{ + "scenario_name": "RSV - Magic Cohort Has a Past RSV Vaccination -Past Booking", + "request_headers": { + "nhs-login-nhs-number": "9900002225" + }, + "config_filenames": ["AUTO_RSV_ELI-222.json"], + "data": [ + { + "NHS_NUMBER": "9900002225", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "other_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900002225", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900002225", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": "<>", + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-6.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-6.json new file mode 100644 index 000000000..0461fb900 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-6.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "RSV -Magic Cohort No Vaccination - No Booking", + "request_headers": { + "nhs-login-nhs-number": "9900002226" + }, + "config_filenames": ["AUTO_RSV_ELI-222.json"], + "data": [ + { + "NHS_NUMBER": "9900002226", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "other_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900002226", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-222-1.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-222-1.json new file mode 100644 index 000000000..9b5387504 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-222-1.json @@ -0,0 +1,24 @@ +{ + "meta": { + "lastUpdated": "2025-07-16T10:32:10.803638+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "AmendNBS", + "actionType": "ButtonWithAuthLink", + "description": "##You have an RSV vaccination appointment\\nYou can view, change or cancel your appointment below.", + "urlLabel": "Manage your appointment", + "urlLink": "http://www.nhs.uk/book-rsv" + } + ], + "condition": "RSV", + "eligibilityCohorts": [], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "9b11bdb0-afa8-4ff7-994c-ee7738313d76" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-222-2.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-222-2.json new file mode 100644 index 000000000..afcbcfb43 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-222-2.json @@ -0,0 +1,27 @@ +{ + "meta": { + "lastUpdated": "2025-07-16T09:58:29.799271+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age", + "cohortStatus": "NotEligible", + "cohortText": "are not aged 75 to 79 years old." + }, + { + "cohortCode": "rsv_age_catchup", + "cohortStatus": "NotEligible", + "cohortText": "did not turn 80 after 1 September 2024" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "3eb44989-523d-48c5-a59d-011fbc7425a6" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-222-3.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-222-3.json new file mode 100644 index 000000000..398df9eee --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-222-3.json @@ -0,0 +1,22 @@ +{ + "meta": { + "lastUpdated": "2025-07-16T10:32:59.478617+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "Already Vaccinated", + "ruleText": "##You've had your RSV vaccination\nWe believe you had your vaccination.", + "ruleType": "S" + } + ] + } + ], + "responseId": "652229c3-0df6-4224-a41e-8482fe274bf5" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-222-4.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-222-4.json new file mode 100644 index 000000000..398df9eee --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-222-4.json @@ -0,0 +1,22 @@ +{ + "meta": { + "lastUpdated": "2025-07-16T10:32:59.478617+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "Already Vaccinated", + "ruleText": "##You've had your RSV vaccination\nWe believe you had your vaccination.", + "ruleType": "S" + } + ] + } + ], + "responseId": "652229c3-0df6-4224-a41e-8482fe274bf5" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-222-5.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-222-5.json new file mode 100644 index 000000000..398df9eee --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-222-5.json @@ -0,0 +1,22 @@ +{ + "meta": { + "lastUpdated": "2025-07-16T10:32:59.478617+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "Already Vaccinated", + "ruleText": "##You've had your RSV vaccination\nWe believe you had your vaccination.", + "ruleType": "S" + } + ] + } + ], + "responseId": "652229c3-0df6-4224-a41e-8482fe274bf5" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-222-6.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-222-6.json new file mode 100644 index 000000000..afcbcfb43 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-222-6.json @@ -0,0 +1,27 @@ +{ + "meta": { + "lastUpdated": "2025-07-16T09:58:29.799271+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age", + "cohortStatus": "NotEligible", + "cohortText": "are not aged 75 to 79 years old." + }, + { + "cohortCode": "rsv_age_catchup", + "cohortStatus": "NotEligible", + "cohortText": "did not turn 80 after 1 September 2024" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "3eb44989-523d-48c5-a59d-011fbc7425a6" +} From bb448eb6d7d9b57d4e75149ddbe0185051feeb80 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 21 Jul 2025 12:43:33 +0100 Subject: [PATCH 56/72] eli-295 cases --- .../AUTO_RSV_ELI_295-01.json | 44 +++++++++++++++ .../AUTO_RSV_ELI_295-02.json | 44 +++++++++++++++ .../AUTO_RSV_ELI_295-03.json | 44 +++++++++++++++ .../AUTO_RSV_ELI_295-04.json | 45 ++++++++++++++++ .../AUTO_RSV_ELI_295-05.json | 44 +++++++++++++++ .../AUTO_RSV_ELI_295-06.json | 44 +++++++++++++++ .../AUTO_RSV_ELI_295-07.json | 44 +++++++++++++++ .../AUTO_RSV_ELI_295-08.json | 44 +++++++++++++++ .../AUTO_RSV_ELI_295-09.json | 51 ++++++++++++++++++ .../AUTO_RSV_ELI_295-10.json | 47 ++++++++++++++++ .../AUTO_RSV_ELI_295-11.json | 49 +++++++++++++++++ .../AUTO_RSV_ELI_295-12.json | 54 +++++++++++++++++++ .../AUTO_RSV_ELI_295-13.json | 44 +++++++++++++++ .../AUTO_RSV_ELI_295-14.json | 44 +++++++++++++++ .../AUTO_RSV_ELI_295-15.json | 51 ++++++++++++++++++ .../AUTO_RSV_ELI_295-16.json | 44 +++++++++++++++ .../AUTO_RSV_ELI_295-17.json | 44 +++++++++++++++ .../AUTO_RSV_ELI_295-01.json | 22 ++++++++ .../AUTO_RSV_ELI_295-02.json | 28 ++++++++++ .../AUTO_RSV_ELI_295-03.json | 23 ++++++++ .../AUTO_RSV_ELI_295-04.json | 30 +++++++++++ .../AUTO_RSV_ELI_295-05.json | 36 +++++++++++++ .../AUTO_RSV_ELI_295-06.json | 30 +++++++++++ .../AUTO_RSV_ELI_295-07.json | 30 +++++++++++ .../AUTO_RSV_ELI_295-08.json | 36 +++++++++++++ .../AUTO_RSV_ELI_295-09.json | 30 +++++++++++ .../AUTO_RSV_ELI_295-10.json | 21 ++++++++ .../AUTO_RSV_ELI_295-11.json | 27 ++++++++++ .../AUTO_RSV_ELI_295-12.json | 21 ++++++++ .../AUTO_RSV_ELI_295-13.json | 37 +++++++++++++ .../AUTO_RSV_ELI_295-14.json | 43 +++++++++++++++ .../AUTO_RSV_ELI_295-15.json | 37 +++++++++++++ .../AUTO_RSV_ELI_295-16.json | 30 +++++++++++ .../AUTO_RSV_ELI_295-17.json | 36 +++++++++++++ 34 files changed, 1298 insertions(+) create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-01.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-02.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-03.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-04.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-05.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-06.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-07.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-08.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-09.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-10.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-11.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-12.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-13.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-14.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-15.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-16.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-17.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-01.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-02.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-03.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-04.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-05.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-06.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-07.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-08.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-09.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-10.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-11.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-12.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-13.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-14.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-15.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-16.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-17.json diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-01.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-01.json new file mode 100644 index 000000000..d87fc3925 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-01.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "ELI-295 - Not Eligible - Blank (\"\") Default X Rule - No Actions", + "request_headers": { + "nhs-login-nhs-number": "9900029501" + }, + "config_filenames": ["AUTO_RSV_ELI-295-1.json"], + "data": [ + { + "NHS_NUMBER": "9900029501", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_295_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029501", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-02.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-02.json new file mode 100644 index 000000000..3e91f83a1 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-02.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "ELI-295 - Not Actionable - Blank (\"\") Default Y Rule - No Actions", + "request_headers": { + "nhs-login-nhs-number": "9900029502" + }, + "config_filenames": ["AUTO_RSV_ELI-295-1.json"], + "data": [ + { + "NHS_NUMBER": "9900029502", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_295_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029502", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-03.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-03.json new file mode 100644 index 000000000..01369d3ac --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-03.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "ELI-295 - Actionable - Blank (\"\") Default R Rule - No Actions", + "request_headers": { + "nhs-login-nhs-number": "9900029503" + }, + "config_filenames": ["AUTO_RSV_ELI-295-2.json"], + "data": [ + { + "NHS_NUMBER": "9900029503", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_295_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029503", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-04.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-04.json new file mode 100644 index 000000000..3095869b1 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-04.json @@ -0,0 +1,45 @@ +{ + "scenario_name": "ELI-295 - Not Eligible -Trigger the Default X Rule", + "note": "triggers the default due to being 75", + "request_headers": { + "nhs-login-nhs-number": "9900029504" + }, + "config_filenames": ["AUTO_RSV_ELI-295-2.json"], + "data": [ + { + "NHS_NUMBER": "9900029504", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "other_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029504", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-05.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-05.json new file mode 100644 index 000000000..8728d9259 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-05.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "ELI-295 - Not Actionable -Trigger the Default Y Rule", + "request_headers": { + "nhs-login-nhs-number": "9900029505" + }, + "config_filenames": ["AUTO_RSV_ELI-295-2.json"], + "data": [ + { + "NHS_NUMBER": "9900029505", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_295_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029505", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-06.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-06.json new file mode 100644 index 000000000..0aaac4f16 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-06.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "ELI-295 - Actionable -Trigger the Default R Rule", + "request_headers": { + "nhs-login-nhs-number": "9900029506" + }, + "config_filenames": ["AUTO_RSV_ELI-295-1.json"], + "data": [ + { + "NHS_NUMBER": "9900029506", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_295_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029506", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-07.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-07.json new file mode 100644 index 000000000..4df6ccfb3 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-07.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "ELI-295 - NotEligible -Trigger an X Rule", + "request_headers": { + "nhs-login-nhs-number": "9900029507" + }, + "config_filenames": ["AUTO_RSV_ELI-295-2.json"], + "data": [ + { + "NHS_NUMBER": "9900029507", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_295_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029507", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-08.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-08.json new file mode 100644 index 000000000..77db115aa --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-08.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "ELI-295 - NotActionable -Trigger the Y Rule", + "request_headers": { + "nhs-login-nhs-number": "9900029508" + }, + "config_filenames": ["AUTO_RSV_ELI-295-2.json"], + "data": [ + { + "NHS_NUMBER": "9900029508", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_295_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029508", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-09.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-09.json new file mode 100644 index 000000000..73fa55e70 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-09.json @@ -0,0 +1,51 @@ +{ + "scenario_name": "ELI-295 - Actionable -Trigger the R Rule", + "request_headers": { + "nhs-login-nhs-number": "9900029509" + }, + "config_filenames": [ + "AUTO_RSV_ELI-295-2.json" + ], + "data": [ + { + "NHS_NUMBER": "9900029509", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_295_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029509", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900029509", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-10.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-10.json new file mode 100644 index 000000000..e333f6595 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-10.json @@ -0,0 +1,47 @@ +{ + "scenario_name": "ELI-295 - NotEligible -Trigger an X Rule - includeActions N", + "request_headers": { + "nhs-login-nhs-number": "9900029510" + }, + "query_params": { + "includeActions": "N" + }, + "config_filenames": ["AUTO_RSV_ELI-295-2.json"], + "data": [ + { + "NHS_NUMBER": "9900029510", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_295_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029510", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-11.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-11.json new file mode 100644 index 000000000..bb14c5273 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-11.json @@ -0,0 +1,49 @@ +{ + "scenario_name": "ELI-295 - NotActionable -Trigger the Y Rule - includeActions N", + "request_headers": { + "nhs-login-nhs-number": "9900029511" + }, + "query_params": { + "includeActions": "N" + }, + "config_filenames": [ + "AUTO_RSV_ELI-295-2.json" + ], + "data": [ + { + "NHS_NUMBER": "9900029511", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_295_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029511", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-12.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-12.json new file mode 100644 index 000000000..72b2d3e85 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-12.json @@ -0,0 +1,54 @@ +{ + "scenario_name": "ELI-295 - Actionable -Trigger the R Rule - includeActions N", + "request_headers": { + "nhs-login-nhs-number": "9900029512" + }, + "query_params": { + "includeActions": "N" + }, + "config_filenames": [ + "AUTO_RSV_ELI-295-2.json" + ], + "data": [ + { + "NHS_NUMBER": "9900029512", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_295_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029512", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900029512", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-13.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-13.json new file mode 100644 index 000000000..780af1bf8 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-13.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "ELI-295 - NotEligible -Trigger an X Rule - Mulitple Actions", + "request_headers": { + "nhs-login-nhs-number": "9900029513" + }, + "config_filenames": ["AUTO_RSV_ELI-295-3.json"], + "data": [ + { + "NHS_NUMBER": "9900029513", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_295_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029513", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-14.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-14.json new file mode 100644 index 000000000..3942e298d --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-14.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "ELI-295 - NotActionable -Trigger the Y Rule - Mulitple Actions", + "request_headers": { + "nhs-login-nhs-number": "9900029514" + }, + "config_filenames": ["AUTO_RSV_ELI-295-3.json"], + "data": [ + { + "NHS_NUMBER": "9900029514", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_295_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029514", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-15.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-15.json new file mode 100644 index 000000000..8e6190e47 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-15.json @@ -0,0 +1,51 @@ +{ + "scenario_name": "ELI-295 - Actionable -Trigger the R Rule - Mulitple Actions", + "request_headers": { + "nhs-login-nhs-number": "9900029515" + }, + "config_filenames": [ + "AUTO_RSV_ELI-295-3.json" + ], + "data": [ + { + "NHS_NUMBER": "9900029515", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_295_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029515", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900029515", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-16.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-16.json new file mode 100644 index 000000000..7a220f9a5 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-16.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "ELI-295 - NotEligible -Trigger an highest prio X Rule", + "request_headers": { + "nhs-login-nhs-number": "9900029516" + }, + "config_filenames": ["AUTO_RSV_ELI-295-4.json"], + "data": [ + { + "NHS_NUMBER": "9900029516", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_295_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029516", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-17.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-17.json new file mode 100644 index 000000000..6d13a98db --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-17.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "ELI-295 - NotActionable -Trigger the highest prio Y Rule", + "request_headers": { + "nhs-login-nhs-number": "9900029517" + }, + "config_filenames": ["AUTO_RSV_ELI-295-4.json"], + "data": [ + { + "NHS_NUMBER": "9900029517", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_295_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900029517", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-01.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-01.json new file mode 100644 index 000000000..b0fa0933e --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-01.json @@ -0,0 +1,22 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T13:44:33.140529+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not currently in eli_295" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "fc6a6876-0ad8-45fb-85b3-9db42116d61b" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-02.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-02.json new file mode 100644 index 000000000..6ef823be4 --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-02.json @@ -0,0 +1,28 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T13:54:13.465387+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "NotActionable", + "cohortText": "You are currently in eli_295" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "Test S Rule", + "ruleText": "Test S Rule", + "ruleType": "S" + } + ] + } + ], + "responseId": "81213bbe-8a5d-47be-8868-eb03bb37bb49" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-03.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-03.json new file mode 100644 index 000000000..0d4be9f79 --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-03.json @@ -0,0 +1,23 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T13:54:13.465387+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in eli_295" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + ] + } + ], + "responseId": "81213bbe-8a5d-47be-8868-eb03bb37bb49" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-04.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-04.json new file mode 100644 index 000000000..24103b438 --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-04.json @@ -0,0 +1,30 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T13:44:33.140529+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "DefaultX", + "actionType": "DefaultXType", + "description": "DefaultXDescription", + "urlLabel": "DefaultXLabel", + "urlLink": "https://www.defaultxaction.com/" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not currently in eli_295" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "fc6a6876-0ad8-45fb-85b3-9db42116d61b" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-05.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-05.json new file mode 100644 index 000000000..02a3d5f8f --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-05.json @@ -0,0 +1,36 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T13:54:13.465387+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "DefaultY", + "actionType": "DefaultYType", + "description": "DefaultYDescription", + "urlLabel": "DefaultYLabel", + "urlLink": "https://www.defaultyaction.com/" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "NotActionable", + "cohortText": "You are currently in eli_295" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "Test S Rule", + "ruleText": "Test S Rule", + "ruleType": "S" + } + ] + } + ], + "responseId": "81213bbe-8a5d-47be-8868-eb03bb37bb49" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-06.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-06.json new file mode 100644 index 000000000..2dcd834ed --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-06.json @@ -0,0 +1,30 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T13:54:13.465387+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "DefaultA", + "actionType": "DefaultAType", + "description": "DefaultADescription", + "urlLabel": "DefaultALabel", + "urlLink": "https://www.defaultaaction.com/" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in eli_295" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "81213bbe-8a5d-47be-8868-eb03bb37bb49" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-07.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-07.json new file mode 100644 index 000000000..3eeb4318e --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-07.json @@ -0,0 +1,30 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T14:29:05.049816+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "OtherX", + "actionType": "OtherXType", + "description": "OtherXDescription", + "urlLabel": "OtherXLabel", + "urlLink": "https://www.otherxaction.com/" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not currently in eli_295" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "5a263695-083e-4218-bbdf-969427913aa7" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-08.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-08.json new file mode 100644 index 000000000..7fee15448 --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-08.json @@ -0,0 +1,36 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T14:33:55.562448+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "OtherY", + "actionType": "OtherYType", + "description": "OtherYDescription", + "urlLabel": "OtherYLabel", + "urlLink": "https://www.otheryaction.com/" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "NotActionable", + "cohortText": "You are currently in eli_295" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "Test S Rule", + "ruleText": "Test S Rule", + "ruleType": "S" + } + ] + } + ], + "responseId": "7e7578a6-cddf-4d9e-8552-9d5502bb4d4c" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-09.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-09.json new file mode 100644 index 000000000..3e391df5c --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-09.json @@ -0,0 +1,30 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T14:46:44.985757+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "AmendNBS", + "actionType": "ButtonWithAuthLink", + "description": "##You have an RSV vaccination appointment\\nYou can view, change or cancel your appointment below.", + "urlLabel": "Manage your appointment", + "urlLink": "http://www.nhs.uk/book-rsv" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in eli_295" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "65e95ff6-11e4-45e7-8844-d2242a422b88" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-10.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-10.json new file mode 100644 index 000000000..68f3ce5cb --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-10.json @@ -0,0 +1,21 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T14:29:05.049816+00:00" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not currently in eli_295" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "5a263695-083e-4218-bbdf-969427913aa7" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-11.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-11.json new file mode 100644 index 000000000..810c543d1 --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-11.json @@ -0,0 +1,27 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T14:33:55.562448+00:00" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "NotActionable", + "cohortText": "You are currently in eli_295" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "Test S Rule", + "ruleText": "Test S Rule", + "ruleType": "S" + } + ] + } + ], + "responseId": "7e7578a6-cddf-4d9e-8552-9d5502bb4d4c" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-12.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-12.json new file mode 100644 index 000000000..a4f3e6b98 --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-12.json @@ -0,0 +1,21 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T14:46:44.985757+00:00" + }, + "processedSuggestions": [ + { + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in eli_295" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "65e95ff6-11e4-45e7-8844-d2242a422b88" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-13.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-13.json new file mode 100644 index 000000000..2b95f2473 --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-13.json @@ -0,0 +1,37 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T15:34:56.695680+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "DefaultX", + "actionType": "DefaultXType", + "description": "DefaultXDescription", + "urlLabel": "DefaultXLabel", + "urlLink": "https://www.defaultxaction.com/" + }, + { + "actionCode": "OtherX", + "actionType": "OtherXType", + "description": "OtherXDescription", + "urlLabel": "OtherXLabel", + "urlLink": "https://www.otherxaction.com/" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not currently in eli_295" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "6dbcf04d-9ae6-415d-b54f-6677c175dc6b" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-14.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-14.json new file mode 100644 index 000000000..a215f55e0 --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-14.json @@ -0,0 +1,43 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T15:33:08.084080+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "DefaultY", + "actionType": "DefaultYType", + "description": "DefaultYDescription", + "urlLabel": "DefaultYLabel", + "urlLink": "https://www.defaultyaction.com/" + }, + { + "actionCode": "OtherY", + "actionType": "OtherYType", + "description": "OtherYDescription", + "urlLabel": "OtherYLabel", + "urlLink": "https://www.otheryaction.com/" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "NotActionable", + "cohortText": "You are currently in eli_295" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "Test S Rule", + "ruleText": "Test S Rule", + "ruleType": "S" + } + ] + } + ], + "responseId": "299ff75e-4d02-4f23-81ca-6bfbf1e075a5" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-15.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-15.json new file mode 100644 index 000000000..a436c06b1 --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-15.json @@ -0,0 +1,37 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T15:31:37.704738+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "AmendNBS", + "actionType": "ButtonWithAuthLink", + "description": "##You have an RSV vaccination appointment\\nYou can view, change or cancel your appointment below.", + "urlLabel": "Manage your appointment", + "urlLink": "http://www.nhs.uk/book-rsv" + }, + { + "actionCode": "DefaultA", + "actionType": "DefaultAType", + "description": "DefaultADescription", + "urlLabel": "DefaultALabel", + "urlLink": "https://www.defaultaaction.com/" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in eli_295" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "65479e78-ee1d-4690-b29e-d75af4273d18" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-16.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-16.json new file mode 100644 index 000000000..1841f2b43 --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-16.json @@ -0,0 +1,30 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T16:14:39.133500+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "HigherX", + "actionType": "HigherXType", + "description": "HigherXDescription", + "urlLabel": "HigherXLabel", + "urlLink": "https://www.higherxaction.com/" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not currently in eli_295" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "906fdcee-63f4-4585-a133-a3ee87c48d24" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-17.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-17.json new file mode 100644 index 000000000..2be0d2099 --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-17.json @@ -0,0 +1,36 @@ +{ + "meta": { + "lastUpdated": "2025-07-18T16:15:15.028078+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "HigherY", + "actionType": "HigherYType", + "description": "HigherYDescription", + "urlLabel": "HigherYLabel", + "urlLink": "https://www.higheryaction.com/" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_295_group", + "cohortStatus": "NotActionable", + "cohortText": "You are currently in eli_295" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "Test S Rule", + "ruleText": "Test S Rule", + "ruleType": "S" + } + ] + } + ], + "responseId": "33b7a6a5-3961-4e73-ad90-89dc5d61b219" +} From cf0f009b99a07514b849621c643f7d1628f687b3 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Tue, 22 Jul 2025 10:31:31 +0100 Subject: [PATCH 57/72] eli-221 cases --- .../storyTestConfigs/AUTO_RSV_ELI-221-01.json | 88 ++++++++++++++++++ .../storyTestConfigs/AUTO_RSV_ELI-221-02.json | 89 +++++++++++++++++++ .../storyTestConfigs/AUTO_RSV_ELI-221-03.json | 87 ++++++++++++++++++ .../AUTO_RSV_ELI_295-01.json | 44 --------- .../AUTO_RSV_ELI_295-02.json | 44 --------- .../AUTO_RSV_ELI_295-03.json | 44 --------- .../AUTO_RSV_ELI_295-05.json | 44 --------- .../AUTO_RSV_ELI_295-06.json | 44 --------- .../AUTO_RSV_ELI_295-07.json | 44 --------- .../AUTO_RSV_ELI_295-08.json | 44 --------- .../AUTO_RSV_ELI_295-10.json | 47 ---------- .../AUTO_RSV_ELI_295-11.json | 49 ---------- .../AUTO_RSV_ELI_295-13.json | 44 --------- .../AUTO_RSV_ELI_295-14.json | 44 --------- .../AUTO_RSV_ELI_295-16.json | 44 --------- .../AUTO_RSV_ELI_295-17.json | 44 --------- .../AUTO_RSV_ELI-221-01.json} | 26 +++--- .../AUTO_RSV_ELI-221-02.json} | 26 +++--- .../AUTO_RSV_ELI-221-03.json} | 29 +++--- .../AUTO_RSV_ELI_295-02.json | 28 ------ .../AUTO_RSV_ELI_295-03.json | 23 ----- .../AUTO_RSV_ELI_295-04.json | 30 ------- .../AUTO_RSV_ELI_295-05.json | 36 -------- .../AUTO_RSV_ELI_295-06.json | 30 ------- .../AUTO_RSV_ELI_295-07.json | 30 ------- .../AUTO_RSV_ELI_295-08.json | 36 -------- .../AUTO_RSV_ELI_295-09.json | 30 ------- .../AUTO_RSV_ELI_295-10.json | 21 ----- .../AUTO_RSV_ELI_295-11.json | 27 ------ .../AUTO_RSV_ELI_295-12.json | 21 ----- .../AUTO_RSV_ELI_295-13.json | 37 -------- .../AUTO_RSV_ELI_295-14.json | 43 --------- .../AUTO_RSV_ELI_295-15.json | 37 -------- .../AUTO_RSV_ELI_295-16.json | 30 ------- .../AUTO_RSV_ELI_295-17.json | 36 -------- .../AUTO_RSV_ELI-221-01.json | 33 +++++++ .../AUTO_RSV_ELI-221-02.json | 38 ++++++++ .../AUTO_RSV_ELI-221-03.json | 38 ++++++++ tests/e2e/tests/test_error_scenario_tests.py | 2 + 39 files changed, 414 insertions(+), 1117 deletions(-) create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-01.json create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-02.json create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-03.json delete mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-01.json delete mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-02.json delete mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-03.json delete mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-05.json delete mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-06.json delete mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-07.json delete mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-08.json delete mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-10.json delete mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-11.json delete mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-13.json delete mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-14.json delete mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-16.json delete mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-17.json rename tests/e2e/data/dynamoDB/{inProgressTestData/AUTO_RSV_ELI_295-09.json => storyTestData/AUTO_RSV_ELI-221-01.json} (59%) rename tests/e2e/data/dynamoDB/{inProgressTestData/AUTO_RSV_ELI_295-15.json => storyTestData/AUTO_RSV_ELI-221-02.json} (59%) rename tests/e2e/data/dynamoDB/{inProgressTestData/AUTO_RSV_ELI_295-12.json => storyTestData/AUTO_RSV_ELI-221-03.json} (58%) delete mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-02.json delete mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-03.json delete mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-04.json delete mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-05.json delete mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-06.json delete mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-07.json delete mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-08.json delete mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-09.json delete mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-10.json delete mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-11.json delete mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-12.json delete mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-13.json delete mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-14.json delete mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-15.json delete mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-16.json delete mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-17.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-221-01.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-221-02.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-221-03.json diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-01.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-01.json new file mode 100644 index 000000000..4242fba04 --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-01.json @@ -0,0 +1,88 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "ELI-221 - Create Rule Stop function - Rule Stop on 2nd rule", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "CONTACT_GP", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "CONTACT_GP", + "Version": "1", + "Name": "ELI-221 - Create Rule Stop function - Rule Stop on Vaccinated", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "DefaultNotEligibleRouting": "", + "DefaultNotActionableRouting": "", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "eli_221_cohort", + "CohortGroup": "eli_221_group", + "PositiveDescription": "You are currently in eli_221_cohort", + "NegativeDescription": "You are not currently in eli_221_cohort", + "Priority": 0 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "Already Vaccinated", + "Description": "Already Vaccinated|We believe that you Are Already Vaccinated", + "Priority": 1000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "D<", + "Comparator": "0" + }, + { + "Type": "S", + "Name": "Other Setting Care", + "Description": "Other Setting|We believe that you will get the vaccination where you are located", + "Priority": 1001, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Other Setting DE", + "Description": "Other Setting|We believe that you will get the vaccination where you are located", + "Priority": 1002, + "AttributeLevel": "PERSON", + "AttributeName": "DE_FLAG", + "Operator": "=", + "Comparator": "Y" + } + ], + "ActionsMapper": { + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "##You have an RSV vaccination appointment\\nYou can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Manage your appointment" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-02.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-02.json new file mode 100644 index 000000000..faad7b00e --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-02.json @@ -0,0 +1,89 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "ELI-221 - Create Rule Stop function - Rule Stop on Vaccinated - Rule Stop: N", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "CONTACT_GP", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "CONTACT_GP", + "Version": "1", + "Name": "ELI-221 - Create Rule Stop function - Rule Stop on Vaccinated - No Rule Stop", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "DefaultNotEligibleRouting": "", + "DefaultNotActionableRouting": "", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "eli_221_cohort", + "CohortGroup": "eli_221_group", + "PositiveDescription": "You are currently in eli_221_cohort", + "NegativeDescription": "You are not currently in eli_221_cohort", + "Priority": 0 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "Already Vaccinated", + "Description": "Already Vaccinated|We believe that you Are Already Vaccinated", + "Priority": 1000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "D<", + "Comparator": "0", + "RuleStop": "N" + }, + { + "Type": "S", + "Name": "Other Setting Care", + "Description": "Other Setting|We believe that you will get the vaccination where you are located", + "Priority": 1001, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y", + "RuleStop": "N" + }, + { + "Type": "S", + "Name": "Other Setting DE", + "Description": "Other Setting|We believe that you will get the vaccination where you are located", + "Priority": 1002, + "AttributeLevel": "PERSON", + "AttributeName": "DE_FLAG", + "Operator": "=", + "Comparator": "Y" + } + ], + "ActionsMapper": { + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "##You have an RSV vaccination appointment\\nYou can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Manage your appointment" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-03.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-03.json new file mode 100644 index 000000000..c330a7da9 --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-03.json @@ -0,0 +1,87 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "ELI-221 - Create Rule Stop function - Rule Stop on Vaccinated - No Rule Stop", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "CONTACT_GP", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "CONTACT_GP", + "Version": "1", + "Name": "ELI-221 - Create Rule Stop function - Rule Stop on Vaccinated - No Rule Stop", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "DefaultNotEligibleRouting": "", + "DefaultNotActionableRouting": "", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "eli_221_cohort", + "CohortGroup": "eli_221_group", + "PositiveDescription": "You are currently in eli_221_cohort", + "NegativeDescription": "You are not currently in eli_221_cohort", + "Priority": 0 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "Already Vaccinated", + "Description": "Already Vaccinated|We believe that you Are Already Vaccinated", + "Priority": 1000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "D<", + "Comparator": "0" + }, + { + "Type": "S", + "Name": "Other Setting Care", + "Description": "Other Setting|We believe that you will get the vaccination where you are located", + "Priority": 1001, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y" + }, + { + "Type": "S", + "Name": "Other Setting DE", + "Description": "Other Setting|We believe that you will get the vaccination where you are located", + "Priority": 1002, + "AttributeLevel": "PERSON", + "AttributeName": "DE_FLAG", + "Operator": "=", + "Comparator": "Y" + } + ], + "ActionsMapper": { + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "##You have an RSV vaccination appointment\\nYou can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Manage your appointment" + } + } + } + ] + } +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-01.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-01.json deleted file mode 100644 index d87fc3925..000000000 --- a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-01.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "scenario_name": "ELI-295 - Not Eligible - Blank (\"\") Default X Rule - No Actions", - "request_headers": { - "nhs-login-nhs-number": "9900029501" - }, - "config_filenames": ["AUTO_RSV_ELI-295-1.json"], - "data": [ - { - "NHS_NUMBER": "9900029501", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_295_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "9900029501", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "SG8 6EG", - "POSTCODE_SECTOR": "SG86", - "POSTCODE_OUTCODE": "SG8", - "MSOA": "E02003792", - "LSOA": "E01018267", - "GP_PRACTICE_CODE": "D81046", - "PCN": "U75549", - "ICB": "QUE", - "COMMISSIONING_REGION": "Y61", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-02.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-02.json deleted file mode 100644 index 3e91f83a1..000000000 --- a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-02.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "scenario_name": "ELI-295 - Not Actionable - Blank (\"\") Default Y Rule - No Actions", - "request_headers": { - "nhs-login-nhs-number": "9900029502" - }, - "config_filenames": ["AUTO_RSV_ELI-295-1.json"], - "data": [ - { - "NHS_NUMBER": "9900029502", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_295_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "9900029502", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "SG8 6EG", - "POSTCODE_SECTOR": "SG86", - "POSTCODE_OUTCODE": "SG8", - "MSOA": "E02003792", - "LSOA": "E01018267", - "GP_PRACTICE_CODE": "D81046", - "PCN": "U75549", - "ICB": "QUE", - "COMMISSIONING_REGION": "Y61", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-03.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-03.json deleted file mode 100644 index 01369d3ac..000000000 --- a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-03.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "scenario_name": "ELI-295 - Actionable - Blank (\"\") Default R Rule - No Actions", - "request_headers": { - "nhs-login-nhs-number": "9900029503" - }, - "config_filenames": ["AUTO_RSV_ELI-295-2.json"], - "data": [ - { - "NHS_NUMBER": "9900029503", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_295_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "9900029503", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "SG8 6EG", - "POSTCODE_SECTOR": "SG86", - "POSTCODE_OUTCODE": "SG8", - "MSOA": "E02003792", - "LSOA": "E01018267", - "GP_PRACTICE_CODE": "D81046", - "PCN": "U75549", - "ICB": "QUE", - "COMMISSIONING_REGION": "Y61", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-05.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-05.json deleted file mode 100644 index 8728d9259..000000000 --- a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-05.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "scenario_name": "ELI-295 - Not Actionable -Trigger the Default Y Rule", - "request_headers": { - "nhs-login-nhs-number": "9900029505" - }, - "config_filenames": ["AUTO_RSV_ELI-295-2.json"], - "data": [ - { - "NHS_NUMBER": "9900029505", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_295_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "9900029505", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "SG8 6EG", - "POSTCODE_SECTOR": "SG86", - "POSTCODE_OUTCODE": "SG8", - "MSOA": "E02003792", - "LSOA": "E01018267", - "GP_PRACTICE_CODE": "D81046", - "PCN": "U75549", - "ICB": "QUE", - "COMMISSIONING_REGION": "Y61", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-06.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-06.json deleted file mode 100644 index 0aaac4f16..000000000 --- a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-06.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "scenario_name": "ELI-295 - Actionable -Trigger the Default R Rule", - "request_headers": { - "nhs-login-nhs-number": "9900029506" - }, - "config_filenames": ["AUTO_RSV_ELI-295-1.json"], - "data": [ - { - "NHS_NUMBER": "9900029506", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_295_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "9900029506", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "SG8 6EG", - "POSTCODE_SECTOR": "SG86", - "POSTCODE_OUTCODE": "SG8", - "MSOA": "E02003792", - "LSOA": "E01018267", - "GP_PRACTICE_CODE": "D81046", - "PCN": "U75549", - "ICB": "QUE", - "COMMISSIONING_REGION": "Y61", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-07.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-07.json deleted file mode 100644 index 4df6ccfb3..000000000 --- a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-07.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "scenario_name": "ELI-295 - NotEligible -Trigger an X Rule", - "request_headers": { - "nhs-login-nhs-number": "9900029507" - }, - "config_filenames": ["AUTO_RSV_ELI-295-2.json"], - "data": [ - { - "NHS_NUMBER": "9900029507", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_295_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "9900029507", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "SG8 6EG", - "POSTCODE_SECTOR": "SG86", - "POSTCODE_OUTCODE": "SG8", - "MSOA": "E02003792", - "LSOA": "E01018267", - "GP_PRACTICE_CODE": "D81046", - "PCN": "U75549", - "ICB": "QUE", - "COMMISSIONING_REGION": "Y61", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-08.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-08.json deleted file mode 100644 index 77db115aa..000000000 --- a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-08.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "scenario_name": "ELI-295 - NotActionable -Trigger the Y Rule", - "request_headers": { - "nhs-login-nhs-number": "9900029508" - }, - "config_filenames": ["AUTO_RSV_ELI-295-2.json"], - "data": [ - { - "NHS_NUMBER": "9900029508", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_295_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "9900029508", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "SG8 6EG", - "POSTCODE_SECTOR": "SG86", - "POSTCODE_OUTCODE": "SG8", - "MSOA": "E02003792", - "LSOA": "E01018267", - "GP_PRACTICE_CODE": "D81046", - "PCN": "U75549", - "ICB": "QUE", - "COMMISSIONING_REGION": "Y61", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-10.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-10.json deleted file mode 100644 index e333f6595..000000000 --- a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-10.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "scenario_name": "ELI-295 - NotEligible -Trigger an X Rule - includeActions N", - "request_headers": { - "nhs-login-nhs-number": "9900029510" - }, - "query_params": { - "includeActions": "N" - }, - "config_filenames": ["AUTO_RSV_ELI-295-2.json"], - "data": [ - { - "NHS_NUMBER": "9900029510", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_295_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "9900029510", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "SG8 6EG", - "POSTCODE_SECTOR": "SG86", - "POSTCODE_OUTCODE": "SG8", - "MSOA": "E02003792", - "LSOA": "E01018267", - "GP_PRACTICE_CODE": "D81046", - "PCN": "U75549", - "ICB": "QUE", - "COMMISSIONING_REGION": "Y61", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-11.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-11.json deleted file mode 100644 index bb14c5273..000000000 --- a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-11.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "scenario_name": "ELI-295 - NotActionable -Trigger the Y Rule - includeActions N", - "request_headers": { - "nhs-login-nhs-number": "9900029511" - }, - "query_params": { - "includeActions": "N" - }, - "config_filenames": [ - "AUTO_RSV_ELI-295-2.json" - ], - "data": [ - { - "NHS_NUMBER": "9900029511", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_295_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "9900029511", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "SG8 6EG", - "POSTCODE_SECTOR": "SG86", - "POSTCODE_OUTCODE": "SG8", - "MSOA": "E02003792", - "LSOA": "E01018267", - "GP_PRACTICE_CODE": "D81046", - "PCN": "U75549", - "ICB": "QUE", - "COMMISSIONING_REGION": "Y61", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-13.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-13.json deleted file mode 100644 index 780af1bf8..000000000 --- a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-13.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "scenario_name": "ELI-295 - NotEligible -Trigger an X Rule - Mulitple Actions", - "request_headers": { - "nhs-login-nhs-number": "9900029513" - }, - "config_filenames": ["AUTO_RSV_ELI-295-3.json"], - "data": [ - { - "NHS_NUMBER": "9900029513", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_295_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "9900029513", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "SG8 6EG", - "POSTCODE_SECTOR": "SG86", - "POSTCODE_OUTCODE": "SG8", - "MSOA": "E02003792", - "LSOA": "E01018267", - "GP_PRACTICE_CODE": "D81046", - "PCN": "U75549", - "ICB": "QUE", - "COMMISSIONING_REGION": "Y61", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-14.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-14.json deleted file mode 100644 index 3942e298d..000000000 --- a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-14.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "scenario_name": "ELI-295 - NotActionable -Trigger the Y Rule - Mulitple Actions", - "request_headers": { - "nhs-login-nhs-number": "9900029514" - }, - "config_filenames": ["AUTO_RSV_ELI-295-3.json"], - "data": [ - { - "NHS_NUMBER": "9900029514", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_295_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "9900029514", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "SG8 6EG", - "POSTCODE_SECTOR": "SG86", - "POSTCODE_OUTCODE": "SG8", - "MSOA": "E02003792", - "LSOA": "E01018267", - "GP_PRACTICE_CODE": "D81046", - "PCN": "U75549", - "ICB": "QUE", - "COMMISSIONING_REGION": "Y61", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-16.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-16.json deleted file mode 100644 index 7a220f9a5..000000000 --- a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-16.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "scenario_name": "ELI-295 - NotEligible -Trigger an highest prio X Rule", - "request_headers": { - "nhs-login-nhs-number": "9900029516" - }, - "config_filenames": ["AUTO_RSV_ELI-295-4.json"], - "data": [ - { - "NHS_NUMBER": "9900029516", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_295_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "9900029516", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "SG8 6EG", - "POSTCODE_SECTOR": "SG86", - "POSTCODE_OUTCODE": "SG8", - "MSOA": "E02003792", - "LSOA": "E01018267", - "GP_PRACTICE_CODE": "D81046", - "PCN": "U75549", - "ICB": "QUE", - "COMMISSIONING_REGION": "Y61", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-17.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-17.json deleted file mode 100644 index 6d13a98db..000000000 --- a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-17.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "scenario_name": "ELI-295 - NotActionable -Trigger the highest prio Y Rule", - "request_headers": { - "nhs-login-nhs-number": "9900029517" - }, - "config_filenames": ["AUTO_RSV_ELI-295-4.json"], - "data": [ - { - "NHS_NUMBER": "9900029517", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_295_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "9900029517", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "SG8 6EG", - "POSTCODE_SECTOR": "SG86", - "POSTCODE_OUTCODE": "SG8", - "MSOA": "E02003792", - "LSOA": "E01018267", - "GP_PRACTICE_CODE": "D81046", - "PCN": "U75549", - "ICB": "QUE", - "COMMISSIONING_REGION": "Y61", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-09.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-01.json similarity index 59% rename from tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-09.json rename to tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-01.json index 73fa55e70..4df99de3f 100644 --- a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-09.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-01.json @@ -1,19 +1,17 @@ { - "scenario_name": "ELI-295 - Actionable -Trigger the R Rule", + "scenario_name": "RSV - RuleStop: Y", "request_headers": { - "nhs-login-nhs-number": "9900029509" + "nhs-login-nhs-number": "9900022201" }, - "config_filenames": [ - "AUTO_RSV_ELI-295-2.json" - ], + "config_filenames": ["AUTO_RSV_ELI-221-01.json"], "data": [ { - "NHS_NUMBER": "9900029509", + "NHS_NUMBER": "9900022201", "ATTRIBUTE_TYPE": "COHORTS", "COHORT_MAP": { "cohorts": { "M": { - "eli_295_cohort": { + "eli_221_cohort": { "M": { "dateJoined": { "S": "20250604" @@ -25,9 +23,9 @@ } }, { - "NHS_NUMBER": "9900029509", + "NHS_NUMBER": "9900022201", "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", + "DATE_OF_BIRTH": "19500601", "GENDER": "0", "POSTCODE": "SG8 6EG", "POSTCODE_SECTOR": "SG86", @@ -39,13 +37,15 @@ "ICB": "QUE", "COMMISSIONING_REGION": "Y61", "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" + "CARE_HOME_FLAG": "Y", + "DE_FLAG": "Y" }, { - "NHS_NUMBER": "9900029509", + "NHS_NUMBER": "9900022201", "ATTRIBUTE_TYPE": "RSV", - "BOOKED_APPOINTMENT_DATE": "<>" + "LAST_SUCCESSFUL_DATE": "<>", + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" } ] } diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-15.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-02.json similarity index 59% rename from tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-15.json rename to tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-02.json index 8e6190e47..c503fd93c 100644 --- a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-15.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-02.json @@ -1,19 +1,17 @@ { - "scenario_name": "ELI-295 - Actionable -Trigger the R Rule - Mulitple Actions", + "scenario_name": "RSV - RuleStop: N", "request_headers": { - "nhs-login-nhs-number": "9900029515" + "nhs-login-nhs-number": "9900022202" }, - "config_filenames": [ - "AUTO_RSV_ELI-295-3.json" - ], + "config_filenames": ["AUTO_RSV_ELI-221-02.json"], "data": [ { - "NHS_NUMBER": "9900029515", + "NHS_NUMBER": "9900022202", "ATTRIBUTE_TYPE": "COHORTS", "COHORT_MAP": { "cohorts": { "M": { - "eli_295_cohort": { + "eli_221_cohort": { "M": { "dateJoined": { "S": "20250604" @@ -25,9 +23,9 @@ } }, { - "NHS_NUMBER": "9900029515", + "NHS_NUMBER": "9900022202", "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", + "DATE_OF_BIRTH": "19500601", "GENDER": "0", "POSTCODE": "SG8 6EG", "POSTCODE_SECTOR": "SG86", @@ -39,13 +37,15 @@ "ICB": "QUE", "COMMISSIONING_REGION": "Y61", "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" + "CARE_HOME_FLAG": "Y", + "DE_FLAG": "Y" }, { - "NHS_NUMBER": "9900029515", + "NHS_NUMBER": "9900022202", "ATTRIBUTE_TYPE": "RSV", - "BOOKED_APPOINTMENT_DATE": "<>" + "LAST_SUCCESSFUL_DATE": "<>", + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" } ] } diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-12.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-03.json similarity index 58% rename from tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-12.json rename to tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-03.json index 72b2d3e85..5192386c2 100644 --- a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-12.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-03.json @@ -1,22 +1,17 @@ { - "scenario_name": "ELI-295 - Actionable -Trigger the R Rule - includeActions N", + "scenario_name": "RSV - RuleStop Not included in rules", "request_headers": { - "nhs-login-nhs-number": "9900029512" + "nhs-login-nhs-number": "9900022203" }, - "query_params": { - "includeActions": "N" - }, - "config_filenames": [ - "AUTO_RSV_ELI-295-2.json" - ], + "config_filenames": ["AUTO_RSV_ELI-221-03.json"], "data": [ { - "NHS_NUMBER": "9900029512", + "NHS_NUMBER": "9900022203", "ATTRIBUTE_TYPE": "COHORTS", "COHORT_MAP": { "cohorts": { "M": { - "eli_295_cohort": { + "eli_221_cohort": { "M": { "dateJoined": { "S": "20250604" @@ -28,9 +23,9 @@ } }, { - "NHS_NUMBER": "9900029512", + "NHS_NUMBER": "9900022203", "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", + "DATE_OF_BIRTH": "19500601", "GENDER": "0", "POSTCODE": "SG8 6EG", "POSTCODE_SECTOR": "SG86", @@ -42,13 +37,15 @@ "ICB": "QUE", "COMMISSIONING_REGION": "Y61", "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" + "CARE_HOME_FLAG": "Y", + "DE_FLAG": "Y" }, { - "NHS_NUMBER": "9900029512", + "NHS_NUMBER": "9900022203", "ATTRIBUTE_TYPE": "RSV", - "BOOKED_APPOINTMENT_DATE": "<>" + "LAST_SUCCESSFUL_DATE": "<>", + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" } ] } diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-02.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-02.json deleted file mode 100644 index 6ef823be4..000000000 --- a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-02.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "meta": { - "lastUpdated": "2025-07-18T13:54:13.465387+00:00" - }, - "processedSuggestions": [ - { - "actions": [], - "condition": "RSV", - "eligibilityCohorts": [ - { - "cohortCode": "eli_295_group", - "cohortStatus": "NotActionable", - "cohortText": "You are currently in eli_295" - } - ], - "status": "NotActionable", - "statusText": "You should have the RSV vaccine", - "suitabilityRules": [ - { - "ruleCode": "Test S Rule", - "ruleText": "Test S Rule", - "ruleType": "S" - } - ] - } - ], - "responseId": "81213bbe-8a5d-47be-8868-eb03bb37bb49" -} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-03.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-03.json deleted file mode 100644 index 0d4be9f79..000000000 --- a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-03.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "meta": { - "lastUpdated": "2025-07-18T13:54:13.465387+00:00" - }, - "processedSuggestions": [ - { - "actions": [], - "condition": "RSV", - "eligibilityCohorts": [ - { - "cohortCode": "eli_295_group", - "cohortStatus": "Actionable", - "cohortText": "You are currently in eli_295" - } - ], - "status": "Actionable", - "statusText": "You should have the RSV vaccine", - "suitabilityRules": [ - ] - } - ], - "responseId": "81213bbe-8a5d-47be-8868-eb03bb37bb49" -} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-04.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-04.json deleted file mode 100644 index 24103b438..000000000 --- a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-04.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "meta": { - "lastUpdated": "2025-07-18T13:44:33.140529+00:00" - }, - "processedSuggestions": [ - { - "actions": [ - { - "actionCode": "DefaultX", - "actionType": "DefaultXType", - "description": "DefaultXDescription", - "urlLabel": "DefaultXLabel", - "urlLink": "https://www.defaultxaction.com/" - } - ], - "condition": "RSV", - "eligibilityCohorts": [ - { - "cohortCode": "eli_295_group", - "cohortStatus": "NotEligible", - "cohortText": "You are not currently in eli_295" - } - ], - "status": "NotEligible", - "statusText": "We do not believe you can have it", - "suitabilityRules": [] - } - ], - "responseId": "fc6a6876-0ad8-45fb-85b3-9db42116d61b" -} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-05.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-05.json deleted file mode 100644 index 02a3d5f8f..000000000 --- a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-05.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "meta": { - "lastUpdated": "2025-07-18T13:54:13.465387+00:00" - }, - "processedSuggestions": [ - { - "actions": [ - { - "actionCode": "DefaultY", - "actionType": "DefaultYType", - "description": "DefaultYDescription", - "urlLabel": "DefaultYLabel", - "urlLink": "https://www.defaultyaction.com/" - } - ], - "condition": "RSV", - "eligibilityCohorts": [ - { - "cohortCode": "eli_295_group", - "cohortStatus": "NotActionable", - "cohortText": "You are currently in eli_295" - } - ], - "status": "NotActionable", - "statusText": "You should have the RSV vaccine", - "suitabilityRules": [ - { - "ruleCode": "Test S Rule", - "ruleText": "Test S Rule", - "ruleType": "S" - } - ] - } - ], - "responseId": "81213bbe-8a5d-47be-8868-eb03bb37bb49" -} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-06.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-06.json deleted file mode 100644 index 2dcd834ed..000000000 --- a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-06.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "meta": { - "lastUpdated": "2025-07-18T13:54:13.465387+00:00" - }, - "processedSuggestions": [ - { - "actions": [ - { - "actionCode": "DefaultA", - "actionType": "DefaultAType", - "description": "DefaultADescription", - "urlLabel": "DefaultALabel", - "urlLink": "https://www.defaultaaction.com/" - } - ], - "condition": "RSV", - "eligibilityCohorts": [ - { - "cohortCode": "eli_295_group", - "cohortStatus": "Actionable", - "cohortText": "You are currently in eli_295" - } - ], - "status": "Actionable", - "statusText": "You should have the RSV vaccine", - "suitabilityRules": [] - } - ], - "responseId": "81213bbe-8a5d-47be-8868-eb03bb37bb49" -} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-07.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-07.json deleted file mode 100644 index 3eeb4318e..000000000 --- a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-07.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "meta": { - "lastUpdated": "2025-07-18T14:29:05.049816+00:00" - }, - "processedSuggestions": [ - { - "actions": [ - { - "actionCode": "OtherX", - "actionType": "OtherXType", - "description": "OtherXDescription", - "urlLabel": "OtherXLabel", - "urlLink": "https://www.otherxaction.com/" - } - ], - "condition": "RSV", - "eligibilityCohorts": [ - { - "cohortCode": "eli_295_group", - "cohortStatus": "NotEligible", - "cohortText": "You are not currently in eli_295" - } - ], - "status": "NotEligible", - "statusText": "We do not believe you can have it", - "suitabilityRules": [] - } - ], - "responseId": "5a263695-083e-4218-bbdf-969427913aa7" -} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-08.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-08.json deleted file mode 100644 index 7fee15448..000000000 --- a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-08.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "meta": { - "lastUpdated": "2025-07-18T14:33:55.562448+00:00" - }, - "processedSuggestions": [ - { - "actions": [ - { - "actionCode": "OtherY", - "actionType": "OtherYType", - "description": "OtherYDescription", - "urlLabel": "OtherYLabel", - "urlLink": "https://www.otheryaction.com/" - } - ], - "condition": "RSV", - "eligibilityCohorts": [ - { - "cohortCode": "eli_295_group", - "cohortStatus": "NotActionable", - "cohortText": "You are currently in eli_295" - } - ], - "status": "NotActionable", - "statusText": "You should have the RSV vaccine", - "suitabilityRules": [ - { - "ruleCode": "Test S Rule", - "ruleText": "Test S Rule", - "ruleType": "S" - } - ] - } - ], - "responseId": "7e7578a6-cddf-4d9e-8552-9d5502bb4d4c" -} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-09.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-09.json deleted file mode 100644 index 3e391df5c..000000000 --- a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-09.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "meta": { - "lastUpdated": "2025-07-18T14:46:44.985757+00:00" - }, - "processedSuggestions": [ - { - "actions": [ - { - "actionCode": "AmendNBS", - "actionType": "ButtonWithAuthLink", - "description": "##You have an RSV vaccination appointment\\nYou can view, change or cancel your appointment below.", - "urlLabel": "Manage your appointment", - "urlLink": "http://www.nhs.uk/book-rsv" - } - ], - "condition": "RSV", - "eligibilityCohorts": [ - { - "cohortCode": "eli_295_group", - "cohortStatus": "Actionable", - "cohortText": "You are currently in eli_295" - } - ], - "status": "Actionable", - "statusText": "You should have the RSV vaccine", - "suitabilityRules": [] - } - ], - "responseId": "65e95ff6-11e4-45e7-8844-d2242a422b88" -} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-10.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-10.json deleted file mode 100644 index 68f3ce5cb..000000000 --- a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-10.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "meta": { - "lastUpdated": "2025-07-18T14:29:05.049816+00:00" - }, - "processedSuggestions": [ - { - "condition": "RSV", - "eligibilityCohorts": [ - { - "cohortCode": "eli_295_group", - "cohortStatus": "NotEligible", - "cohortText": "You are not currently in eli_295" - } - ], - "status": "NotEligible", - "statusText": "We do not believe you can have it", - "suitabilityRules": [] - } - ], - "responseId": "5a263695-083e-4218-bbdf-969427913aa7" -} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-11.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-11.json deleted file mode 100644 index 810c543d1..000000000 --- a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-11.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "meta": { - "lastUpdated": "2025-07-18T14:33:55.562448+00:00" - }, - "processedSuggestions": [ - { - "condition": "RSV", - "eligibilityCohorts": [ - { - "cohortCode": "eli_295_group", - "cohortStatus": "NotActionable", - "cohortText": "You are currently in eli_295" - } - ], - "status": "NotActionable", - "statusText": "You should have the RSV vaccine", - "suitabilityRules": [ - { - "ruleCode": "Test S Rule", - "ruleText": "Test S Rule", - "ruleType": "S" - } - ] - } - ], - "responseId": "7e7578a6-cddf-4d9e-8552-9d5502bb4d4c" -} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-12.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-12.json deleted file mode 100644 index a4f3e6b98..000000000 --- a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-12.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "meta": { - "lastUpdated": "2025-07-18T14:46:44.985757+00:00" - }, - "processedSuggestions": [ - { - "condition": "RSV", - "eligibilityCohorts": [ - { - "cohortCode": "eli_295_group", - "cohortStatus": "Actionable", - "cohortText": "You are currently in eli_295" - } - ], - "status": "Actionable", - "statusText": "You should have the RSV vaccine", - "suitabilityRules": [] - } - ], - "responseId": "65e95ff6-11e4-45e7-8844-d2242a422b88" -} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-13.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-13.json deleted file mode 100644 index 2b95f2473..000000000 --- a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-13.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "meta": { - "lastUpdated": "2025-07-18T15:34:56.695680+00:00" - }, - "processedSuggestions": [ - { - "actions": [ - { - "actionCode": "DefaultX", - "actionType": "DefaultXType", - "description": "DefaultXDescription", - "urlLabel": "DefaultXLabel", - "urlLink": "https://www.defaultxaction.com/" - }, - { - "actionCode": "OtherX", - "actionType": "OtherXType", - "description": "OtherXDescription", - "urlLabel": "OtherXLabel", - "urlLink": "https://www.otherxaction.com/" - } - ], - "condition": "RSV", - "eligibilityCohorts": [ - { - "cohortCode": "eli_295_group", - "cohortStatus": "NotEligible", - "cohortText": "You are not currently in eli_295" - } - ], - "status": "NotEligible", - "statusText": "We do not believe you can have it", - "suitabilityRules": [] - } - ], - "responseId": "6dbcf04d-9ae6-415d-b54f-6677c175dc6b" -} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-14.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-14.json deleted file mode 100644 index a215f55e0..000000000 --- a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-14.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "meta": { - "lastUpdated": "2025-07-18T15:33:08.084080+00:00" - }, - "processedSuggestions": [ - { - "actions": [ - { - "actionCode": "DefaultY", - "actionType": "DefaultYType", - "description": "DefaultYDescription", - "urlLabel": "DefaultYLabel", - "urlLink": "https://www.defaultyaction.com/" - }, - { - "actionCode": "OtherY", - "actionType": "OtherYType", - "description": "OtherYDescription", - "urlLabel": "OtherYLabel", - "urlLink": "https://www.otheryaction.com/" - } - ], - "condition": "RSV", - "eligibilityCohorts": [ - { - "cohortCode": "eli_295_group", - "cohortStatus": "NotActionable", - "cohortText": "You are currently in eli_295" - } - ], - "status": "NotActionable", - "statusText": "You should have the RSV vaccine", - "suitabilityRules": [ - { - "ruleCode": "Test S Rule", - "ruleText": "Test S Rule", - "ruleType": "S" - } - ] - } - ], - "responseId": "299ff75e-4d02-4f23-81ca-6bfbf1e075a5" -} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-15.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-15.json deleted file mode 100644 index a436c06b1..000000000 --- a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-15.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "meta": { - "lastUpdated": "2025-07-18T15:31:37.704738+00:00" - }, - "processedSuggestions": [ - { - "actions": [ - { - "actionCode": "AmendNBS", - "actionType": "ButtonWithAuthLink", - "description": "##You have an RSV vaccination appointment\\nYou can view, change or cancel your appointment below.", - "urlLabel": "Manage your appointment", - "urlLink": "http://www.nhs.uk/book-rsv" - }, - { - "actionCode": "DefaultA", - "actionType": "DefaultAType", - "description": "DefaultADescription", - "urlLabel": "DefaultALabel", - "urlLink": "https://www.defaultaaction.com/" - } - ], - "condition": "RSV", - "eligibilityCohorts": [ - { - "cohortCode": "eli_295_group", - "cohortStatus": "Actionable", - "cohortText": "You are currently in eli_295" - } - ], - "status": "Actionable", - "statusText": "You should have the RSV vaccine", - "suitabilityRules": [] - } - ], - "responseId": "65479e78-ee1d-4690-b29e-d75af4273d18" -} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-16.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-16.json deleted file mode 100644 index 1841f2b43..000000000 --- a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-16.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "meta": { - "lastUpdated": "2025-07-18T16:14:39.133500+00:00" - }, - "processedSuggestions": [ - { - "actions": [ - { - "actionCode": "HigherX", - "actionType": "HigherXType", - "description": "HigherXDescription", - "urlLabel": "HigherXLabel", - "urlLink": "https://www.higherxaction.com/" - } - ], - "condition": "RSV", - "eligibilityCohorts": [ - { - "cohortCode": "eli_295_group", - "cohortStatus": "NotEligible", - "cohortText": "You are not currently in eli_295" - } - ], - "status": "NotEligible", - "statusText": "We do not believe you can have it", - "suitabilityRules": [] - } - ], - "responseId": "906fdcee-63f4-4585-a133-a3ee87c48d24" -} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-17.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-17.json deleted file mode 100644 index 2be0d2099..000000000 --- a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-17.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "meta": { - "lastUpdated": "2025-07-18T16:15:15.028078+00:00" - }, - "processedSuggestions": [ - { - "actions": [ - { - "actionCode": "HigherY", - "actionType": "HigherYType", - "description": "HigherYDescription", - "urlLabel": "HigherYLabel", - "urlLink": "https://www.higheryaction.com/" - } - ], - "condition": "RSV", - "eligibilityCohorts": [ - { - "cohortCode": "eli_295_group", - "cohortStatus": "NotActionable", - "cohortText": "You are currently in eli_295" - } - ], - "status": "NotActionable", - "statusText": "You should have the RSV vaccine", - "suitabilityRules": [ - { - "ruleCode": "Test S Rule", - "ruleText": "Test S Rule", - "ruleType": "S" - } - ] - } - ], - "responseId": "33b7a6a5-3961-4e73-ad90-89dc5d61b219" -} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-221-01.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-221-01.json new file mode 100644 index 000000000..3dc537ca7 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-221-01.json @@ -0,0 +1,33 @@ +{ + "meta": { + "lastUpdated": "2025-07-22T07:29:26.220684+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_221_group", + "cohortStatus": "NotActionable", + "cohortText": "You are currently in eli_221_cohort" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "Already Vaccinated", + "ruleText": "Already Vaccinated|We believe that you Are Already Vaccinated", + "ruleType": "S" + }, + { + "ruleCode": "Other Setting Care", + "ruleText": "Other Setting|We believe that you will get the vaccination where you are located", + "ruleType": "S" + } + ] + } + ], + "responseId": "47a0b0cb-a5b4-457c-966f-294321f5c1c7" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-221-02.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-221-02.json new file mode 100644 index 000000000..4f3234878 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-221-02.json @@ -0,0 +1,38 @@ +{ + "meta": { + "lastUpdated": "2025-07-22T07:35:17.550067+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_221_group", + "cohortStatus": "NotActionable", + "cohortText": "You are currently in eli_221_cohort" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "Already Vaccinated", + "ruleText": "Already Vaccinated|We believe that you Are Already Vaccinated", + "ruleType": "S" + }, + { + "ruleCode": "Other Setting Care", + "ruleText": "Other Setting|We believe that you will get the vaccination where you are located", + "ruleType": "S" + }, + { + "ruleCode": "Other Setting DE", + "ruleText": "Other Setting|We believe that you will get the vaccination where you are located", + "ruleType": "S" + } + ] + } + ], + "responseId": "ae6577f5-ddd5-4199-b387-35b9fdb15172" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-221-03.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-221-03.json new file mode 100644 index 000000000..4f3234878 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-221-03.json @@ -0,0 +1,38 @@ +{ + "meta": { + "lastUpdated": "2025-07-22T07:35:17.550067+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_221_group", + "cohortStatus": "NotActionable", + "cohortText": "You are currently in eli_221_cohort" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "Already Vaccinated", + "ruleText": "Already Vaccinated|We believe that you Are Already Vaccinated", + "ruleType": "S" + }, + { + "ruleCode": "Other Setting Care", + "ruleText": "Other Setting|We believe that you will get the vaccination where you are located", + "ruleType": "S" + }, + { + "ruleCode": "Other Setting DE", + "ruleText": "Other Setting|We believe that you will get the vaccination where you are located", + "ruleType": "S" + } + ] + } + ], + "responseId": "ae6577f5-ddd5-4199-b387-35b9fdb15172" +} diff --git a/tests/e2e/tests/test_error_scenario_tests.py b/tests/e2e/tests/test_error_scenario_tests.py index 13237767f..aade6374d 100644 --- a/tests/e2e/tests/test_error_scenario_tests.py +++ b/tests/e2e/tests/test_error_scenario_tests.py @@ -4,6 +4,7 @@ from tests.e2e.utils.s3_config_manager import delete_all_configs_from_s3 + @pytest.mark.errorscenarios @pytest.mark.smoketest def test_check_for_missing_person(eligibility_client): @@ -226,6 +227,7 @@ def test_query_param_errors(eligibility_client, test_case): assert response["body"] == test_case["expected_body"], f"{test_case['scenario']} failed on response body" assert response["headers"].get("Content-Type".lower()) == "application/fhir+json" + @pytest.mark.errorscenarios def test_no_config_error(eligibility_client): expected_response = { From d7f460d4fea01a73e4e4eec5189b6de37e66c4a0 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Wed, 23 Jul 2025 09:19:58 +0100 Subject: [PATCH 58/72] Added a WEEK Case to _resolve_placeholder_value updated "Type": "M" to "Type": "O" in a number of config files Added ELI-236 Scenarios Extend functionality to add placeholder values in the configs to support offset and NVL --- .../storyTestConfigs/AUTO_RSV_ELI-155.json | 2 +- .../storyTestConfigs/AUTO_RSV_ELI-216.json | 2 +- .../storyTestConfigs/AUTO_RSV_ELI-219-1.json | 2 +- .../storyTestConfigs/AUTO_RSV_ELI-219-2.json | 2 +- .../storyTestConfigs/AUTO_RSV_ELI-219-3.json | 2 +- .../storyTestConfigs/AUTO_RSV_ELI-222.json | 2 +- .../storyTestConfigs/AUTO_RSV_ELI-236.json | 149 ++++++++++++++++++ .../storyTestConfigs/AUTO_RSV_ELI-295-1.json | 2 +- .../storyTestConfigs/AUTO_RSV_ELI-295-2.json | 2 +- .../storyTestConfigs/AUTO_RSV_ELI-295-3.json | 2 +- .../storyTestConfigs/AUTO_RSV_ELI-295-4.json | 2 +- .../storyTestConfigs/AUTO_RSV_ELI-317-1.json | 2 +- .../storyTestConfigs/AUTO_RSV_ELI-317-2.json | 2 +- .../storyTestConfigs/AUTO_RSV_ELI-317-3.json | 2 +- .../storyTestConfigs/AUTO_RSV_ELI-317-4.json | 2 +- .../storyTestData/AUTO_RSV_ELI-236-01.json | 59 +++++++ .../storyTestData/AUTO_RSV_ELI-236-02.json | 59 +++++++ .../AUTO_RSV_ELI_295-01.json | 22 --- .../AUTO_RSV_ELI-236-01.json | 68 ++++++++ .../AUTO_RSV_ELI-236-02.json | 68 ++++++++ tests/e2e/utils/placeholder_utils.py | 2 + tests/e2e/utils/s3_config_manager.py | 33 +++- 22 files changed, 448 insertions(+), 40 deletions(-) create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-236.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-236-01.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-236-02.json delete mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-01.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-236-01.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-236-02.json diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-155.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-155.json index 59ff46862..571375411 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-155.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-155.json @@ -29,7 +29,7 @@ "DefaultNotActionableRouting": "", "ApprovalMinimum": 0, "ApprovalMaximum": 0, - "Type": "M", + "Type": "O", "IterationCohorts": [ { "CohortLabel": "eli_155_in", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json index ae164ce7e..69574e7db 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json @@ -29,7 +29,7 @@ "DefaultNotActionableRouting": "", "ApprovalMinimum": 0, "ApprovalMaximum": 0, - "Type": "M", + "Type": "O", "IterationCohorts": [ { "CohortLabel": "eli_216_cohort", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-1.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-1.json index 49101eb29..78ffcaafd 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-1.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-1.json @@ -29,7 +29,7 @@ "DefaultNotActionableRouting": "", "ApprovalMinimum": 0, "ApprovalMaximum": 0, - "Type": "M", + "Type": "O", "IterationCohorts": [ { "CohortLabel": "eli_291_cohort_1", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-2.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-2.json index 9cc24bf42..3f83a2a30 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-2.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-2.json @@ -29,7 +29,7 @@ "DefaultNotActionableRouting": "", "ApprovalMinimum": 0, "ApprovalMaximum": 0, - "Type": "M", + "Type": "O", "IterationCohorts": [ { "CohortLabel": "eli_291_cohort_2", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-3.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-3.json index b2f044036..3e68a1b2e 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-3.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-3.json @@ -29,7 +29,7 @@ "DefaultNotActionableRouting": "", "ApprovalMinimum": 0, "ApprovalMaximum": 0, - "Type": "M", + "Type": "O", "IterationCohorts": [ { "CohortLabel": "eli_291_cohort_3", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-222.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-222.json index 0950f974f..7369a7abb 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-222.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-222.json @@ -29,7 +29,7 @@ "DefaultNotActionableRouting": "", "ApprovalMinimum": 0, "ApprovalMaximum": 0, - "Type": "M", + "Type": "O", "IterationCohorts": [ { "CohortLabel": "rsv_75to79", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-236.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-236.json new file mode 100644 index 000000000..bd9164207 --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-236.json @@ -0,0 +1,149 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "ELI-236 - NVL Functionality", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "CONTACT_GP", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "", + "Version": "1", + "Name": "ELI-236 - NVL Functionality", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "DefaultNotEligibleRouting": "", + "DefaultNotActionableRouting": "", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "M", + "IterationCohorts": [ + { + "CohortLabel": "eli_236_cohort", + "CohortGroup": "eli_236_group", + "PositiveDescription": "You are currently in eli_236", + "NegativeDescription": "You are not currently in eli_236", + "Priority": 0 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "13Q Flag NVL equal Test", + "Description": "13Q Flag NVL Test|13Q Flag NVL Test", + "Priority": 1000, + "AttributeLevel": "PERSON", + "AttributeName": "13Q_FLAG", + "Operator": "=", + "Comparator": "Y[[NVL:Y]]" + }, + { + "Type": "S", + "Name": "CARE_HOME_FLAG not equal NVL Test", + "Description": "CARE_HOME_FLAG not equal NVL Test", + "Priority": 1001, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "!=", + "Comparator": "N[[NVL:Y]]" + }, + { + "Type": "S", + "Name": "COMMISSIONING_REGION greater than NVL Test", + "Description": "COMMISSIONING_REGION greater than NVL Test", + "Priority": 1002, + "AttributeLevel": "PERSON", + "AttributeName": "COMMISSIONING_REGION", + "Operator": ">", + "Comparator": "0[[NVL:1]]" + }, + { + "Type": "S", + "Name": "DATE_OF_BIRTH less than NVL Test", + "Description": "DATE_OF_BIRTH less than NVL Test", + "Priority": 1003, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "<", + "Comparator": "19801116[[NVL:19801115]]" + }, + { + "Type": "S", + "Name": "GENDER greater than or equal to NVL Test", + "Description": "GENDER greater than or equal to NVL Test", + "Priority": 1004, + "AttributeLevel": "PERSON", + "AttributeName": "GENDER", + "Operator": ">=", + "Comparator": "1[[NVL:1]]" + }, + { + "Type": "S", + "Name": "GP_PRACTICE_CODE less or equal to NVL Test", + "Description": "GP_PRACTICE_CODE less than or equal to NVL Test", + "Priority": 1005, + "AttributeLevel": "PERSON", + "AttributeName": "GP_PRACTICE_CODE", + "Operator": "<=", + "Comparator": "10563[[NVL:10563]]" + }, + { + "Type": "S", + "Name": "BOOKED_APPOINTMENT_DATE D<= NVL Test", + "Description": "BOOKED_APPOINTMENT_DATE D<= NVL Test", + "Priority": 1006, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D<=", + "Comparator": "-10[[NVL:<>]]" + }, + { + "Type": "S", + "Name": "LAST_INVITE_DATE W> NVL Test", + "Description": "LAST_INVITE_DATE W> NVL Test", + "Priority": 1007, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_INVITE_DATE", + "Operator": "W>", + "Comparator": "10[[NVL:<>]]" + }, + { + "Type": "S", + "Name": "LAST_VALID_DOSE_DATE Y< NVL Test", + "Description": "LAST_VALID_DOSE_DATE Y< NVL Test", + "Priority": 1008, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_INVITE_DATE", + "Operator": "Y<", + "Comparator": "-5[[NVL:<>]]" + } + ], + "ActionsMapper": { + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "##You have an RSV vaccination appointment\\nYou can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Manage your appointment" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-1.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-1.json index 5a4ee801c..292ed8fa1 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-1.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-1.json @@ -29,7 +29,7 @@ "CommsType": "I", "ApprovalMinimum": 0, "ApprovalMaximum": 0, - "Type": "M", + "Type": "O", "IterationCohorts": [ { "CohortLabel": "eli_295_cohort", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-2.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-2.json index 9496a4989..af74b5b8f 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-2.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-2.json @@ -29,7 +29,7 @@ "CommsType": "I", "ApprovalMinimum": 0, "ApprovalMaximum": 0, - "Type": "M", + "Type": "O", "IterationCohorts": [ { "CohortLabel": "eli_295_cohort", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-3.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-3.json index 349db97af..8c2c4f9c4 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-3.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-3.json @@ -29,7 +29,7 @@ "CommsType": "I", "ApprovalMinimum": 0, "ApprovalMaximum": 0, - "Type": "M", + "Type": "O", "IterationCohorts": [ { "CohortLabel": "eli_295_cohort", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-4.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-4.json index fcb5bc463..b22930d8f 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-4.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-4.json @@ -29,7 +29,7 @@ "CommsType": "I", "ApprovalMinimum": 0, "ApprovalMaximum": 0, - "Type": "M", + "Type": "O", "IterationCohorts": [ { "CohortLabel": "eli_295_cohort", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-1.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-1.json index beb979f0f..18dc56c37 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-1.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-1.json @@ -29,7 +29,7 @@ "DefaultNotActionableRouting": "", "ApprovalMinimum": 0, "ApprovalMaximum": 0, - "Type": "M", + "Type": "O", "IterationCohorts": [ { "CohortLabel": "eli_317_cohort_1", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-2.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-2.json index a6496f087..2d85993ae 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-2.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-2.json @@ -29,7 +29,7 @@ "DefaultNotActionableRouting": "", "ApprovalMinimum": 0, "ApprovalMaximum": 0, - "Type": "M", + "Type": "O", "IterationCohorts": [ { "CohortLabel": "eli_317_cohort_2", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-3.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-3.json index fc3724e47..e9cf95f25 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-3.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-3.json @@ -29,7 +29,7 @@ "DefaultNotActionableRouting": "", "ApprovalMinimum": 0, "ApprovalMaximum": 0, - "Type": "M", + "Type": "O", "IterationCohorts": [ { "CohortLabel": "eli_317_cohort_3", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-4.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-4.json index e5ec359c4..b91d30762 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-4.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-4.json @@ -29,7 +29,7 @@ "DefaultNotActionableRouting": "", "ApprovalMinimum": 0, "ApprovalMaximum": 0, - "Type": "M", + "Type": "O", "IterationCohorts": [ { "CohortLabel": "eli_317_cohort_4", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-236-01.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-236-01.json new file mode 100644 index 000000000..89955a63b --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-236-01.json @@ -0,0 +1,59 @@ +{ + "scenario_name": "RSV - ELI-236 - Add NVL Default to Operators - All null", + "config_filenames": [ + "AUTO_RSV_ELI-236.json" + ], + "request_headers": { + "nhs-login-nhs-number": "9900023601" + }, + "data": [ + { + "NHS_NUMBER": "9900023601", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_236_cohort": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900023601", + "ATTRIBUTE_TYPE": "PERSON", + "13Q_FLAG": null, + "CARE_HOME_FLAG": null, + "COMMISSIONING_REGION": null, + "DATE_OF_BIRTH": null, + "DE_FLAG": null, + "GENDER": null, + "GP_PRACTICE_CODE": null, + "ICB": null, + "LOCAL_AUTHORITY": null, + "LSOA": null, + "MSOA": null, + "PCN": null, + "POSTCODE": null, + "POSTCODE_OUTCODE": null, + "POSTCODE_SECTOR": null + }, + { + "NHS_NUMBER": "9411876722", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": null, + "BOOKED_APPOINTMENT_PROVIDER": null, + "INVALID_DOSES_COUNT": null, + "LAST_INVITE_DATE": null, + "LAST_INVITE_STATUS": null, + "LAST_SUCCESSFUL_DATE": null, + "LAST_VALID_DOSE_DATE": null, + "VALID_DOSES_COUNT": null + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-236-02.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-236-02.json new file mode 100644 index 000000000..f799013b0 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-236-02.json @@ -0,0 +1,59 @@ +{ + "scenario_name": "RSV - ELI-236 - Add NVL Default to Operators - Nulls Filled", + "config_filenames": [ + "AUTO_RSV_ELI-236.json" + ], + "request_headers": { + "nhs-login-nhs-number": "9900023602" + }, + "data": [ + { + "NHS_NUMBER": "9900023602", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_236_cohort": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9900023602", + "ATTRIBUTE_TYPE": "PERSON", + "13Q_FLAG": "Y", + "CARE_HOME_FLAG": "Y", + "COMMISSIONING_REGION": "1", + "DATE_OF_BIRTH": "19801115", + "DE_FLAG": null, + "GENDER": "1", + "GP_PRACTICE_CODE": "10563", + "ICB": null, + "LOCAL_AUTHORITY": null, + "LSOA": null, + "MSOA": null, + "PCN": null, + "POSTCODE": null, + "POSTCODE_OUTCODE": null, + "POSTCODE_SECTOR": null + }, + { + "NHS_NUMBER": "9411876722", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": null, + "INVALID_DOSES_COUNT": null, + "LAST_INVITE_DATE": "<>", + "LAST_INVITE_STATUS": null, + "LAST_SUCCESSFUL_DATE": null, + "LAST_VALID_DOSE_DATE": "<>", + "VALID_DOSES_COUNT": null + } + ] +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-01.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-01.json deleted file mode 100644 index b0fa0933e..000000000 --- a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI_295-01.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "meta": { - "lastUpdated": "2025-07-18T13:44:33.140529+00:00" - }, - "processedSuggestions": [ - { - "actions": [], - "condition": "RSV", - "eligibilityCohorts": [ - { - "cohortCode": "eli_295_group", - "cohortStatus": "NotEligible", - "cohortText": "You are not currently in eli_295" - } - ], - "status": "NotEligible", - "statusText": "We do not believe you can have it", - "suitabilityRules": [] - } - ], - "responseId": "fc6a6876-0ad8-45fb-85b3-9db42116d61b" -} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-236-01.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-236-01.json new file mode 100644 index 000000000..aa9cc296e --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-236-01.json @@ -0,0 +1,68 @@ +{ + "meta": { + "lastUpdated": "2025-07-22T21:07:51.351855+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_236_group", + "cohortStatus": "NotActionable", + "cohortText": "You are currently in eli_236" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "13Q Flag NVL equal Test", + "ruleText": "13Q Flag NVL Test|13Q Flag NVL Test", + "ruleType": "S" + }, + { + "ruleCode": "CARE_HOME_FLAG not equal NVL Test", + "ruleText": "CARE_HOME_FLAG not equal NVL Test", + "ruleType": "S" + }, + { + "ruleCode": "COMMISSIONING_REGION greater than NVL Test", + "ruleText": "COMMISSIONING_REGION greater than NVL Test", + "ruleType": "S" + }, + { + "ruleCode": "DATE_OF_BIRTH less than NVL Test", + "ruleText": "DATE_OF_BIRTH less than NVL Test", + "ruleType": "S" + }, + { + "ruleCode": "GENDER greater than or equal to NVL Test", + "ruleText": "GENDER greater than or equal to NVL Test", + "ruleType": "S" + }, + { + "ruleCode": "GP_PRACTICE_CODE less or equal to NVL Test", + "ruleText": "GP_PRACTICE_CODE less than or equal to NVL Test", + "ruleType": "S" + }, + { + "ruleCode": "BOOKED_APPOINTMENT_DATE D<= NVL Test", + "ruleText": "BOOKED_APPOINTMENT_DATE D<= NVL Test", + "ruleType": "S" + }, + { + "ruleCode": "LAST_INVITE_DATE W> NVL Test", + "ruleText": "LAST_INVITE_DATE W> NVL Test", + "ruleType": "S" + }, + { + "ruleCode": "LAST_VALID_DOSE_DATE Y< NVL Test", + "ruleText": "LAST_VALID_DOSE_DATE Y< NVL Test", + "ruleType": "S" + } + ] + } + ], + "responseId": "edb0e191-897e-41c0-b632-eef98d484867" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-236-02.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-236-02.json new file mode 100644 index 000000000..aa9cc296e --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-236-02.json @@ -0,0 +1,68 @@ +{ + "meta": { + "lastUpdated": "2025-07-22T21:07:51.351855+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "eli_236_group", + "cohortStatus": "NotActionable", + "cohortText": "You are currently in eli_236" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "13Q Flag NVL equal Test", + "ruleText": "13Q Flag NVL Test|13Q Flag NVL Test", + "ruleType": "S" + }, + { + "ruleCode": "CARE_HOME_FLAG not equal NVL Test", + "ruleText": "CARE_HOME_FLAG not equal NVL Test", + "ruleType": "S" + }, + { + "ruleCode": "COMMISSIONING_REGION greater than NVL Test", + "ruleText": "COMMISSIONING_REGION greater than NVL Test", + "ruleType": "S" + }, + { + "ruleCode": "DATE_OF_BIRTH less than NVL Test", + "ruleText": "DATE_OF_BIRTH less than NVL Test", + "ruleType": "S" + }, + { + "ruleCode": "GENDER greater than or equal to NVL Test", + "ruleText": "GENDER greater than or equal to NVL Test", + "ruleType": "S" + }, + { + "ruleCode": "GP_PRACTICE_CODE less or equal to NVL Test", + "ruleText": "GP_PRACTICE_CODE less than or equal to NVL Test", + "ruleType": "S" + }, + { + "ruleCode": "BOOKED_APPOINTMENT_DATE D<= NVL Test", + "ruleText": "BOOKED_APPOINTMENT_DATE D<= NVL Test", + "ruleType": "S" + }, + { + "ruleCode": "LAST_INVITE_DATE W> NVL Test", + "ruleText": "LAST_INVITE_DATE W> NVL Test", + "ruleType": "S" + }, + { + "ruleCode": "LAST_VALID_DOSE_DATE Y< NVL Test", + "ruleText": "LAST_VALID_DOSE_DATE Y< NVL Test", + "ruleType": "S" + } + ] + } + ], + "responseId": "edb0e191-897e-41c0-b632-eef98d484867" +} diff --git a/tests/e2e/utils/placeholder_utils.py b/tests/e2e/utils/placeholder_utils.py index 36a16c273..98541cd92 100644 --- a/tests/e2e/utils/placeholder_utils.py +++ b/tests/e2e/utils/placeholder_utils.py @@ -49,6 +49,8 @@ def _resolve_placeholder_value(placeholder: str) -> str: result = _resolve_age_placeholder(today, int(arg), parts[0]) elif date_type == "DAY": result = _format_date(today + timedelta(days=int(arg)), parts[0]) + elif date_type == "WEEK": + result = _format_date(today + timedelta(weeks=int(arg)), parts[0]) elif date_type == "MONTH": result = _format_date(today + relativedelta(months=int(arg)), parts[0]) elif date_type == "YEAR": diff --git a/tests/e2e/utils/s3_config_manager.py b/tests/e2e/utils/s3_config_manager.py index cf1c6a1fa..e7bd6994e 100644 --- a/tests/e2e/utils/s3_config_manager.py +++ b/tests/e2e/utils/s3_config_manager.py @@ -1,4 +1,5 @@ import hashlib +import json import logging import os from pathlib import Path @@ -7,6 +8,9 @@ import botocore from dotenv import load_dotenv +from tests.e2e.utils.data_helper import resolve_placeholders_in_data +from tests.e2e.utils.placeholder_context import PlaceholderDTO + load_dotenv(dotenv_path=".env") logger = logging.getLogger(__name__) @@ -87,23 +91,44 @@ def upload_all_configs(self, local_paths: list[Path]) -> None: desired_keys = {self._s3_key(name) for name in desired_filenames} existing_keys = self._list_existing_keys() - - # Delete only unwanted ones (not in desired_keys) keys_to_delete = [key for key in existing_keys if key not in desired_keys] if keys_to_delete: self._delete_keys(keys_to_delete) + dto = PlaceholderDTO() + for path in local_paths: filename = path.name s3_key = self._s3_key(filename) - if self.config_exists_and_matches(path, s3_key): + logger.info("๐Ÿ”ง Resolving placeholders in config: %s", filename) + + with path.open() as f: + raw_data = json.load(f) + + resolved = resolve_placeholders_in_data(raw_data, dto, filename) + resolved_json_str = json.dumps(resolved, indent=2) + + if self.config_exists_and_matches_str(resolved_json_str, s3_key): logger.info("โœ… Config '%s' is unchanged in S3. Skipping upload.", filename) else: logger.info("โฌ†๏ธ Uploading config '%s' to S3...", filename) - self.s3_client.upload_file(path, self.bucket_name, s3_key) + self.s3_client.put_object( + Body=resolved_json_str.encode("utf-8"), + Bucket=self.bucket_name, + Key=s3_key, + ContentType="application/json", + ) logger.info("๐Ÿ“„ Uploaded to s3://%s/%s", self.bucket_name, s3_key) + def config_exists_and_matches_str(self, local_json_str: str, s3_key: str) -> bool: + try: + response = self.s3_client.get_object(Bucket=self.bucket_name, Key=s3_key) + remote_str = response["Body"].read().decode("utf-8") + return local_json_str.strip() == remote_str.strip() + except self.s3_client.exceptions.NoSuchKey: + return False + def _list_existing_keys(self) -> list[str]: """List all object keys under the current S3 prefix.""" response = self.s3_client.list_objects_v2(Bucket=self.bucket_name, Prefix=self.s3_prefix) From 271f8ec1e3f7c82b5865ecabfc78e8d9d4c321a3 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Wed, 23 Jul 2025 10:29:39 +0100 Subject: [PATCH 59/72] sean demo tests --- .../inProgressTestConfigs/sean_demo.json | 134 ++++++++++++++++++ ..._RSV_ELI_295-04.json => sean_demo_01.json} | 33 ++++- .../inProgressTestResponses/sean_demo_01.json | 28 ++++ 3 files changed, 188 insertions(+), 7 deletions(-) create mode 100644 tests/e2e/data/configs/inProgressTestConfigs/sean_demo.json rename tests/e2e/data/dynamoDB/{inProgressTestData/AUTO_RSV_ELI_295-04.json => sean_demo_01.json} (52%) create mode 100644 tests/e2e/data/responses/inProgressTestResponses/sean_demo_01.json diff --git a/tests/e2e/data/configs/inProgressTestConfigs/sean_demo.json b/tests/e2e/data/configs/inProgressTestConfigs/sean_demo.json new file mode 100644 index 000000000..f89970983 --- /dev/null +++ b/tests/e2e/data/configs/inProgressTestConfigs/sean_demo.json @@ -0,0 +1,134 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "Sean Demo Campaign", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "CONTACT_GP", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "DEFAULT_A", + "DefaultNotEligibleRouting": "", + "DefaultNotActionableRouting": "", + "Version": "1", + "Name": "Sean Demo Iteration", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_cohort", + "CohortGroup": "rsv_group", + "PositiveDescription": "You are currently in an rsv cohort", + "NegativeDescription": "You are not currently not in an rsv cohort", + "Priority": 0 + }, + { + "CohortLabel": "very_obesity_cohort", + "CohortGroup": "clinical_risk_group", + "PositiveDescription": "You are currently in a clinical risk group", + "NegativeDescription": "You are currently in not in a clinical risk group", + "Priority": 1 + }, + { + "CohortLabel": "obesity_cohort", + "CohortGroup": "clinical_risk_group", + "PositiveDescription": "You are currently in a clinical risk group", + "NegativeDescription": "You are currently in not in a clinical risk group", + "Priority": 2 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "Test S Rule - Vaccinated", + "Description": "Test S Rule - Vaccinated", + "Priority": 500, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "D<", + "Comparator": "0", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Test S Rule - In a Carehome", + "Description": "Test S Rule - In a Carehome", + "Priority": 501, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y" + } + ], + "ActionsMapper": { + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "##You have an RSV vaccination appointment\\nYou can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Manage your appointment" + }, + "ALREADY_VACCS": { + "ExternalRoutingCode": "ALREADYVACCS", + "ActionDescription": "##Our records show you have already been vaccinated\\nIf you think this is wrong then contact your healthcare professional.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "" + }, + "DEFAULT_A": { + "ExternalRoutingCode": "DefaultA", + "ActionDescription": "DefaultADescription", + "ActionType": "DefaultAType", + "UrlLink": "https://www.defaultaaction.com", + "UrlLabel": "DefaultALabel" + }, + "DEFAULT_X": { + "ExternalRoutingCode": "DefaultX", + "ActionDescription": "If you think this is wrong then contact your healthcare professional", + "ActionType": "DefaultXType", + "UrlLink": "https://www.defaultxaction.com", + "UrlLabel": "DefaultXLabel" + }, + "OTHER_X": { + "ExternalRoutingCode": "OtherX", + "ActionDescription": "OtherXDescription", + "ActionType": "OtherXType", + "UrlLink": "https://www.otherxaction.com", + "UrlLabel": "OtherXLabel" + }, + "OTHER_Y": { + "ExternalRoutingCode": "OtherY", + "ActionDescription": "OtherYDescription", + "ActionType": "OtherYType", + "UrlLink": "https://www.otheryaction.com", + "UrlLabel": "OtherYLabel" + }, + "DEFAULT_Y": { + "ExternalRoutingCode": "DefaultY", + "ActionDescription": "DefaultYDescription", + "ActionType": "DefaultYType", + "UrlLink": "https://www.defaultyaction.com", + "UrlLabel": "DefaultYLabel" + } + } + } + ] + } +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-04.json b/tests/e2e/data/dynamoDB/sean_demo_01.json similarity index 52% rename from tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-04.json rename to tests/e2e/data/dynamoDB/sean_demo_01.json index 3095869b1..63cfe6f93 100644 --- a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI_295-04.json +++ b/tests/e2e/data/dynamoDB/sean_demo_01.json @@ -1,13 +1,14 @@ { - "scenario_name": "ELI-295 - Not Eligible -Trigger the Default X Rule", - "note": "triggers the default due to being 75", + "scenario_name": "Sean Demo", "request_headers": { - "nhs-login-nhs-number": "9900029504" + "nhs-login-nhs-number": "9111111111" }, - "config_filenames": ["AUTO_RSV_ELI-295-2.json"], + "config_filenames": [ + "sean_demo.json" + ], "data": [ { - "NHS_NUMBER": "9900029504", + "NHS_NUMBER": "9111111111", "ATTRIBUTE_TYPE": "COHORTS", "COHORT_MAP": { "cohorts": { @@ -18,13 +19,20 @@ "S": "20250604" } } + }, + "rsv_cohort": { + "M": { + "dateJoined": { + "S": "20250604" + } + } } } } } }, { - "NHS_NUMBER": "9900029504", + "NHS_NUMBER": "9111111111", "ATTRIBUTE_TYPE": "PERSON", "DATE_OF_BIRTH": "<>", "GENDER": "0", @@ -38,8 +46,19 @@ "ICB": "QUE", "COMMISSIONING_REGION": "Y61", "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", + "CARE_HOME_FLAG": "Y", "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9111111111", + "ATTRIBUTE_TYPE": "COVID", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + }, + { + "NHS_NUMBER": "9111111111", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": "<>" } ] } diff --git a/tests/e2e/data/responses/inProgressTestResponses/sean_demo_01.json b/tests/e2e/data/responses/inProgressTestResponses/sean_demo_01.json new file mode 100644 index 000000000..22c46a300 --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/sean_demo_01.json @@ -0,0 +1,28 @@ +{ + "meta": { + "lastUpdated": "2025-07-22T08:06:18.119910+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_group", + "cohortStatus": "NotActionable", + "cohortText": "You are currently in an rsv cohort" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "Test S Rule - Vaccinated", + "ruleText": "Test S Rule - Vaccinated", + "ruleType": "S" + } + ] + } + ], + "responseId": "abcaf256-ed6a-4ffe-a3ea-de1539a72120" +} From 40decc1d4643bf5e94ddaaf3a91eedf41dd24995 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 28 Jul 2025 10:26:49 +0100 Subject: [PATCH 60/72] Lots of new scenarios and scenario updates --- .../AUTO_RSV_ELI-355.json | 88 +++++++++++++++++++ .../storyTestConfigs/AUTO_RSV_ELI-222.json | 11 +++ .../storyTestConfigs/AUTO_RSV_ELI-236.json | 2 +- tests/e2e/data/dynamoDB/AUTO_RSV_ELI-355.json | 53 +++++++++++ .../regressionTestData/AUTO_RSV_REG_001.json | 17 ++-- .../regressionTestData/AUTO_RSV_REG_002.json | 28 ++---- .../regressionTestData/AUTO_RSV_REG_003.json | 30 +++---- .../AUTO_RSV_REG_010-1.json | 35 ++++++++ .../regressionTestData/AUTO_RSV_REG_010.json | 17 ++-- .../regressionTestData/AUTO_RSV_REG_011.json | 17 ++-- .../smokeTestData/AUTO_RSV_SB_001.json | 17 ++-- .../smokeTestData/AUTO_RSV_SB_002.json | 17 ++-- .../smokeTestData/AUTO_RSV_SB_003.json | 17 ++-- .../smokeTestData/AUTO_RSV_SB_004.json | 17 ++-- .../smokeTestData/AUTO_RSV_SB_005.json | 17 ++-- .../smokeTestData/AUTO_RSV_SB_006.json | 17 ++-- .../smokeTestData/AUTO_RSV_SB_007.json | 17 ++-- .../smokeTestData/AUTO_RSV_SB_008.json | 17 ++-- .../smokeTestData/AUTO_RSV_SB_009.json | 17 ++-- .../smokeTestData/AUTO_RSV_SB_010.json | 19 ++-- .../smokeTestData/AUTO_RSV_SB_011.json | 17 ++-- .../smokeTestData/AUTO_RSV_SB_012.json | 7 -- .../smokeTestData/AUTO_RSV_SB_013.json | 17 ++-- .../smokeTestData/AUTO_RSV_SB_014.json | 17 ++-- .../storyTestData/AUTO_RSV_ELI-155.json | 25 ++---- .../storyTestData/AUTO_RSV_ELI-216-1.json | 25 ++---- .../storyTestData/AUTO_RSV_ELI-219-1.json | 25 ++---- .../storyTestData/AUTO_RSV_ELI-219-2.json | 25 ++---- .../storyTestData/AUTO_RSV_ELI-219-3.json | 27 +++--- .../storyTestData/AUTO_RSV_ELI-221-01.json | 25 ++---- .../storyTestData/AUTO_RSV_ELI-221-02.json | 27 +++--- .../storyTestData/AUTO_RSV_ELI-221-03.json | 25 ++---- .../storyTestData/AUTO_RSV_ELI-222-1.json | 27 +++--- .../storyTestData/AUTO_RSV_ELI-222-2.json | 31 +++---- .../storyTestData/AUTO_RSV_ELI-222-3.json | 12 +-- .../storyTestData/AUTO_RSV_ELI-222-4.json | 31 +++---- .../storyTestData/AUTO_RSV_ELI-222-5.json | 31 +++---- .../storyTestData/AUTO_RSV_ELI-222-6.json | 29 +++--- .../storyTestData/AUTO_RSV_ELI-222-7.json | 39 ++++++++ .../storyTestData/AUTO_RSV_ELI-222-8.json | 44 ++++++++++ .../storyTestData/AUTO_RSV_ELI-236-01.json | 21 ++--- .../storyTestData/AUTO_RSV_ELI-236-02.json | 19 ++-- ...I_295-01.json => AUTO_RSV_ELI-295-01.json} | 17 ++-- ...I_295-02.json => AUTO_RSV_ELI-295-02.json} | 19 ++-- ...I_295-03.json => AUTO_RSV_ELI-295-03.json} | 17 ++-- ...I_295-04.json => AUTO_RSV_ELI-295-04.json} | 19 ++-- ...I_295-05.json => AUTO_RSV_ELI-295-05.json} | 19 ++-- ...I_295-06.json => AUTO_RSV_ELI-295-06.json} | 21 ++--- ...I_295-07.json => AUTO_RSV_ELI-295-07.json} | 19 ++-- ...I_295-08.json => AUTO_RSV_ELI-295-08.json} | 19 ++-- ...I_295-09.json => AUTO_RSV_ELI-295-09.json} | 19 ++-- ...I_295-10.json => AUTO_RSV_ELI-295-10.json} | 19 ++-- ...I_295-11.json => AUTO_RSV_ELI-295-11.json} | 19 ++-- ...I_295-12.json => AUTO_RSV_ELI-295-12.json} | 19 ++-- ...I_295-13.json => AUTO_RSV_ELI-295-13.json} | 19 ++-- ...I_295-14.json => AUTO_RSV_ELI-295-14.json} | 19 ++-- ...I_295-15.json => AUTO_RSV_ELI-295-15.json} | 19 ++-- ...I_295-16.json => AUTO_RSV_ELI-295-16.json} | 17 ++-- ...I_295-17.json => AUTO_RSV_ELI-295-17.json} | 17 ++-- .../storyTestData/AUTO_RSV_ELI-317-1.json | 25 ++---- .../storyTestData/AUTO_RSV_ELI-317-2.json | 25 ++---- .../storyTestData/AUTO_RSV_ELI-317-3.json | 25 ++---- .../storyTestData/AUTO_RSV_ELI-317-4.json | 23 ++--- .../storyTestData/AUTO_RSV_ELI-317-5.json | 23 ++--- .../storyTestData/AUTO_RSV_ELI-320-1.json | 34 +++---- .../storyTestData/AUTO_RSV_ELI-320-10.json | 46 ++++------ .../storyTestData/AUTO_RSV_ELI-320-11.json | 39 +++----- .../storyTestData/AUTO_RSV_ELI-320-2.json | 28 ++---- .../storyTestData/AUTO_RSV_ELI-320-3.json | 28 ++---- .../storyTestData/AUTO_RSV_ELI-320-4.json | 28 ++---- .../storyTestData/AUTO_RSV_ELI-320-5.json | 28 ++---- .../storyTestData/AUTO_RSV_ELI-320-6.json | 46 ++++------ .../storyTestData/AUTO_RSV_ELI-320-7.json | 39 +++----- .../storyTestData/AUTO_RSV_ELI-320-8.json | 39 +++----- .../storyTestData/AUTO_RSV_ELI-320-9.json | 39 +++----- .../AUTO_RSV_REG_010-1.json | 27 ++++++ .../AUTO_RSV_ELI-222-7.json | 27 ++++++ .../AUTO_RSV_ELI-222-8.json | 22 +++++ ...I_295-01.json => AUTO_RSV_ELI-295-01.json} | 0 ...I_295-02.json => AUTO_RSV_ELI-295-02.json} | 0 ...I_295-03.json => AUTO_RSV_ELI-295-03.json} | 0 ...I_295-04.json => AUTO_RSV_ELI-295-04.json} | 0 ...I_295-05.json => AUTO_RSV_ELI-295-05.json} | 0 ...I_295-06.json => AUTO_RSV_ELI-295-06.json} | 0 ...I_295-07.json => AUTO_RSV_ELI-295-07.json} | 0 ...I_295-08.json => AUTO_RSV_ELI-295-08.json} | 0 ...I_295-09.json => AUTO_RSV_ELI-295-09.json} | 0 ...I_295-10.json => AUTO_RSV_ELI-295-10.json} | 0 ...I_295-11.json => AUTO_RSV_ELI-295-11.json} | 0 ...I_295-12.json => AUTO_RSV_ELI-295-12.json} | 0 ...I_295-13.json => AUTO_RSV_ELI-295-13.json} | 0 ...I_295-14.json => AUTO_RSV_ELI-295-14.json} | 0 ...I_295-15.json => AUTO_RSV_ELI-295-15.json} | 0 ...I_295-16.json => AUTO_RSV_ELI-295-16.json} | 0 ...I_295-17.json => AUTO_RSV_ELI-295-17.json} | 0 95 files changed, 885 insertions(+), 1039 deletions(-) create mode 100644 tests/e2e/data/configs/inProgressTestConfigs/AUTO_RSV_ELI-355.json create mode 100644 tests/e2e/data/dynamoDB/AUTO_RSV_ELI-355.json create mode 100644 tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_010-1.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-7.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-8.json rename tests/e2e/data/dynamoDB/storyTestData/{AUTO_RSV_ELI_295-01.json => AUTO_RSV_ELI-295-01.json} (76%) rename tests/e2e/data/dynamoDB/storyTestData/{AUTO_RSV_ELI_295-02.json => AUTO_RSV_ELI-295-02.json} (75%) rename tests/e2e/data/dynamoDB/storyTestData/{AUTO_RSV_ELI_295-03.json => AUTO_RSV_ELI-295-03.json} (76%) rename tests/e2e/data/dynamoDB/storyTestData/{AUTO_RSV_ELI_295-04.json => AUTO_RSV_ELI-295-04.json} (76%) rename tests/e2e/data/dynamoDB/storyTestData/{AUTO_RSV_ELI_295-05.json => AUTO_RSV_ELI-295-05.json} (75%) rename tests/e2e/data/dynamoDB/storyTestData/{AUTO_RSV_ELI_295-06.json => AUTO_RSV_ELI-295-06.json} (71%) rename tests/e2e/data/dynamoDB/storyTestData/{AUTO_RSV_ELI_295-07.json => AUTO_RSV_ELI-295-07.json} (75%) rename tests/e2e/data/dynamoDB/storyTestData/{AUTO_RSV_ELI_295-08.json => AUTO_RSV_ELI-295-08.json} (75%) rename tests/e2e/data/dynamoDB/storyTestData/{AUTO_RSV_ELI_295-09.json => AUTO_RSV_ELI-295-09.json} (78%) rename tests/e2e/data/dynamoDB/storyTestData/{AUTO_RSV_ELI_295-10.json => AUTO_RSV_ELI-295-10.json} (76%) rename tests/e2e/data/dynamoDB/storyTestData/{AUTO_RSV_ELI_295-11.json => AUTO_RSV_ELI-295-11.json} (77%) rename tests/e2e/data/dynamoDB/storyTestData/{AUTO_RSV_ELI_295-12.json => AUTO_RSV_ELI-295-12.json} (79%) rename tests/e2e/data/dynamoDB/storyTestData/{AUTO_RSV_ELI_295-13.json => AUTO_RSV_ELI-295-13.json} (75%) rename tests/e2e/data/dynamoDB/storyTestData/{AUTO_RSV_ELI_295-14.json => AUTO_RSV_ELI-295-14.json} (75%) rename tests/e2e/data/dynamoDB/storyTestData/{AUTO_RSV_ELI_295-15.json => AUTO_RSV_ELI-295-15.json} (78%) rename tests/e2e/data/dynamoDB/storyTestData/{AUTO_RSV_ELI_295-16.json => AUTO_RSV_ELI-295-16.json} (76%) rename tests/e2e/data/dynamoDB/storyTestData/{AUTO_RSV_ELI_295-17.json => AUTO_RSV_ELI-295-17.json} (76%) create mode 100644 tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_010-1.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-222-7.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-222-8.json rename tests/e2e/data/responses/storyTestResponses/{AUTO_RSV_ELI_295-01.json => AUTO_RSV_ELI-295-01.json} (100%) rename tests/e2e/data/responses/storyTestResponses/{AUTO_RSV_ELI_295-02.json => AUTO_RSV_ELI-295-02.json} (100%) rename tests/e2e/data/responses/storyTestResponses/{AUTO_RSV_ELI_295-03.json => AUTO_RSV_ELI-295-03.json} (100%) rename tests/e2e/data/responses/storyTestResponses/{AUTO_RSV_ELI_295-04.json => AUTO_RSV_ELI-295-04.json} (100%) rename tests/e2e/data/responses/storyTestResponses/{AUTO_RSV_ELI_295-05.json => AUTO_RSV_ELI-295-05.json} (100%) rename tests/e2e/data/responses/storyTestResponses/{AUTO_RSV_ELI_295-06.json => AUTO_RSV_ELI-295-06.json} (100%) rename tests/e2e/data/responses/storyTestResponses/{AUTO_RSV_ELI_295-07.json => AUTO_RSV_ELI-295-07.json} (100%) rename tests/e2e/data/responses/storyTestResponses/{AUTO_RSV_ELI_295-08.json => AUTO_RSV_ELI-295-08.json} (100%) rename tests/e2e/data/responses/storyTestResponses/{AUTO_RSV_ELI_295-09.json => AUTO_RSV_ELI-295-09.json} (100%) rename tests/e2e/data/responses/storyTestResponses/{AUTO_RSV_ELI_295-10.json => AUTO_RSV_ELI-295-10.json} (100%) rename tests/e2e/data/responses/storyTestResponses/{AUTO_RSV_ELI_295-11.json => AUTO_RSV_ELI-295-11.json} (100%) rename tests/e2e/data/responses/storyTestResponses/{AUTO_RSV_ELI_295-12.json => AUTO_RSV_ELI-295-12.json} (100%) rename tests/e2e/data/responses/storyTestResponses/{AUTO_RSV_ELI_295-13.json => AUTO_RSV_ELI-295-13.json} (100%) rename tests/e2e/data/responses/storyTestResponses/{AUTO_RSV_ELI_295-14.json => AUTO_RSV_ELI-295-14.json} (100%) rename tests/e2e/data/responses/storyTestResponses/{AUTO_RSV_ELI_295-15.json => AUTO_RSV_ELI-295-15.json} (100%) rename tests/e2e/data/responses/storyTestResponses/{AUTO_RSV_ELI_295-16.json => AUTO_RSV_ELI-295-16.json} (100%) rename tests/e2e/data/responses/storyTestResponses/{AUTO_RSV_ELI_295-17.json => AUTO_RSV_ELI-295-17.json} (100%) diff --git a/tests/e2e/data/configs/inProgressTestConfigs/AUTO_RSV_ELI-355.json b/tests/e2e/data/configs/inProgressTestConfigs/AUTO_RSV_ELI-355.json new file mode 100644 index 000000000..54700a823 --- /dev/null +++ b/tests/e2e/data/configs/inProgressTestConfigs/AUTO_RSV_ELI-355.json @@ -0,0 +1,88 @@ +{ + "CampaignConfig": { + "ID": "<>", + "Version": "1", + "Name": "ELI-355 - Date of Birth Integer Issue", + "Type": "V", + "Target": "RSV", + "Manager": "person1@nhs.net", + "Approver": "person1@nhs.net", + "Reviewer": "person1@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "DefaultCommsRouting": "CONTACT_GP", + "StartDate": "20250601", + "EndDate": "20260601", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Iterations": [ + { + "ID": ",<>", + "DefaultCommsRouting": "CONTACT_GP", + "Version": "1", + "Name": "ELI-155 - cohort_label is not supported in R rules", + "IterationDate": "20250601", + "IterationNumber": 1, + "CommsType": "I", + "DefaultNotEligibleRouting": "", + "DefaultNotActionableRouting": "", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "eli_355_cohort", + "CohortGroup": "eli_355_group", + "PositiveDescription": "You are currently in eli_355", + "NegativeDescription": "You are not currently eli_355", + "Priority": 0 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "Test S DOB", + "Description": "Test S DOB", + "Priority": 1000, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": ">", + "Comparator": "19000101" + }, + { + "Type": "S", + "Name": "Test S Valid Dose", + "Description": "Test S Valid Dose", + "Priority": 1001, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "VALID_DOSES_COUNT", + "Operator": ">", + "Comparator": "0" + }, + { + "Type": "S", + "Name": "Test S Invalid Dose", + "Description": "Test S invalid Dose", + "Priority": 1002, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "INVALID_DOSES_COUNT", + "Operator": ">", + "Comparator": "0" + } + ], + "ActionsMapper": { + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "##You have an RSV vaccination appointment\\nYou can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Manage your appointment" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-222.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-222.json index 7369a7abb..838abfa04 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-222.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-222.json @@ -102,6 +102,17 @@ "CohortLabel": "elid_all_people", "Priority": 110 }, + { + "Type": "F", + "Name": "Filter those with incorrect age from rsv_75to79 cohort", + "Description": "Filter those with incorrect age from rsv_75to79 cohort", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_75to79", + "Priority": 120 + }, { "Type": "S", "Name": "Already Vaccinated", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-236.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-236.json index bd9164207..df0c96aaf 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-236.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-236.json @@ -129,7 +129,7 @@ "Priority": 1008, "AttributeLevel": "TARGET", "AttributeTarget": "RSV", - "AttributeName": "LAST_INVITE_DATE", + "AttributeName": "LAST_VALID_DOSE_DATE", "Operator": "Y<", "Comparator": "-5[[NVL:<>]]" } diff --git a/tests/e2e/data/dynamoDB/AUTO_RSV_ELI-355.json b/tests/e2e/data/dynamoDB/AUTO_RSV_ELI-355.json new file mode 100644 index 000000000..e7b3d8c62 --- /dev/null +++ b/tests/e2e/data/dynamoDB/AUTO_RSV_ELI-355.json @@ -0,0 +1,53 @@ +{ + "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested actions (with booking)", + "config_filenames": [ + "AUTO_RSV_ELI-355.json" + ], + "request_headers": { + "nhs-login-nhs-number": "9990035501" + }, + "data": [ + { + "NHS_NUMBER": "9990035501", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MAP": { + "cohorts": { + "M": { + "eli_355_cohort": { + "M": { + "dateJoined": { + "S": "20230515" + } + } + } + } + } + } + }, + { + "NHS_NUMBER": "9990035501", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500107", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS1", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9990035501", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "VALID_DOSES_COUNT": "1", + "INVALID_DOSES_COUNT": "2" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_001.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_001.json index 7c43889f0..0aec6aed8 100644 --- a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_001.json +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_001.json @@ -6,19 +6,12 @@ { "NHS_NUMBER": "1000000001", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_cohort_2": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_cohort_2", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "1000000001", diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_002.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_002.json index 97292ccaa..8aeb280fd 100644 --- a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_002.json +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_002.json @@ -6,26 +6,16 @@ { "NHS_NUMBER": "1000000002", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_cohort_2": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - }, - "rsv_cohort_3": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_cohort_2", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_cohort_3", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "1000000002", diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_003.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_003.json index 307422476..c473c3ce7 100644 --- a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_003.json +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_003.json @@ -3,29 +3,19 @@ "request_headers": {"nhs-login-nhs-number": "1000000003"}, "config_filenames": ["AUTO_RSV_REG_001.json"], "data": [ - { + { "NHS_NUMBER": "1000000003", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_cohort_2": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - }, - "rsv_cohort_1": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_cohort_2", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_cohort_1", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "1000000003", diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_010-1.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_010-1.json new file mode 100644 index 000000000..bcfa1ccf4 --- /dev/null +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_010-1.json @@ -0,0 +1,35 @@ +{ + "scenario_name": "RSV - Regression - Not Eligible, In Cohort - Future Date", + "request_headers": {"nhs-login-nhs-number": "1100000010"}, + "config_filenames": ["AUTO_RSV_REG_001.json"], + "data": [ + { + "NHS_NUMBER": "1100000010", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_cohort_2", + "DATE_JOINED": "<>" + } + ] + }, + { + "NHS_NUMBER": "1100000010", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "LS1 1AB", + "POSTCODE_SECTOR": "LS2", + "POSTCODE_OUTCODE": "1AB", + "MSOA": "E02001111", + "LSOA": "E01005348", + "GP_PRACTICE_CODE": "B87008", + "PCN": "U43084", + "ICB": "QWO", + "COMMISSIONING_REGION": "Y63", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_010.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_010.json index 676db5821..52321d5ed 100644 --- a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_010.json +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_010.json @@ -6,19 +6,12 @@ { "NHS_NUMBER": "1000000010", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_cohort_22": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_cohort_22", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "1000000010", diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_011.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_011.json index 9de307d9c..4fb2d7298 100644 --- a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_011.json +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_011.json @@ -6,19 +6,12 @@ { "NHS_NUMBER": "1000000011", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_cohort_1": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_cohort_1", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "1000000011", diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json index 91780aeb4..48e951bbe 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_001.json @@ -10,19 +10,12 @@ { "NHS_NUMBER": "5000000001", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_75_rolling": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_75_rolling", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "5000000001", diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json index b38fc046b..aab28ffe8 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_002.json @@ -10,19 +10,12 @@ { "NHS_NUMBER": "5000000002", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_75_rolling": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_75_rolling", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "5000000002", diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json index 4df520b9e..2ad4133a7 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_003.json @@ -10,19 +10,12 @@ { "NHS_NUMBER": "5000000003", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_75to79_2024": { - "M": { - "dateJoined": { - "S": "20221120" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_75to79_2024", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "5000000003", diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_004.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_004.json index 11ed624db..bff06eff0 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_004.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_004.json @@ -10,19 +10,12 @@ { "NHS_NUMBER": "5000000004", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "no_group_description": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "no_group_description", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "5000000004", diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_005.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_005.json index cc2fbc347..fab88f8be 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_005.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_005.json @@ -10,19 +10,12 @@ { "NHS_NUMBER": "5000000005", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "no_group_description": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "no_group_description", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "5000000005", diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_006.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_006.json index 11300ffa9..7f5071755 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_006.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_006.json @@ -11,19 +11,12 @@ { "NHS_NUMBER": "5000000006", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "no_group_description": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "no_group_description", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "5000000006", diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_007.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_007.json index 3d348c282..20516426e 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_007.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_007.json @@ -10,19 +10,12 @@ { "NHS_NUMBER": "5000000007", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_75_rolling": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_75_rolling", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "5000000007", diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_008.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_008.json index 351533a4d..cc1f9289a 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_008.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_008.json @@ -10,19 +10,12 @@ { "NHS_NUMBER": "5000000008", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_75_rolling": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_75_rolling", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "5000000008", diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_009.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_009.json index 6c602f330..288e76542 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_009.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_009.json @@ -10,19 +10,12 @@ { "NHS_NUMBER": "5000000009", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_75_rolling": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_75_rolling", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "5000000009", diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json index 299a00d66..e042205c7 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json @@ -8,21 +8,14 @@ ], "data": [ { - "NHS_NUMBER": "5000000010", + "NHS_NUMBER": "50000000010", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_75_rolling": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_75_rolling", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "5000000010", diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_011.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_011.json index bfe3e02b5..e19d0428e 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_011.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_011.json @@ -10,19 +10,12 @@ { "NHS_NUMBER": "5000000011", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_75_rolling": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_75_rolling", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "5000000011", diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_012.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_012.json index 10802907e..533899dff 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_012.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_012.json @@ -7,13 +7,6 @@ "AUTO_RSV_SB_001.json" ], "data": [ - { - "NHS_NUMBER": "5000000012", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": {} - } - }, { "NHS_NUMBER": "5000000012", "ATTRIBUTE_TYPE": "PERSON", diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_013.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_013.json index bb7c22fdb..c32b47340 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_013.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_013.json @@ -10,19 +10,12 @@ { "NHS_NUMBER": "5000000013", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "covid_16+_immunosuppression": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "covid_16+_immunosuppression", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "5000000013", diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_014.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_014.json index 1c26c5414..5e9d8f40a 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_014.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_014.json @@ -10,19 +10,12 @@ { "NHS_NUMBER": "5000000014", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "covid_16+_immunosuppression": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "covid_16+_immunosuppression", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "5000000014", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json index eb10bcb57..5ee21f2ec 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-155.json @@ -4,28 +4,21 @@ "AUTO_RSV_ELI-155.json" ], "request_headers": { - "nhs-login-nhs-number": "9000000001" + "nhs-login-nhs-number": "9000015501" }, "data": [ { - "NHS_NUMBER": "9000000001", + "NHS_NUMBER": "9000015501", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_155_in": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "eli_155_in", + "DATE_JOINED": "20231020" } - } + ] }, { - "NHS_NUMBER": "9000000001", + "NHS_NUMBER": "9000015501", "ATTRIBUTE_TYPE": "PERSON", "DATE_OF_BIRTH": "<>", "GENDER": "0", @@ -43,7 +36,7 @@ "DE_FLAG": "N" }, { - "NHS_NUMBER": "9000000001", + "NHS_NUMBER": "9000015501", "ATTRIBUTE_TYPE": "RSV", "BOOKED_APPOINTMENT_DATE": "<>" } diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-1.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-1.json index dc0b7f0d9..bb6f90337 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-1.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-216-1.json @@ -4,28 +4,21 @@ "AUTO_RSV_ELI-216.json" ], "request_headers": { - "nhs-login-nhs-number": "9000000002" + "nhs-login-nhs-number": "9900021601" }, "data": [ { - "NHS_NUMBER": "9000000002", + "NHS_NUMBER": "9900021601", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_216_cohort": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "eli_216_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { - "NHS_NUMBER": "9000000002", + "NHS_NUMBER": "9900021601", "ATTRIBUTE_TYPE": "PERSON", "DATE_OF_BIRTH": "<>", "GENDER": "0", @@ -43,7 +36,7 @@ "DE_FLAG": "N" }, { - "NHS_NUMBER": "9000000002", + "NHS_NUMBER": "9900021601", "ATTRIBUTE_TYPE": "RSV", "BOOKED_APPOINTMENT_DATE": "<>", "LAST_SUCCESSFUL_DATE": "<>" diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json index 1bae1fccf..e2123faa6 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-1.json @@ -4,28 +4,21 @@ "AUTO_RSV_ELI-219-1.json" ], "request_headers": { - "nhs-login-nhs-number": "9000000003" + "nhs-login-nhs-number": "9900021901" }, "data": [ { - "NHS_NUMBER": "9000000003", + "NHS_NUMBER": "9900021901", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_291_cohort_1": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "eli_291_cohort_1", + "DATE_JOINED": "20231020" } - } + ] }, { - "NHS_NUMBER": "9000000003", + "NHS_NUMBER": "9900021901", "ATTRIBUTE_TYPE": "PERSON", "DATE_OF_BIRTH": "<>", "GENDER": "0", @@ -43,7 +36,7 @@ "DE_FLAG": "N" }, { - "NHS_NUMBER": "9000000003", + "NHS_NUMBER": "9900021901", "ATTRIBUTE_TYPE": "RSV", "BOOKED_APPOINTMENT_DATE": "<>", "LAST_SUCCESSFUL_DATE": "<>" diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-2.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-2.json index 64994bddd..d7fb26134 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-2.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-2.json @@ -4,28 +4,21 @@ "AUTO_RSV_ELI-219-2.json" ], "request_headers": { - "nhs-login-nhs-number": "9000000004" + "nhs-login-nhs-number": "9900021902" }, "data": [ { - "NHS_NUMBER": "9000000004", + "NHS_NUMBER": "9900021902", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_291_cohort_2": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "eli_291_cohort_2", + "DATE_JOINED": "20231020" } - } + ] }, { - "NHS_NUMBER": "9000000004", + "NHS_NUMBER": "9900021902", "ATTRIBUTE_TYPE": "PERSON", "DATE_OF_BIRTH": "<>", "GENDER": "0", @@ -43,7 +36,7 @@ "DE_FLAG": "N" }, { - "NHS_NUMBER": "9000000004", + "NHS_NUMBER": "9900021902", "ATTRIBUTE_TYPE": "RSV", "BOOKED_APPOINTMENT_DATE": "<>", "LAST_SUCCESSFUL_DATE": "<>" diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-3.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-3.json index ca47b2124..b1ed5a453 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-3.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-3.json @@ -4,28 +4,21 @@ "AUTO_RSV_ELI-219-3.json" ], "request_headers": { - "nhs-login-nhs-number": "9000000005" + "nhs-login-nhs-number": "9900021903" }, "data": [ - { - "NHS_NUMBER": "9000000005", +{ + "NHS_NUMBER": "9900021903", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_291_cohort_3": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "eli_291_cohort_3", + "DATE_JOINED": "20231020" } - } + ] }, { - "NHS_NUMBER": "9000000005", + "NHS_NUMBER": "9900021903", "ATTRIBUTE_TYPE": "PERSON", "DATE_OF_BIRTH": "<>", "GENDER": "0", @@ -43,7 +36,7 @@ "DE_FLAG": "N" }, { - "NHS_NUMBER": "9000000005", + "NHS_NUMBER": "9900021903", "ATTRIBUTE_TYPE": "RSV", "BOOKED_APPOINTMENT_DATE": "<>", "LAST_SUCCESSFUL_DATE": "<>" diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-01.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-01.json index 4df99de3f..1d8be93a2 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-01.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-01.json @@ -1,29 +1,22 @@ { "scenario_name": "RSV - RuleStop: Y", "request_headers": { - "nhs-login-nhs-number": "9900022201" + "nhs-login-nhs-number": "9900022101" }, "config_filenames": ["AUTO_RSV_ELI-221-01.json"], "data": [ { - "NHS_NUMBER": "9900022201", + "NHS_NUMBER": "9900022101", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_221_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "eli_221_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { - "NHS_NUMBER": "9900022201", + "NHS_NUMBER": "9900022101", "ATTRIBUTE_TYPE": "PERSON", "DATE_OF_BIRTH": "19500601", "GENDER": "0", @@ -41,7 +34,7 @@ "DE_FLAG": "Y" }, { - "NHS_NUMBER": "9900022201", + "NHS_NUMBER": "9900022101", "ATTRIBUTE_TYPE": "RSV", "LAST_SUCCESSFUL_DATE": "<>", "BOOKED_APPOINTMENT_DATE": "<>", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-02.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-02.json index c503fd93c..8d3a10155 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-02.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-02.json @@ -1,29 +1,22 @@ { "scenario_name": "RSV - RuleStop: N", "request_headers": { - "nhs-login-nhs-number": "9900022202" + "nhs-login-nhs-number": "9900022102" }, "config_filenames": ["AUTO_RSV_ELI-221-02.json"], "data": [ - { - "NHS_NUMBER": "9900022202", + { + "NHS_NUMBER": "9900022102", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_221_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "eli_221_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { - "NHS_NUMBER": "9900022202", + "NHS_NUMBER": "9900022102", "ATTRIBUTE_TYPE": "PERSON", "DATE_OF_BIRTH": "19500601", "GENDER": "0", @@ -41,7 +34,7 @@ "DE_FLAG": "Y" }, { - "NHS_NUMBER": "9900022202", + "NHS_NUMBER": "9900022102", "ATTRIBUTE_TYPE": "RSV", "LAST_SUCCESSFUL_DATE": "<>", "BOOKED_APPOINTMENT_DATE": "<>", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-03.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-03.json index 5192386c2..843aa899c 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-03.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-03.json @@ -1,29 +1,22 @@ { "scenario_name": "RSV - RuleStop Not included in rules", "request_headers": { - "nhs-login-nhs-number": "9900022203" + "nhs-login-nhs-number": "9900022103" }, "config_filenames": ["AUTO_RSV_ELI-221-03.json"], "data": [ { - "NHS_NUMBER": "9900022203", + "NHS_NUMBER": "9900022103", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_221_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "eli_221_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { - "NHS_NUMBER": "9900022203", + "NHS_NUMBER": "9900022103", "ATTRIBUTE_TYPE": "PERSON", "DATE_OF_BIRTH": "19500601", "GENDER": "0", @@ -41,7 +34,7 @@ "DE_FLAG": "Y" }, { - "NHS_NUMBER": "9900022203", + "NHS_NUMBER": "9900022103", "ATTRIBUTE_TYPE": "RSV", "LAST_SUCCESSFUL_DATE": "<>", "BOOKED_APPOINTMENT_DATE": "<>", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-1.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-1.json index d90ae3396..22f7ca90f 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-1.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-1.json @@ -1,31 +1,24 @@ { "scenario_name": "RSV - Magic Cohort Has Future booking No Vaccination", "request_headers": { - "nhs-login-nhs-number": "9900002221" + "nhs-login-nhs-number": "9900022201" }, "config_filenames": ["AUTO_RSV_ELI-222.json"], "data": [ { - "NHS_NUMBER": "9900002221", + "NHS_NUMBER": "9900022201", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "other_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "other_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { - "NHS_NUMBER": "9900002221", + "NHS_NUMBER": "9900022201", "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "19500601", + "DATE_OF_BIRTH": "<>", "GENDER": "0", "POSTCODE": "SG8 6EG", "POSTCODE_SECTOR": "SG86", @@ -41,7 +34,7 @@ "DE_FLAG": "N" }, { - "NHS_NUMBER": "9900002221", + "NHS_NUMBER": "9900022201", "ATTRIBUTE_TYPE": "RSV", "BOOKED_APPOINTMENT_DATE": "<>", "BOOKED_APPOINTMENT_PROVIDER": "NBS" diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-2.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-2.json index 40b51ee18..a259872f1 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-2.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-2.json @@ -1,31 +1,26 @@ { "scenario_name": "RSV - Magic Cohort Has Past booking No Vaccination", "request_headers": { - "nhs-login-nhs-number": "9900002222" + "nhs-login-nhs-number": "9900022202" }, - "config_filenames": ["AUTO_RSV_ELI-222.json"], + "config_filenames": [ + "AUTO_RSV_ELI-222.json" + ], "data": [ { - "NHS_NUMBER": "9900002222", + "NHS_NUMBER": "9900022202", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "other_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "other_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { - "NHS_NUMBER": "9900002222", + "NHS_NUMBER": "9900022202", "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "19500601", + "DATE_OF_BIRTH": "<>", "GENDER": "0", "POSTCODE": "SG8 6EG", "POSTCODE_SECTOR": "SG86", @@ -41,7 +36,7 @@ "DE_FLAG": "N" }, { - "NHS_NUMBER": "9900002222", + "NHS_NUMBER": "9900022202", "ATTRIBUTE_TYPE": "RSV", "BOOKED_APPOINTMENT_DATE": "<>", "BOOKED_APPOINTMENT_PROVIDER": "NBS" diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-3.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-3.json index 0d69913fb..0a64750b4 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-3.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-3.json @@ -1,14 +1,16 @@ { "scenario_name": "RSV - Magic Cohort Has a Past RSV Vaccination - Future Booking", "request_headers": { - "nhs-login-nhs-number": "9900002223" + "nhs-login-nhs-number": "9900022203" }, - "config_filenames": ["AUTO_RSV_ELI-222.json"], + "config_filenames": [ + "AUTO_RSV_ELI-222.json" + ], "data": [ { - "NHS_NUMBER": "9900002223", + "NHS_NUMBER": "9900022203", "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "19500601", + "DATE_OF_BIRTH": "<>", "GENDER": "0", "POSTCODE": "SG8 6EG", "POSTCODE_SECTOR": "SG86", @@ -24,7 +26,7 @@ "DE_FLAG": "N" }, { - "NHS_NUMBER": "9900002223", + "NHS_NUMBER": "9900022203", "ATTRIBUTE_TYPE": "RSV", "LAST_SUCCESSFUL_DATE": "<>", "BOOKED_APPOINTMENT_DATE": "<>", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-4.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-4.json index 58e307ada..769e7ce50 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-4.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-4.json @@ -1,31 +1,26 @@ { "scenario_name": "RSV - Magic Cohort Has a Past RSV Vaccination - No Booking", "request_headers": { - "nhs-login-nhs-number": "9900002224" + "nhs-login-nhs-number": "9900022204" }, - "config_filenames": ["AUTO_RSV_ELI-222.json"], + "config_filenames": [ + "AUTO_RSV_ELI-222.json" + ], "data": [ { - "NHS_NUMBER": "9900002224", + "NHS_NUMBER": "9900022204", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "other_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "other_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { - "NHS_NUMBER": "9900002224", + "NHS_NUMBER": "9900022204", "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "19500601", + "DATE_OF_BIRTH": "<>", "GENDER": "0", "POSTCODE": "SG8 6EG", "POSTCODE_SECTOR": "SG86", @@ -41,7 +36,7 @@ "DE_FLAG": "N" }, { - "NHS_NUMBER": "9900002224", + "NHS_NUMBER": "9900022204", "ATTRIBUTE_TYPE": "RSV", "LAST_SUCCESSFUL_DATE": "<>" } diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-5.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-5.json index f072103d9..709043a33 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-5.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-5.json @@ -1,31 +1,26 @@ { "scenario_name": "RSV - Magic Cohort Has a Past RSV Vaccination -Past Booking", "request_headers": { - "nhs-login-nhs-number": "9900002225" + "nhs-login-nhs-number": "9900022205" }, - "config_filenames": ["AUTO_RSV_ELI-222.json"], + "config_filenames": [ + "AUTO_RSV_ELI-222.json" + ], "data": [ { - "NHS_NUMBER": "9900002225", + "NHS_NUMBER": "9900022205", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "other_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "other_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { - "NHS_NUMBER": "9900002225", + "NHS_NUMBER": "9900022205", "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "19500601", + "DATE_OF_BIRTH": "<>", "GENDER": "0", "POSTCODE": "SG8 6EG", "POSTCODE_SECTOR": "SG86", @@ -41,7 +36,7 @@ "DE_FLAG": "N" }, { - "NHS_NUMBER": "9900002225", + "NHS_NUMBER": "9900022205", "ATTRIBUTE_TYPE": "RSV", "LAST_SUCCESSFUL_DATE": "<>", "BOOKED_APPOINTMENT_DATE": "<>", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-6.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-6.json index 0461fb900..c3f8b6359 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-6.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-6.json @@ -1,31 +1,26 @@ { "scenario_name": "RSV -Magic Cohort No Vaccination - No Booking", "request_headers": { - "nhs-login-nhs-number": "9900002226" + "nhs-login-nhs-number": "9900022206" }, - "config_filenames": ["AUTO_RSV_ELI-222.json"], + "config_filenames": [ + "AUTO_RSV_ELI-222.json" + ], "data": [ { - "NHS_NUMBER": "9900002226", + "NHS_NUMBER": "9900022206", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "other_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "other_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { - "NHS_NUMBER": "9900002226", + "NHS_NUMBER": "9900022206", "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "19500601", + "DATE_OF_BIRTH": "<>", "GENDER": "0", "POSTCODE": "SG8 6EG", "POSTCODE_SECTOR": "SG86", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-7.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-7.json new file mode 100644 index 000000000..53226c1e3 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-7.json @@ -0,0 +1,39 @@ +{ + "scenario_name": "RSV - Magic Cohort - NotEligible - In Eligible Cohort - Filtered by age rule", + "request_headers": { + "nhs-login-nhs-number": "9900002227" + }, + "config_filenames": [ + "AUTO_RSV_ELI-222.json" + ], + "data": [ + { + "NHS_NUMBER": "9900002227", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_75to79", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900002227", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-8.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-8.json new file mode 100644 index 000000000..0dbd77f5b --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-8.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "RSV - Magic Cohort - NotActionable - In Eligible Cohort - Filtered by age, but Vaccinated", + "request_headers": { + "nhs-login-nhs-number": "9900002228" + }, + "config_filenames": [ + "AUTO_RSV_ELI-222.json" + ], + "data": [ + { + "NHS_NUMBER": "9900002228", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_75to79", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900002228", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900002228", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-236-01.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-236-01.json index 89955a63b..c8d0ca3c3 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-236-01.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-236-01.json @@ -7,22 +7,15 @@ "nhs-login-nhs-number": "9900023601" }, "data": [ - { + { "NHS_NUMBER": "9900023601", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_236_cohort": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "eli_236_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "9900023601", @@ -44,7 +37,7 @@ "POSTCODE_SECTOR": null }, { - "NHS_NUMBER": "9411876722", + "NHS_NUMBER": "9900023601", "ATTRIBUTE_TYPE": "RSV", "BOOKED_APPOINTMENT_DATE": null, "BOOKED_APPOINTMENT_PROVIDER": null, diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-236-02.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-236-02.json index f799013b0..a9180cde0 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-236-02.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-236-02.json @@ -10,19 +10,12 @@ { "NHS_NUMBER": "9900023602", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_236_cohort": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "eli_236_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "9900023602", @@ -44,7 +37,7 @@ "POSTCODE_SECTOR": null }, { - "NHS_NUMBER": "9411876722", + "NHS_NUMBER": "9900023602", "ATTRIBUTE_TYPE": "RSV", "BOOKED_APPOINTMENT_DATE": "<>", "BOOKED_APPOINTMENT_PROVIDER": null, diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-01.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-01.json similarity index 76% rename from tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-01.json rename to tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-01.json index d87fc3925..60a39caec 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-01.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-01.json @@ -8,19 +8,12 @@ { "NHS_NUMBER": "9900029501", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_295_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "eli_295_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "9900029501", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-02.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-02.json similarity index 75% rename from tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-02.json rename to tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-02.json index 3e91f83a1..210e2e359 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-02.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-02.json @@ -5,22 +5,15 @@ }, "config_filenames": ["AUTO_RSV_ELI-295-1.json"], "data": [ - { + { "NHS_NUMBER": "9900029502", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_295_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "eli_295_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "9900029502", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-03.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-03.json similarity index 76% rename from tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-03.json rename to tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-03.json index 01369d3ac..ca35dc601 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-03.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-03.json @@ -8,19 +8,12 @@ { "NHS_NUMBER": "9900029503", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_295_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "eli_295_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "9900029503", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-04.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-04.json similarity index 76% rename from tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-04.json rename to tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-04.json index 3095869b1..f5c8e01da 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-04.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-04.json @@ -6,22 +6,15 @@ }, "config_filenames": ["AUTO_RSV_ELI-295-2.json"], "data": [ - { + { "NHS_NUMBER": "9900029504", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "other_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "other_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "9900029504", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-05.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-05.json similarity index 75% rename from tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-05.json rename to tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-05.json index 8728d9259..73def4d00 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-05.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-05.json @@ -5,22 +5,15 @@ }, "config_filenames": ["AUTO_RSV_ELI-295-2.json"], "data": [ - { + { "NHS_NUMBER": "9900029505", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_295_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "eli_295_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "9900029505", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-06.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-06.json similarity index 71% rename from tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-06.json rename to tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-06.json index 0aaac4f16..7c91ff9ee 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-06.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-06.json @@ -3,24 +3,19 @@ "request_headers": { "nhs-login-nhs-number": "9900029506" }, - "config_filenames": ["AUTO_RSV_ELI-295-1.json"], + "config_filenames": [ + "AUTO_RSV_ELI-295-1.json" + ], "data": [ { "NHS_NUMBER": "9900029506", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_295_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "eli_295_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "9900029506", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-07.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-07.json similarity index 75% rename from tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-07.json rename to tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-07.json index 4df6ccfb3..96f3e9fc5 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-07.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-07.json @@ -5,22 +5,15 @@ }, "config_filenames": ["AUTO_RSV_ELI-295-2.json"], "data": [ - { + { "NHS_NUMBER": "9900029507", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_295_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "eli_295_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "9900029507", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-08.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-08.json similarity index 75% rename from tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-08.json rename to tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-08.json index 77db115aa..471991d14 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-08.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-08.json @@ -5,22 +5,15 @@ }, "config_filenames": ["AUTO_RSV_ELI-295-2.json"], "data": [ - { + { "NHS_NUMBER": "9900029508", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_295_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "eli_295_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "9900029508", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-09.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-09.json similarity index 78% rename from tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-09.json rename to tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-09.json index 73fa55e70..7887476fc 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-09.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-09.json @@ -7,22 +7,15 @@ "AUTO_RSV_ELI-295-2.json" ], "data": [ - { + { "NHS_NUMBER": "9900029509", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_295_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "eli_295_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "9900029509", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-10.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-10.json similarity index 76% rename from tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-10.json rename to tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-10.json index e333f6595..4257bd7bf 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-10.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-10.json @@ -8,22 +8,15 @@ }, "config_filenames": ["AUTO_RSV_ELI-295-2.json"], "data": [ - { + { "NHS_NUMBER": "9900029510", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_295_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "eli_295_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "9900029510", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-11.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-11.json similarity index 77% rename from tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-11.json rename to tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-11.json index bb14c5273..0ca834700 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-11.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-11.json @@ -10,22 +10,15 @@ "AUTO_RSV_ELI-295-2.json" ], "data": [ - { + { "NHS_NUMBER": "9900029511", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_295_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "eli_295_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "9900029511", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-12.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-12.json similarity index 79% rename from tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-12.json rename to tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-12.json index 72b2d3e85..d87edd044 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-12.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-12.json @@ -10,22 +10,15 @@ "AUTO_RSV_ELI-295-2.json" ], "data": [ - { + { "NHS_NUMBER": "9900029512", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_295_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "eli_295_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "9900029512", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-13.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-13.json similarity index 75% rename from tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-13.json rename to tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-13.json index 780af1bf8..23da335b6 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-13.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-13.json @@ -5,22 +5,15 @@ }, "config_filenames": ["AUTO_RSV_ELI-295-3.json"], "data": [ - { + { "NHS_NUMBER": "9900029513", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_295_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "other_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "9900029513", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-14.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-14.json similarity index 75% rename from tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-14.json rename to tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-14.json index 3942e298d..c17b1f561 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-14.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-14.json @@ -5,22 +5,15 @@ }, "config_filenames": ["AUTO_RSV_ELI-295-3.json"], "data": [ - { + { "NHS_NUMBER": "9900029514", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_295_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "eli_295_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "9900029514", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-15.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-15.json similarity index 78% rename from tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-15.json rename to tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-15.json index 8e6190e47..fadba0dec 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-15.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-15.json @@ -7,22 +7,15 @@ "AUTO_RSV_ELI-295-3.json" ], "data": [ - { + { "NHS_NUMBER": "9900029515", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_295_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "eli_295_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "9900029515", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-16.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-16.json similarity index 76% rename from tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-16.json rename to tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-16.json index 7a220f9a5..e64c15e90 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-16.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-16.json @@ -8,19 +8,12 @@ { "NHS_NUMBER": "9900029516", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_295_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "eli_295_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "9900029516", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-17.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-17.json similarity index 76% rename from tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-17.json rename to tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-17.json index 6d13a98db..93b329923 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI_295-17.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-17.json @@ -8,19 +8,12 @@ { "NHS_NUMBER": "9900029517", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_295_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "eli_295_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "9900029517", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-1.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-1.json index 6655ff443..0c33dfd70 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-1.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-1.json @@ -4,31 +4,24 @@ "AUTO_RSV_ELI-317-1.json" ], "request_headers": { - "nhs-login-nhs-number": "9000000006" + "nhs-login-nhs-number": "9900031701" }, "query_params": { "includeActions": "Y" }, "data": [ { - "NHS_NUMBER": "9000000006", + "NHS_NUMBER": "9900031701", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_317_cohort_1": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "eli_317_cohort_1", + "DATE_JOINED": "20231020" } - } + ] }, { - "NHS_NUMBER": "9000000006", + "NHS_NUMBER": "9900031701", "ATTRIBUTE_TYPE": "PERSON", "DATE_OF_BIRTH": "<>", "GENDER": "0", @@ -46,7 +39,7 @@ "DE_FLAG": "N" }, { - "NHS_NUMBER": "9000000006", + "NHS_NUMBER": "9900031701", "ATTRIBUTE_TYPE": "RSV", "BOOKED_APPOINTMENT_DATE": "<>", "LAST_SUCCESSFUL_DATE": "<>" diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-2.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-2.json index 219afa8d8..299340742 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-2.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-2.json @@ -4,31 +4,24 @@ "AUTO_RSV_ELI-317-2.json" ], "request_headers": { - "nhs-login-nhs-number": "9000000007" + "nhs-login-nhs-number": "9900031702" }, "query_params": { "includeActions": "Y" }, "data": [ { - "NHS_NUMBER": "9000000007", + "NHS_NUMBER": "9900031702", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_317_cohort_2": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "eli_317_cohort_2", + "DATE_JOINED": "20231020" } - } + ] }, { - "NHS_NUMBER": "9000000007", + "NHS_NUMBER": "9900031702", "ATTRIBUTE_TYPE": "PERSON", "DATE_OF_BIRTH": "<>", "GENDER": "0", @@ -46,7 +39,7 @@ "DE_FLAG": "N" }, { - "NHS_NUMBER": "9000000007", + "NHS_NUMBER": "9900031702", "ATTRIBUTE_TYPE": "RSV", "BOOKED_APPOINTMENT_DATE": "<>", "LAST_SUCCESSFUL_DATE": "<>" diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-3.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-3.json index fe0b15dcb..e9ce9df25 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-3.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-3.json @@ -4,31 +4,24 @@ "AUTO_RSV_ELI-317-3.json" ], "request_headers": { - "nhs-login-nhs-number": "9000000008" + "nhs-login-nhs-number": "9900031703" }, "query_params": { "includeActions": "N" }, "data": [ { - "NHS_NUMBER": "9000000008", + "NHS_NUMBER": "9900031703", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_317_cohort_3": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "eli_317_cohort_3", + "DATE_JOINED": "20231020" } - } + ] }, { - "NHS_NUMBER": "9000000008", + "NHS_NUMBER": "9900031703", "ATTRIBUTE_TYPE": "PERSON", "DATE_OF_BIRTH": "<>", "GENDER": "0", @@ -46,7 +39,7 @@ "DE_FLAG": "N" }, { - "NHS_NUMBER": "9000000008", + "NHS_NUMBER": "9900031703", "ATTRIBUTE_TYPE": "RSV", "BOOKED_APPOINTMENT_DATE": "<>", "LAST_SUCCESSFUL_DATE": "<>" diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-4.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-4.json index 7c6e9db5a..ce14e995d 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-4.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-4.json @@ -4,31 +4,24 @@ "AUTO_RSV_ELI-317-4.json" ], "request_headers": { - "nhs-login-nhs-number": "9000000009" + "nhs-login-nhs-number": "9900031704" }, "query_params": { "includeActions": "Y" }, "data": [ { - "NHS_NUMBER": "9000000009", + "NHS_NUMBER": "9900031704", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_317_cohort_4": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "eli_317_cohort_4", + "DATE_JOINED": "20231020" } - } + ] }, { - "NHS_NUMBER": "9000000009", + "NHS_NUMBER": "9900031704", "ATTRIBUTE_TYPE": "PERSON", "DATE_OF_BIRTH": "<>", "GENDER": "0", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-5.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-5.json index 64faef323..28f0efa7a 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-5.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-317-5.json @@ -4,31 +4,24 @@ "AUTO_RSV_ELI-317-5.json" ], "request_headers": { - "nhs-login-nhs-number": "9000000010" + "nhs-login-nhs-number": "9900031705" }, "query_params": { "includeActions": "Y" }, "data": [ { - "NHS_NUMBER": "9000000010", + "NHS_NUMBER": "9900031705", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_317_cohort_5": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "eli_317_cohort_5", + "DATE_JOINED": "20231020" } - } + ] }, { - "NHS_NUMBER": "9000000010", + "NHS_NUMBER": "9900031705", "ATTRIBUTE_TYPE": "PERSON", "DATE_OF_BIRTH": "<>", "GENDER": "0", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-1.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-1.json index 0a985164f..40fef6018 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-1.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-1.json @@ -3,31 +3,25 @@ "request_headers": { "nhs-login-nhs-number": "9990032010" }, - "config_filenames": ["AUTO_RSV_ELI-320-COVID.json","AUTO_RSV_ELI-320-RSV.json", "AUTO_RSV_ELI-320-MMR.json"], + "config_filenames": [ + "AUTO_RSV_ELI-320-COVID.json", + "AUTO_RSV_ELI-320-RSV.json", + "AUTO_RSV_ELI-320-MMR.json" + ], "data": [ { "NHS_NUMBER": "9990032010", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - }, - "covid_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "covid_cohort", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "9990032010", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-10.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-10.json index 12a5face0..458d220f7 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-10.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-10.json @@ -7,38 +7,30 @@ "category": "VACCINATIONS", "conditions": "covid" }, - "config_filenames": ["AUTO_RSV_ELI-320-COVID.json","AUTO_RSV_ELI-320-MMR.json", "AUTO_RSV_ELI-320-SCREENING-1.json", "AUTO_RSV_ELI-320-SCREENING-2.json"], + "config_filenames": [ + "AUTO_RSV_ELI-320-COVID.json", + "AUTO_RSV_ELI-320-MMR.json", + "AUTO_RSV_ELI-320-SCREENING-1.json", + "AUTO_RSV_ELI-320-SCREENING-2.json" + ], "data": [ { "NHS_NUMBER": "9900032010", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - }, - "covid_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - }, - "FLU_screening_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "covid_cohort", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_cohort", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "FLU_screening_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "9900032010", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-11.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-11.json index 159ba5cc3..31a7c5ea2 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-11.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-11.json @@ -12,33 +12,20 @@ { "NHS_NUMBER": "9900032011", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - }, - "covid_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - }, - "FLU_screening_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "covid_cohort", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_cohort", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "FLU_screening_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "9900032011", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-2.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-2.json index b856294f5..6545acd11 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-2.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-2.json @@ -11,26 +11,16 @@ { "NHS_NUMBER": "9990032020", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - }, - "covid_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "covid_cohort", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "9990032020", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-3.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-3.json index ca015864e..bbb95d4d5 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-3.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-3.json @@ -11,26 +11,16 @@ { "NHS_NUMBER": "9990032030", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - }, - "covid_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "covid_cohort", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "9990032030", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-4.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-4.json index 76d0dab2c..6cbdc1698 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-4.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-4.json @@ -11,26 +11,16 @@ { "NHS_NUMBER": "9990032040", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - }, - "covid_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "covid_cohort", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "9990032040", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-5.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-5.json index a9861ed29..fa39d5bc1 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-5.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-5.json @@ -11,26 +11,16 @@ { "NHS_NUMBER": "9990032050", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - }, - "covid_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "covid_cohort", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "9990032050", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-6.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-6.json index 4bde84657..dfeba54b7 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-6.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-6.json @@ -6,38 +6,30 @@ "query_params": { "category": null }, - "config_filenames": ["AUTO_RSV_ELI-320-COVID.json","AUTO_RSV_ELI-320-MMR.json", "AUTO_RSV_ELI-320-SCREENING-1.json", "AUTO_RSV_ELI-320-SCREENING-2.json"], + "config_filenames": [ + "AUTO_RSV_ELI-320-COVID.json", + "AUTO_RSV_ELI-320-MMR.json", + "AUTO_RSV_ELI-320-SCREENING-1.json", + "AUTO_RSV_ELI-320-SCREENING-2.json" + ], "data": [ { "NHS_NUMBER": "9990032060", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - }, - "covid_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - }, - "FLU_screening_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "covid_cohort", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_cohort", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "FLU_screening_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "9990032060", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-7.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-7.json index 705196839..09d976aa8 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-7.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-7.json @@ -11,33 +11,20 @@ { "NHS_NUMBER": "9990032070", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - }, - "covid_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - }, - "FLU_screening_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "covid_cohort", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_cohort", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "FLU_screening_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "9990032070", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-8.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-8.json index 79bf028b9..2082f3188 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-8.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-8.json @@ -11,33 +11,20 @@ { "NHS_NUMBER": "9990032080", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - }, - "covid_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - }, - "FLU_screening_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "covid_cohort", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_cohort", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "FLU_screening_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "9990032080", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-9.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-9.json index 369f12b29..348263932 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-9.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-9.json @@ -11,33 +11,20 @@ { "NHS_NUMBER": "9900032090", "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "rsv_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - }, - "covid_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - }, - "FLU_screening_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "covid_cohort", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_cohort", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "FLU_screening_cohort", + "DATE_JOINED": "20231020" } - } + ] }, { "NHS_NUMBER": "9900032090", diff --git a/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_010-1.json b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_010-1.json new file mode 100644 index 000000000..0fdf41d8f --- /dev/null +++ b/tests/e2e/data/responses/regressionTestResponses/AUTO_RSV_REG_010-1.json @@ -0,0 +1,27 @@ +{ + "meta": { + "lastUpdated": "2025-07-01T20:20:47.764973+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not in an age group 2." + }, + { + "cohortCode": "rsv_other_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not in an other group." + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "49db2d41-326f-45d4-afaf-0dd6897413af" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-222-7.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-222-7.json new file mode 100644 index 000000000..99d6cd6a0 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-222-7.json @@ -0,0 +1,27 @@ +{ + "meta": { + "lastUpdated": "2025-07-25T15:00:10.943057+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age", + "cohortStatus": "NotEligible", + "cohortText": "are not aged 75 to 79 years old." + }, + { + "cohortCode": "rsv_age_catchup", + "cohortStatus": "NotEligible", + "cohortText": "did not turn 80 after 1 September 2024" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "526cbf5b-7049-4870-85c5-23c58b48973d" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-222-8.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-222-8.json new file mode 100644 index 000000000..663d20f03 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-222-8.json @@ -0,0 +1,22 @@ +{ + "meta": { + "lastUpdated": "2025-07-25T15:07:17.633204+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "Already Vaccinated", + "ruleText": "##You've had your RSV vaccination\nWe believe you had your vaccination.", + "ruleType": "S" + } + ] + } + ], + "responseId": "85ef07db-b5d8-4451-8f63-e3229a424332" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-01.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-01.json similarity index 100% rename from tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-01.json rename to tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-01.json diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-02.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-02.json similarity index 100% rename from tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-02.json rename to tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-02.json diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-03.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-03.json similarity index 100% rename from tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-03.json rename to tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-03.json diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-04.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-04.json similarity index 100% rename from tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-04.json rename to tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-04.json diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-05.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-05.json similarity index 100% rename from tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-05.json rename to tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-05.json diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-06.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-06.json similarity index 100% rename from tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-06.json rename to tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-06.json diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-07.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-07.json similarity index 100% rename from tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-07.json rename to tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-07.json diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-08.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-08.json similarity index 100% rename from tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-08.json rename to tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-08.json diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-09.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-09.json similarity index 100% rename from tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-09.json rename to tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-09.json diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-10.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-10.json similarity index 100% rename from tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-10.json rename to tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-10.json diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-11.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-11.json similarity index 100% rename from tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-11.json rename to tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-11.json diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-12.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-12.json similarity index 100% rename from tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-12.json rename to tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-12.json diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-13.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-13.json similarity index 100% rename from tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-13.json rename to tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-13.json diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-14.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-14.json similarity index 100% rename from tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-14.json rename to tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-14.json diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-15.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-15.json similarity index 100% rename from tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-15.json rename to tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-15.json diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-16.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-16.json similarity index 100% rename from tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-16.json rename to tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-16.json diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-17.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-17.json similarity index 100% rename from tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI_295-17.json rename to tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-295-17.json From 12fc10f49c916571147f4fc917a19c80929e9f06 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Fri, 1 Aug 2025 13:16:44 +0100 Subject: [PATCH 61/72] added vita int tests, ELI-365, --- .../AUTO_RSV_ELI-355.json | 88 ---- .../inProgressTestConfigs/sean_demo.json | 134 ----- .../storyTestConfigs/AUTO_RSV_ELI-365.json | 429 ++++++++++++++++ .../vita_integration_test_config.json | 473 ++++++++++++++++++ tests/e2e/data/dynamoDB/AUTO_RSV_ELI-355.json | 53 -- tests/e2e/data/dynamoDB/sean_demo_01.json | 64 --- .../storyTestData/AUTO_RSV_ELI-222-1.json | 3 +- .../storyTestData/AUTO_RSV_ELI-222-2.json | 3 +- .../storyTestData/AUTO_RSV_ELI-222-4.json | 3 +- .../storyTestData/AUTO_RSV_ELI-222-7.json | 7 + .../storyTestData/AUTO_RSV_ELI-365_001.json | 43 ++ .../storyTestData/AUTO_RSV_ELI-365_002.json | 44 ++ .../storyTestData/AUTO_RSV_ELI-365_003.json | 34 ++ .../storyTestData/AUTO_RSV_ELI-365_004.json | 43 ++ .../storyTestData/AUTO_RSV_ELI-365_005.json | 44 ++ .../storyTestData/AUTO_RSV_ELI-365_006.json | 37 ++ .../storyTestData/AUTO_RSV_ELI-365_007.json | 44 ++ .../storyTestData/AUTO_RSV_ELI-365_008.json | 42 ++ .../storyTestData/AUTO_RSV_ELI-365_009.json | 44 ++ .../storyTestData/AUTO_RSV_ELI-365_010.json | 44 ++ .../storyTestData/AUTO_RSV_ELI-365_011.json | 44 ++ .../storyTestData/AUTO_RSV_ELI-365_012.json | 44 ++ .../storyTestData/AUTO_RSV_ELI-365_013.json | 44 ++ .../storyTestData/AUTO_RSV_ELI-365_014.json | 44 ++ .../storyTestData/AUTO_RSV_ELI-365_015.json | 44 ++ .../storyTestData/AUTO_RSV_ELI-365_016.json | 51 ++ .../storyTestData/AUTO_RSV_ELI-365_017.json | 44 ++ .../storyTestData/AUTO_RSV_ELI-365_018.json | 44 ++ .../storyTestData/AUTO_RSV_ELI-365_019.json | 44 ++ .../storyTestData/AUTO_RSV_ELI-365_020.json | 44 ++ .../storyTestData/AUTO_RSV_ELI-365_021.json | 45 ++ .../storyTestData/AUTO_RSV_ELI-365_022.json | 45 ++ .../storyTestData/AUTO_RSV_ELI-365_023.json | 45 ++ .../AUTO_RSV_VITA_INT_001.json | 38 ++ .../AUTO_RSV_VITA_INT_002.json | 37 ++ .../AUTO_RSV_VITA_INT_003.json | 37 ++ .../AUTO_RSV_VITA_INT_004.json | 33 ++ .../AUTO_RSV_VITA_INT_005.json | 33 ++ .../AUTO_RSV_VITA_INT_006.json | 32 ++ .../AUTO_RSV_VITA_INT_007.json | 37 ++ .../AUTO_RSV_VITA_INT_008.json | 37 ++ .../AUTO_RSV_VITA_INT_009.json | 37 ++ .../AUTO_RSV_VITA_INT_011.json | 37 ++ .../AUTO_RSV_VITA_INT_012.json | 37 ++ .../AUTO_RSV_VITA_INT_013.json | 33 ++ .../AUTO_RSV_VITA_INT_014.json | 32 ++ .../AUTO_RSV_VITA_INT_015.json | 27 + .../AUTO_RSV_VITA_INT_500.json | 37 ++ .../AUTO_RSV_ELI-365_001.json | 24 + .../AUTO_RSV_ELI-365_002.json | 35 ++ .../AUTO_RSV_ELI-365_003.json | 30 ++ .../AUTO_RSV_ELI-365_004.json | 30 ++ .../AUTO_RSV_ELI-365_005.json | 30 ++ .../AUTO_RSV_ELI-365_006.json | 35 ++ .../AUTO_RSV_ELI-365_007.json | 35 ++ .../AUTO_RSV_ELI-365_008.json | 30 ++ .../AUTO_RSV_ELI-365_009.json | 35 ++ .../AUTO_RSV_ELI-365_010.json | 35 ++ .../AUTO_RSV_ELI-365_011.json | 35 ++ .../AUTO_RSV_ELI-365_012.json | 35 ++ .../AUTO_RSV_ELI-365_013.json | 30 ++ .../AUTO_RSV_ELI-365_014.json | 36 ++ .../AUTO_RSV_ELI-365_015.json | 36 ++ .../AUTO_RSV_ELI-365_016.json | 36 ++ .../AUTO_RSV_ELI-365_017.json | 24 + .../AUTO_RSV_ELI-365_018.json | 24 + .../AUTO_RSV_ELI-365_019.json | 24 + .../AUTO_RSV_ELI-365_020.json | 44 ++ .../AUTO_RSV_ELI-365_021.json | 44 ++ .../AUTO_RSV_ELI-365_022.json | 37 ++ .../AUTO_RSV_ELI-365_023.json | 37 ++ .../AUTO_RSV_VITA_INT_001.json | 44 ++ .../AUTO_RSV_VITA_INT_002.json | 37 ++ .../AUTO_RSV_VITA_INT_003.json | 37 ++ .../AUTO_RSV_VITA_INT_004.json | 24 + .../AUTO_RSV_VITA_INT_005.json | 24 + .../AUTO_RSV_VITA_INT_006.json | 30 ++ .../AUTO_RSV_VITA_INT_007.json} | 12 +- .../AUTO_RSV_VITA_INT_008.json | 37 ++ .../AUTO_RSV_VITA_INT_009.json | 28 ++ .../AUTO_RSV_VITA_INT_011.json | 28 ++ .../AUTO_RSV_VITA_INT_012.json | 36 ++ .../AUTO_RSV_VITA_INT_013.json | 30 ++ .../AUTO_RSV_VITA_INT_014.json | 30 ++ .../AUTO_RSV_VITA_INT_015.json | 35 ++ .../AUTO_RSV_VITA_INT_500.json | 23 + tests/e2e/tests/conftest.py | 58 --- tests/e2e/tests/test_config.py | 7 +- 88 files changed, 3655 insertions(+), 408 deletions(-) delete mode 100644 tests/e2e/data/configs/inProgressTestConfigs/AUTO_RSV_ELI-355.json delete mode 100644 tests/e2e/data/configs/inProgressTestConfigs/sean_demo.json create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-365.json create mode 100644 tests/e2e/data/configs/vitaIntegrationTestConfigs/vita_integration_test_config.json delete mode 100644 tests/e2e/data/dynamoDB/AUTO_RSV_ELI-355.json delete mode 100644 tests/e2e/data/dynamoDB/sean_demo_01.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_001.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_002.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_003.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_004.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_005.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_006.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_007.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_008.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_009.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_010.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_011.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_012.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_013.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_014.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_015.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_016.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_017.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_018.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_019.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_020.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_021.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_022.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_023.json create mode 100644 tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_001.json create mode 100644 tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_002.json create mode 100644 tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_003.json create mode 100644 tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_004.json create mode 100644 tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_005.json create mode 100644 tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_006.json create mode 100644 tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_007.json create mode 100644 tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_008.json create mode 100644 tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_009.json create mode 100644 tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_011.json create mode 100644 tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_012.json create mode 100644 tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_013.json create mode 100644 tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_014.json create mode 100644 tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_015.json create mode 100644 tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_500.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_001.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_002.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_003.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_004.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_005.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_006.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_007.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_008.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_009.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_010.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_011.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_012.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_013.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_014.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_015.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_016.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_017.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_018.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_019.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_020.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_021.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_022.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_023.json create mode 100644 tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_001.json create mode 100644 tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_002.json create mode 100644 tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_003.json create mode 100644 tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_004.json create mode 100644 tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_005.json create mode 100644 tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_006.json rename tests/e2e/data/responses/{inProgressTestResponses/sean_demo_01.json => vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_007.json} (53%) create mode 100644 tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_008.json create mode 100644 tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_009.json create mode 100644 tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_011.json create mode 100644 tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_012.json create mode 100644 tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_013.json create mode 100644 tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_014.json create mode 100644 tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_015.json create mode 100644 tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_500.json diff --git a/tests/e2e/data/configs/inProgressTestConfigs/AUTO_RSV_ELI-355.json b/tests/e2e/data/configs/inProgressTestConfigs/AUTO_RSV_ELI-355.json deleted file mode 100644 index 54700a823..000000000 --- a/tests/e2e/data/configs/inProgressTestConfigs/AUTO_RSV_ELI-355.json +++ /dev/null @@ -1,88 +0,0 @@ -{ - "CampaignConfig": { - "ID": "<>", - "Version": "1", - "Name": "ELI-355 - Date of Birth Integer Issue", - "Type": "V", - "Target": "RSV", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", - "IterationFrequency": "X", - "IterationType": "O", - "IterationTime": "07:00:00", - "DefaultCommsRouting": "CONTACT_GP", - "StartDate": "20250601", - "EndDate": "20260601", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Iterations": [ - { - "ID": ",<>", - "DefaultCommsRouting": "CONTACT_GP", - "Version": "1", - "Name": "ELI-155 - cohort_label is not supported in R rules", - "IterationDate": "20250601", - "IterationNumber": 1, - "CommsType": "I", - "DefaultNotEligibleRouting": "", - "DefaultNotActionableRouting": "", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Type": "O", - "IterationCohorts": [ - { - "CohortLabel": "eli_355_cohort", - "CohortGroup": "eli_355_group", - "PositiveDescription": "You are currently in eli_355", - "NegativeDescription": "You are not currently eli_355", - "Priority": 0 - } - ], - "IterationRules": [ - { - "Type": "S", - "Name": "Test S DOB", - "Description": "Test S DOB", - "Priority": 1000, - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Operator": ">", - "Comparator": "19000101" - }, - { - "Type": "S", - "Name": "Test S Valid Dose", - "Description": "Test S Valid Dose", - "Priority": 1001, - "AttributeLevel": "TARGET", - "AttributeTarget": "RSV", - "AttributeName": "VALID_DOSES_COUNT", - "Operator": ">", - "Comparator": "0" - }, - { - "Type": "S", - "Name": "Test S Invalid Dose", - "Description": "Test S invalid Dose", - "Priority": 1002, - "AttributeLevel": "TARGET", - "AttributeTarget": "RSV", - "AttributeName": "INVALID_DOSES_COUNT", - "Operator": ">", - "Comparator": "0" - } - ], - "ActionsMapper": { - "AMEND_NBS": { - "ExternalRoutingCode": "AmendNBS", - "ActionDescription": "##You have an RSV vaccination appointment\\nYou can view, change or cancel your appointment below.", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Manage your appointment" - } - } - } - ] - } -} diff --git a/tests/e2e/data/configs/inProgressTestConfigs/sean_demo.json b/tests/e2e/data/configs/inProgressTestConfigs/sean_demo.json deleted file mode 100644 index f89970983..000000000 --- a/tests/e2e/data/configs/inProgressTestConfigs/sean_demo.json +++ /dev/null @@ -1,134 +0,0 @@ -{ - "CampaignConfig": { - "ID": "<>", - "Version": "1", - "Name": "Sean Demo Campaign", - "Type": "V", - "Target": "RSV", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", - "IterationFrequency": "X", - "IterationType": "O", - "IterationTime": "07:00:00", - "DefaultCommsRouting": "CONTACT_GP", - "StartDate": "20250601", - "EndDate": "20260601", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Iterations": [ - { - "ID": ",<>", - "DefaultCommsRouting": "DEFAULT_A", - "DefaultNotEligibleRouting": "", - "DefaultNotActionableRouting": "", - "Version": "1", - "Name": "Sean Demo Iteration", - "IterationDate": "20250601", - "IterationNumber": 1, - "CommsType": "I", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Type": "O", - "IterationCohorts": [ - { - "CohortLabel": "rsv_cohort", - "CohortGroup": "rsv_group", - "PositiveDescription": "You are currently in an rsv cohort", - "NegativeDescription": "You are not currently not in an rsv cohort", - "Priority": 0 - }, - { - "CohortLabel": "very_obesity_cohort", - "CohortGroup": "clinical_risk_group", - "PositiveDescription": "You are currently in a clinical risk group", - "NegativeDescription": "You are currently in not in a clinical risk group", - "Priority": 1 - }, - { - "CohortLabel": "obesity_cohort", - "CohortGroup": "clinical_risk_group", - "PositiveDescription": "You are currently in a clinical risk group", - "NegativeDescription": "You are currently in not in a clinical risk group", - "Priority": 2 - } - ], - "IterationRules": [ - { - "Type": "S", - "Name": "Test S Rule - Vaccinated", - "Description": "Test S Rule - Vaccinated", - "Priority": 500, - "AttributeLevel": "TARGET", - "AttributeTarget": "RSV", - "AttributeName": "LAST_SUCCESSFUL_DATE", - "Operator": "D<", - "Comparator": "0", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "Test S Rule - In a Carehome", - "Description": "Test S Rule - In a Carehome", - "Priority": 501, - "AttributeLevel": "PERSON", - "AttributeName": "CARE_HOME_FLAG", - "Operator": "=", - "Comparator": "Y" - } - ], - "ActionsMapper": { - "AMEND_NBS": { - "ExternalRoutingCode": "AmendNBS", - "ActionDescription": "##You have an RSV vaccination appointment\\nYou can view, change or cancel your appointment below.", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Manage your appointment" - }, - "ALREADY_VACCS": { - "ExternalRoutingCode": "ALREADYVACCS", - "ActionDescription": "##Our records show you have already been vaccinated\\nIf you think this is wrong then contact your healthcare professional.", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "" - }, - "DEFAULT_A": { - "ExternalRoutingCode": "DefaultA", - "ActionDescription": "DefaultADescription", - "ActionType": "DefaultAType", - "UrlLink": "https://www.defaultaaction.com", - "UrlLabel": "DefaultALabel" - }, - "DEFAULT_X": { - "ExternalRoutingCode": "DefaultX", - "ActionDescription": "If you think this is wrong then contact your healthcare professional", - "ActionType": "DefaultXType", - "UrlLink": "https://www.defaultxaction.com", - "UrlLabel": "DefaultXLabel" - }, - "OTHER_X": { - "ExternalRoutingCode": "OtherX", - "ActionDescription": "OtherXDescription", - "ActionType": "OtherXType", - "UrlLink": "https://www.otherxaction.com", - "UrlLabel": "OtherXLabel" - }, - "OTHER_Y": { - "ExternalRoutingCode": "OtherY", - "ActionDescription": "OtherYDescription", - "ActionType": "OtherYType", - "UrlLink": "https://www.otheryaction.com", - "UrlLabel": "OtherYLabel" - }, - "DEFAULT_Y": { - "ExternalRoutingCode": "DefaultY", - "ActionDescription": "DefaultYDescription", - "ActionType": "DefaultYType", - "UrlLink": "https://www.defaultyaction.com", - "UrlLabel": "DefaultYLabel" - } - } - } - ] - } -} diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-365.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-365.json new file mode 100644 index 000000000..10f472afd --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-365.json @@ -0,0 +1,429 @@ +{ + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": "1", + "Name": "EliD RSV example config", + "Type": "V", + "Target": "RSV", + "Manager": "example@nhs.net", + "Approver": "example@nhs.net", + "Reviewer": "example@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "BOOK_LOCAL|HELP_SUPPORT", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "CHECK_CORRECT_X", + "Version": "1", + "Name": "EliD RSV example config", + "IterationDate": "20250717", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_75to79", + "CohortGroup": "rsv_age", + "PositiveDescription": "are aged 75 to 79 years old", + "NegativeDescription": "are not aged 75 to 79 years old", + "Priority": 0 + }, + { + "CohortLabel": "rsv_80_since_02_Sept_2024", + "CohortGroup": "rsv_age_catchup", + "PositiveDescription": "turned 80 after 1st September 2024", + "NegativeDescription": "did not turn 80 after 1 September 2024", + "Priority": 10 + }, + { + "CohortLabel": "elid_all_people", + "CohortGroup": "magic_cohort", + "PositiveDescription": "", + "NegativeDescription": "", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "Remove from magic cohort unless already vaccinated or have future booking", + "Description": "Remove anyone NOT already vaccinated within the last 25 years and do not have a future booking from the magic cohort", + "Operator": "Y<=", + "Comparator": "-25[[NVL:18000101]]", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "elid_all_people", + "Priority": 100 + }, + { + "Type": "F", + "Name": "Remove from magic cohort unless already vaccinated or have future booking", + "Description": "Remove anyone without a future booking from magic cohort", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CohortLabel": "elid_all_people", + "Priority": 100 + }, + { + "Type": "F", + "Name": "Remove under 75 Years on day of execution", + "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", + "Priority": 120, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75", + "CohortLabel": "rsv_75to79" + }, + { + "Type": "F", + "Name": "Remove over 80 on day of execution", + "Description": "Exclude anyone who turned 80 before 2nd September 2024", + "Priority": 130, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "<", + "Comparator": "19440902", + "CohortLabel": "rsv_75to79" + }, + { + "Type": "F", + "Name": "Remove under 75 Years on day of execution", + "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", + "Priority": 140, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75", + "CohortLabel": "rsv_80_since_02_Sept_2024" + }, + { + "Type": "F", + "Name": "Remove over 80 on day of execution", + "Description": "Exclude anyone who turned 80 before 2nd September 2024", + "Priority": 150, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "<", + "Comparator": "19440902", + "CohortLabel": "rsv_80_since_02_Sept_2024" + }, + { + "Type": "F", + "Name": "Remove from rsv 80 cohort if already vaccinated", + "Description": "Remove anyone already vaccinated from 80 cohort", + "Operator": "Y>=", + "Comparator": "-25", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "Priority": 160 + }, + { + "Type": "F", + "Name": "Remove from rsv 80 cohort if future booking", + "Description": "Remove anyone with a future booking from RSV 80 cohort", + "Operator": "D>=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "Priority": 170 + }, + { + "Type": "F", + "Name": "Remove from rsv 75-79 cohort if already vaccinated", + "Description": "Remove anyone already vaccinated from 75-79 cohort", + "Operator": "Y>=", + "Comparator": "-25", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "rsv_75to79", + "Priority": 180 + }, + { + "Type": "F", + "Name": "Remove from rsv 75-79 cohort if future booking", + "Description": "Remove anyone with a future booking from RSV 75-79 cohort", + "Operator": "D>=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CohortLabel": "rsv_75to79", + "Priority": 190 + }, + { + "Type": "S", + "Name": "Already Vaccinated", + "Description": "## You've had your RSV vaccination\n\nWe believe you had your vaccination.", + "Priority": 200, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "Y>=", + "Comparator": "-25", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Other Setting", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 510, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Other Setting", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 520, + "AttributeLevel": "PERSON", + "AttributeName": "DE_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Other Setting", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 530, + "AttributeLevel": "PERSON", + "AttributeName": "13Q_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Other Setting", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 540, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_75to79", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Other Setting with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 550, + "AttributeLevel": "PERSON", + "AttributeName": "DE_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_75to79", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Other Setting", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 560, + "AttributeLevel": "PERSON", + "AttributeName": "13Q_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_75to79", + "RuleStop": "Y" + }, + { + "Type": "R", + "Name": "Actionable Future Booked NBS Appointment", + "Description": "Amend NBS future booking", + "Priority": 1000, + "Operator": "D>=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked NBS Appointment", + "Description": "Amend NBS future booking", + "Priority": 1000, + "Operator": "=", + "Comparator": "NBS", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Local Appointment", + "Description": "Amend local future booking", + "Priority": 1100, + "Operator": "D>=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "MANAGE_LOCAL" + }, + { + "Type": "R", + "Name": "Within CP Expansion ICB", + "Description": "Book an appointment on NBS as within CP expansion", + "Priority": 1200, + "Operator": "in", + "Comparator": "QH8,QJG", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" + }, + { + "Type": "R", + "Name": "Within CP Expansion Local Authority", + "Description": "Book an appointment on NBS as within CP expansion", + "Priority": 1300, + "Operator": "in", + "Comparator": "E08000028,E08000031,E08000025,E06000016,E06000008,E07000117,E07000120,E08000011,E08000012,E07000122,E07000123,E08000014,E07000126,E08000013,E07000127,E08000015,E07000128", + "AttributeLevel": "PERSON", + "AttributeName": "LOCAL_AUTHORITY", + "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" + }, + { + "Type": "Y", + "Name": "Already vaccinated default text", + "Description": "Already vaccinated default text", + "Priority": 3000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "Y>=", + "Comparator": "-25", + "CommsRouting": "CHECK_CORRECT_ALREADY_VACCINATED" + }, + { + "Type": "Y", + "Name": "Other setting default text", + "Description": "Other setting default text", + "Priority": 3100, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y", + "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" + }, + { + "Type": "Y", + "Name": "Other setting default text", + "Description": "Other setting default text", + "Priority": 3200, + "AttributeLevel": "PERSON", + "AttributeName": "DE_FLAG", + "Operator": "=", + "Comparator": "Y", + "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" + }, + { + "Type": "Y", + "Name": "Other setting default text", + "Description": "Other setting default text", + "Priority": 3300, + "AttributeLevel": "PERSON", + "AttributeName": "13Q_FLAG", + "Operator": "=", + "Comparator": "Y", + "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + }, + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Manage your appointment" + }, + "CONTACT_GP": { + "ExternalRoutingCode": "ContactGP", + "ActionDescription": "Contact your GP", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "BOOK_LOCAL": { + "ExternalRoutingCode": "BookLocal", + "ActionDescription": "##Getting the vaccine\n\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "MANAGE_LOCAL": { + "ExternalRoutingCode": "ManageLocal", + "ActionDescription": "##You have an RSV vaccination appointment\n\nContact your healthcare provider to change or cancel your appointment.", + "ActionType": "CardWithText", + "UrlLink": null, + "UrlLabel": "" + }, + "HELP_SUPPORT": { + "ExternalRoutingCode": "HelpSupportInfo", + "ActionDescription": "## CONTENT TBC\n\nBlah blah blah.", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "CHECK_CORRECT_X": { + "ExternalRoutingCode": "HealthcareProInfo", + "ActionDescription": "## If you think this is incorrect\n\nSpeak to your healthcare professional if you think you should be offered this vaccine.\n\nFor anything else, visit our help and support page. (ADD LINK)", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "CHECK_CORRECT_ALREADY_VACCINATED": { + "ExternalRoutingCode": "AlreadyVaccinatedInfo", + "ActionDescription": "## If you think this is incorrect\n\nIf you believe you've not been vaccinated against RSV, speak to your healthcare professional.\n\nFor anything else please see our help and support page. (ADD LINK).", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "CHECK_CORRECT_OTHER_SETTING": { + "ExternalRoutingCode": "ManagedSettingInfo", + "ActionDescription": "## If you think this is incorrect\n\nIf you have already had this vaccination or your personal details are wrong, visit our help and support page. (ADD LINK).", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/vitaIntegrationTestConfigs/vita_integration_test_config.json b/tests/e2e/data/configs/vitaIntegrationTestConfigs/vita_integration_test_config.json new file mode 100644 index 000000000..1f94a88c1 --- /dev/null +++ b/tests/e2e/data/configs/vitaIntegrationTestConfigs/vita_integration_test_config.json @@ -0,0 +1,473 @@ +{ + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": "1", + "Name": "EliD RSV example config", + "Type": "V", + "Target": "RSV", + "Manager": "example@nhs.net", + "Approver": "example@nhs.net", + "Reviewer": "example@nhs.net", + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "BOOK_LOCAL|HELP_SUPPORT", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "CHECK_CORRECT_X", + "Version": "1", + "Name": "EliD RSV example config", + "IterationDate": "20250717", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_75to79", + "CohortGroup": "rsv_age", + "PositiveDescription": "are aged 75 to 79 years old", + "NegativeDescription": "are not aged 75 to 79 years old", + "Priority": 0 + }, + { + "CohortLabel": "rsv_80_since_02_Sept_2024", + "CohortGroup": "rsv_age_catchup", + "PositiveDescription": "turned 80 after 1st September 2024", + "NegativeDescription": "did not turn 80 after 1 September 2024", + "Priority": 10 + }, + { + "CohortLabel": "elid_all_people", + "CohortGroup": "magic_cohort", + "PositiveDescription": "", + "NegativeDescription": "", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "Remove from magic cohort unless already vaccinated or have future booking", + "Description": "Remove anyone NOT already vaccinated within the last 25 years and do not have a future booking from the magic cohort", + "Operator": "Y<=", + "Comparator": "-25[[NVL:18000101]]", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "elid_all_people", + "Priority": 100 + }, + { + "Type": "F", + "Name": "Remove from magic cohort unless already vaccinated or have future booking", + "Description": "Remove anyone without a future booking from magic cohort", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CohortLabel": "elid_all_people", + "Priority": 100 + }, + { + "Type": "F", + "Name": "Remove under 75 Years on day of execution", + "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", + "Priority": 120, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75", + "CohortLabel": "rsv_75to79" + }, + { + "Type": "F", + "Name": "Remove over 80 on day of execution", + "Description": "Exclude anyone who turned 80 before 2nd September 2024", + "Priority": 130, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "<", + "Comparator": "19440902", + "CohortLabel": "rsv_75to79" + }, + { + "Type": "F", + "Name": "Remove under 75 Years on day of execution", + "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", + "Priority": 140, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75", + "CohortLabel": "rsv_80_since_02_Sept_2024" + }, + { + "Type": "F", + "Name": "Remove over 80 on day of execution", + "Description": "Exclude anyone who turned 80 before 2nd September 2024", + "Priority": 150, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "<", + "Comparator": "19440902", + "CohortLabel": "rsv_80_since_02_Sept_2024" + }, + { + "Type": "F", + "Name": "Remove from rsv 80 cohort if already vaccinated", + "Description": "Remove anyone already vaccinated from 80 cohort", + "Operator": "Y>=", + "Comparator": "-25", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "Priority": 160 + }, + { + "Type": "F", + "Name": "Remove from rsv 80 cohort if future booking", + "Description": "Remove anyone with a future booking from RSV 80 cohort", + "Operator": "D>=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "Priority": 170 + }, + { + "Type": "F", + "Name": "Remove from rsv 75-79 cohort if already vaccinated", + "Description": "Remove anyone already vaccinated from 75-79 cohort", + "Operator": "Y>=", + "Comparator": "-25", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "rsv_75to79", + "Priority": 180 + }, + { + "Type": "F", + "Name": "Remove from rsv 75-79 cohort if future booking", + "Description": "Remove anyone with a future booking from RSV 75-79 cohort", + "Operator": "D>=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CohortLabel": "rsv_75to79", + "Priority": 190 + }, + { + "Type": "S", + "Name": "Not Available", + "Description": "##RSV vaccinations are not currently available\n\nPlease try again soon.", + "Priority": 195, + "AttributeLevel": "PERSON", + "AttributeName": "NHS_NUMBER", + "Operator": "=", + "Comparator": "9658218997", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "NotYetDue", + "Description": "##Your RSV vaccination is not yet due\\n\\nYour next dose will be due in 3 months.", + "Priority": 196, + "AttributeLevel": "PERSON", + "AttributeName": "NHS_NUMBER", + "Operator": "=", + "Comparator": "9658219012", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "TooClose", + "Description": "##You have recently have the RSV vaccination\n\nYou must leave 90 days between doses of the RSV vaccine. Please try again soon.", + "Priority": 197, + "AttributeLevel": "PERSON", + "AttributeName": "NHS_NUMBER", + "Operator": "=", + "Comparator": "9658220142", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Empty Suggestion", + "Description": "", + "Priority": 198, + "AttributeLevel": "PERSON", + "AttributeName": "NHS_NUMBER", + "Operator": "=", + "Comparator": "9658219004", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Already Vaccinated", + "Description": "## You've had your RSV vaccination\n\nWe believe you had your vaccination.", + "Priority": 200, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "Y>=", + "Comparator": "-25", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Other Setting", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 510, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Other Setting", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 520, + "AttributeLevel": "PERSON", + "AttributeName": "DE_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Other Setting", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 530, + "AttributeLevel": "PERSON", + "AttributeName": "13Q_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Other Setting", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 540, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_75to79", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Other Setting with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 550, + "AttributeLevel": "PERSON", + "AttributeName": "DE_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_75to79", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Other Setting", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 560, + "AttributeLevel": "PERSON", + "AttributeName": "13Q_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_75to79", + "RuleStop": "Y" + }, + { + "Type": "R", + "Name": "Actionable Future Booked NBS Appointment", + "Description": "Amend NBS future booking", + "Priority": 1000, + "Operator": "D>=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked NBS Appointment", + "Description": "Amend NBS future booking", + "Priority": 1000, + "Operator": "=", + "Comparator": "NBS", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Local Appointment", + "Description": "Amend local future booking", + "Priority": 1100, + "Operator": "D>=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "MANAGE_LOCAL" + }, + { + "Type": "R", + "Name": "Within CP Expansion ICB", + "Description": "Book an appointment on NBS as within CP expansion", + "Priority": 1200, + "Operator": "in", + "Comparator": "QH8,QJG", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" + }, + { + "Type": "R", + "Name": "Within CP Expansion Local Authority", + "Description": "Book an appointment on NBS as within CP expansion", + "Priority": 1300, + "Operator": "in", + "Comparator": "E08000028,E08000031,E08000025,E06000016,E06000008,E07000117,E07000120,E08000011,E08000012,E07000122,E07000123,E08000014,E07000126,E08000013,E07000127,E08000015,E07000128", + "AttributeLevel": "PERSON", + "AttributeName": "LOCAL_AUTHORITY", + "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" + }, + { + "Type": "Y", + "Name": "Already vaccinated default text", + "Description": "Already vaccinated default text", + "Priority": 3000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "Y>=", + "Comparator": "-25", + "CommsRouting": "CHECK_CORRECT_ALREADY_VACCINATED" + }, + { + "Type": "Y", + "Name": "Other setting default text", + "Description": "Other setting default text", + "Priority": 3100, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y", + "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" + }, + { + "Type": "Y", + "Name": "Other setting default text", + "Description": "Other setting default text", + "Priority": 3200, + "AttributeLevel": "PERSON", + "AttributeName": "DE_FLAG", + "Operator": "=", + "Comparator": "Y", + "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" + }, + { + "Type": "Y", + "Name": "Other setting default text", + "Description": "Other setting default text", + "Priority": 3300, + "AttributeLevel": "PERSON", + "AttributeName": "13Q_FLAG", + "Operator": "=", + "Comparator": "Y", + "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + }, + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Manage your appointment" + }, + "CONTACT_GP": { + "ExternalRoutingCode": "ContactGP", + "ActionDescription": "Contact your GP", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "BOOK_LOCAL": { + "ExternalRoutingCode": "BookLocal", + "ActionDescription": "##Getting the vaccine\n\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "MANAGE_LOCAL": { + "ExternalRoutingCode": "ManageLocal", + "ActionDescription": "##You have an RSV vaccination appointment\n\nContact your healthcare provider to change or cancel your appointment.", + "ActionType": "CardWithText", + "UrlLink": null, + "UrlLabel": "" + }, + "HELP_SUPPORT": { + "ExternalRoutingCode": "HelpSupportInfo", + "ActionDescription": "## CONTENT TBC\n\nBlah blah blah.", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "CHECK_CORRECT_X": { + "ExternalRoutingCode": "HealthcareProInfo", + "ActionDescription": "## If you think this is incorrect\n\nSpeak to your healthcare professional if you think you should be offered this vaccine.\n\nFor anything else, visit our help and support page. (ADD LINK)", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "CHECK_CORRECT_ALREADY_VACCINATED": { + "ExternalRoutingCode": "AlreadyVaccinatedInfo", + "ActionDescription": "## If you think this is incorrect\n\nIf you believe you've not been vaccinated against RSV, speak to your healthcare professional.\n\nFor anything else please see our help and support page. (ADD LINK).", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "CHECK_CORRECT_OTHER_SETTING": { + "ExternalRoutingCode": "ManagedSettingInfo", + "ActionDescription": "## If you think this is incorrect\n\nIf you have already had this vaccination or your personal details are wrong, visit our help and support page. (ADD LINK).", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + } + } + } + ] + } +} diff --git a/tests/e2e/data/dynamoDB/AUTO_RSV_ELI-355.json b/tests/e2e/data/dynamoDB/AUTO_RSV_ELI-355.json deleted file mode 100644 index e7b3d8c62..000000000 --- a/tests/e2e/data/dynamoDB/AUTO_RSV_ELI-355.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "scenario_name": "RSV - Actionable due to membership of an Age Cohort incl. suggested actions (with booking)", - "config_filenames": [ - "AUTO_RSV_ELI-355.json" - ], - "request_headers": { - "nhs-login-nhs-number": "9990035501" - }, - "data": [ - { - "NHS_NUMBER": "9990035501", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "eli_355_cohort": { - "M": { - "dateJoined": { - "S": "20230515" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "9990035501", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "19500107", - "GENDER": "0", - "POSTCODE": "LS1 1AB", - "POSTCODE_SECTOR": "LS1", - "POSTCODE_OUTCODE": "1AB", - "MSOA": "E02001111", - "LSOA": "E01005348", - "GP_PRACTICE_CODE": "B87008", - "PCN": "U43084", - "ICB": "QWO", - "COMMISSIONING_REGION": "Y63", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - }, - { - "NHS_NUMBER": "9990035501", - "ATTRIBUTE_TYPE": "RSV", - "BOOKED_APPOINTMENT_DATE": "<>", - "VALID_DOSES_COUNT": "1", - "INVALID_DOSES_COUNT": "2" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/sean_demo_01.json b/tests/e2e/data/dynamoDB/sean_demo_01.json deleted file mode 100644 index 63cfe6f93..000000000 --- a/tests/e2e/data/dynamoDB/sean_demo_01.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "scenario_name": "Sean Demo", - "request_headers": { - "nhs-login-nhs-number": "9111111111" - }, - "config_filenames": [ - "sean_demo.json" - ], - "data": [ - { - "NHS_NUMBER": "9111111111", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - "other_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - }, - "rsv_cohort": { - "M": { - "dateJoined": { - "S": "20250604" - } - } - } - } - } - } - }, - { - "NHS_NUMBER": "9111111111", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "<>", - "GENDER": "0", - "POSTCODE": "SG8 6EG", - "POSTCODE_SECTOR": "SG86", - "POSTCODE_OUTCODE": "SG8", - "MSOA": "E02003792", - "LSOA": "E01018267", - "GP_PRACTICE_CODE": "D81046", - "PCN": "U75549", - "ICB": "QUE", - "COMMISSIONING_REGION": "Y61", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "Y", - "DE_FLAG": "N" - }, - { - "NHS_NUMBER": "9111111111", - "ATTRIBUTE_TYPE": "COVID", - "BOOKED_APPOINTMENT_DATE": "<>", - "LAST_SUCCESSFUL_DATE": "<>" - }, - { - "NHS_NUMBER": "9111111111", - "ATTRIBUTE_TYPE": "RSV", - "LAST_SUCCESSFUL_DATE": "<>" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-1.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-1.json index 22f7ca90f..e36d9878a 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-1.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-1.json @@ -37,7 +37,8 @@ "NHS_NUMBER": "9900022201", "ATTRIBUTE_TYPE": "RSV", "BOOKED_APPOINTMENT_DATE": "<>", - "BOOKED_APPOINTMENT_PROVIDER": "NBS" + "BOOKED_APPOINTMENT_PROVIDER": "NBS", + "LAST_SUCCESSFUL_DATE": null } ] } diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-2.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-2.json index a259872f1..41c86b84c 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-2.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-2.json @@ -39,7 +39,8 @@ "NHS_NUMBER": "9900022202", "ATTRIBUTE_TYPE": "RSV", "BOOKED_APPOINTMENT_DATE": "<>", - "BOOKED_APPOINTMENT_PROVIDER": "NBS" + "BOOKED_APPOINTMENT_PROVIDER": "NBS", + "LAST_SUCCESSFUL_DATE": null } ] } diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-4.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-4.json index 769e7ce50..d2c506c1e 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-4.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-4.json @@ -38,7 +38,8 @@ { "NHS_NUMBER": "9900022204", "ATTRIBUTE_TYPE": "RSV", - "LAST_SUCCESSFUL_DATE": "<>" + "LAST_SUCCESSFUL_DATE": "<>", + "BOOKED_APPOINTMENT_DATE": null } ] } diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-7.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-7.json index 53226c1e3..507ab6c3c 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-7.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-7.json @@ -34,6 +34,13 @@ "13Q_FLAG": "N", "CARE_HOME_FLAG": "N", "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900002227", + "ATTRIBUTE_TYPE": "COVID", + "LAST_SUCCESSFUL_DATE": "<>", + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" } ] } diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_001.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_001.json new file mode 100644 index 000000000..3ca2b715e --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_001.json @@ -0,0 +1,43 @@ +{ + "scenario_name": "RSV - Actionable - Magic Cohort Has Future booking No Vaccination", + "request_headers": { + "nhs-login-nhs-number": "9900036501" + }, + "config_filenames": ["AUTO_RSV_ELI-365.json"], + "data": [ + { + "NHS_NUMBER": "9900036501", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "other_cohort", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900036501", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900036501", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_002.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_002.json new file mode 100644 index 000000000..8db7b0832 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_002.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "RSV - NotEligible -Magic Cohort Has Past booking No Vaccination", + "request_headers": { + "nhs-login-nhs-number": "9900036502" + }, + "config_filenames": ["AUTO_RSV_ELI-365.json"], + "data": [ + { + "NHS_NUMBER": "9900036502", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "other_cohort", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900036502", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900036502", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS", + "LAST_SUCCESSFUL_DATE": null + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_003.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_003.json new file mode 100644 index 000000000..96c386845 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_003.json @@ -0,0 +1,34 @@ +{ + "scenario_name": "RSV - NotActionable - Magic Cohort Has a Past RSV Vaccination - Future Booking", + "request_headers": { + "nhs-login-nhs-number": "9900036503" + }, + "config_filenames": ["AUTO_RSV_ELI-365.json"], + "data": [ + { + "NHS_NUMBER": "9900036503", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900036503", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": "<>", + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "ACC" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_004.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_004.json new file mode 100644 index 000000000..9b5be5a51 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_004.json @@ -0,0 +1,43 @@ +{ + "scenario_name": "RSV - NotActionable - Magic Cohort Has a Past RSV Vaccination - No Booking", + "request_headers": { + "nhs-login-nhs-number": "9900036504" + }, + "config_filenames": ["AUTO_RSV_ELI-365.json"], + "data": [ + { + "NHS_NUMBER": "9900036504", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "other_cohort", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900036504", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900036504", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": "<>", + "BOOKED_APPOINTMENT_DATE": null + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_005.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_005.json new file mode 100644 index 000000000..b9fb7b290 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_005.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "RSV - NotActionable - Magic Cohort Has a Past RSV Vaccination - Past Booking", + "request_headers": { + "nhs-login-nhs-number": "9900036505" + }, + "config_filenames": ["AUTO_RSV_ELI-365.json"], + "data": [ + { + "NHS_NUMBER": "9900036505", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "other_cohort", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900036505", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900036505", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": "<>", + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_006.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_006.json new file mode 100644 index 000000000..f6f33380d --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_006.json @@ -0,0 +1,37 @@ +{ + "scenario_name": "RSV - NotEligible -Magic Cohort No Vaccination - No Booking", + "request_headers": { + "nhs-login-nhs-number": "9900036506" + }, + "config_filenames": ["AUTO_RSV_ELI-365.json"], + "data": [ + { + "NHS_NUMBER": "9900036506", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "other_cohort", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900036506", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_007.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_007.json new file mode 100644 index 000000000..a59e0aca1 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_007.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "RSV - NotEligible - Magic Cohort - In Eligible Cohort - Filtered by age rule", + "request_headers": { + "nhs-login-nhs-number": "9900036507" + }, + "config_filenames": ["AUTO_RSV_ELI-365.json"], + "data": [ + { + "NHS_NUMBER": "9900036507", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_75to79", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900036507", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900036507", + "ATTRIBUTE_TYPE": "COVID", + "LAST_SUCCESSFUL_DATE": "<>", + "BOOKED_APPOINTMENT_DATES": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_008.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_008.json new file mode 100644 index 000000000..e6f4d6880 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_008.json @@ -0,0 +1,42 @@ +{ + "scenario_name": "RSV - NotActionable - Magic Cohort - In Eligible Cohort - Filtered by age, but Vaccinated", + "request_headers": { + "nhs-login-nhs-number": "9900036508" + }, + "config_filenames": ["AUTO_RSV_ELI-365.json"], + "data": [ + { + "NHS_NUMBER": "9900036508", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_75to79", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900036508", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900036508", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_009.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_009.json new file mode 100644 index 000000000..ed1357e09 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_009.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "RSV - NotEligible - Under Age F Rule - rsv_75to79", + "request_headers": { + "nhs-login-nhs-number": "9900036509" + }, + "config_filenames": ["AUTO_RSV_ELI-365.json"], + "data": [ + { + "NHS_NUMBER": "9900036509", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_75to79", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900036509", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900036509", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE" : null, + "BOOKED_APPOINTMENT_PROVIDER": null + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_010.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_010.json new file mode 100644 index 000000000..67f80d2b7 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_010.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "RSV - NotEligible - Over Age F Rule - rsv_75to79", + "request_headers": { + "nhs-login-nhs-number": "9900036510" + }, + "config_filenames": ["AUTO_RSV_ELI-365.json"], + "data": [ + { + "NHS_NUMBER": "9900036510", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_75to79", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900036510", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19440901", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900036510", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE" : null, + "BOOKED_APPOINTMENT_PROVIDER": null + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_011.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_011.json new file mode 100644 index 000000000..1fb932066 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_011.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "RSV - NotEligible - Under Age F Rule - rsv_80_since_02_Sept_2024", + "request_headers": { + "nhs-login-nhs-number": "9900036511" + }, + "config_filenames": ["AUTO_RSV_ELI-365.json"], + "data": [ + { + "NHS_NUMBER": "9900036511", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_80_since_02_Sept_2024", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900036511", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900036511", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE" : null, + "BOOKED_APPOINTMENT_PROVIDER": null + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_012.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_012.json new file mode 100644 index 000000000..fd13c856b --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_012.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "RSV - NotEligible - Over Age F Rule - rsv_80_since_02_Sept_2024", + "request_headers": { + "nhs-login-nhs-number": "9900036512" + }, + "config_filenames": ["AUTO_RSV_ELI-365.json"], + "data": [ + { + "NHS_NUMBER": "9900036512", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_80_since_02_Sept_2024", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900036512", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19440901", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900036512", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE" : null, + "BOOKED_APPOINTMENT_PROVIDER": null + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_013.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_013.json new file mode 100644 index 000000000..ac210c779 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_013.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "RSV - NotActionable - In Eligible Cohort - Already Vaccinated", + "request_headers": { + "nhs-login-nhs-number": "9900036513" + }, + "config_filenames": ["AUTO_RSV_ELI-365.json"], + "data": [ + { + "NHS_NUMBER": "9900036513", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_80_since_02_Sept_2024", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900036513", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900036513", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": "<>", + "BOOKED_APPOINTMENT_DATE" : null, + "BOOKED_APPOINTMENT_PROVIDER": null + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_014.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_014.json new file mode 100644 index 000000000..d297874f8 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_014.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "RSV - NotActionable - In rsv_80_since_02_Sept_2024 Cohort - Carehome - No Booking", + "request_headers": { + "nhs-login-nhs-number": "9900036514" + }, + "config_filenames": ["AUTO_RSV_ELI-365.json"], + "data": [ + { + "NHS_NUMBER": "9900036514", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_80_since_02_Sept_2024", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900036514", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "Y", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900036514", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE" : null, + "BOOKED_APPOINTMENT_PROVIDER": null + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_015.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_015.json new file mode 100644 index 000000000..29db6c359 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_015.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "RSV - NotActionable - In rsv_75to79 Cohort - Detained Estates - No Booking", + "request_headers": { + "nhs-login-nhs-number": "9900036515" + }, + "config_filenames": ["AUTO_RSV_ELI-365.json"], + "data": [ + { + "NHS_NUMBER": "9900036515", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_75to79", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900036515", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "Y" + }, + { + "NHS_NUMBER": "9900036515", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE" : null, + "BOOKED_APPOINTMENT_PROVIDER": null + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_016.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_016.json new file mode 100644 index 000000000..50b4f6965 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_016.json @@ -0,0 +1,51 @@ +{ + "scenario_name": "RSV - Actionable - In rsv_75to79 Cohort - 13Q - No Booking", + "request_headers": { + "nhs-login-nhs-number": "9900036516" + }, + "config_filenames": ["AUTO_RSV_ELI-365.json"], + "data": [ + { + "NHS_NUMBER": "9900036516", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_75to79", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900036516", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "Y", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900036516", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE" : null, + "BOOKED_APPOINTMENT_PROVIDER": null + }, + { + "NHS_NUMBER": "9900036516", + "ATTRIBUTE_TYPE": "COVID", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE" : null, + "BOOKED_APPOINTMENT_PROVIDER": null + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_017.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_017.json new file mode 100644 index 000000000..21074c9be --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_017.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "RSV - Actionable - In rsv_75to79 Cohort - Carehome - With Local Booking", + "request_headers": { + "nhs-login-nhs-number": "9900036517" + }, + "config_filenames": ["AUTO_RSV_ELI-365.json"], + "data": [ + { + "NHS_NUMBER": "9900036517", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_75to79", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900036517", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "Y", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900036517", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE" : "<>", + "BOOKED_APPOINTMENT_PROVIDER": "ACC" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_018.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_018.json new file mode 100644 index 000000000..deccd2359 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_018.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "RSV - Actionable - In rsv_80_since_02_Sept_2024 Cohort - Detained Estates - NBS Booking", + "request_headers": { + "nhs-login-nhs-number": "9900036518" + }, + "config_filenames": ["AUTO_RSV_ELI-365.json"], + "data": [ + { + "NHS_NUMBER": "9900036518", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_80_since_02_Sept_2024", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900036518", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "Y" + }, + { + "NHS_NUMBER": "9900036518", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE" : "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_019.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_019.json new file mode 100644 index 000000000..530a79359 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_019.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "RSV - Actionable - In rsv_80_since_02_Sept_2024 Cohort - 13Q - NBS Booking", + "request_headers": { + "nhs-login-nhs-number": "9900036519" + }, + "config_filenames": ["AUTO_RSV_ELI-365.json"], + "data": [ + { + "NHS_NUMBER": "9900036519", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_80_since_02_Sept_2024", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900036519", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "Y", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900036519", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE" : "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_020.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_020.json new file mode 100644 index 000000000..2c77fc177 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_020.json @@ -0,0 +1,44 @@ +{ + "scenario_name": "RSV - Actionable - In rsv_80_since_02_Sept_2024 Cohort - CP Expansion Area - ICB", + "request_headers": { + "nhs-login-nhs-number": "9900036520" + }, + "config_filenames": ["AUTO_RSV_ELI-365.json"], + "data": [ + { + "NHS_NUMBER": "9900036520", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_80_since_02_Sept_2024", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900036520", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QJG", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900036520", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE" : null, + "BOOKED_APPOINTMENT_PROVIDER": null + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_021.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_021.json new file mode 100644 index 000000000..1f953ddb8 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_021.json @@ -0,0 +1,45 @@ +{ + "scenario_name": "RSV - Actionable - In rsv_80_since_02_Sept_2024 Cohort - CP Expansion Area - Local Authority", + "request_headers": { + "nhs-login-nhs-number": "9900036521" + }, + "config_filenames": ["AUTO_RSV_ELI-365.json"], + "data": [ + { + "NHS_NUMBER": "9900036521", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_80_since_02_Sept_2024", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900036521", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "ABC", + "LOCAL_AUTHORITY": "E08000011", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900036521", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE" : null, + "BOOKED_APPOINTMENT_PROVIDER": null + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_022.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_022.json new file mode 100644 index 000000000..17be92a34 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_022.json @@ -0,0 +1,45 @@ +{ + "scenario_name": "RSV - Actionable - In rsv_80_since_02_Sept_2024 Cohort - Not in CP Expansion Area", + "request_headers": { + "nhs-login-nhs-number": "9900036522" + }, + "config_filenames": ["AUTO_RSV_ELI-365.json"], + "data": [ + { + "NHS_NUMBER": "9900036522", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_80_since_02_Sept_2024", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900036522", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "ABC", + "LOCAL_AUTHORITY": "ZZ8000011", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900036522", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE" : null, + "BOOKED_APPOINTMENT_PROVIDER": null + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_023.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_023.json new file mode 100644 index 000000000..6091129e0 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_023.json @@ -0,0 +1,45 @@ +{ + "scenario_name": "RSV - Actionable - In rsv_80_since_02_Sept_2024 and rsv_75to79 Cohorts - Not in CP Expansion Area", + "request_headers": { + "nhs-login-nhs-number": "9900036523" + }, + "config_filenames": ["AUTO_RSV_ELI-365.json"], + "data": [ + { + "NHS_NUMBER": "9900036523", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_80_since_02_Sept_2024", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900036523", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "ABC", + "LOCAL_AUTHORITY": "ZZ8000011", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900036523", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE" : null, + "BOOKED_APPOINTMENT_PROVIDER": null + } + ] +} diff --git a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_001.json b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_001.json new file mode 100644 index 000000000..e7e0b5b37 --- /dev/null +++ b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_001.json @@ -0,0 +1,38 @@ +{ + "scenario_name": "RSV - Vita Integration - Actionable - You should have the RSV vaccine ( in CP area ) - age rolling - BookNBS", + "request_headers": { + "nhs-login-nhs-number": "9686368973" + }, + "config_filenames": ["vita_integration_test_config.json"], + "data": [ + { + "NHS_NUMBER": "9686368973", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_75to79", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9686368973", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "LOCAL_AUTHORITY": "E08000011", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_002.json b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_002.json new file mode 100644 index 000000000..2ac0aaa1a --- /dev/null +++ b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_002.json @@ -0,0 +1,37 @@ +{ + "scenario_name": "RSV - Vita Integration - Actionable - You should have the RSV vaccine ( out CP area ) - age rolling - age rolling - BookLocal", + "request_headers": { + "nhs-login-nhs-number": "9686368906" + }, + "config_filenames": ["vita_integration_test_config.json"], + "data": [ + { + "NHS_NUMBER": "9686368906", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_75to79", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9686368906", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "2", + "POSTCODE": "CB3 8DX", + "POSTCODE_SECTOR": "CB38", + "POSTCODE_OUTCODE": "CB3", + "MSOA": "E02007085", + "LSOA": "E01018223", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_003.json b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_003.json new file mode 100644 index 000000000..aa1be4725 --- /dev/null +++ b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_003.json @@ -0,0 +1,37 @@ +{ + "scenario_name": "RSV - Vita Integration - Actionable - You should have the RSV vaccine ( out CP area ) - age catchup - BookLocal", + "request_headers": { + "nhs-login-nhs-number": "9658218873" + }, + "config_filenames": ["vita_integration_test_config.json"], + "data": [ + { + "NHS_NUMBER": "9658218873", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_80_since_02_Sept_2024", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9658218873", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "2", + "POSTCODE": "CB3 8DX", + "POSTCODE_SECTOR": "CB38", + "POSTCODE_OUTCODE": "CB3", + "MSOA": "E02007085", + "LSOA": "E01018223", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_004.json b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_004.json new file mode 100644 index 000000000..01e433f44 --- /dev/null +++ b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_004.json @@ -0,0 +1,33 @@ +{ + "scenario_name": "RSV - Vita Integration - Actionable - You should have the RSV vaccine ( existing NBS booking ) - empty cohorts - AmendNBS", + "request_headers": { + "nhs-login-nhs-number": "9658218881" + }, + "config_filenames": ["vita_integration_test_config.json"], + "data": [ + { + "NHS_NUMBER": "9658218881", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "2", + "POSTCODE": "CB3 8DX", + "POSTCODE_SECTOR": "CB38", + "POSTCODE_OUTCODE": "CB3", + "MSOA": "E02007085", + "LSOA": "E01018223", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9658218881", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_005.json b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_005.json new file mode 100644 index 000000000..ca4128ae6 --- /dev/null +++ b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_005.json @@ -0,0 +1,33 @@ +{ + "scenario_name": "RSV - Vita Integration - Actionable - You should have the RSV vaccine ( existing non-NBS booking ) - empty cohorts - ManageLocal", + "request_headers": { + "nhs-login-nhs-number": "9658218903" + }, + "config_filenames": ["vita_integration_test_config.json"], + "data": [ + { + "NHS_NUMBER": "9658218903", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "2", + "POSTCODE": "CB3 8DX", + "POSTCODE_SECTOR": "CB38", + "POSTCODE_OUTCODE": "CB3", + "MSOA": "E02007085", + "LSOA": "E01018223", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9658218903", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "ACC" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_006.json b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_006.json new file mode 100644 index 000000000..192d57f6e --- /dev/null +++ b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_006.json @@ -0,0 +1,32 @@ +{ + "scenario_name": "RSV - Vita Integration - NotActionable - You should have the RSV vaccine (already vaccd) - AlreadyVaccinated", + "request_headers": { + "nhs-login-nhs-number": "9658218989" + }, + "config_filenames": ["vita_integration_test_config.json"], + "data": [ + { + "NHS_NUMBER": "9658218989", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "2", + "POSTCODE": "CB3 8DX", + "POSTCODE_SECTOR": "CB38", + "POSTCODE_OUTCODE": "CB3", + "MSOA": "E02007085", + "LSOA": "E01018223", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9658218989", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_007.json b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_007.json new file mode 100644 index 000000000..8013cf75f --- /dev/null +++ b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_007.json @@ -0,0 +1,37 @@ +{ + "scenario_name": "RSV - Vita Integration - NotActionable - You should have the RSV vaccine ( not available ) - age rolling - NotAvailable", + "request_headers": { + "nhs-login-nhs-number": "9658218997" + }, + "config_filenames": ["vita_integration_test_config.json"], + "data": [ + { + "NHS_NUMBER": "9658218997", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_75to79", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9658218997", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "2", + "POSTCODE": "CB3 8DX", + "POSTCODE_SECTOR": "CB38", + "POSTCODE_OUTCODE": "CB3", + "MSOA": "E02007085", + "LSOA": "E01018223", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_008.json b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_008.json new file mode 100644 index 000000000..2f4cb8d8b --- /dev/null +++ b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_008.json @@ -0,0 +1,37 @@ +{ + "scenario_name": "RSV - Vita Integration - NotActionable - empty suggestions - fallback to static", + "request_headers": { + "nhs-login-nhs-number": "9658219004" + }, + "config_filenames": ["vita_integration_test_config.json"], + "data": [ + { + "NHS_NUMBER": "9658219004", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_75to79", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9658219004", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "2", + "POSTCODE": "CB3 8DX", + "POSTCODE_SECTOR": "CB38", + "POSTCODE_OUTCODE": "CB3", + "MSOA": "E02007085", + "LSOA": "E01018223", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_009.json b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_009.json new file mode 100644 index 000000000..5d2ca0ea6 --- /dev/null +++ b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_009.json @@ -0,0 +1,37 @@ +{ + "scenario_name": "RSV - Vita Integration - NotActionable - You should have the RSV vaccine - age rolling - NotYetDue", + "request_headers": { + "nhs-login-nhs-number": "9658219012" + }, + "config_filenames": ["vita_integration_test_config.json"], + "data": [ + { + "NHS_NUMBER": "9658219012", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_75to79", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9658219012", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "2", + "POSTCODE": "CB3 8DX", + "POSTCODE_SECTOR": "CB38", + "POSTCODE_OUTCODE": "CB3", + "MSOA": "E02007085", + "LSOA": "E01018223", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_011.json b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_011.json new file mode 100644 index 000000000..5c7a7eb8e --- /dev/null +++ b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_011.json @@ -0,0 +1,37 @@ +{ + "scenario_name": "RSV - Vita Integration - NotActionable - You should have the RSV vaccine - age rolling - TooClose", + "request_headers": { + "nhs-login-nhs-number": "9658220142" + }, + "config_filenames": ["vita_integration_test_config.json"], + "data": [ + { + "NHS_NUMBER": "9658220142", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_75to79", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9658220142", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "2", + "POSTCODE": "CB3 8DX", + "POSTCODE_SECTOR": "CB38", + "POSTCODE_OUTCODE": "CB3", + "MSOA": "E02007085", + "LSOA": "E01018223", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_012.json b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_012.json new file mode 100644 index 000000000..a8f8ade05 --- /dev/null +++ b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_012.json @@ -0,0 +1,37 @@ +{ + "scenario_name": "RSV - Vita Integration - NotActionable - You should have the RSV vaccine ( managed setting ) - age rolling - OtherSetting", + "request_headers": { + "nhs-login-nhs-number": "9658220150" + }, + "config_filenames": ["vita_integration_test_config.json"], + "data": [ + { + "NHS_NUMBER": "9658220150", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "2", + "POSTCODE": "CB3 8DX", + "POSTCODE_SECTOR": "CB38", + "POSTCODE_OUTCODE": "CB3", + "MSOA": "E02007085", + "LSOA": "E01018223", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "Y", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9658220150", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_75to79", + "DATE_JOINED": "20231020" + } + ] + } + ] +} diff --git a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_013.json b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_013.json new file mode 100644 index 000000000..039686abf --- /dev/null +++ b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_013.json @@ -0,0 +1,33 @@ +{ + "scenario_name": "RSV - Vita Integration - NotActionable - You should have the RSV vaccine (already vaccinated) - unknown membership - AlreadyVaccinated", + "request_headers": { + "nhs-login-nhs-number": "9450114080" + }, + "config_filenames": ["vita_integration_test_config.json"], + "data": [ + { + "NHS_NUMBER": "9450114080", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "2", + "POSTCODE": "CB3 8DX", + "POSTCODE_SECTOR": "CB38", + "POSTCODE_OUTCODE": "CB3", + "MSOA": "E02007085", + "LSOA": "E01018223", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "Y", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9450114080", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": "<>" + } + + ] +} diff --git a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_014.json b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_014.json new file mode 100644 index 000000000..0f8b3c63f --- /dev/null +++ b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_014.json @@ -0,0 +1,32 @@ +{ + "scenario_name": "RSV - Vita Integration - NotActionable - You should have the RSV vaccine (already vaccinated) - empty cohorts - AlreadyVaccinated", + "request_headers": { + "nhs-login-nhs-number": "9466447939" + }, + "config_filenames": ["vita_integration_test_config.json"], + "data": [ + { + "NHS_NUMBER": "9466447939", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": "<>" + }, + { + "NHS_NUMBER": "9466447939", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "2", + "POSTCODE": "CB3 8DX", + "POSTCODE_SECTOR": "CB38", + "POSTCODE_OUTCODE": "CB3", + "MSOA": "E02007085", + "LSOA": "E01018223", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "Y", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_015.json b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_015.json new file mode 100644 index 000000000..02b343b97 --- /dev/null +++ b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_015.json @@ -0,0 +1,27 @@ +{ + "scenario_name": "RSV - Vita Integration - - Not Eligible", + "request_headers": { + "nhs-login-nhs-number": "9657933617" + }, + "config_filenames": ["vita_integration_test_config.json"], + "data": [ + { + "NHS_NUMBER": "9657933617", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "2", + "POSTCODE": "CB3 8DX", + "POSTCODE_SECTOR": "CB38", + "POSTCODE_OUTCODE": "CB3", + "MSOA": "E02007085", + "LSOA": "E01018223", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "Y", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_500.json b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_500.json new file mode 100644 index 000000000..a548548b0 --- /dev/null +++ b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_500.json @@ -0,0 +1,37 @@ +{ + "scenario_name": "RSV - Vita Integration - 500", + "request_headers": { + "nhs-login-nhs-number": "9436793375" + }, + "config_filenames": ["vita_integration_test_config.json"], + "data": [ + { + "NHS_NUMBER": "9436793375", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_75to79", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9436793375", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": 19500601, + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_001.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_001.json new file mode 100644 index 000000000..d1c5e9563 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_001.json @@ -0,0 +1,24 @@ +{ + "meta": { + "lastUpdated": "2025-07-16T10:32:10.803638+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "AmendNBS", + "actionType": "ButtonWithAuthLink", + "description": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", + "urlLabel": "Manage your appointment", + "urlLink": "http://www.nhs.uk/book-rsv" + } + ], + "condition": "RSV", + "eligibilityCohorts": [], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "9b11bdb0-afa8-4ff7-994c-ee7738313d76" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_002.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_002.json new file mode 100644 index 000000000..52fd438d6 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_002.json @@ -0,0 +1,35 @@ +{ + "meta": { + "lastUpdated": "2025-07-28T15:46:13.437131+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "HealthcareProInfo", + "actionType": "InfoText", + "description": "## If you think this is incorrect\n\nSpeak to your healthcare professional if you think you should be offered this vaccine.\n\nFor anything else, visit our help and support page. (ADD LINK)", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age", + "cohortStatus": "NotEligible", + "cohortText": "are not aged 75 to 79 years old" + }, + { + "cohortCode": "rsv_age_catchup", + "cohortStatus": "NotEligible", + "cohortText": "did not turn 80 after 1 September 2024" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "c4aba395-5f53-49b7-8506-9ab8f6bd7750" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_003.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_003.json new file mode 100644 index 000000000..f00ac3dc4 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_003.json @@ -0,0 +1,30 @@ +{ + "meta": { + "lastUpdated": "2025-07-28T15:47:55.146282+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "AlreadyVaccinatedInfo", + "actionType": "InfoText", + "description": "## If you think this is incorrect\n\nIf you believe you've not been vaccinated against RSV, speak to your healthcare professional.\n\nFor anything else please see our help and support page. (ADD LINK).", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "Already Vaccinated", + "ruleText": "## You've had your RSV vaccination\n\nWe believe you had your vaccination.", + "ruleType": "S" + } + ] + } + ], + "responseId": "0587f923-0f83-4f2b-aba4-024f11c7cf4e" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_004.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_004.json new file mode 100644 index 000000000..78d7940bf --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_004.json @@ -0,0 +1,30 @@ +{ + "meta": { + "lastUpdated": "2025-07-28T15:48:45.576609+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "AlreadyVaccinatedInfo", + "actionType": "InfoText", + "description": "## If you think this is incorrect\n\nIf you believe you've not been vaccinated against RSV, speak to your healthcare professional.\n\nFor anything else please see our help and support page. (ADD LINK).", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "Already Vaccinated", + "ruleText": "## You've had your RSV vaccination\n\nWe believe you had your vaccination.", + "ruleType": "S" + } + ] + } + ], + "responseId": "24041024-19c7-45ca-a564-0394e15f634c" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_005.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_005.json new file mode 100644 index 000000000..601e189ef --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_005.json @@ -0,0 +1,30 @@ +{ + "meta": { + "lastUpdated": "2025-07-28T15:49:21.809706+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "AlreadyVaccinatedInfo", + "actionType": "InfoText", + "description": "## If you think this is incorrect\n\nIf you believe you've not been vaccinated against RSV, speak to your healthcare professional.\n\nFor anything else please see our help and support page. (ADD LINK).", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "Already Vaccinated", + "ruleText": "## You've had your RSV vaccination\n\nWe believe you had your vaccination.", + "ruleType": "S" + } + ] + } + ], + "responseId": "dc283180-9a04-4005-9da1-2dec682af213" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_006.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_006.json new file mode 100644 index 000000000..8e1e3f5a2 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_006.json @@ -0,0 +1,35 @@ +{ + "meta": { + "lastUpdated": "2025-07-28T15:52:33.593209+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "HealthcareProInfo", + "actionType": "InfoText", + "description": "## If you think this is incorrect\n\nSpeak to your healthcare professional if you think you should be offered this vaccine.\n\nFor anything else, visit our help and support page. (ADD LINK)", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age", + "cohortStatus": "NotEligible", + "cohortText": "are not aged 75 to 79 years old" + }, + { + "cohortCode": "rsv_age_catchup", + "cohortStatus": "NotEligible", + "cohortText": "did not turn 80 after 1 September 2024" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "9bdf8ca7-e5b1-4e1e-a17f-a9e17bd4c203" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_007.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_007.json new file mode 100644 index 000000000..17443852e --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_007.json @@ -0,0 +1,35 @@ +{ + "meta": { + "lastUpdated": "2025-07-28T15:53:18.270563+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "HealthcareProInfo", + "actionType": "InfoText", + "description": "## If you think this is incorrect\n\nSpeak to your healthcare professional if you think you should be offered this vaccine.\n\nFor anything else, visit our help and support page. (ADD LINK)", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age", + "cohortStatus": "NotEligible", + "cohortText": "are not aged 75 to 79 years old" + }, + { + "cohortCode": "rsv_age_catchup", + "cohortStatus": "NotEligible", + "cohortText": "did not turn 80 after 1 September 2024" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "d8144e93-4cfd-402b-ac3d-7699aa21b48f" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_008.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_008.json new file mode 100644 index 000000000..8b7d4b585 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_008.json @@ -0,0 +1,30 @@ +{ + "meta": { + "lastUpdated": "2025-07-28T15:53:59.174715+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "AlreadyVaccinatedInfo", + "actionType": "InfoText", + "description": "## If you think this is incorrect\n\nIf you believe you've not been vaccinated against RSV, speak to your healthcare professional.\n\nFor anything else please see our help and support page. (ADD LINK).", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "Already Vaccinated", + "ruleText": "## You've had your RSV vaccination\n\nWe believe you had your vaccination.", + "ruleType": "S" + } + ] + } + ], + "responseId": "54f1841a-93e5-4da1-916b-6ed1cff5903f" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_009.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_009.json new file mode 100644 index 000000000..204fb5de9 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_009.json @@ -0,0 +1,35 @@ +{ + "meta": { + "lastUpdated": "2025-07-28T16:08:36.057890+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "HealthcareProInfo", + "actionType": "InfoText", + "description": "## If you think this is incorrect\n\nSpeak to your healthcare professional if you think you should be offered this vaccine.\n\nFor anything else, visit our help and support page. (ADD LINK)", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age", + "cohortStatus": "NotEligible", + "cohortText": "are not aged 75 to 79 years old" + }, + { + "cohortCode": "rsv_age_catchup", + "cohortStatus": "NotEligible", + "cohortText": "did not turn 80 after 1 September 2024" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "84069cc8-d3a4-48aa-adcf-d5e292fb1a56" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_010.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_010.json new file mode 100644 index 000000000..204fb5de9 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_010.json @@ -0,0 +1,35 @@ +{ + "meta": { + "lastUpdated": "2025-07-28T16:08:36.057890+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "HealthcareProInfo", + "actionType": "InfoText", + "description": "## If you think this is incorrect\n\nSpeak to your healthcare professional if you think you should be offered this vaccine.\n\nFor anything else, visit our help and support page. (ADD LINK)", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age", + "cohortStatus": "NotEligible", + "cohortText": "are not aged 75 to 79 years old" + }, + { + "cohortCode": "rsv_age_catchup", + "cohortStatus": "NotEligible", + "cohortText": "did not turn 80 after 1 September 2024" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "84069cc8-d3a4-48aa-adcf-d5e292fb1a56" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_011.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_011.json new file mode 100644 index 000000000..204fb5de9 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_011.json @@ -0,0 +1,35 @@ +{ + "meta": { + "lastUpdated": "2025-07-28T16:08:36.057890+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "HealthcareProInfo", + "actionType": "InfoText", + "description": "## If you think this is incorrect\n\nSpeak to your healthcare professional if you think you should be offered this vaccine.\n\nFor anything else, visit our help and support page. (ADD LINK)", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age", + "cohortStatus": "NotEligible", + "cohortText": "are not aged 75 to 79 years old" + }, + { + "cohortCode": "rsv_age_catchup", + "cohortStatus": "NotEligible", + "cohortText": "did not turn 80 after 1 September 2024" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "84069cc8-d3a4-48aa-adcf-d5e292fb1a56" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_012.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_012.json new file mode 100644 index 000000000..204fb5de9 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_012.json @@ -0,0 +1,35 @@ +{ + "meta": { + "lastUpdated": "2025-07-28T16:08:36.057890+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "HealthcareProInfo", + "actionType": "InfoText", + "description": "## If you think this is incorrect\n\nSpeak to your healthcare professional if you think you should be offered this vaccine.\n\nFor anything else, visit our help and support page. (ADD LINK)", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age", + "cohortStatus": "NotEligible", + "cohortText": "are not aged 75 to 79 years old" + }, + { + "cohortCode": "rsv_age_catchup", + "cohortStatus": "NotEligible", + "cohortText": "did not turn 80 after 1 September 2024" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "84069cc8-d3a4-48aa-adcf-d5e292fb1a56" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_013.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_013.json new file mode 100644 index 000000000..faff88634 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_013.json @@ -0,0 +1,30 @@ +{ + "meta": { + "lastUpdated": "2025-07-28T18:54:18.927265+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "AlreadyVaccinatedInfo", + "actionType": "InfoText", + "description": "## If you think this is incorrect\n\nIf you believe you've not been vaccinated against RSV, speak to your healthcare professional.\n\nFor anything else please see our help and support page. (ADD LINK).", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "Already Vaccinated", + "ruleText": "## You've had your RSV vaccination\n\nWe believe you had your vaccination.", + "ruleType": "S" + } + ] + } + ], + "responseId": "622dc3d6-6c91-427a-8e03-b8a42fef140b" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_014.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_014.json new file mode 100644 index 000000000..c77bdbc6f --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_014.json @@ -0,0 +1,36 @@ +{ + "meta": { + "lastUpdated": "2025-07-29T07:26:50.668718+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "ManagedSettingInfo", + "actionType": "InfoText", + "description": "## If you think this is incorrect\n\nIf you have already had this vaccination or your personal details are wrong, visit our help and support page. (ADD LINK).", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_catchup", + "cohortStatus": "NotActionable", + "cohortText": "turned 80 after 1st September 2024" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "Other Setting", + "ruleText": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "ruleType": "S" + } + ] + } + ], + "responseId": "4cab3850-0478-4cc5-a562-7c1b113740dc" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_015.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_015.json new file mode 100644 index 000000000..b3020f0e8 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_015.json @@ -0,0 +1,36 @@ +{ + "meta": { + "lastUpdated": "2025-07-29T07:38:36.598629+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "ManagedSettingInfo", + "actionType": "InfoText", + "description": "## If you think this is incorrect\n\nIf you have already had this vaccination or your personal details are wrong, visit our help and support page. (ADD LINK).", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age", + "cohortStatus": "NotActionable", + "cohortText": "are aged 75 to 79 years old" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "Other Setting with no future booking", + "ruleText": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "ruleType": "S" + } + ] + } + ], + "responseId": "8cd0c26a-626d-4e02-9e3e-817c01fd4afa" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_016.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_016.json new file mode 100644 index 000000000..b6aef56b1 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_016.json @@ -0,0 +1,36 @@ +{ + "meta": { + "lastUpdated": "2025-07-29T07:38:36.598629+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "ManagedSettingInfo", + "actionType": "InfoText", + "description": "## If you think this is incorrect\n\nIf you have already had this vaccination or your personal details are wrong, visit our help and support page. (ADD LINK).", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age", + "cohortStatus": "NotActionable", + "cohortText": "are aged 75 to 79 years old" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "Other Setting", + "ruleText": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "ruleType": "S" + } + ] + } + ], + "responseId": "8cd0c26a-626d-4e02-9e3e-817c01fd4afa" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_017.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_017.json new file mode 100644 index 000000000..c7508e1f8 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_017.json @@ -0,0 +1,24 @@ +{ + "meta": { + "lastUpdated": "2025-07-29T13:30:49.717741+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "ManageLocal", + "actionType": "CardWithText", + "description": "##You have an RSV vaccination appointment\n\nContact your healthcare provider to change or cancel your appointment.", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "1693bf53-e5a0-4320-b2cf-dbcd467bbb04" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_018.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_018.json new file mode 100644 index 000000000..710812e5c --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_018.json @@ -0,0 +1,24 @@ +{ + "meta": { + "lastUpdated": "2025-07-29T13:35:47.326787+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "AmendNBS", + "actionType": "ButtonWithAuthLink", + "description": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", + "urlLabel": "Manage your appointment", + "urlLink": "http://www.nhs.uk/book-rsv" + } + ], + "condition": "RSV", + "eligibilityCohorts": [], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "3b13b33f-51d8-45df-b5f2-854950a8724c" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_019.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_019.json new file mode 100644 index 000000000..710812e5c --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_019.json @@ -0,0 +1,24 @@ +{ + "meta": { + "lastUpdated": "2025-07-29T13:35:47.326787+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "AmendNBS", + "actionType": "ButtonWithAuthLink", + "description": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", + "urlLabel": "Manage your appointment", + "urlLink": "http://www.nhs.uk/book-rsv" + } + ], + "condition": "RSV", + "eligibilityCohorts": [], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "3b13b33f-51d8-45df-b5f2-854950a8724c" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_020.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_020.json new file mode 100644 index 000000000..6d3ea76b3 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_020.json @@ -0,0 +1,44 @@ +{ + "meta": { + "lastUpdated": "2025-07-29T14:04:16.063150+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "BookLocal", + "actionType": "InfoText", + "description": "##Getting the vaccine\n\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "urlLabel": "", + "urlLink": "" + }, + { + "actionCode": "BookNBS", + "actionType": "ButtonWithAuthLink", + "description": "", + "urlLabel": "Continue to booking", + "urlLink": "http://www.nhs.uk/book-rsv" + }, + { + "actionCode": "HelpSupportInfo", + "actionType": "InfoText", + "description": "## CONTENT TBC\n\nBlah blah blah.", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_catchup", + "cohortStatus": "Actionable", + "cohortText": "turned 80 after 1st September 2024" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "f0f2023c-3997-44aa-9a3a-35b5c432336c" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_021.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_021.json new file mode 100644 index 000000000..6d3ea76b3 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_021.json @@ -0,0 +1,44 @@ +{ + "meta": { + "lastUpdated": "2025-07-29T14:04:16.063150+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "BookLocal", + "actionType": "InfoText", + "description": "##Getting the vaccine\n\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "urlLabel": "", + "urlLink": "" + }, + { + "actionCode": "BookNBS", + "actionType": "ButtonWithAuthLink", + "description": "", + "urlLabel": "Continue to booking", + "urlLink": "http://www.nhs.uk/book-rsv" + }, + { + "actionCode": "HelpSupportInfo", + "actionType": "InfoText", + "description": "## CONTENT TBC\n\nBlah blah blah.", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_catchup", + "cohortStatus": "Actionable", + "cohortText": "turned 80 after 1st September 2024" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "f0f2023c-3997-44aa-9a3a-35b5c432336c" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_022.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_022.json new file mode 100644 index 000000000..a4f4656b8 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_022.json @@ -0,0 +1,37 @@ +{ + "meta": { + "lastUpdated": "2025-07-29T16:27:41.967804+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "BookLocal", + "actionType": "InfoText", + "description": "##Getting the vaccine\n\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "urlLabel": "", + "urlLink": "" + }, + { + "actionCode": "HelpSupportInfo", + "actionType": "InfoText", + "description": "## CONTENT TBC\n\nBlah blah blah.", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_catchup", + "cohortStatus": "Actionable", + "cohortText": "turned 80 after 1st September 2024" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "ad09ee8c-ee37-4a7f-8fde-22aec6c90d89" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_023.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_023.json new file mode 100644 index 000000000..a4f4656b8 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_023.json @@ -0,0 +1,37 @@ +{ + "meta": { + "lastUpdated": "2025-07-29T16:27:41.967804+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "BookLocal", + "actionType": "InfoText", + "description": "##Getting the vaccine\n\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "urlLabel": "", + "urlLink": "" + }, + { + "actionCode": "HelpSupportInfo", + "actionType": "InfoText", + "description": "## CONTENT TBC\n\nBlah blah blah.", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_catchup", + "cohortStatus": "Actionable", + "cohortText": "turned 80 after 1st September 2024" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "ad09ee8c-ee37-4a7f-8fde-22aec6c90d89" +} diff --git a/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_001.json b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_001.json new file mode 100644 index 000000000..61e943908 --- /dev/null +++ b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_001.json @@ -0,0 +1,44 @@ +{ + "meta": { + "lastUpdated": "2025-07-31T15:33:02.541065+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "BookLocal", + "actionType": "InfoText", + "description": "##Getting the vaccine\n\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "urlLabel": "", + "urlLink": "" + }, + { + "actionCode": "BookNBS", + "actionType": "ButtonWithAuthLink", + "description": "", + "urlLabel": "Continue to booking", + "urlLink": "http://www.nhs.uk/book-rsv" + }, + { + "actionCode": "HelpSupportInfo", + "actionType": "InfoText", + "description": "## CONTENT TBC\n\nBlah blah blah.", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age", + "cohortStatus": "Actionable", + "cohortText": "are aged 75 to 79 years old" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "da7c72bb-0a3e-47c1-a4c6-12a7994c4f31" +} diff --git a/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_002.json b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_002.json new file mode 100644 index 000000000..e75e42133 --- /dev/null +++ b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_002.json @@ -0,0 +1,37 @@ +{ + "meta": { + "lastUpdated": "2025-07-31T15:34:16.758583+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "BookLocal", + "actionType": "InfoText", + "description": "##Getting the vaccine\n\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "urlLabel": "", + "urlLink": "" + }, + { + "actionCode": "HelpSupportInfo", + "actionType": "InfoText", + "description": "## CONTENT TBC\n\nBlah blah blah.", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age", + "cohortStatus": "Actionable", + "cohortText": "are aged 75 to 79 years old" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "04e12c58-86a4-46e9-a6ef-d640eb9a8e94" +} diff --git a/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_003.json b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_003.json new file mode 100644 index 000000000..ff2e37c65 --- /dev/null +++ b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_003.json @@ -0,0 +1,37 @@ +{ + "meta": { + "lastUpdated": "2025-07-31T15:35:30.455708+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "BookLocal", + "actionType": "InfoText", + "description": "##Getting the vaccine\n\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "urlLabel": "", + "urlLink": "" + }, + { + "actionCode": "HelpSupportInfo", + "actionType": "InfoText", + "description": "## CONTENT TBC\n\nBlah blah blah.", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_catchup", + "cohortStatus": "Actionable", + "cohortText": "turned 80 after 1st September 2024" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "ad2f0a17-938a-423f-a2c7-82cfda58ced9" +} diff --git a/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_004.json b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_004.json new file mode 100644 index 000000000..633262788 --- /dev/null +++ b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_004.json @@ -0,0 +1,24 @@ +{ + "meta": { + "lastUpdated": "2025-07-31T15:36:33.935778+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "AmendNBS", + "actionType": "ButtonWithAuthLink", + "description": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", + "urlLabel": "Manage your appointment", + "urlLink": "http://www.nhs.uk/book-rsv" + } + ], + "condition": "RSV", + "eligibilityCohorts": [], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "4fe9ae68-7347-46f1-91c7-5e723a0b0249" +} diff --git a/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_005.json b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_005.json new file mode 100644 index 000000000..af3da5ed0 --- /dev/null +++ b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_005.json @@ -0,0 +1,24 @@ +{ + "meta": { + "lastUpdated": "2025-07-31T15:37:10.340372+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "ManageLocal", + "actionType": "CardWithText", + "description": "##You have an RSV vaccination appointment\n\nContact your healthcare provider to change or cancel your appointment.", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "594ce550-9721-4c1a-8dae-92ecdba2e763" +} diff --git a/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_006.json b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_006.json new file mode 100644 index 000000000..2272fda9b --- /dev/null +++ b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_006.json @@ -0,0 +1,30 @@ +{ + "meta": { + "lastUpdated": "2025-07-31T15:37:46.783236+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "AlreadyVaccinatedInfo", + "actionType": "InfoText", + "description": "## If you think this is incorrect\n\nIf you believe you've not been vaccinated against RSV, speak to your healthcare professional.\n\nFor anything else please see our help and support page. (ADD LINK).", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "Already Vaccinated", + "ruleText": "## You've had your RSV vaccination\n\nWe believe you had your vaccination.", + "ruleType": "S" + } + ] + } + ], + "responseId": "303c446b-738e-493b-8c5f-3ab023750de7" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/sean_demo_01.json b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_007.json similarity index 53% rename from tests/e2e/data/responses/inProgressTestResponses/sean_demo_01.json rename to tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_007.json index 22c46a300..1629b9158 100644 --- a/tests/e2e/data/responses/inProgressTestResponses/sean_demo_01.json +++ b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_007.json @@ -1,6 +1,6 @@ { "meta": { - "lastUpdated": "2025-07-22T08:06:18.119910+00:00" + "lastUpdated": "2025-08-01T07:49:46.208763+00:00" }, "processedSuggestions": [ { @@ -8,21 +8,21 @@ "condition": "RSV", "eligibilityCohorts": [ { - "cohortCode": "rsv_group", + "cohortCode": "rsv_age", "cohortStatus": "NotActionable", - "cohortText": "You are currently in an rsv cohort" + "cohortText": "are aged 75 to 79 years old" } ], "status": "NotActionable", "statusText": "You should have the RSV vaccine", "suitabilityRules": [ { - "ruleCode": "Test S Rule - Vaccinated", - "ruleText": "Test S Rule - Vaccinated", + "ruleCode": "Not Available", + "ruleText": "##RSV vaccinations are not currently available\n\nPlease try again soon.", "ruleType": "S" } ] } ], - "responseId": "abcaf256-ed6a-4ffe-a3ea-de1539a72120" + "responseId": "aa5a5552-f94d-4dcf-a2d0-f888945b1641" } diff --git a/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_008.json b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_008.json new file mode 100644 index 000000000..8c12bd373 --- /dev/null +++ b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_008.json @@ -0,0 +1,37 @@ +{ + "scenario_name": "RSV - Vita Integration - NotActionable - empty suggestions - fallback to static", + "request_headers": { + "nhs-login-nhs-number": "9658219004" + }, + "config_filename": "vita_integration_test_config.json", + "data": [ + { + "NHS_NUMBER": "9658219004", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_75_rolling", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9658219004", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "2", + "POSTCODE": "CB3 8DX", + "POSTCODE_SECTOR": "CB38", + "POSTCODE_OUTCODE": "CB3", + "MSOA": "E02007085", + "LSOA": "E01018223", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + } + ] +} diff --git a/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_009.json b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_009.json new file mode 100644 index 000000000..82fab2ff7 --- /dev/null +++ b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_009.json @@ -0,0 +1,28 @@ +{ + "meta": { + "lastUpdated": "2025-08-01T07:56:59.488641+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age", + "cohortStatus": "NotActionable", + "cohortText": "are aged 75 to 79 years old" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "NotYetDue", + "ruleText": "##Your RSV vaccination is not yet due\\n\\nYour next dose will be due in 3 months.", + "ruleType": "S" + } + ] + } + ], + "responseId": "7399cb75-afd1-4861-94d4-63c8f615db61" +} diff --git a/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_011.json b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_011.json new file mode 100644 index 000000000..698ff9861 --- /dev/null +++ b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_011.json @@ -0,0 +1,28 @@ +{ + "meta": { + "lastUpdated": "2025-08-01T08:09:38.885621+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age", + "cohortStatus": "NotActionable", + "cohortText": "are aged 75 to 79 years old" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "TooClose", + "ruleText": "##You have recently have the RSV vaccination\n\nYou must leave 90 days between doses of the RSV vaccine. Please try again soon.", + "ruleType": "S" + } + ] + } + ], + "responseId": "ef603f99-9a14-4f5e-8f54-d071201d478e" +} diff --git a/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_012.json b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_012.json new file mode 100644 index 000000000..5ca38ff4b --- /dev/null +++ b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_012.json @@ -0,0 +1,36 @@ +{ + "meta": { + "lastUpdated": "2025-08-01T08:10:25.890939+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "ManagedSettingInfo", + "actionType": "InfoText", + "description": "## If you think this is incorrect\n\nIf you have already had this vaccination or your personal details are wrong, visit our help and support page. (ADD LINK).", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age", + "cohortStatus": "NotActionable", + "cohortText": "are aged 75 to 79 years old" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "Other Setting", + "ruleText": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "ruleType": "S" + } + ] + } + ], + "responseId": "2effe07d-747b-4288-84ef-9b7770dc7430" +} diff --git a/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_013.json b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_013.json new file mode 100644 index 000000000..03870ebd2 --- /dev/null +++ b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_013.json @@ -0,0 +1,30 @@ +{ + "meta": { + "lastUpdated": "2025-08-01T08:11:38.412584+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "AlreadyVaccinatedInfo", + "actionType": "InfoText", + "description": "## If you think this is incorrect\n\nIf you believe you've not been vaccinated against RSV, speak to your healthcare professional.\n\nFor anything else please see our help and support page. (ADD LINK).", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "Already Vaccinated", + "ruleText": "## You've had your RSV vaccination\n\nWe believe you had your vaccination.", + "ruleType": "S" + } + ] + } + ], + "responseId": "970bede3-4428-4456-9f4f-f1bf73c16953" +} diff --git a/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_014.json b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_014.json new file mode 100644 index 000000000..03870ebd2 --- /dev/null +++ b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_014.json @@ -0,0 +1,30 @@ +{ + "meta": { + "lastUpdated": "2025-08-01T08:11:38.412584+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "AlreadyVaccinatedInfo", + "actionType": "InfoText", + "description": "## If you think this is incorrect\n\nIf you believe you've not been vaccinated against RSV, speak to your healthcare professional.\n\nFor anything else please see our help and support page. (ADD LINK).", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "Already Vaccinated", + "ruleText": "## You've had your RSV vaccination\n\nWe believe you had your vaccination.", + "ruleType": "S" + } + ] + } + ], + "responseId": "970bede3-4428-4456-9f4f-f1bf73c16953" +} diff --git a/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_015.json b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_015.json new file mode 100644 index 000000000..8c1d56072 --- /dev/null +++ b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_015.json @@ -0,0 +1,35 @@ +{ + "meta": { + "lastUpdated": "2025-08-01T08:12:24.020848+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "HealthcareProInfo", + "actionType": "InfoText", + "description": "## If you think this is incorrect\n\nSpeak to your healthcare professional if you think you should be offered this vaccine.\n\nFor anything else, visit our help and support page. (ADD LINK)", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age", + "cohortStatus": "NotEligible", + "cohortText": "are not aged 75 to 79 years old" + }, + { + "cohortCode": "rsv_age_catchup", + "cohortStatus": "NotEligible", + "cohortText": "did not turn 80 after 1 September 2024" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "b79bdab6-ce92-4bea-90c4-eabf3b4adc90" +} diff --git a/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_500.json b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_500.json new file mode 100644 index 000000000..c6b5dcd75 --- /dev/null +++ b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_500.json @@ -0,0 +1,23 @@ +{ + "resourceType": "OperationOutcome", + "id": "a4639b8e-4359-4f17-8016-d325846bbc2a", + "meta": { + "lastUpdated": "2025-08-01T08:16:47.379654Z" + }, + "issue": [ + { + "severity": "error", + "code": "processing", + "details": { + "coding": [ + { + "system": "https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1", + "code": "INTERNAL_SERVER_ERROR", + "display": "An unexpected internal server error occurred." + } + ] + }, + "diagnostics": "An unexpected error occurred." + } + ] +} diff --git a/tests/e2e/tests/conftest.py b/tests/e2e/tests/conftest.py index 975ab8b20..18c545b31 100644 --- a/tests/e2e/tests/conftest.py +++ b/tests/e2e/tests/conftest.py @@ -16,7 +16,6 @@ # Constants BASE_URL = os.getenv("BASE_URL", "https://test.eligibility-signposting-api.nhs.uk/patient-check") API_KEY = os.getenv("API_KEY", "") -VALID_NHS_NUMBER = os.getenv("VALID_NHS_NUMBER", "50000000004") DYNAMODB_TABLE_NAME = os.getenv("DYNAMODB_TABLE_NAME", "eligibility_data_store") AWS_REGION = os.getenv("AWS_REGION", "eu-west-2") @@ -26,63 +25,6 @@ logger = logging.getLogger(__name__) - -def pytest_addoption(parser): - parser.addoption("--keep-seed", action="store_true", default=False, help="Keep DynamoDB seed data after tests") - - -@pytest.fixture(scope="session", autouse=False) -def setup_dynamodb_data(request): - """Insert test data into DynamoDB before tests and optionally clean up after.""" - logger.info("[โš™] Connecting to DynamoDB table: %s in region %s", DYNAMODB_TABLE_NAME, AWS_REGION) - logger.info("[TEST] DynamoDB fixture executing โ€” REGION: %s, TABLE: %s", AWS_REGION, DYNAMODB_TABLE_NAME) - logger.info("[TEST] Seed file path: %s โ†’ Exists: %s", DYNAMO_DATA_PATH, DYNAMO_DATA_PATH.exists()) - import botocore.exceptions - - try: - dynamodb = boto3.resource("dynamodb", region_name=AWS_REGION) - table = dynamodb.Table(DYNAMODB_TABLE_NAME) - _ = table.table_status # Force connection check - except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: - pytest.skip(f"[x] DynamoDB not accessible: {e}") - - if not DYNAMO_DATA_PATH.exists(): - pytest.skip(f"[x] Test data file not found: {DYNAMO_DATA_PATH}") - else: - logger.info("[โœ“] Found test data file: %s", DYNAMO_DATA_PATH) - - with DYNAMO_DATA_PATH.open() as f: - items = json.load(f) - - logger.info("[โ†’] Inserting %d items into DynamoDB...", len(items)) - success_count = 0 - for item in items: - try: - table.put_item(Item=item) - success_count += 1 - except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError): - logger.exception("[x] Failed to insert item %s due to BotoCoreError", item.get("PK", "")) - - logger.info("[โœ“] Inserted %d/%d items", success_count, len(items)) - - yield - - # Handle tear-down based on --keep-seed flag - if request.config.getoption("--keep-seed"): - logger.info("[โ†ฉ] Skipping DynamoDB cleanup due to --keep-seed flag") - return - - logger.info("[๐Ÿงน] Deleting seeded items from DynamoDB...") - delete_count = 0 - for item in items: - try: - table.delete_item(Key={"PK": item["PK"], "SK": item["SK"]}) - delete_count += 1 - except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError): - logger.exception("[x] Failed to delete item %s", item.get("PK", "")) - logger.info("[โœ“] Deleted %d/%d items", delete_count, len(items)) - - @pytest.fixture(scope="session") def eligibility_client(): return EligibilityApiClient(BASE_URL, cert_dir="certs") diff --git a/tests/e2e/tests/test_config.py b/tests/e2e/tests/test_config.py index cab8a01e9..16f2035f3 100644 --- a/tests/e2e/tests/test_config.py +++ b/tests/e2e/tests/test_config.py @@ -11,9 +11,12 @@ REGRESSION_RESPONSES: str = "data/responses/regressionTestResponses/" REGRESSION_CONFIGS: str = "data/configs/regressionTestConfigs" -# Story Test Data Paths +# In Progress Test Data Paths IN_PROGRESS_TEST_DATA: str = "data/dynamoDB/inProgressTestData/" IN_PROGRESS_RESPONSES: str = "data/responses/inProgressTestResponses/" IN_PROGRESS_CONFIGS: str = "data/configs/inProgressTestConfigs" -INTEGRATION_TEST_DATA: str = "data/dynamoDB/integrationTestData/" +# Vita Integration Test Data Paths +VITA_INTEGRATION_TEST_DATA: str = "data/dynamoDB/vitaIntegrationTestData/" +VITA_INTEGRATION_RESPONSES: str = "data/responses/vitaIntegrationTestResponses/" +VITA_INTEGRATION_CONFIGS: str = "data/configs/vitaIntegrationTestConfigs" From 16bb8796b611b2808ee99cf12f5814cac15df5d7 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Tue, 5 Aug 2025 08:36:06 +0100 Subject: [PATCH 62/72] updated configs for 311 for. added ability to assert the response code in the tests --- poetry.lock | 673 +++++++++++++++++- pyproject.toml | 4 + .../AUTO_RSV_REG_001.json | 10 +- .../.temp/AUTO_RSV_SB_001.json | 10 +- .../.temp/AUTO_RSV_SB_008.json | 10 +- .../smokeTestConfigs/AUTO_RSV_SB_001.json | 10 +- .../smokeTestConfigs/AUTO_RSV_SB_008.json | 10 +- .../storyTestConfigs/AUTO_RSV_ELI-155.json | 10 +- .../storyTestConfigs/AUTO_RSV_ELI-216.json | 10 +- .../storyTestConfigs/AUTO_RSV_ELI-219-1.json | 10 +- .../storyTestConfigs/AUTO_RSV_ELI-219-2.json | 10 +- .../storyTestConfigs/AUTO_RSV_ELI-219-3.json | 10 +- .../storyTestConfigs/AUTO_RSV_ELI-221-01.json | 10 +- .../storyTestConfigs/AUTO_RSV_ELI-221-02.json | 10 +- .../storyTestConfigs/AUTO_RSV_ELI-221-03.json | 10 +- .../storyTestConfigs/AUTO_RSV_ELI-222.json | 10 +- .../storyTestConfigs/AUTO_RSV_ELI-236.json | 10 +- .../storyTestConfigs/AUTO_RSV_ELI-295-1.json | 10 +- .../storyTestConfigs/AUTO_RSV_ELI-295-2.json | 8 +- .../storyTestConfigs/AUTO_RSV_ELI-295-3.json | 10 +- .../storyTestConfigs/AUTO_RSV_ELI-295-4.json | 10 +- .../storyTestConfigs/AUTO_RSV_ELI-317-1.json | 10 +- .../storyTestConfigs/AUTO_RSV_ELI-317-2.json | 10 +- .../storyTestConfigs/AUTO_RSV_ELI-317-3.json | 10 +- .../storyTestConfigs/AUTO_RSV_ELI-317-4.json | 10 +- .../storyTestConfigs/AUTO_RSV_ELI-317-5.json | 10 +- .../AUTO_RSV_ELI-320-COVID.json | 10 +- .../AUTO_RSV_ELI-320-MMR.json | 10 +- .../AUTO_RSV_ELI-320-RSV.json | 10 +- .../AUTO_RSV_ELI-320-SCREENING-1.json | 10 +- .../AUTO_RSV_ELI-320-SCREENING-2.json | 10 +- .../storyTestConfigs/AUTO_RSV_ELI-365.json | 10 +- .../vita_integration_test_config.json | 10 +- .../smokeTestData/AUTO_RSV_SB_010.json | 2 +- .../AUTO_RSV_VITA_INT_500.json | 5 +- tests/e2e/tests/conftest.py | 25 +- tests/e2e/tests/test_regression_tests.py | 7 +- tests/e2e/tests/test_smoke_tests.py | 7 +- tests/e2e/tests/test_story_tests.py | 5 +- .../e2e/tests/test_vita_integration_tests.py | 35 + 40 files changed, 880 insertions(+), 191 deletions(-) create mode 100644 tests/e2e/tests/test_vita_integration_tests.py diff --git a/poetry.lock b/poetry.lock index 269c8ac2f..963595b81 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -168,6 +168,26 @@ doc = ["Sphinx (>=8.2,<9.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", test = ["anyio[trio]", "blockbuster (>=1.5.23)", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1) ; python_version >= \"3.10\"", "uvloop (>=0.21) ; platform_python_implementation == \"CPython\" and platform_system != \"Windows\" and python_version < \"3.14\""] trio = ["trio (>=0.26.1)"] +[[package]] +name = "arrow" +version = "1.3.0" +description = "Better dates & times for Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "arrow-1.3.0-py3-none-any.whl", hash = "sha256:c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80"}, + {file = "arrow-1.3.0.tar.gz", hash = "sha256:d4540617648cb5f895730f1ad8c82a65f2dad0166f57b75f3ca54759c4d67a85"}, +] + +[package.dependencies] +python-dateutil = ">=2.7.0" +types-python-dateutil = ">=2.8.10" + +[package.extras] +doc = ["doc8", "sphinx (>=7.0.0)", "sphinx-autobuild", "sphinx-autodoc-typehints", "sphinx_rtd_theme (>=1.3.0)"] +test = ["dateparser (==1.*)", "pre-commit", "pytest", "pytest-cov", "pytest-mock", "pytz (==2021.1)", "simplejson (==3.*)"] + [[package]] name = "asgiref" version = "3.8.1" @@ -189,7 +209,7 @@ version = "25.3.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.8" -groups = ["dev"] +groups = ["main", "dev"] files = [ {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"}, {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"}, @@ -255,6 +275,18 @@ localstack-client = "*" [package.extras] ver1 = ["awscli"] +[[package]] +name = "backoff" +version = "2.2.1" +description = "Function decoration for backoff and retry" +optional = false +python-versions = ">=3.7,<4.0" +groups = ["main"] +files = [ + {file = "backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8"}, + {file = "backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba"}, +] + [[package]] name = "beautifulsoup4" version = "4.13.4" @@ -515,13 +547,25 @@ files = [ [package.dependencies] pycparser = "*" +[[package]] +name = "chardet" +version = "5.2.0" +description = "Universal encoding detector for Python 3" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"}, + {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, +] + [[package]] name = "charset-normalizer" version = "3.4.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" -groups = ["dev"] +groups = ["main", "dev"] files = [ {file = "charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941"}, {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd"}, @@ -643,7 +687,6 @@ files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -markers = {main = "platform_system == \"Windows\""} [[package]] name = "coverage" @@ -982,6 +1025,18 @@ werkzeug = ">=3.1.0" async = ["asgiref (>=3.2)"] dotenv = ["python-dotenv"] +[[package]] +name = "fqdn" +version = "1.5.1" +description = "Validates fully-qualified domain names against RFC 1123, so that they are acceptable to modern bowsers" +optional = false +python-versions = ">=2.7, !=3.0, !=3.1, !=3.2, !=3.3, !=3.4, <4" +groups = ["main"] +files = [ + {file = "fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014"}, + {file = "fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f"}, +] + [[package]] name = "freezegun" version = "1.5.2" @@ -1161,6 +1216,18 @@ gitdb = ">=4.0.1,<5" doc = ["sphinx (>=7.1.2,<7.2)", "sphinx-autodoc-typehints", "sphinx_rtd_theme"] test = ["coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock ; python_version < \"3.8\"", "mypy", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar", "typing-extensions ; python_version < \"3.11\""] +[[package]] +name = "graphql-core" +version = "3.2.6" +description = "GraphQL implementation for Python, a port of GraphQL.js, the JavaScript reference implementation for GraphQL." +optional = false +python-versions = "<4,>=3.6" +groups = ["main"] +files = [ + {file = "graphql_core-3.2.6-py3-none-any.whl", hash = "sha256:78b016718c161a6fb20a7d97bbf107f331cd1afe53e45566c59f776ed7f0b45f"}, + {file = "graphql_core-3.2.6.tar.gz", hash = "sha256:c08eec22f9e40f0bd61d805907e3b3b1b9a320bc606e23dc145eebca07c8fbab"}, +] + [[package]] name = "h11" version = "0.16.0" @@ -1173,6 +1240,24 @@ files = [ {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, ] +[[package]] +name = "harfile" +version = "0.3.0" +description = "Writer for HTTP Archive (HAR) files" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "harfile-0.3.0-py3-none-any.whl", hash = "sha256:ac11177e06c88c9553c8c73c16ab20428a176d1d2ebe00b41ce527ff0bdc47e6"}, + {file = "harfile-0.3.0.tar.gz", hash = "sha256:23be8037e1296bb4787a15543a37835ed91f408c8296988f9ba022a44accad9e"}, +] + +[package.extras] +bench = ["pytest-codspeed (==2.2.1)"] +cov = ["coverage-enable-subprocess", "coverage[toml] (>=7)"] +dev = ["coverage (>=7)", "coverage-enable-subprocess", "coverage[toml] (>=7)", "hypothesis (>=6)", "hypothesis-jsonschema (>=0.23.1)", "jsonschema (>=4.18.0)", "pytest (>=6.2.0,<8)", "pytest-codspeed (==2.2.1)"] +tests = ["coverage (>=7)", "hypothesis (>=6)", "hypothesis-jsonschema (>=0.23.1)", "jsonschema (>=4.18.0)", "pytest (>=6.2.0,<8)"] + [[package]] name = "httpcore" version = "1.0.9" @@ -1220,6 +1305,77 @@ http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] zstd = ["zstandard (>=0.18.0)"] +[[package]] +name = "hypothesis" +version = "6.137.1" +description = "A library for property-based testing" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "hypothesis-6.137.1-py3-none-any.whl", hash = "sha256:7cbda6a98ed4d32aad31a5fc5bff5e119b9275fe2579a7b08863cba313a4b9be"}, + {file = "hypothesis-6.137.1.tar.gz", hash = "sha256:b086e644456da79ad460fdaf8fbf90a41a661e8a4076232dd4ea64cfbc0d0529"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +sortedcontainers = ">=2.1.0,<3.0.0" + +[package.extras] +all = ["black (>=19.10b0)", "click (>=7.0)", "crosshair-tool (>=0.0.93)", "django (>=4.2)", "dpcontracts (>=0.4)", "hypothesis-crosshair (>=0.0.24)", "lark (>=0.10.1)", "libcst (>=0.3.16)", "numpy (>=1.19.3)", "pandas (>=1.1)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "rich (>=9.0.0)", "tzdata (>=2025.2) ; sys_platform == \"win32\" or sys_platform == \"emscripten\"", "watchdog (>=4.0.0)"] +cli = ["black (>=19.10b0)", "click (>=7.0)", "rich (>=9.0.0)"] +codemods = ["libcst (>=0.3.16)"] +crosshair = ["crosshair-tool (>=0.0.93)", "hypothesis-crosshair (>=0.0.24)"] +dateutil = ["python-dateutil (>=1.4)"] +django = ["django (>=4.2)"] +dpcontracts = ["dpcontracts (>=0.4)"] +ghostwriter = ["black (>=19.10b0)"] +lark = ["lark (>=0.10.1)"] +numpy = ["numpy (>=1.19.3)"] +pandas = ["pandas (>=1.1)"] +pytest = ["pytest (>=4.6)"] +pytz = ["pytz (>=2014.1)"] +redis = ["redis (>=3.0.0)"] +watchdog = ["watchdog (>=4.0.0)"] +zoneinfo = ["tzdata (>=2025.2) ; sys_platform == \"win32\" or sys_platform == \"emscripten\""] + +[[package]] +name = "hypothesis-graphql" +version = "0.11.1" +description = "Hypothesis strategies for GraphQL queries" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "hypothesis_graphql-0.11.1-py3-none-any.whl", hash = "sha256:a6968f703bcdc31fbe1b26be69185aa2c824eb3b478057a66aa85967c81cadca"}, + {file = "hypothesis_graphql-0.11.1.tar.gz", hash = "sha256:bd49ab6804a3f488ecab2e39c20dba6dfc2101525c6742f5831cfa9eff95285a"}, +] + +[package.dependencies] +graphql-core = ">=3.1.0,<3.3.0" +hypothesis = ">=6.84.3,<7.0" + +[package.extras] +cov = ["coverage-enable-subprocess", "coverage[toml] (>=7)"] +dev = ["coverage (>=7)", "coverage-enable-subprocess", "coverage[toml] (>=7)", "pytest (>=6.2.0,<8)", "pytest-xdist (>=2.5,<3.0)"] +tests = ["coverage (>=7)", "pytest (>=6.2.0,<8)", "pytest-xdist (>=2.5,<3.0)"] + +[[package]] +name = "hypothesis-jsonschema" +version = "0.23.1" +description = "Generate test data from JSON schemata with Hypothesis" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "hypothesis-jsonschema-0.23.1.tar.gz", hash = "sha256:f4ac032024342a4149a10253984f5a5736b82b3fe2afb0888f3834a31153f215"}, + {file = "hypothesis_jsonschema-0.23.1-py3-none-any.whl", hash = "sha256:a4d74d9516dd2784fbbae82e009f62486c9104ac6f4e3397091d98a1d5ee94a2"}, +] + +[package.dependencies] +hypothesis = ">=6.84.3" +jsonschema = ">=4.18.0" + [[package]] name = "idna" version = "3.10" @@ -1241,12 +1397,27 @@ version = "2.1.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.8" -groups = ["dev"] +groups = ["main", "dev"] files = [ {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, ] +[[package]] +name = "isoduration" +version = "20.11.0" +description = "Operations with ISO 8601 durations" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "isoduration-20.11.0-py3-none-any.whl", hash = "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042"}, + {file = "isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9"}, +] + +[package.dependencies] +arrow = ">=0.15.0" + [[package]] name = "itsdangerous" version = "2.2.0" @@ -1305,27 +1476,65 @@ decorator = "*" ply = "*" six = "*" +[[package]] +name = "jsonpointer" +version = "3.0.0" +description = "Identify specific nodes in a JSON document (RFC 6901)" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942"}, + {file = "jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef"}, +] + [[package]] name = "jsonschema" -version = "4.24.0" +version = "4.25.0" description = "An implementation of JSON Schema validation for Python" optional = false python-versions = ">=3.9" -groups = ["dev"] +groups = ["main", "dev"] files = [ - {file = "jsonschema-4.24.0-py3-none-any.whl", hash = "sha256:a462455f19f5faf404a7902952b6f0e3ce868f3ee09a359b05eca6673bd8412d"}, - {file = "jsonschema-4.24.0.tar.gz", hash = "sha256:0b4e8069eb12aedfa881333004bccaec24ecef5a8a6a4b6df142b2cc9599d196"}, + {file = "jsonschema-4.25.0-py3-none-any.whl", hash = "sha256:24c2e8da302de79c8b9382fee3e76b355e44d2a4364bb207159ce10b517bd716"}, + {file = "jsonschema-4.25.0.tar.gz", hash = "sha256:e63acf5c11762c0e6672ffb61482bdf57f0876684d8d249c0fe2d730d48bc55f"}, ] [package.dependencies] attrs = ">=22.2.0" +fqdn = {version = "*", optional = true, markers = "extra == \"format\""} +idna = {version = "*", optional = true, markers = "extra == \"format\""} +isoduration = {version = "*", optional = true, markers = "extra == \"format\""} +jsonpointer = {version = ">1.13", optional = true, markers = "extra == \"format\""} jsonschema-specifications = ">=2023.03.6" referencing = ">=0.28.4" +rfc3339-validator = {version = "*", optional = true, markers = "extra == \"format\""} +rfc3987 = {version = "*", optional = true, markers = "extra == \"format\""} rpds-py = ">=0.7.1" +uri-template = {version = "*", optional = true, markers = "extra == \"format\""} +webcolors = {version = ">=1.11", optional = true, markers = "extra == \"format\""} [package.extras] format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] -format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=24.6.0)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "rfc3987-syntax (>=1.1.0)", "uri-template", "webcolors (>=24.6.0)"] + +[[package]] +name = "jsonschema-path" +version = "0.3.4" +description = "JSONSchema Spec with object-oriented paths" +optional = false +python-versions = "<4.0.0,>=3.8.0" +groups = ["dev"] +files = [ + {file = "jsonschema_path-0.3.4-py3-none-any.whl", hash = "sha256:f502191fdc2b22050f9a81c9237be9d27145b9001c55842bece5e94e382e52f8"}, + {file = "jsonschema_path-0.3.4.tar.gz", hash = "sha256:8365356039f16cc65fddffafda5f58766e34bebab7d6d105616ab52bc4297001"}, +] + +[package.dependencies] +pathable = ">=0.4.1,<0.5.0" +PyYAML = ">=5.1" +referencing = "<0.37.0" +requests = ">=2.31.0,<3.0.0" [[package]] name = "jsonschema-specifications" @@ -1333,7 +1542,7 @@ version = "2025.4.1" description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" optional = false python-versions = ">=3.9" -groups = ["dev"] +groups = ["main", "dev"] files = [ {file = "jsonschema_specifications-2025.4.1-py3-none-any.whl", hash = "sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af"}, {file = "jsonschema_specifications-2025.4.1.tar.gz", hash = "sha256:630159c9f4dbea161a6a2205c3011cc4f18ff381b189fff48bb39b9bf26ae608"}, @@ -1342,6 +1551,45 @@ files = [ [package.dependencies] referencing = ">=0.31.0" +[[package]] +name = "junit-xml" +version = "1.9" +description = "Creates JUnit XML test result documents that can be read by tools such as Jenkins" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "junit-xml-1.9.tar.gz", hash = "sha256:de16a051990d4e25a3982b2dd9e89d671067548718866416faec14d9de56db9f"}, + {file = "junit_xml-1.9-py2.py3-none-any.whl", hash = "sha256:ec5ca1a55aefdd76d28fcc0b135251d156c7106fa979686a4b48d62b761b4732"}, +] + +[package.dependencies] +six = "*" + +[[package]] +name = "lazy-object-proxy" +version = "1.11.0" +description = "A fast and thorough lazy object proxy." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "lazy_object_proxy-1.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:132bc8a34f2f2d662a851acfd1b93df769992ed1b81e2b1fda7db3e73b0d5a18"}, + {file = "lazy_object_proxy-1.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:01261a3afd8621a1accb5682df2593dc7ec7d21d38f411011a5712dcd418fbed"}, + {file = "lazy_object_proxy-1.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:090935756cc041e191f22f4f9c7fd4fe9a454717067adf5b1bbd2ce3046b556e"}, + {file = "lazy_object_proxy-1.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:76ec715017f06410f57df442c1a8d66e6b5f7035077785b129817f5ae58810a4"}, + {file = "lazy_object_proxy-1.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9a9f39098e93a63618a79eef2889ae3cf0605f676cd4797fdfd49fcd7ddc318b"}, + {file = "lazy_object_proxy-1.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:ee13f67f4fcd044ef27bfccb1c93d39c100046fec1fad6e9a1fcdfd17492aeb3"}, + {file = "lazy_object_proxy-1.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fd4c84eafd8dd15ea16f7d580758bc5c2ce1f752faec877bb2b1f9f827c329cd"}, + {file = "lazy_object_proxy-1.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:d2503427bda552d3aefcac92f81d9e7ca631e680a2268cbe62cd6a58de6409b7"}, + {file = "lazy_object_proxy-1.11.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0613116156801ab3fccb9e2b05ed83b08ea08c2517fdc6c6bc0d4697a1a376e3"}, + {file = "lazy_object_proxy-1.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:bb03c507d96b65f617a6337dedd604399d35face2cdf01526b913fb50c4cb6e8"}, + {file = "lazy_object_proxy-1.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28c174db37946f94b97a97b579932ff88f07b8d73a46b6b93322b9ac06794a3b"}, + {file = "lazy_object_proxy-1.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:d662f0669e27704495ff1f647070eb8816931231c44e583f4d0701b7adf6272f"}, + {file = "lazy_object_proxy-1.11.0-py3-none-any.whl", hash = "sha256:a56a5093d433341ff7da0e89f9b486031ccd222ec8e52ec84d0ec1cdc819674b"}, + {file = "lazy_object_proxy-1.11.0.tar.gz", hash = "sha256:18874411864c9fbbbaa47f9fc1dd7aea754c86cfde21278ef427639d1dd78e9c"}, +] + [[package]] name = "localstack" version = "4.5.0" @@ -1531,11 +1779,8 @@ files = [ {file = "lxml-5.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:7ce1a171ec325192c6a636b64c94418e71a1964f56d002cc28122fceff0b6121"}, {file = "lxml-5.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:795f61bcaf8770e1b37eec24edf9771b307df3af74d1d6f27d812e15a9ff3872"}, {file = "lxml-5.4.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:29f451a4b614a7b5b6c2e043d7b64a15bd8304d7e767055e8ab68387a8cacf4e"}, - {file = "lxml-5.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:891f7f991a68d20c75cb13c5c9142b2a3f9eb161f1f12a9489c82172d1f133c0"}, {file = "lxml-5.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4aa412a82e460571fad592d0f93ce9935a20090029ba08eca05c614f99b0cc92"}, - {file = "lxml-5.4.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:ac7ba71f9561cd7d7b55e1ea5511543c0282e2b6450f122672a2694621d63b7e"}, {file = "lxml-5.4.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:c5d32f5284012deaccd37da1e2cd42f081feaa76981f0eaa474351b68df813c5"}, - {file = "lxml-5.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:ce31158630a6ac85bddd6b830cffd46085ff90498b397bd0a259f59d27a12188"}, {file = "lxml-5.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:31e63621e073e04697c1b2d23fcb89991790eef370ec37ce4d5d469f40924ed6"}, {file = "lxml-5.4.0-cp37-cp37m-win32.whl", hash = "sha256:be2ba4c3c5b7900246a8f866580700ef0d538f2ca32535e991027bdaba944063"}, {file = "lxml-5.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:09846782b1ef650b321484ad429217f5154da4d6e786636c38e434fa32e94e49"}, @@ -1615,7 +1860,7 @@ version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = false python-versions = ">=3.8" -groups = ["dev"] +groups = ["main", "dev"] files = [ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, @@ -1711,7 +1956,7 @@ version = "0.1.2" description = "Markdown URL utilities" optional = false python-versions = ">=3.7" -groups = ["dev"] +groups = ["main", "dev"] files = [ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, @@ -1895,6 +2140,41 @@ files = [ {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, ] +[[package]] +name = "openapi-schema-validator" +version = "0.6.3" +description = "OpenAPI schema validation for Python" +optional = false +python-versions = "<4.0.0,>=3.8.0" +groups = ["dev"] +files = [ + {file = "openapi_schema_validator-0.6.3-py3-none-any.whl", hash = "sha256:f3b9870f4e556b5a62a1c39da72a6b4b16f3ad9c73dc80084b1b11e74ba148a3"}, + {file = "openapi_schema_validator-0.6.3.tar.gz", hash = "sha256:f37bace4fc2a5d96692f4f8b31dc0f8d7400fd04f3a937798eaf880d425de6ee"}, +] + +[package.dependencies] +jsonschema = ">=4.19.1,<5.0.0" +jsonschema-specifications = ">=2023.5.2" +rfc3339-validator = "*" + +[[package]] +name = "openapi-spec-validator" +version = "0.7.2" +description = "OpenAPI 2.0 (aka Swagger) and OpenAPI 3 spec validator" +optional = false +python-versions = "<4.0.0,>=3.8.0" +groups = ["dev"] +files = [ + {file = "openapi_spec_validator-0.7.2-py3-none-any.whl", hash = "sha256:4bbdc0894ec85f1d1bea1d6d9c8b2c3c8d7ccaa13577ef40da9c006c9fd0eb60"}, + {file = "openapi_spec_validator-0.7.2.tar.gz", hash = "sha256:cc029309b5c5dbc7859df0372d55e9d1ff43e96d678b9ba087f7c56fc586f734"}, +] + +[package.dependencies] +jsonschema = ">=4.18.0,<5.0.0" +jsonschema-path = ">=0.3.1,<0.4.0" +lazy-object-proxy = ">=1.7.1,<2.0.0" +openapi-schema-validator = ">=0.6.0,<0.7.0" + [[package]] name = "orderedmultidict" version = "1.0.1" @@ -1916,7 +2196,7 @@ version = "25.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" -groups = ["dev"] +groups = ["main", "dev"] files = [ {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, @@ -1955,13 +2235,25 @@ develop = ["build (>=0.5.1)", "coverage (>=4.4)", "pylint", "pytest (<5.0) ; pyt docs = ["Sphinx (>=1.6)", "sphinx-bootstrap-theme (>=0.6.0)"] testing = ["pytest (<5.0) ; python_version < \"3.0\"", "pytest (>=5.0) ; python_version >= \"3.0\"", "pytest-html (>=1.19.0)"] +[[package]] +name = "pathable" +version = "0.4.4" +description = "Object-oriented paths" +optional = false +python-versions = "<4.0.0,>=3.7.0" +groups = ["dev"] +files = [ + {file = "pathable-0.4.4-py3-none-any.whl", hash = "sha256:5ae9e94793b6ef5a4cbe0a7ce9dbbefc1eec38df253763fd0aeeacf2762dbbc2"}, + {file = "pathable-0.4.4.tar.gz", hash = "sha256:6905a3cd17804edfac7875b5f6c9142a218c7caef78693c2dbbbfbac186d88b2"}, +] + [[package]] name = "pluggy" version = "1.6.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.9" -groups = ["dev"] +groups = ["main", "dev"] files = [ {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, @@ -2023,6 +2315,32 @@ odmantic = ["odmantic (<1.0.0)", "pydantic[email]"] pydantic = ["pydantic[email] (>=1.10)"] sqlalchemy = ["sqlalchemy (>=1.4.29)"] +[[package]] +name = "prance" +version = "25.4.8.0" +description = "Resolving Swagger/OpenAPI 2.0 and 3.0.0 Parser" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "prance-25.4.8.0-py3-none-any.whl", hash = "sha256:d3c362036d625b12aeee495621cb1555fd50b2af3632af3d825176bfb50e073b"}, + {file = "prance-25.4.8.0.tar.gz", hash = "sha256:2f72d2983d0474b6f53fd604eb21690c1ebdb00d79a6331b7ec95fb4f25a1f65"}, +] + +[package.dependencies] +chardet = ">=5.2" +packaging = ">=24.2" +requests = ">=2.32.3" +"ruamel.yaml" = ">=0.18.10" + +[package.extras] +cli = ["click (>=8.1.8)"] +dev = ["bumpversion (>=0.6.0)", "pytest (>=8.3.5)", "pytest-cov (>=6.0)", "sphinx (>=8.1.3)", "towncrier (>=24.8)", "tox (>=4.23.2)"] +flex = ["flex (>=6.14.1,<6.15.0)"] +icu = ["PyICU (>=2.14,<3.0)"] +osv = ["openapi-spec-validator (>=0.7.1,<0.8.0)"] +ssv = ["swagger-spec-validator (>=3.0.4,<3.1.0)"] + [[package]] name = "propcache" version = "0.3.2" @@ -2395,7 +2713,7 @@ version = "2.19.1" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" -groups = ["dev"] +groups = ["main", "dev"] files = [ {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, @@ -2470,6 +2788,22 @@ files = [ {file = "pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8"}, ] +[[package]] +name = "pyrate-limiter" +version = "3.9.0" +description = "Python Rate-Limiter using Leaky-Bucket Algorithm" +optional = false +python-versions = "<4.0,>=3.8" +groups = ["main"] +files = [ + {file = "pyrate_limiter-3.9.0-py3-none-any.whl", hash = "sha256:77357840c8cf97a36d67005d4e090787043f54000c12c2b414ff65657653e378"}, + {file = "pyrate_limiter-3.9.0.tar.gz", hash = "sha256:6b882e2c77cda07a241d3730975daea4258344b39c878f1dd8849df73f70b0ce"}, +] + +[package.extras] +all = ["filelock (>=3.0)", "psycopg[pool] (>=3.1.18,<4.0.0)", "redis (>=5.0.0,<6.0.0)"] +docs = ["furo (>=2022.3.4,<2023.0.0)", "myst-parser (>=0.17)", "sphinx (>=4.3.0,<5.0.0)", "sphinx-autodoc-typehints (>=1.17,<2.0)", "sphinx-copybutton (>=0.5)", "sphinxcontrib-apidoc (>=0.3,<0.4)"] + [[package]] name = "pyright" version = "1.1.402" @@ -2497,7 +2831,7 @@ version = "8.4.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.9" -groups = ["dev"] +groups = ["main", "dev"] files = [ {file = "pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7"}, {file = "pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c"}, @@ -2615,6 +2949,22 @@ toml = ">=0.10.2,<0.11.0" typing-extensions = ">=4.11.0,<5.0.0" wheel = ">=0.37.1,<0.39.0" +[[package]] +name = "pytest-subtests" +version = "0.14.2" +description = "unittest subTest() support and subtests fixture" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pytest_subtests-0.14.2-py3-none-any.whl", hash = "sha256:8da0787c994ab372a13a0ad7d390533ad2e4385cac167b3ac501258c885d0b66"}, + {file = "pytest_subtests-0.14.2.tar.gz", hash = "sha256:7154a8665fd528ee70a76d00216a44d139dc3c9c83521a0f779f7b0ad4f800de"}, +] + +[package.dependencies] +attrs = ">=19.2.0" +pytest = ">=7.4" + [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -2666,7 +3016,7 @@ version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" -groups = ["dev"] +groups = ["main", "dev"] files = [ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, @@ -2729,7 +3079,7 @@ version = "0.36.2" description = "JSON Referencing + Python" optional = false python-versions = ">=3.9" -groups = ["dev"] +groups = ["main", "dev"] files = [ {file = "referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0"}, {file = "referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa"}, @@ -2745,7 +3095,7 @@ version = "2.32.4" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" -groups = ["dev"] +groups = ["main", "dev"] files = [ {file = "requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c"}, {file = "requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422"}, @@ -2781,13 +3131,40 @@ urllib3 = ">=1.25.10,<3.0" [package.extras] tests = ["coverage (>=6.0.0)", "flake8", "mypy", "pytest (>=7.0.0)", "pytest-asyncio", "pytest-cov", "pytest-httpserver", "tomli ; python_version < \"3.11\"", "tomli-w", "types-PyYAML", "types-requests"] +[[package]] +name = "rfc3339-validator" +version = "0.1.4" +description = "A pure python RFC3339 validator" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +groups = ["main", "dev"] +files = [ + {file = "rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa"}, + {file = "rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b"}, +] + +[package.dependencies] +six = "*" + +[[package]] +name = "rfc3987" +version = "1.3.8" +description = "Parsing and validation of URIs (RFC 3986) and IRIs (RFC 3987)" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "rfc3987-1.3.8-py2.py3-none-any.whl", hash = "sha256:10702b1e51e5658843460b189b185c0366d2cf4cff716f13111b0ea9fd2dce53"}, + {file = "rfc3987-1.3.8.tar.gz", hash = "sha256:d3c4d257a560d544e9826b38bc81db676890c79ab9d7ac92b39c7a253d5ca733"}, +] + [[package]] name = "rich" version = "14.0.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.8.0" -groups = ["dev"] +groups = ["main", "dev"] files = [ {file = "rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0"}, {file = "rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725"}, @@ -2806,7 +3183,7 @@ version = "0.25.1" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.9" -groups = ["dev"] +groups = ["main", "dev"] files = [ {file = "rpds_py-0.25.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f4ad628b5174d5315761b67f212774a32f5bad5e61396d38108bd801c0a8f5d9"}, {file = "rpds_py-0.25.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8c742af695f7525e559c16f1562cf2323db0e3f0fbdcabdf6865b095256b2d40"}, @@ -2942,6 +3319,82 @@ files = [ [package.dependencies] pyasn1 = ">=0.1.3" +[[package]] +name = "ruamel-yaml" +version = "0.18.14" +description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "ruamel.yaml-0.18.14-py3-none-any.whl", hash = "sha256:710ff198bb53da66718c7db27eec4fbcc9aa6ca7204e4c1df2f282b6fe5eb6b2"}, + {file = "ruamel.yaml-0.18.14.tar.gz", hash = "sha256:7227b76aaec364df15936730efbf7d72b30c0b79b1d578bbb8e3dcb2d81f52b7"}, +] + +[package.dependencies] +"ruamel.yaml.clib" = {version = ">=0.2.7", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.14\""} + +[package.extras] +docs = ["mercurial (>5.7)", "ryd"] +jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] + +[[package]] +name = "ruamel-yaml-clib" +version = "0.2.12" +description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" +optional = false +python-versions = ">=3.9" +groups = ["main"] +markers = "platform_python_implementation == \"CPython\" and python_version < \"3.14\"" +files = [ + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:11f891336688faf5156a36293a9c362bdc7c88f03a8a027c2c1d8e0bcde998e5"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a606ef75a60ecf3d924613892cc603b154178ee25abb3055db5062da811fd969"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd5415dded15c3822597455bc02bcd66e81ef8b7a48cb71a33628fc9fdde39df"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f66efbc1caa63c088dead1c4170d148eabc9b80d95fb75b6c92ac0aad2437d76"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:22353049ba4181685023b25b5b51a574bce33e7f51c759371a7422dcae5402a6"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:932205970b9f9991b34f55136be327501903f7c66830e9760a8ffb15b07f05cd"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a52d48f4e7bf9005e8f0a89209bf9a73f7190ddf0489eee5eb51377385f59f2a"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-win32.whl", hash = "sha256:3eac5a91891ceb88138c113f9db04f3cebdae277f5d44eaa3651a4f573e6a5da"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-win_amd64.whl", hash = "sha256:ab007f2f5a87bd08ab1499bdf96f3d5c6ad4dcfa364884cb4549aa0154b13a28"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:4a6679521a58256a90b0d89e03992c15144c5f3858f40d7c18886023d7943db6"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:d84318609196d6bd6da0edfa25cedfbabd8dbde5140a0a23af29ad4b8f91fb1e"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb43a269eb827806502c7c8efb7ae7e9e9d0573257a46e8e952f4d4caba4f31e"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:811ea1594b8a0fb466172c384267a4e5e367298af6b228931f273b111f17ef52"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cf12567a7b565cbf65d438dec6cfbe2917d3c1bdddfce84a9930b7d35ea59642"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7dd5adc8b930b12c8fc5b99e2d535a09889941aa0d0bd06f4749e9a9397c71d2"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1492a6051dab8d912fc2adeef0e8c72216b24d57bd896ea607cb90bb0c4981d3"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-win32.whl", hash = "sha256:bd0a08f0bab19093c54e18a14a10b4322e1eacc5217056f3c063bd2f59853ce4"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-win_amd64.whl", hash = "sha256:a274fb2cb086c7a3dea4322ec27f4cb5cc4b6298adb583ab0e211a4682f241eb"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:943f32bc9dedb3abff9879edc134901df92cfce2c3d5c9348f172f62eb2d771d"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95c3829bb364fdb8e0332c9931ecf57d9be3519241323c5274bd82f709cebc0c"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b82a7c94a498853aa0b272fd5bc67f29008da798d4f93a2f9f289feb8426a58d"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-win32.whl", hash = "sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-win_amd64.whl", hash = "sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4c8c5d82f50bb53986a5e02d1b3092b03622c02c2eb78e29bec33fd9593bae1a"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:e7e3736715fbf53e9be2a79eb4db68e4ed857017344d697e8b9749444ae57475"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7e75b4965e1d4690e93021adfcecccbca7d61c7bddd8e22406ef2ff20d74ef"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96777d473c05ee3e5e3c3e999f5d23c6f4ec5b0c38c098b3a5229085f74236c6"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:3bc2a80e6420ca8b7d3590791e2dfc709c88ab9152c00eeb511c9875ce5778bf"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e188d2699864c11c36cdfdada94d781fd5d6b0071cd9c427bceb08ad3d7c70e1"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f6f3eac23941b32afccc23081e1f50612bdbe4e982012ef4f5797986828cd01"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-win32.whl", hash = "sha256:6442cb36270b3afb1b4951f060eccca1ce49f3d087ca1ca4563a6eb479cb3de6"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-win_amd64.whl", hash = "sha256:e5b8daf27af0b90da7bb903a876477a9e6d7270be6146906b276605997c7e9a3"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:fc4b630cd3fa2cf7fce38afa91d7cfe844a9f75d7f0f36393fa98815e911d987"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bc5f1e1c28e966d61d2519f2a3d451ba989f9ea0f2307de7bc45baa526de9e45"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a0e060aace4c24dcaf71023bbd7d42674e3b230f7e7b97317baf1e953e5b519"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2f1c3765db32be59d18ab3953f43ab62a761327aafc1594a2a1fbe038b8b8a7"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d85252669dc32f98ebcd5d36768f5d4faeaeaa2d655ac0473be490ecdae3c285"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e143ada795c341b56de9418c58d028989093ee611aa27ffb9b7f609c00d813ed"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2c59aa6170b990d8d2719323e628aaf36f3bfbc1c26279c0eeeb24d05d2d11c7"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-win32.whl", hash = "sha256:beffaed67936fbbeffd10966a4eb53c402fafd3d6833770516bf7314bc6ffa12"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-win_amd64.whl", hash = "sha256:040ae85536960525ea62868b642bdb0c2cc6021c9f9d507810c0c604e66f5a7b"}, + {file = "ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f"}, +] + [[package]] name = "ruff" version = "0.11.13" @@ -2988,6 +3441,47 @@ botocore = ">=1.37.4,<2.0a.0" [package.extras] crt = ["botocore[crt] (>=1.37.4,<2.0a.0)"] +[[package]] +name = "schemathesis" +version = "4.0.21" +description = "Property-based testing framework for Open API and GraphQL based apps" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "schemathesis-4.0.21-py3-none-any.whl", hash = "sha256:329ad303f0d119e3d8f187e48479027c9de27c6d04cb7112fc15867e5486c08d"}, + {file = "schemathesis-4.0.21.tar.gz", hash = "sha256:2004f6b0bba6508ec48377d00d22aca2eeaa0d1fc9613b6f0c8dd1f52c69c1a8"}, +] + +[package.dependencies] +backoff = ">=2.1.2,<3.0" +click = ">=8.0,<9" +colorama = ">=0.4,<1.0" +harfile = ">=0.3.0,<1.0" +httpx = ">=0.22.0,<1.0" +hypothesis = ">=6.108.0,<7" +hypothesis-graphql = ">=0.11.1,<1" +hypothesis-jsonschema = ">=0.23.1,<0.24" +jsonschema = {version = ">=4.18.0,<5.0", extras = ["format"]} +junit-xml = ">=1.9,<2.0" +pyrate-limiter = ">=3.0,<4.0" +pytest = ">=8,<9" +pytest-subtests = ">=0.11,<0.15.0" +pyyaml = ">=5.1,<7.0" +requests = ">=2.22,<3" +rich = ">=13.9.4" +starlette-testclient = ">=0.4.1,<1" +tomli = ">=2.2.1" +typing-extensions = ">=4.12.2" +werkzeug = ">=0.16.0,<4" + +[package.extras] +bench = ["pytest-codspeed (==2.2.1)"] +cov = ["coverage-enable-subprocess", "coverage[toml] (>=5.3)"] +dev = ["aiohttp (>=3.9.1,<4.0)", "coverage (>=6)", "coverage-enable-subprocess", "coverage[toml] (>=5.3)", "fastapi (>=0.86.0)", "flask (>=2.1.1,<3.0)", "hypothesis-openapi (>=0.2,<1) ; python_version >= \"3.10\"", "mkdocs-material", "mkdocstrings[python]", "pydantic (>=1.10.2)", "pytest-asyncio (>=0.18.0,<1.0)", "pytest-codspeed (==2.2.1)", "pytest-httpserver (>=1.0,<2.0)", "pytest-mock (>=3.7.0,<4.0)", "pytest-trio (>=0.8,<1.0)", "pytest-xdist (>=3,<4.0)", "strawberry-graphql[fastapi] (>=0.109.0)", "syrupy (>=2,<5.0)", "tomli-w (>=1.2.0)", "trustme (>=0.9.0,<1.0)"] +docs = ["mkdocs-material", "mkdocstrings[python]"] +tests = ["aiohttp (>=3.9.1,<4.0)", "coverage (>=6)", "fastapi (>=0.86.0)", "flask (>=2.1.1,<3.0)", "hypothesis-openapi (>=0.2,<1) ; python_version >= \"3.10\"", "pydantic (>=1.10.2)", "pytest-asyncio (>=0.18.0,<1.0)", "pytest-httpserver (>=1.0,<2.0)", "pytest-mock (>=3.7.0,<4.0)", "pytest-trio (>=0.8,<1.0)", "pytest-xdist (>=3,<4.0)", "strawberry-graphql[fastapi] (>=0.109.0)", "syrupy (>=2,<5.0)", "tomli-w (>=1.2.0)", "trustme (>=0.9.0,<1.0)"] + [[package]] name = "semver" version = "3.0.4" @@ -3036,6 +3530,18 @@ files = [ {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, ] +[[package]] +name = "sortedcontainers" +version = "2.4.0" +description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, + {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, +] + [[package]] name = "soupsieve" version = "2.7" @@ -3069,6 +3575,40 @@ docs = ["furo", "myst-parser", "prometheus-client", "sphinx (>=7.2.2)", "sphinx- tests = ["anyio", "dirty-equals", "pytest"] typing = ["mypy (>=1.4)"] +[[package]] +name = "starlette" +version = "0.47.2" +description = "The little ASGI library that shines." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "starlette-0.47.2-py3-none-any.whl", hash = "sha256:c5847e96134e5c5371ee9fac6fdf1a67336d5815e09eb2a01fdb57a351ef915b"}, + {file = "starlette-0.47.2.tar.gz", hash = "sha256:6ae9aa5db235e4846decc1e7b79c4f346adf41e9777aebeb49dfd09bbd7023d8"}, +] + +[package.dependencies] +anyio = ">=3.6.2,<5" + +[package.extras] +full = ["httpx (>=0.27.0,<0.29.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.18)", "pyyaml"] + +[[package]] +name = "starlette-testclient" +version = "0.4.1" +description = "A backport of Starlette TestClient using requests! โช๏ธ" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "starlette_testclient-0.4.1-py3-none-any.whl", hash = "sha256:dcf0eb237dc47f062ef5925f98330af46f67e547cb587119c9ae78c17ae6c1d1"}, + {file = "starlette_testclient-0.4.1.tar.gz", hash = "sha256:9e993ffe12fab45606116257813986612262fe15c1bb6dc9e39cc68693ac1fc5"}, +] + +[package.dependencies] +requests = "*" +starlette = ">=0.20.1" + [[package]] name = "tabulate" version = "0.9.0" @@ -3123,6 +3663,60 @@ files = [ {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] +[[package]] +name = "tomli" +version = "2.2.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, + {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, + {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, + {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, + {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, + {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, + {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, + {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, + {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, +] + +[[package]] +name = "types-python-dateutil" +version = "2.9.0.20250708" +description = "Typing stubs for python-dateutil" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "types_python_dateutil-2.9.0.20250708-py3-none-any.whl", hash = "sha256:4d6d0cc1cc4d24a2dc3816024e502564094497b713f7befda4d5bc7a8e3fd21f"}, + {file = "types_python_dateutil-2.9.0.20250708.tar.gz", hash = "sha256:ccdbd75dab2d6c9696c350579f34cffe2c281e4c5f27a585b2a2438dd1d5c8ab"}, +] + [[package]] name = "typing-extensions" version = "4.14.0" @@ -3162,6 +3756,21 @@ files = [ {file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"}, ] +[[package]] +name = "uri-template" +version = "1.3.0" +description = "RFC 6570 URI Template Processor" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "uri-template-1.3.0.tar.gz", hash = "sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7"}, + {file = "uri_template-1.3.0-py3-none-any.whl", hash = "sha256:a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363"}, +] + +[package.extras] +dev = ["flake8", "flake8-annotations", "flake8-bandit", "flake8-bugbear", "flake8-commas", "flake8-comprehensions", "flake8-continuation", "flake8-datetimez", "flake8-docstrings", "flake8-import-order", "flake8-literal", "flake8-modern-annotations", "flake8-noqa", "flake8-pyproject", "flake8-requirements", "flake8-typechecking-import", "flake8-use-fstring", "mypy", "pep8-naming", "types-PyYAML"] + [[package]] name = "urllib3" version = "2.5.0" @@ -3180,6 +3789,18 @@ h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] +[[package]] +name = "webcolors" +version = "24.11.1" +description = "A library for working with the color formats defined by HTML and CSS." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "webcolors-24.11.1-py3-none-any.whl", hash = "sha256:515291393b4cdf0eb19c155749a096f779f7d909f7cceea072791cb9095b92e9"}, + {file = "webcolors-24.11.1.tar.gz", hash = "sha256:ecb3d768f32202af770477b8b65f318fa4f566c22948673a977b00d589dd80f6"}, +] + [[package]] name = "werkzeug" version = "3.1.3" @@ -3478,4 +4099,4 @@ propcache = ">=0.2.1" [metadata] lock-version = "2.1" python-versions = "^3.13" -content-hash = "350611ee13ef6aefb16bb0a69766b37d5691ba38f74e6dc6f4c08be2b0223b26" +content-hash = "8a3beb3f554d833b708cd02fe37440bb95158f42a995715351e2293e124ad087" diff --git a/pyproject.toml b/pyproject.toml index b55498847..9931737ff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,6 +35,9 @@ python-json-logger = "^3.3.0" fhir-resources = "^8.0.0" python-dateutil = "^2.9.0" pyhamcrest = "^2.1.0" +schemathesis = "^4.0.21" +prance = "^25.4.8.0" +jsonschema = "^4.25.0" [tool.poetry.group.dev.dependencies] ruff = "^0.11.13" @@ -62,6 +65,7 @@ jsonschema = "^4.24.0" behave = "^1.2.6" boto3 = "^1.38.42" python-dotenv = "^1.1.1" +openapi-spec-validator = "^0.7.2" [tool.poetry-plugin-lambda-build] docker-image = "public.ecr.aws/sam/build-python3.13:1.139-x86_64" # See https://gallery.ecr.aws/search?searchTerm=%22python%22&architecture=x86-64&popularRegistries=amazon&verified=verified&operatingSystems=Linux diff --git a/tests/e2e/data/configs/regressionTestConfigs/AUTO_RSV_REG_001.json b/tests/e2e/data/configs/regressionTestConfigs/AUTO_RSV_REG_001.json index c65ce9e8c..5f96bf0cc 100644 --- a/tests/e2e/data/configs/regressionTestConfigs/AUTO_RSV_REG_001.json +++ b/tests/e2e/data/configs/regressionTestConfigs/AUTO_RSV_REG_001.json @@ -1,13 +1,13 @@ { "CampaignConfig": { "ID": "Automation RSV - Regression Test Config - Camp ID", - "Version": "1", + "Version": 1, "Name": "Automation RSV - Regression Test Config", "Type": "V", "Target": "RSV", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -20,7 +20,7 @@ { "ID": "Automation RSV - Regression Test Config - It ID", "DefaultCommsRouting": "DEFAULT_ACTION", - "Version": "1", + "Version": 1, "Name": "Automation RSV - Regression Test Config Iteration", "IterationDate": "20250601", "IterationNumber": 1, diff --git a/tests/e2e/data/configs/smokeTestConfigs/.temp/AUTO_RSV_SB_001.json b/tests/e2e/data/configs/smokeTestConfigs/.temp/AUTO_RSV_SB_001.json index 862ced3f0..3bae11c85 100644 --- a/tests/e2e/data/configs/smokeTestConfigs/.temp/AUTO_RSV_SB_001.json +++ b/tests/e2e/data/configs/smokeTestConfigs/.temp/AUTO_RSV_SB_001.json @@ -1,13 +1,13 @@ { "CampaignConfig": { "ID": "<>", - "Version": "1", + "Version": 1, "Name": "Automation RSV - Smoke Test Config", "Type": "V", "Target": "RSV", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -20,7 +20,7 @@ { "ID": ",<>", "DefaultCommsRouting": "BOOK_LOCAL", - "Version": "1", + "Version": 1, "Name": "Automation RSV - Smoke Test Config Iteration", "IterationDate": "20250601", "IterationNumber": 1, diff --git a/tests/e2e/data/configs/smokeTestConfigs/.temp/AUTO_RSV_SB_008.json b/tests/e2e/data/configs/smokeTestConfigs/.temp/AUTO_RSV_SB_008.json index 41a8d3415..918ebcf42 100644 --- a/tests/e2e/data/configs/smokeTestConfigs/.temp/AUTO_RSV_SB_008.json +++ b/tests/e2e/data/configs/smokeTestConfigs/.temp/AUTO_RSV_SB_008.json @@ -1,13 +1,13 @@ { "CampaignConfig": { "ID": "<>", - "Version": "1", + "Version": 1, "Name": "Automation RSV - Smoke Test Config", "Type": "V", "Target": "RSV", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -20,7 +20,7 @@ { "ID": ",<>", "DefaultCommsRouting": "BOOK_LOCAL", - "Version": "1", + "Version": 1, "Name": "Automation RSV - Smoke Test Config Iteration", "IterationDate": "20250601", "IterationNumber": 1, diff --git a/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_001.json b/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_001.json index 7a5366d27..a21f9240a 100644 --- a/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_001.json +++ b/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_001.json @@ -1,13 +1,13 @@ { "CampaignConfig": { "ID": "<>", - "Version": "1", + "Version": 1, "Name": "Automation RSV - Smoke Test Config", "Type": "V", "Target": "RSV", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -20,7 +20,7 @@ { "ID": ",<>", "DefaultCommsRouting": "BOOK_LOCAL", - "Version": "1", + "Version": 1, "Name": "Automation RSV - Smoke Test Config Iteration", "IterationDate": "20250601", "IterationNumber": 1, diff --git a/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_008.json b/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_008.json index a0dffe1e7..3bf657272 100644 --- a/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_008.json +++ b/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_008.json @@ -1,13 +1,13 @@ { "CampaignConfig": { "ID": "<>", - "Version": "1", + "Version": 1, "Name": "Automation RSV - Smoke Test Config", "Type": "V", "Target": "RSV", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -20,7 +20,7 @@ { "ID": ",<>", "DefaultCommsRouting": "BOOK_LOCAL", - "Version": "1", + "Version": 1, "Name": "Automation RSV - Smoke Test Config Iteration", "IterationDate": "20250601", "IterationNumber": 1, diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-155.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-155.json index 571375411..be504d28a 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-155.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-155.json @@ -1,13 +1,13 @@ { "CampaignConfig": { "ID": "<>", - "Version": "1", + "Version": 1, "Name": "ELI-155 - cohort_label is not supported in R rules", "Type": "V", "Target": "RSV", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -20,7 +20,7 @@ { "ID": ",<>", "DefaultCommsRouting": "CONTACT_GP", - "Version": "1", + "Version": 1, "Name": "ELI-155 - cohort_label is not supported in R rules", "IterationDate": "20250601", "IterationNumber": 1, diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json index 69574e7db..119ab9025 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json @@ -1,13 +1,13 @@ { "CampaignConfig": { "ID": "<>", - "Version": "1", + "Version": 1, "Name": "ELI-216 - NHS Number check (NHS login) - 9000000006", "Type": "V", "Target": "RSV", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -20,7 +20,7 @@ { "ID": ",<>", "DefaultCommsRouting": "DEFAULT_ACTION", - "Version": "1", + "Version": 1, "Name": "ELI-155 - Support Actions R Rules", "IterationDate": "20250601", "IterationNumber": 1, diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-1.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-1.json index 78ffcaafd..dae24bbe2 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-1.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-1.json @@ -1,13 +1,13 @@ { "CampaignConfig": { "ID": "<>", - "Version": "1", + "Version": 1, "Name": "ELI-219 - Support Actions R Rules - 9000000002", "Type": "V", "Target": "RSV", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -20,7 +20,7 @@ { "ID": ",<>", "DefaultCommsRouting": "DEFAULT_ACTION", - "Version": "1", + "Version": 1, "Name": "ELI-155 - Support Actions R Rules", "IterationDate": "20250601", "IterationNumber": 1, diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-2.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-2.json index 3f83a2a30..477e61b63 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-2.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-2.json @@ -1,13 +1,13 @@ { "CampaignConfig": { "ID": "<>", - "Version": "1", + "Version": 1, "Name": "ELI-219 - Support Actions R Rules - 9000000003", "Type": "V", "Target": "RSV", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -20,7 +20,7 @@ { "ID": ",<>", "DefaultCommsRouting": "DEFAULT_ACTION", - "Version": "1", + "Version": 1, "Name": "ELI-155 - Support Actions R Rules", "IterationDate": "20250601", "IterationNumber": 1, diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-3.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-3.json index 3e68a1b2e..5fe1ccbcc 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-3.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-3.json @@ -1,13 +1,13 @@ { "CampaignConfig": { "ID": "<>", - "Version": "1", + "Version": 1, "Name": "ELI-219 - Support Actions R Rules - 9000000004", "Type": "V", "Target": "RSV", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -20,7 +20,7 @@ { "ID": ",<>", "DefaultCommsRouting": "DEFAULT_ACTION", - "Version": "1", + "Version": 1, "Name": "ELI-219 - Support Actions R Rules - 9000000004", "IterationDate": "20250601", "IterationNumber": 1, diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-01.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-01.json index 4242fba04..8d5eac5e0 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-01.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-01.json @@ -1,13 +1,13 @@ { "CampaignConfig": { "ID": "<>", - "Version": "1", + "Version": 1, "Name": "ELI-221 - Create Rule Stop function - Rule Stop on 2nd rule", "Type": "V", "Target": "RSV", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -20,7 +20,7 @@ { "ID": ",<>", "DefaultCommsRouting": "CONTACT_GP", - "Version": "1", + "Version": 1, "Name": "ELI-221 - Create Rule Stop function - Rule Stop on Vaccinated", "IterationDate": "20250601", "IterationNumber": 1, diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-02.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-02.json index faad7b00e..07075f5d5 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-02.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-02.json @@ -1,13 +1,13 @@ { "CampaignConfig": { "ID": "<>", - "Version": "1", + "Version": 1, "Name": "ELI-221 - Create Rule Stop function - Rule Stop on Vaccinated - Rule Stop: N", "Type": "V", "Target": "RSV", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -20,7 +20,7 @@ { "ID": ",<>", "DefaultCommsRouting": "CONTACT_GP", - "Version": "1", + "Version": 1, "Name": "ELI-221 - Create Rule Stop function - Rule Stop on Vaccinated - No Rule Stop", "IterationDate": "20250601", "IterationNumber": 1, diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-03.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-03.json index c330a7da9..5ce13aa38 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-03.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-03.json @@ -1,13 +1,13 @@ { "CampaignConfig": { "ID": "<>", - "Version": "1", + "Version": 1, "Name": "ELI-221 - Create Rule Stop function - Rule Stop on Vaccinated - No Rule Stop", "Type": "V", "Target": "RSV", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -20,7 +20,7 @@ { "ID": ",<>", "DefaultCommsRouting": "CONTACT_GP", - "Version": "1", + "Version": 1, "Name": "ELI-221 - Create Rule Stop function - Rule Stop on Vaccinated - No Rule Stop", "IterationDate": "20250601", "IterationNumber": 1, diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-222.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-222.json index 838abfa04..ee1e2d028 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-222.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-222.json @@ -1,13 +1,13 @@ { "CampaignConfig": { "ID": "<>", - "Version": "1", + "Version": 1, "Name": "ELI-222 - Implement magic cohort", "Type": "V", "Target": "RSV", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -20,7 +20,7 @@ { "ID": ",<>", "DefaultCommsRouting": "CONTACT_GP", - "Version": "1", + "Version": 1, "Name": "ELI-222 - Implement magic cohort", "IterationDate": "20250601", "IterationNumber": 1, diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-236.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-236.json index df0c96aaf..38cf3269e 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-236.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-236.json @@ -1,13 +1,13 @@ { "CampaignConfig": { "ID": "<>", - "Version": "1", + "Version": 1, "Name": "ELI-236 - NVL Functionality", "Type": "V", "Target": "RSV", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -20,7 +20,7 @@ { "ID": ",<>", "DefaultCommsRouting": "", - "Version": "1", + "Version": 1, "Name": "ELI-236 - NVL Functionality", "IterationDate": "20250601", "IterationNumber": 1, diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-1.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-1.json index 292ed8fa1..0ba38160e 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-1.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-1.json @@ -1,13 +1,13 @@ { "CampaignConfig": { "ID": "<>", - "Version": "1", + "Version": 1, "Name": "ELI-295 - include generic text for Not Eligible (X/Y Rules)", "Type": "V", "Target": "RSV", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -22,7 +22,7 @@ "DefaultCommsRouting": "DEFAULT_A", "DefaultNotEligibleRouting": "", "DefaultNotActionableRouting": "", - "Version": "1", + "Version": 1, "Name": "ELI-295 - Iteration", "IterationDate": "20250601", "IterationNumber": 1, diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-2.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-2.json index af74b5b8f..c064b96a9 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-2.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-2.json @@ -5,9 +5,9 @@ "Name": "ELI-295 - include generic text for Not Eligible (X/Y Rules)", "Type": "V", "Target": "RSV", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -22,7 +22,7 @@ "DefaultCommsRouting": "", "DefaultNotEligibleRouting": "DEFAULT_X", "DefaultNotActionableRouting": "DEFAULT_Y", - "Version": "1", + "Version": 1, "Name": "ELI-295 - Iteration", "IterationDate": "20250601", "IterationNumber": 1, diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-3.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-3.json index 8c2c4f9c4..227009e35 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-3.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-3.json @@ -1,13 +1,13 @@ { "CampaignConfig": { "ID": "<>", - "Version": "1", + "Version": 1, "Name": "ELI-295 - include generic text for Not Eligible (X/Y Rules)", "Type": "V", "Target": "RSV", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -22,7 +22,7 @@ "DefaultCommsRouting": "", "DefaultNotEligibleRouting": "DEFAULT_X", "DefaultNotActionableRouting": "DEFAULT_Y", - "Version": "1", + "Version": 1, "Name": "ELI-295 - Iteration", "IterationDate": "20250601", "IterationNumber": 1, diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-4.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-4.json index b22930d8f..c4ae151c2 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-4.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-4.json @@ -1,13 +1,13 @@ { "CampaignConfig": { "ID": "<>", - "Version": "1", + "Version": 1, "Name": "ELI-295 - include generic text for Not Eligible (X/Y Rules)", "Type": "V", "Target": "RSV", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -22,7 +22,7 @@ "DefaultCommsRouting": "", "DefaultNotEligibleRouting": "DEFAULT_X", "DefaultNotActionableRouting": "DEFAULT_Y", - "Version": "1", + "Version": 1, "Name": "ELI-295 - Iteration", "IterationDate": "20250601", "IterationNumber": 1, diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-1.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-1.json index 18dc56c37..a69840055 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-1.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-1.json @@ -1,13 +1,13 @@ { "CampaignConfig": { "ID": "<>", - "Version": "1", + "Version": 1, "Name": "ELI-317-1 - Empty Actions - 9000000005", "Type": "V", "Target": "RSV", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -20,7 +20,7 @@ { "ID": ",<>", "DefaultCommsRouting": "DEFAULT_ACTION", - "Version": "1", + "Version": 1, "Name": "ELI-317 - Empty Actions - 9000000005", "IterationDate": "20250601", "IterationNumber": 1, diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-2.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-2.json index 2d85993ae..17721a620 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-2.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-2.json @@ -1,13 +1,13 @@ { "CampaignConfig": { "ID": "<>", - "Version": "1", + "Version": 1, "Name": "ELI-317 - Empty Actions - 9000000006", "Type": "V", "Target": "RSV", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -20,7 +20,7 @@ { "ID": ",<>", "DefaultCommsRouting": "DEFAULT_ACTION", - "Version": "1", + "Version": 1, "Name": "ELI-317 - Empty Actions - 9000000006", "IterationDate": "20250601", "IterationNumber": 1, diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-3.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-3.json index e9cf95f25..cab0cde65 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-3.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-3.json @@ -1,13 +1,13 @@ { "CampaignConfig": { "ID": "<>", - "Version": "1", + "Version": 1, "Name": "ELI-317 - includeActions=N - 9000000008", "Type": "V", "Target": "RSV", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -20,7 +20,7 @@ { "ID": ",<>", "DefaultCommsRouting": "DEFAULT_ACTION", - "Version": "1", + "Version": 1, "Name": "ELI-317 - Empty Actions - 9000000006", "IterationDate": "20250601", "IterationNumber": 1, diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-4.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-4.json index b91d30762..75677718b 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-4.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-4.json @@ -1,13 +1,13 @@ { "CampaignConfig": { "ID": "<>", - "Version": "1", + "Version": 1, "Name": "ELI-317 - includeActions=N - 9000000009", "Type": "V", "Target": "RSV", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -20,7 +20,7 @@ { "ID": ",<>", "DefaultCommsRouting": "", - "Version": "1", + "Version": 1, "Name": "ELI-317 - Empty Actions - 9000000006", "IterationDate": "20250601", "IterationNumber": 1, diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-5.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-5.json index 50c3bb0d1..e860547a6 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-5.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-5.json @@ -1,13 +1,13 @@ { "CampaignConfig": { "ID": "<>", - "Version": "1", + "Version": 1, "Name": "ELI-317 - Multiple Actions - 9000000010", "Type": "V", "Target": "RSV", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -20,7 +20,7 @@ { "ID": ",<>", "DefaultCommsRouting": "DEFAULT_ACTION", - "Version": "1", + "Version": 1, "Name": "ELI-317 - Multiple Actions - 9000000010", "IterationDate": "20250601", "IterationNumber": 1, diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-COVID.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-COVID.json index 78bcae7e8..271e955a0 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-COVID.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-COVID.json @@ -1,13 +1,13 @@ { "CampaignConfig": { "ID": "CC1234", - "Version": "1", + "Version": 1, "Name": "ELI-320 - COVID Config", "Type": "V", "Target": "COVID", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -20,7 +20,7 @@ { "ID": "IC1234", "DefaultCommsRouting": "CONTACT_GP", - "Version": "1", + "Version": 1, "Name": "ELI-320 - COVID Iteration Config", "IterationDate": "20250601", "IterationNumber": 1, diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-MMR.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-MMR.json index 920f0a30d..188c61e9a 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-MMR.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-MMR.json @@ -1,13 +1,13 @@ { "CampaignConfig": { "ID": "CC8901", - "Version": "1", + "Version": 1, "Name": "ELI-320 - MMR Config", "Type": "V", "Target": "MMR", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -20,7 +20,7 @@ { "ID": "IC8901", "DefaultCommsRouting": "CONTACT_GP", - "Version": "1", + "Version": 1, "Name": "ELI-320 - MMR Iteration Config", "IterationDate": "20250601", "IterationNumber": 1, diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-RSV.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-RSV.json index c6d20c966..81ffe627a 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-RSV.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-RSV.json @@ -1,13 +1,13 @@ { "CampaignConfig": { "ID": "CC5678", - "Version": "1", + "Version": 1, "Name": "ELI-320 - RSV Config", "Type": "V", "Target": "RSV", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -20,7 +20,7 @@ { "ID": "IC5678", "DefaultCommsRouting": "AmendNBS", - "Version": "1", + "Version": 1, "Name": "ELI-320 - RSV Iteration Config", "IterationDate": "20250601", "IterationNumber": 1, diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-SCREENING-1.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-SCREENING-1.json index 9048c1c86..4cc81a50f 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-SCREENING-1.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-SCREENING-1.json @@ -1,13 +1,13 @@ { "CampaignConfig": { "ID": "CC56789", - "Version": "1", + "Version": 1, "Name": "ELI-320 - FLU SCREENING Config", "Type": "S", "Target": "FLU", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -20,7 +20,7 @@ { "ID": "IC56789", "DefaultCommsRouting": "AmendNBS", - "Version": "1", + "Version": 1, "Name": "ELI-320 - FLU Screening Iteration Config", "IterationDate": "20250601", "IterationNumber": 1, diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-SCREENING-2.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-SCREENING-2.json index 00ad0bfc6..18ab67e61 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-SCREENING-2.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-SCREENING-2.json @@ -1,13 +1,13 @@ { "CampaignConfig": { "ID": "CC567891", - "Version": "1", + "Version": 1, "Name": "ELI-320 - RSV SCREENING Config", "Type": "S", "Target": "RSV", - "Manager": "person1@nhs.net", - "Approver": "person1@nhs.net", - "Reviewer": "person1@nhs.net", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -20,7 +20,7 @@ { "ID": "IC567891", "DefaultCommsRouting": "AmendNBS", - "Version": "1", + "Version": 1, "Name": "ELI-320 - RSV SCREENING Iteration Config", "IterationDate": "20250601", "IterationNumber": 1, diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-365.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-365.json index 10f472afd..0385102c8 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-365.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-365.json @@ -1,13 +1,13 @@ { "CampaignConfig": { "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", - "Version": "1", + "Version": 1, "Name": "EliD RSV example config", "Type": "V", "Target": "RSV", - "Manager": "example@nhs.net", - "Approver": "example@nhs.net", - "Reviewer": "example@nhs.net", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -22,7 +22,7 @@ "DefaultCommsRouting": "BOOK_LOCAL|HELP_SUPPORT", "DefaultNotActionableRouting": "", "DefaultNotEligibleRouting": "CHECK_CORRECT_X", - "Version": "1", + "Version": 1, "Name": "EliD RSV example config", "IterationDate": "20250717", "IterationNumber": 1, diff --git a/tests/e2e/data/configs/vitaIntegrationTestConfigs/vita_integration_test_config.json b/tests/e2e/data/configs/vitaIntegrationTestConfigs/vita_integration_test_config.json index 1f94a88c1..405bcd949 100644 --- a/tests/e2e/data/configs/vitaIntegrationTestConfigs/vita_integration_test_config.json +++ b/tests/e2e/data/configs/vitaIntegrationTestConfigs/vita_integration_test_config.json @@ -1,13 +1,13 @@ { "CampaignConfig": { "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", - "Version": "1", + "Version": 1, "Name": "EliD RSV example config", "Type": "V", "Target": "RSV", - "Manager": "example@nhs.net", - "Approver": "example@nhs.net", - "Reviewer": "example@nhs.net", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -22,7 +22,7 @@ "DefaultCommsRouting": "BOOK_LOCAL|HELP_SUPPORT", "DefaultNotActionableRouting": "", "DefaultNotEligibleRouting": "CHECK_CORRECT_X", - "Version": "1", + "Version": 1, "Name": "EliD RSV example config", "IterationDate": "20250717", "IterationNumber": 1, diff --git a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json index e042205c7..aee9d565f 100644 --- a/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json +++ b/tests/e2e/data/dynamoDB/smokeTestData/AUTO_RSV_SB_010.json @@ -8,7 +8,7 @@ ], "data": [ { - "NHS_NUMBER": "50000000010", + "NHS_NUMBER": "5000000010", "ATTRIBUTE_TYPE": "COHORTS", "COHORT_MEMBERSHIPS": [ { diff --git a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_500.json b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_500.json index a548548b0..79aff5d5e 100644 --- a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_500.json +++ b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_500.json @@ -3,7 +3,10 @@ "request_headers": { "nhs-login-nhs-number": "9436793375" }, - "config_filenames": ["vita_integration_test_config.json"], + "expected_response_code": 500, + "config_filenames": [ + "vita_integration_test_config.json" + ], "data": [ { "NHS_NUMBER": "9436793375", diff --git a/tests/e2e/tests/conftest.py b/tests/e2e/tests/conftest.py index 18c545b31..bfa2e1738 100644 --- a/tests/e2e/tests/conftest.py +++ b/tests/e2e/tests/conftest.py @@ -39,9 +39,32 @@ def _setup(scenario, config_path): config_filenames = scenario.get("config_filenames", []) request_headers = scenario.get("request_headers", {}) query_params = scenario.get("query_params", {}) + expected_response_code = scenario["expected_response_code"] upload_configs_to_s3(config_filenames, config_path) - return nhs_number, config_filenames, request_headers, query_params + return nhs_number, config_filenames, request_headers, query_params, expected_response_code return _setup + +@pytest.fixture(scope="session") +def validate_against_spec(): + """ + Validates an API response against the Eligibility Signposting API spec. + Loads the spec once per test session. + """ + parser = ResolvingParser(SPEC_PATH) + openapi_spec = parser.specification + + def _validate(status_code, response_body): + try: + method_spec = openapi_spec["paths"][API_PATH][API_METHOD] + response_schema = method_spec["responses"][str(status_code)]["content"]["application/json"]["schema"] + + validate(instance=response_body, schema=response_schema) + except KeyError as e: + pytest.fail(f"Spec does not define schema for GET {API_PATH} {status_code}: {e}") + except Exception as e: + pytest.fail(f"Schema validation failed: {e}") + + return _validate diff --git a/tests/e2e/tests/test_regression_tests.py b/tests/e2e/tests/test_regression_tests.py index a9fa5a211..d4a17f288 100644 --- a/tests/e2e/tests/test_regression_tests.py +++ b/tests/e2e/tests/test_regression_tests.py @@ -17,13 +17,14 @@ @pytest.mark.functionale2eregression @pytest.mark.parametrize(("filename", "scenario"), param_list, ids=id_list) def test_run_regression_tests(filename, scenario, eligibility_client, get_scenario_params): - nhs_number, config_filenames, request_headers, query_params = get_scenario_params(scenario, config_path) + nhs_number, config_filenames, request_headers, query_params, expected_response_code = get_scenario_params(scenario, config_path) actual_response = eligibility_client.make_request(nhs_number, headers=request_headers, strict_ssl=False) expected_response = all_expected_responses.get(filename).get("response_items", {}) - # Assert and show details on failure - assert actual_response["status_code"] == http.HTTPStatus.OK + expected_response_code = expected_response_code or http.HTTPStatus.OK + + assert actual_response["status_code"] == expected_response_code assert actual_response["body"] == expected_response, ( f"\nโŒ Mismatch in test: {filename}\n" f"NHS Number: {nhs_number}\n" diff --git a/tests/e2e/tests/test_smoke_tests.py b/tests/e2e/tests/test_smoke_tests.py index f99fd3265..8b2271cf3 100644 --- a/tests/e2e/tests/test_smoke_tests.py +++ b/tests/e2e/tests/test_smoke_tests.py @@ -17,15 +17,16 @@ @pytest.mark.sandboxtests @pytest.mark.parametrize(("filename", "scenario"), param_list, ids=id_list) def test_run_smoke_case(filename, scenario, eligibility_client, get_scenario_params): - nhs_number, config_filenames, request_headers, query_params = get_scenario_params(scenario, config_path) + nhs_number, config_filenames, request_headers, query_params, expected_response_code = get_scenario_params(scenario, config_path) actual_response = eligibility_client.make_request( nhs_number, headers=request_headers, query_params=query_params, strict_ssl=False ) expected_response = all_expected_responses.get(filename).get("response_items", {}) - # Assert and show details on failure - assert actual_response["status_code"] == http.HTTPStatus.OK + expected_response_code = expected_response_code or http.HTTPStatus.OK + + assert actual_response["status_code"] == expected_response_code assert actual_response["body"] == expected_response, ( f"\nโŒ Mismatch in test: {filename}\n" f"NHS Number: {nhs_number}\n" diff --git a/tests/e2e/tests/test_story_tests.py b/tests/e2e/tests/test_story_tests.py index a3e328dba..586d2e257 100644 --- a/tests/e2e/tests/test_story_tests.py +++ b/tests/e2e/tests/test_story_tests.py @@ -16,13 +16,14 @@ @pytest.mark.functionale2eregression @pytest.mark.parametrize(("filename", "scenario"), param_list, ids=id_list) -def test_run_story_test_cases(filename, scenario, eligibility_client, get_scenario_params): - nhs_number, config_filenames, request_headers, query_params = get_scenario_params(scenario, config_path) +def test_run_story_test_cases(filename, scenario, eligibility_client, get_scenario_params, validate_against_spec): + nhs_number, config_filenames, request_headers, query_params, expected_response_code = get_scenario_params(scenario, config_path) actual_response = eligibility_client.make_request( nhs_number=nhs_number, headers=request_headers, query_params=query_params, strict_ssl=False ) expected_response = all_expected_responses.get(filename).get("response_items", {}) + expected_response_code = expected_response_code or http.HTTPStatus.OK assert actual_response["status_code"] == http.HTTPStatus.OK assert actual_response["body"] == expected_response, ( diff --git a/tests/e2e/tests/test_vita_integration_tests.py b/tests/e2e/tests/test_vita_integration_tests.py new file mode 100644 index 000000000..5162ee839 --- /dev/null +++ b/tests/e2e/tests/test_vita_integration_tests.py @@ -0,0 +1,35 @@ +import http + +import pytest + +from tests.e2e.tests import test_config +from tests.e2e.utils.data_helper import initialise_tests, load_all_expected_responses + +# Update the below with the configuration values specified in test_config.py +all_data, dto = initialise_tests(test_config.VITA_INTEGRATION_TEST_DATA) +all_expected_responses = load_all_expected_responses(test_config.VITA_INTEGRATION_RESPONSES) +config_path = test_config.VITA_INTEGRATION_CONFIGS + +param_list = list(all_data.items()) +id_list = [f"{filename} - {scenario.get('scenario_name', 'No Scenario')}" for filename, scenario in param_list] + + +@pytest.mark.functionale2eregression +@pytest.mark.parametrize(("filename", "scenario"), param_list, ids=id_list) +def test_run_story_test_cases(filename, scenario, eligibility_client, get_scenario_params): + nhs_number, config_filenames, request_headers, query_params, expected_response_code = get_scenario_params(scenario, config_path) + + actual_response = eligibility_client.make_request( + nhs_number=nhs_number, headers=request_headers, query_params=query_params, strict_ssl=False + ) + expected_response = all_expected_responses.get(filename).get("response_items", {}) + + expected_response_code = expected_response_code or http.HTTPStatus.OK + + assert actual_response["status_code"] == expected_response_code + assert actual_response["body"] == expected_response, ( + f"\nโŒ Mismatch in test: {filename}\n" + f"NHS Number: {nhs_number}\n" + f"Expected: {expected_response}\n" + f"Actual: {actual_response}\n" + ) From 06f98473f71a3ca5bb6694ac25ac320e2c4dcf0e Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Tue, 5 Aug 2025 10:56:00 +0100 Subject: [PATCH 63/72] removed some breaking code --- tests/e2e/tests/conftest.py | 22 ---------------------- tests/e2e/tests/test_story_tests.py | 4 ++-- 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/tests/e2e/tests/conftest.py b/tests/e2e/tests/conftest.py index bfa2e1738..30dd5808e 100644 --- a/tests/e2e/tests/conftest.py +++ b/tests/e2e/tests/conftest.py @@ -46,25 +46,3 @@ def _setup(scenario, config_path): return nhs_number, config_filenames, request_headers, query_params, expected_response_code return _setup - -@pytest.fixture(scope="session") -def validate_against_spec(): - """ - Validates an API response against the Eligibility Signposting API spec. - Loads the spec once per test session. - """ - parser = ResolvingParser(SPEC_PATH) - openapi_spec = parser.specification - - def _validate(status_code, response_body): - try: - method_spec = openapi_spec["paths"][API_PATH][API_METHOD] - response_schema = method_spec["responses"][str(status_code)]["content"]["application/json"]["schema"] - - validate(instance=response_body, schema=response_schema) - except KeyError as e: - pytest.fail(f"Spec does not define schema for GET {API_PATH} {status_code}: {e}") - except Exception as e: - pytest.fail(f"Schema validation failed: {e}") - - return _validate diff --git a/tests/e2e/tests/test_story_tests.py b/tests/e2e/tests/test_story_tests.py index 586d2e257..541fb7c77 100644 --- a/tests/e2e/tests/test_story_tests.py +++ b/tests/e2e/tests/test_story_tests.py @@ -16,7 +16,7 @@ @pytest.mark.functionale2eregression @pytest.mark.parametrize(("filename", "scenario"), param_list, ids=id_list) -def test_run_story_test_cases(filename, scenario, eligibility_client, get_scenario_params, validate_against_spec): +def test_run_story_test_cases(filename, scenario, eligibility_client, get_scenario_params): nhs_number, config_filenames, request_headers, query_params, expected_response_code = get_scenario_params(scenario, config_path) actual_response = eligibility_client.make_request( @@ -25,7 +25,7 @@ def test_run_story_test_cases(filename, scenario, eligibility_client, get_scenar expected_response = all_expected_responses.get(filename).get("response_items", {}) expected_response_code = expected_response_code or http.HTTPStatus.OK - assert actual_response["status_code"] == http.HTTPStatus.OK + assert actual_response["status_code"] == expected_response_code assert actual_response["body"] == expected_response, ( f"\nโŒ Mismatch in test: {filename}\n" f"NHS Number: {nhs_number}\n" From 7ae2bb6f898b78383c627fb1840860802841cd7e Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Thu, 7 Aug 2025 14:56:30 +0100 Subject: [PATCH 64/72] ELI-320 new sceanrio --- .../storyTestData/AUTO_RSV_ELI-320-12.json | 60 ++++++++++++++ .../AUTO_RSV_ELI-320-12.json | 78 +++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-12.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-12.json diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-12.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-12.json new file mode 100644 index 000000000..186f1a402 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-12.json @@ -0,0 +1,60 @@ +{ + "scenario_name": "ELI-320 - Multiple Category Campaigns - Category=VACCINATIONS,screening", + "request_headers": { + "nhs-login-nhs-number": "9990032012" + }, + "query_params": { + "category": "VACCINATIONS,screening" + }, + "config_filenames": ["AUTO_RSV_ELI-320-COVID.json","AUTO_RSV_ELI-320-MMR.json", "AUTO_RSV_ELI-320-SCREENING-1.json", "AUTO_RSV_ELI-320-SCREENING-2.json"], + "data": [ + { + "NHS_NUMBER": "9990032012", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "covid_cohort", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_cohort", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "FLU_screening_cohort", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9990032012", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "19500601", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9990032012", + "ATTRIBUTE_TYPE": "RSV", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + }, + { + "NHS_NUMBER": "9990032012", + "ATTRIBUTE_TYPE": "COVID", + "BOOKED_APPOINTMENT_DATE": "<>", + "LAST_SUCCESSFUL_DATE": "<>" + } + ] +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-12.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-12.json new file mode 100644 index 000000000..c57312555 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-12.json @@ -0,0 +1,78 @@ +{ + "meta": { + "lastUpdated": "2025-07-15T14:52:52.785698+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "COVID", + "eligibilityCohorts": [ + { + "cohortCode": "covid_cohort_group", + "cohortStatus": "NotActionable", + "cohortText": "You are currently in a covid cohort" + } + ], + "status": "NotActionable", + "statusText": "You should have the COVID vaccine", + "suitabilityRules": [ + { + "ruleCode": "AlreadyVaccinated", + "ruleText": "##You've had your COVID vaccination\nWe believe you already had your COVID vaccination.", + "ruleType": "S" + } + ] + }, + { + "actions": [ + { + "actionCode": "AmendNBS", + "actionType": "ButtonWithAuthLink", + "description": "##You have an flu screening appointment\nYou can view, change or cancel your appointment below.", + "urlLabel": "Manage your appointment", + "urlLink": "http://www.nhs.uk/book-bs" + } + ], + "condition": "FLU", + "eligibilityCohorts": [ + { + "cohortCode": "FLU_screening_cohort_group", + "cohortStatus": "Actionable", + "cohortText": "You are currently in an flu SCREENING cohort" + } + ], + "status": "Actionable", + "statusText": "You should have the FLU vaccine", + "suitabilityRules": [] + }, + { + "actions": [], + "condition": "MMR", + "eligibilityCohorts": [ + { + "cohortCode": "mmr_cohort_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not currently in an mmr cohort" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + }, + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_screening_cohort_group", + "cohortStatus": "NotEligible", + "cohortText": "You are not currently in an RSV SCREENING cohort" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "5c8d1cb3-8326-40b1-93ad-1b7fa24c2595" +} From 8e0f86fcbe6a74200e163754207c0a6c0cd3f8a1 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Fri, 8 Aug 2025 15:39:42 +0100 Subject: [PATCH 65/72] added the in progress test --- tests/e2e/tests/test_in_progress.py | 34 +++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 tests/e2e/tests/test_in_progress.py diff --git a/tests/e2e/tests/test_in_progress.py b/tests/e2e/tests/test_in_progress.py new file mode 100644 index 000000000..4204f4519 --- /dev/null +++ b/tests/e2e/tests/test_in_progress.py @@ -0,0 +1,34 @@ +import http + +import pytest + +from tests.e2e.tests import test_config +from tests.e2e.utils.data_helper import initialise_tests, load_all_expected_responses + +# Update the below with the configuration values specified in test_config.py +all_data, dto = initialise_tests(test_config.IN_PROGRESS_TEST_DATA) +all_expected_responses = load_all_expected_responses(test_config.IN_PROGRESS_RESPONSES) +config_path = test_config.IN_PROGRESS_CONFIGS + +param_list = list(all_data.items()) +id_list = [f"{filename} - {scenario.get('scenario_name', 'No Scenario')}" for filename, scenario in param_list] + + +@pytest.mark.parametrize(("filename", "scenario"), param_list, ids=id_list) +def test_run_in_progress_tests(filename, scenario, eligibility_client, get_scenario_params): + nhs_number, config_filenames, request_headers, query_params, expected_response_code = get_scenario_params(scenario, config_path) + + actual_response = eligibility_client.make_request( + nhs_number, headers=request_headers, query_params=query_params, strict_ssl=False + ) + expected_response = all_expected_responses.get(filename).get("response_items", {}) + + expected_response_code = expected_response_code or http.HTTPStatus.OK + + assert actual_response["status_code"] == expected_response_code + assert actual_response["body"] == expected_response, ( + f"\nโŒ Mismatch in test: {filename}\n" + f"NHS Number: {nhs_number}\n" + f"Expected: {expected_response}\n" + f"Actual: {actual_response}\n" + ) From 8f5847844e5bc46cb4dc1f6a140fa9971de3a91e Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 11 Aug 2025 16:03:43 +0100 Subject: [PATCH 66/72] added ELI-399 regression tests tidying the VITA int tests removed 320-12 as invalid --- .../storyTestData/AUTO_RSV_ELI-320-12.json | 60 -------------- .../AUTO_RSV_VITA_INT_015.json | 6 ++ .../AUTO_RSV_ELI-320-12.json | 78 ------------------- .../AUTO_RSV_VITA_INT_500.json | 46 +++++------ tests/e2e/tests/test_story_tests.py | 2 +- .../e2e/tests/test_vita_integration_tests.py | 2 +- 6 files changed, 31 insertions(+), 163 deletions(-) delete mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-12.json delete mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-12.json diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-12.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-12.json deleted file mode 100644 index 186f1a402..000000000 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-12.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "scenario_name": "ELI-320 - Multiple Category Campaigns - Category=VACCINATIONS,screening", - "request_headers": { - "nhs-login-nhs-number": "9990032012" - }, - "query_params": { - "category": "VACCINATIONS,screening" - }, - "config_filenames": ["AUTO_RSV_ELI-320-COVID.json","AUTO_RSV_ELI-320-MMR.json", "AUTO_RSV_ELI-320-SCREENING-1.json", "AUTO_RSV_ELI-320-SCREENING-2.json"], - "data": [ - { - "NHS_NUMBER": "9990032012", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MEMBERSHIPS": [ - { - "COHORT_LABEL": "covid_cohort", - "DATE_JOINED": "20231020" - }, - { - "COHORT_LABEL": "rsv_cohort", - "DATE_JOINED": "20231020" - }, - { - "COHORT_LABEL": "FLU_screening_cohort", - "DATE_JOINED": "20231020" - } - ] - }, - { - "NHS_NUMBER": "9990032012", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "19500601", - "GENDER": "0", - "POSTCODE": "SG8 6EG", - "POSTCODE_SECTOR": "SG86", - "POSTCODE_OUTCODE": "SG8", - "MSOA": "E02003792", - "LSOA": "E01018267", - "GP_PRACTICE_CODE": "D81046", - "PCN": "U75549", - "ICB": "QUE", - "COMMISSIONING_REGION": "Y61", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - }, - { - "NHS_NUMBER": "9990032012", - "ATTRIBUTE_TYPE": "RSV", - "BOOKED_APPOINTMENT_DATE": "<>", - "LAST_SUCCESSFUL_DATE": "<>" - }, - { - "NHS_NUMBER": "9990032012", - "ATTRIBUTE_TYPE": "COVID", - "BOOKED_APPOINTMENT_DATE": "<>", - "LAST_SUCCESSFUL_DATE": "<>" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_015.json b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_015.json index 02b343b97..bf6397698 100644 --- a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_015.json +++ b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_015.json @@ -22,6 +22,12 @@ "13Q_FLAG": "N", "CARE_HOME_FLAG": "Y", "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9657933617", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": null } ] } diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-12.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-12.json deleted file mode 100644 index c57312555..000000000 --- a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-12.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "meta": { - "lastUpdated": "2025-07-15T14:52:52.785698+00:00" - }, - "processedSuggestions": [ - { - "actions": [], - "condition": "COVID", - "eligibilityCohorts": [ - { - "cohortCode": "covid_cohort_group", - "cohortStatus": "NotActionable", - "cohortText": "You are currently in a covid cohort" - } - ], - "status": "NotActionable", - "statusText": "You should have the COVID vaccine", - "suitabilityRules": [ - { - "ruleCode": "AlreadyVaccinated", - "ruleText": "##You've had your COVID vaccination\nWe believe you already had your COVID vaccination.", - "ruleType": "S" - } - ] - }, - { - "actions": [ - { - "actionCode": "AmendNBS", - "actionType": "ButtonWithAuthLink", - "description": "##You have an flu screening appointment\nYou can view, change or cancel your appointment below.", - "urlLabel": "Manage your appointment", - "urlLink": "http://www.nhs.uk/book-bs" - } - ], - "condition": "FLU", - "eligibilityCohorts": [ - { - "cohortCode": "FLU_screening_cohort_group", - "cohortStatus": "Actionable", - "cohortText": "You are currently in an flu SCREENING cohort" - } - ], - "status": "Actionable", - "statusText": "You should have the FLU vaccine", - "suitabilityRules": [] - }, - { - "actions": [], - "condition": "MMR", - "eligibilityCohorts": [ - { - "cohortCode": "mmr_cohort_group", - "cohortStatus": "NotEligible", - "cohortText": "You are not currently in an mmr cohort" - } - ], - "status": "NotEligible", - "statusText": "We do not believe you can have it", - "suitabilityRules": [] - }, - { - "actions": [], - "condition": "RSV", - "eligibilityCohorts": [ - { - "cohortCode": "rsv_screening_cohort_group", - "cohortStatus": "NotEligible", - "cohortText": "You are not currently in an RSV SCREENING cohort" - } - ], - "status": "NotEligible", - "statusText": "We do not believe you can have it", - "suitabilityRules": [] - } - ], - "responseId": "5c8d1cb3-8326-40b1-93ad-1b7fa24c2595" -} diff --git a/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_500.json b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_500.json index c6b5dcd75..fd6e101bf 100644 --- a/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_500.json +++ b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_500.json @@ -1,23 +1,23 @@ -{ - "resourceType": "OperationOutcome", - "id": "a4639b8e-4359-4f17-8016-d325846bbc2a", - "meta": { - "lastUpdated": "2025-08-01T08:16:47.379654Z" - }, - "issue": [ - { - "severity": "error", - "code": "processing", - "details": { - "coding": [ - { - "system": "https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1", - "code": "INTERNAL_SERVER_ERROR", - "display": "An unexpected internal server error occurred." - } - ] - }, - "diagnostics": "An unexpected error occurred." - } - ] -} + { + "resourceType": "OperationOutcome", + "id": "a4639b8e-4359-4f17-8016-d325846bbc2a", + "meta": { + "lastUpdated": "2025-08-01T08:16:47.379654Z" + }, + "issue": [ + { + "severity": "error", + "code": "processing", + "details": { + "coding": [ + { + "system": "https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1", + "code": "INTERNAL_SERVER_ERROR", + "display": "An unexpected internal server error occurred." + } + ] + }, + "diagnostics": "An unexpected error occurred." + } + ] + } diff --git a/tests/e2e/tests/test_story_tests.py b/tests/e2e/tests/test_story_tests.py index 541fb7c77..757510c22 100644 --- a/tests/e2e/tests/test_story_tests.py +++ b/tests/e2e/tests/test_story_tests.py @@ -14,7 +14,7 @@ id_list = [f"{filename} - {scenario.get('scenario_name', 'No Scenario')}" for filename, scenario in param_list] -@pytest.mark.functionale2eregression +@pytest.mark.storyregressiontests @pytest.mark.parametrize(("filename", "scenario"), param_list, ids=id_list) def test_run_story_test_cases(filename, scenario, eligibility_client, get_scenario_params): nhs_number, config_filenames, request_headers, query_params, expected_response_code = get_scenario_params(scenario, config_path) diff --git a/tests/e2e/tests/test_vita_integration_tests.py b/tests/e2e/tests/test_vita_integration_tests.py index 5162ee839..9658fc335 100644 --- a/tests/e2e/tests/test_vita_integration_tests.py +++ b/tests/e2e/tests/test_vita_integration_tests.py @@ -14,7 +14,7 @@ id_list = [f"{filename} - {scenario.get('scenario_name', 'No Scenario')}" for filename, scenario in param_list] -@pytest.mark.functionale2eregression +@pytest.mark.vitaintegration @pytest.mark.parametrize(("filename", "scenario"), param_list, ids=id_list) def test_run_story_test_cases(filename, scenario, eligibility_client, get_scenario_params): nhs_number, config_filenames, request_headers, query_params, expected_response_code = get_scenario_params(scenario, config_path) From b2191347dd89dcc7db7d7118d29c7e97a520b7ec Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Mon, 11 Aug 2025 20:41:04 +0100 Subject: [PATCH 67/72] added ELI-399, 373 regression tests tidying the VITA int tests removed 320-12 as invalid --- .../storyTestConfigs/AUTO_RSV_ELI-373-01.json | 453 ++++++++++++++++++ .../storyTestConfigs/AUTO_RSV_ELI-373-02.json | 447 +++++++++++++++++ .../storyTestConfigs/AUTO_RSV_ELI-399-01.json | 67 +++ .../storyTestConfigs/AUTO_RSV_ELI-399-02.json | 186 +++++++ .../storyTestConfigs/AUTO_RSV_ELI-399-03.json | 186 +++++++ .../storyTestConfigs/AUTO_RSV_ELI-399-04.json | 186 +++++++ .../storyTestConfigs/AUTO_RSV_ELI-399-05.json | 104 ++++ .../storyTestConfigs/AUTO_RSV_ELI-399-06.json | 104 ++++ .../storyTestConfigs/AUTO_RSV_ELI-399-07.json | 67 +++ .../storyTestData/AUTO_RSV_ELI-373_001.json | 46 ++ .../storyTestData/AUTO_RSV_ELI-373_002.json | 46 ++ .../storyTestData/AUTO_RSV_ELI-373_003.json | 50 ++ .../storyTestData/AUTO_RSV_ELI-373_004.json | 46 ++ .../storyTestData/AUTO_RSV_ELI-373_005.json | 46 ++ .../storyTestData/AUTO_RSV_ELI-373_006.json | 50 ++ .../storyTestData/AUTO_RSV_ELI-373_007.json | 50 ++ .../storyTestData/AUTO_RSV_ELI-373_008.json | 50 ++ .../storyTestData/AUTO_RSV_ELI-399_001.json | 54 +++ .../storyTestData/AUTO_RSV_ELI-399_002.json | 54 +++ .../storyTestData/AUTO_RSV_ELI-399_003.json | 54 +++ .../storyTestData/AUTO_RSV_ELI-399_004.json | 54 +++ .../storyTestData/AUTO_RSV_ELI-399_005.json | 54 +++ .../storyTestData/AUTO_RSV_ELI-399_006.json | 54 +++ .../storyTestData/AUTO_RSV_ELI-399_007.json | 55 +++ .../AUTO_RSV_ELI-373_001.json | 37 ++ .../AUTO_RSV_ELI-373_002.json | 37 ++ .../AUTO_RSV_ELI-373_003.json | 35 ++ .../AUTO_RSV_ELI-373_004.json | 36 ++ .../AUTO_RSV_ELI-373_005.json | 36 ++ .../AUTO_RSV_ELI-373_006.json | 35 ++ .../AUTO_RSV_ELI-373_007.json | 42 ++ .../AUTO_RSV_ELI-373_008.json | 51 ++ .../AUTO_RSV_ELI-399_001.json | 7 + .../AUTO_RSV_ELI-399_002.json | 27 ++ .../AUTO_RSV_ELI-399_003.json | 27 ++ .../AUTO_RSV_ELI-399_004.json | 7 + .../AUTO_RSV_ELI-399_005.json | 7 + .../AUTO_RSV_ELI-399_006.json | 7 + .../AUTO_RSV_ELI-399_007.json | 22 + 39 files changed, 2976 insertions(+) create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-373-01.json create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-373-02.json create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-01.json create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-02.json create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-03.json create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-04.json create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-05.json create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-06.json create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-07.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_001.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_002.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_003.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_004.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_005.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_006.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_007.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_008.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-399_001.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-399_002.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-399_003.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-399_004.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-399_005.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-399_006.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-399_007.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-373_001.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-373_002.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-373_003.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-373_004.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-373_005.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-373_006.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-373_007.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-373_008.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-399_001.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-399_002.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-399_003.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-399_004.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-399_005.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-399_006.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-399_007.json diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-373-01.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-373-01.json new file mode 100644 index 000000000..c96b688bd --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-373-01.json @@ -0,0 +1,453 @@ +{ + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "EliD RSV example config", + "Type": "V", + "Target": "RSV", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "BOOK_LOCAL|HELP_SUPPORT", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "CHECK_CORRECT_X", + "Version": 1, + "Name": "EliD RSV example config", + "IterationDate": "20250717", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_75to79", + "CohortGroup": "rsv_age", + "PositiveDescription": "are aged 75 to 79 years old", + "NegativeDescription": "are not aged 75 to 79 years old", + "Priority": 0 + }, + { + "CohortLabel": "rsv_80_since_02_Sept_2024", + "CohortGroup": "rsv_age_catchup", + "PositiveDescription": "turned 80 after 1st September 2024", + "NegativeDescription": "did not turn 80 after 1 September 2024", + "Priority": 10 + }, + { + "CohortLabel": "elid_all_people", + "CohortGroup": "magic_cohort", + "PositiveDescription": "", + "NegativeDescription": "", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "Remove anyone NOT already vaccinated within the last 25 years and do not have a future booking from the magic cohort", + "Description": "Remove anyone NOT already vaccinated within the last 25 years and do not have a future booking from the magic cohort", + "Operator": "Y<=", + "Comparator": "-25[[NVL:18000101]]", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "elid_all_people", + "Priority": 100 + }, + { + "Type": "F", + "Name": "Remove anyone without a future booking from magic cohort", + "Description": "Remove anyone without a future booking from magic cohort", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CohortLabel": "elid_all_people", + "Priority": 100 + }, + { + "Type": "F", + "Name": "Under Age - Under 75 Years on day of execution", + "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", + "Priority": 120, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75", + "CohortLabel": "rsv_75to79" + }, + { + "Type": "F", + "Name": "Exclude Too OLD", + "Description": "Exclude anyone who turned 80 before 2nd September 2024", + "Priority": 130, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "<", + "Comparator": "19440902", + "CohortLabel": "rsv_75to79" + }, + { + "Type": "F", + "Name": "Under Age - Under 75 Years on day of execution", + "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", + "Priority": 140, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75", + "CohortLabel": "rsv_80_since_02_Sept_2024" + }, + { + "Type": "F", + "Name": "Exclude Too OLD", + "Description": "Exclude anyone who turned 80 before 2nd September 2024", + "Priority": 150, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "<", + "Comparator": "19440902", + "CohortLabel": "rsv_80_since_02_Sept_2024" + }, + { + "Type": "S", + "Name": "Already Vaccinated", + "Description": "## You've had your RSV vaccination\n\nWe believe you had your vaccination.", + "Priority": 200, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "Y>=", + "Comparator": "-25", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "80 plus - Other Setting (Care Home) with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 510, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "80 plus - Other Setting (Care Home) with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 510, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "80 plus - Other Setting (Detained Estates) with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 520, + "AttributeLevel": "PERSON", + "AttributeName": "DE_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "80 plus - Other Setting (Detained Estates) with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 520, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "80 plus - Other Setting (13Q) with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 530, + "AttributeLevel": "PERSON", + "AttributeName": "13Q_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "80 plus - Other Setting (13Q) with no future booking - 2", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 530, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "75 to 79 - Other Setting (Care Home) with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 540, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_75to79", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "75 to 79 - Other Setting (Care Home) with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 540, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "75 to 79 - Other Setting (Detained Estates) with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 550, + "AttributeLevel": "PERSON", + "AttributeName": "DE_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_75to79", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "75 to 79 - Other Setting (Detained Estates) with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 550, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "75 to 79 - Other Setting (13Q) with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 560, + "AttributeLevel": "PERSON", + "AttributeName": "13Q_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_75to79", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "75 to 79 - Other Setting (13Q) with no future booking - 2", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 560, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]", + "RuleStop": "Y" + }, + { + "Type": "R", + "Name": "Actionable Future Booked NBS Appointment", + "Description": "Amend NBS future booking", + "Priority": 1000, + "Operator": "D>=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked NBS Appointment - 2", + "Description": "Amend NBS future booking", + "Priority": 1000, + "Operator": "=", + "Comparator": "NBS", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Local Appointment", + "Description": "Amend local future booking", + "Priority": 1100, + "Operator": "D>=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "MANAGE_LOCAL" + }, + { + "Type": "R", + "Name": "Within CP Expansion ICB", + "Description": "Book an appointment on NBS as within CP expansion", + "Priority": 1200, + "Operator": "in", + "Comparator": "QH8,QJG", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" + }, + { + "Type": "R", + "Name": "Within CP Expansion Local Authority", + "Description": "Book an appointment on NBS as within CP expansion", + "Priority": 1300, + "Operator": "in", + "Comparator": "E08000028,E08000031,E08000025,E06000016,E06000008,E07000117,E07000120,E08000011,E08000012,E07000122,E07000123,E08000014,E07000126,E08000013,E07000127,E08000015,E07000128", + "AttributeLevel": "PERSON", + "AttributeName": "LOCAL_AUTHORITY", + "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" + }, + { + "Type": "Y", + "Name": "Already vaccinated default text", + "Description": "Already vaccinated default text", + "Priority": 3000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "Y>=", + "Comparator": "-25", + "CommsRouting": "CHECK_CORRECT_ALREADY_VACCINATED" + }, + { + "Type": "Y", + "Name": "Other setting default text", + "Description": "Other setting default text", + "Priority": 3100, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y", + "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" + }, + { + "Type": "Y", + "Name": "Other setting default text", + "Description": "Other setting default text", + "Priority": 3200, + "AttributeLevel": "PERSON", + "AttributeName": "DE_FLAG", + "Operator": "=", + "Comparator": "Y", + "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" + }, + { + "Type": "Y", + "Name": "Other setting default text", + "Description": "Other setting default text", + "Priority": 3300, + "AttributeLevel": "PERSON", + "AttributeName": "13Q_FLAG", + "Operator": "=", + "Comparator": "Y", + "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + }, + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Manage your appointment" + }, + "CONTACT_GP": { + "ExternalRoutingCode": "ContactGP", + "ActionDescription": "Contact your GP", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "BOOK_LOCAL": { + "ExternalRoutingCode": "BookLocal", + "ActionDescription": "##Getting the vaccine\n\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "MANAGE_LOCAL": { + "ExternalRoutingCode": "ManageLocal", + "ActionDescription": "##You have an RSV vaccination appointment\n\nContact your healthcare provider to change or cancel your appointment.", + "ActionType": "CardWithText", + "UrlLink": null, + "UrlLabel": "" + }, + "HELP_SUPPORT": { + "ExternalRoutingCode": "HelpSupportInfo", + "ActionDescription": "## CONTENT TBC\n\nBlah blah blah.", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "CHECK_CORRECT_X": { + "ExternalRoutingCode": "HealthcareProInfo", + "ActionDescription": "## If you think this is incorrect\n\nSpeak to your healthcare professional if you think you should be offered this vaccine.\n\nFor anything else, visit our help and support page. (ADD LINK)", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "CHECK_CORRECT_ALREADY_VACCINATED": { + "ExternalRoutingCode": "AlreadyVaccinatedInfo", + "ActionDescription": "## If you think this is incorrect\n\nIf you believe you've not been vaccinated against RSV, speak to your healthcare professional.\n\nFor anything else please see our help and support page. (ADD LINK).", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "CHECK_CORRECT_OTHER_SETTING": { + "ExternalRoutingCode": "ManagedSettingInfo", + "ActionDescription": "## If you think this is incorrect\n\nIf you have already had this vaccination or your personal details are wrong, visit our help and support page. (ADD LINK).", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-373-02.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-373-02.json new file mode 100644 index 000000000..0cc3ee248 --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-373-02.json @@ -0,0 +1,447 @@ +{ + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "EliD RSV example config", + "Type": "V", + "Target": "RSV", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "BOOK_LOCAL|HELP_SUPPORT", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "CHECK_CORRECT_X", + "Version": 1, + "Name": "EliD RSV example config", + "IterationDate": "20250717", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_75to79", + "CohortGroup": "rsv_age", + "PositiveDescription": "are aged 75 to 79 years old", + "NegativeDescription": "are not aged 75 to 79 years old", + "Priority": 0 + }, + { + "CohortLabel": "rsv_80_since_02_Sept_2024", + "CohortGroup": "rsv_age_catchup", + "PositiveDescription": "turned 80 after 1st September 2024", + "NegativeDescription": "did not turn 80 after 1 September 2024", + "Priority": 10 + }, + { + "CohortLabel": "elid_all_people", + "CohortGroup": "magic_cohort", + "PositiveDescription": "", + "NegativeDescription": "", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "Remove anyone NOT already vaccinated within the last 25 years and do not have a future booking from the magic cohort", + "Description": "Remove anyone NOT already vaccinated within the last 25 years and do not have a future booking from the magic cohort", + "Operator": "Y<=", + "Comparator": "-25[[NVL:18000101]]", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "elid_all_people", + "Priority": 100 + }, + { + "Type": "F", + "Name": "Remove anyone without a future booking from magic cohort", + "Description": "Remove anyone without a future booking from magic cohort", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CohortLabel": "elid_all_people", + "Priority": 100 + }, + { + "Type": "F", + "Name": "Under Age - Under 75 Years on day of execution", + "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", + "Priority": 120, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75", + "CohortLabel": "rsv_75to79" + }, + { + "Type": "F", + "Name": "Exclude Too OLD", + "Description": "Exclude anyone who turned 80 before 2nd September 2024", + "Priority": 130, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "<", + "Comparator": "19440902", + "CohortLabel": "rsv_75to79" + }, + { + "Type": "F", + "Name": "Under Age - Under 75 Years on day of execution", + "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", + "Priority": 140, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75", + "CohortLabel": "rsv_80_since_02_Sept_2024" + }, + { + "Type": "F", + "Name": "Exclude Too OLD", + "Description": "Exclude anyone who turned 80 before 2nd September 2024", + "Priority": 150, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "<", + "Comparator": "19440902", + "CohortLabel": "rsv_80_since_02_Sept_2024" + }, + { + "Type": "S", + "Name": "Already Vaccinated", + "Description": "## You've had your RSV vaccination\n\nWe believe you had your vaccination.", + "Priority": 200, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "Y>=", + "Comparator": "-25", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "80 plus - Other Setting (Care Home) with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 510, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "RuleStop": "N" + }, + { + "Type": "S", + "Name": "80 plus - Other Setting (Care Home) with no future booking", + "Description": "", + "Priority": 510, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]" + }, + { + "Type": "S", + "Name": "80 plus - Other Setting (Detained Estates) with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 520, + "AttributeLevel": "PERSON", + "AttributeName": "DE_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "80 plus - Other Setting (Detained Estates) with no future booking", + "Description": "", + "Priority": 520, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]" + }, + { + "Type": "S", + "Name": "80 plus - Other Setting (13Q) with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 530, + "AttributeLevel": "PERSON", + "AttributeName": "13Q_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "80 plus - Other Setting (13Q) with no future booking", + "Description": "", + "Priority": 530, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]" + }, + { + "Type": "S", + "Name": "75 to 79 - Other Setting (Care Home) with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 540, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_75to79", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "75 to 79 - Other Setting (Care Home) with no future booking", + "Description": "", + "Priority": 540, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]" + }, + { + "Type": "S", + "Name": "75 to 79 - Other Setting (Detained Estates) with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 550, + "AttributeLevel": "PERSON", + "AttributeName": "DE_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_75to79" + }, + { + "Type": "S", + "Name": "75 to 79 - Other Setting (Detained Estates) with no future booking", + "Description": "", + "Priority": 550, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "75 to 79 - Other Setting (13Q) with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 560, + "AttributeLevel": "PERSON", + "AttributeName": "13Q_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_75to79", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "75 to 79 - Other Setting (13Q) with no future booking - 2", + "Description": "", + "Priority": 560, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]" + }, + { + "Type": "R", + "Name": "Actionable Future Booked NBS Appointment", + "Description": "Amend NBS future booking", + "Priority": 1000, + "Operator": "D>=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked NBS Appointment - 2", + "Description": "Amend NBS future booking", + "Priority": 1000, + "Operator": "=", + "Comparator": "NBS", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Local Appointment", + "Description": "Amend local future booking", + "Priority": 1100, + "Operator": "D>=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "MANAGE_LOCAL" + }, + { + "Type": "R", + "Name": "Within CP Expansion ICB", + "Description": "Book an appointment on NBS as within CP expansion", + "Priority": 1200, + "Operator": "in", + "Comparator": "QH8,QJG", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" + }, + { + "Type": "R", + "Name": "Within CP Expansion Local Authority", + "Description": "Book an appointment on NBS as within CP expansion", + "Priority": 1300, + "Operator": "in", + "Comparator": "E08000028,E08000031,E08000025,E06000016,E06000008,E07000117,E07000120,E08000011,E08000012,E07000122,E07000123,E08000014,E07000126,E08000013,E07000127,E08000015,E07000128", + "AttributeLevel": "PERSON", + "AttributeName": "LOCAL_AUTHORITY", + "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" + }, + { + "Type": "Y", + "Name": "Already vaccinated default text", + "Description": "Already vaccinated default text", + "Priority": 3000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "Y>=", + "Comparator": "-25", + "CommsRouting": "CHECK_CORRECT_ALREADY_VACCINATED" + }, + { + "Type": "Y", + "Name": "Other setting default text", + "Description": "Other setting default text", + "Priority": 3100, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y", + "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" + }, + { + "Type": "Y", + "Name": "Other setting default text", + "Description": "Other setting default text", + "Priority": 3200, + "AttributeLevel": "PERSON", + "AttributeName": "DE_FLAG", + "Operator": "=", + "Comparator": "Y", + "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" + }, + { + "Type": "Y", + "Name": "Other setting default text", + "Description": "Other setting default text", + "Priority": 3300, + "AttributeLevel": "PERSON", + "AttributeName": "13Q_FLAG", + "Operator": "=", + "Comparator": "Y", + "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + }, + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Manage your appointment" + }, + "CONTACT_GP": { + "ExternalRoutingCode": "ContactGP", + "ActionDescription": "Contact your GP", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "BOOK_LOCAL": { + "ExternalRoutingCode": "BookLocal", + "ActionDescription": "##Getting the vaccine\n\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "MANAGE_LOCAL": { + "ExternalRoutingCode": "ManageLocal", + "ActionDescription": "##You have an RSV vaccination appointment\n\nContact your healthcare provider to change or cancel your appointment.", + "ActionType": "CardWithText", + "UrlLink": null, + "UrlLabel": "" + }, + "HELP_SUPPORT": { + "ExternalRoutingCode": "HelpSupportInfo", + "ActionDescription": "## CONTENT TBC\n\nBlah blah blah.", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "CHECK_CORRECT_X": { + "ExternalRoutingCode": "HealthcareProInfo", + "ActionDescription": "## If you think this is incorrect\n\nSpeak to your healthcare professional if you think you should be offered this vaccine.\n\nFor anything else, visit our help and support page. (ADD LINK)", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "CHECK_CORRECT_ALREADY_VACCINATED": { + "ExternalRoutingCode": "AlreadyVaccinatedInfo", + "ActionDescription": "## If you think this is incorrect\n\nIf you believe you've not been vaccinated against RSV, speak to your healthcare professional.\n\nFor anything else please see our help and support page. (ADD LINK).", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "CHECK_CORRECT_OTHER_SETTING": { + "ExternalRoutingCode": "ManagedSettingInfo", + "ActionDescription": "## If you think this is incorrect\n\nIf you have already had this vaccination or your personal details are wrong, visit our help and support page. (ADD LINK).", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-01.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-01.json new file mode 100644 index 000000000..8e173675b --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-01.json @@ -0,0 +1,67 @@ +{ + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-399-01-Iteration-Config", + "Type": "V", + "Target": "RSV", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "<>", + "EndDate": "<>", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "ELI-399-01-Iteration-Config", + "IterationDate": "<>", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_399_cohort_1", + "CohortGroup": "rsv_eli_399_cohort_group", + "PositiveDescription": "are a member of eli_399_cohort_group_10", + "NegativeDescription": "are not a member of eli_399_cohort_group_10", + "Priority": 10 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason 1", + "Description": "Description 1", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_399_cohort_1", + "Priority": 100 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-02.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-02.json new file mode 100644 index 000000000..848d93f32 --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-02.json @@ -0,0 +1,186 @@ +{ + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-399-02-Iteration-Config", + "Type": "V", + "Target": "RSV", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "<>", + "EndDate": "<>", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "inactive-future-iteration-id", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "inactive-future-iteration", + "IterationDate": "<>", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_399_inactive_cohort_1", + "CohortGroup": "rsv_eli_399_inactive_cohort_group", + "PositiveDescription": "are a member of eli_399_inactive_cohort_group", + "NegativeDescription": "are not a member of eli_399_inactive_cohort_group", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_399_inactive_cohort_2", + "CohortGroup": "rsv_eli_399_inactive_cohort_group", + "PositiveDescription": "are a member of eli_399_inactive_cohort_group", + "NegativeDescription": "are not a member of eli_399_inactive_cohort_group", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_399_inactive_cohort_3", + "CohortGroup": "rsv_eli_399_inactive_cohort_group_other", + "PositiveDescription": "are a member of eli_399_inactive_cohort_group_other", + "NegativeDescription": "are not a member of eli_399_inactive_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_399_inactive_cohort_1", + "Priority": 100, + "RuleStop:": "Y" + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 4", + "Operator": "=", + "Comparator": "AAB", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CohortLabel": "rsv_eli_399_inactive_cohort_2", + "Priority": 110 + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 3", + "Operator": "=", + "Comparator": "ZZY", + "AttributeLevel": "PERSON", + "AttributeName": "COMMISSIONING_REGION", + "CohortLabel": "rsv_eli_399_inactive_cohort_3", + "Priority": 120 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + }, + { + "ID": "active-current-iteration-id", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "active-current-iteration", + "IterationDate": "<>", + "IterationNumber": 2, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_399_active_cohort_1", + "CohortGroup": "rsv_eli_399_active_cohort_group", + "PositiveDescription": "are a member of eli_399_active_cohort_group", + "NegativeDescription": "are not a member of eli_399_active_cohort_group", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_399_active_cohort_2", + "CohortGroup": "rsv_eli_399_active_cohort_group", + "PositiveDescription": "are a member of eli_399_active_cohort_group", + "NegativeDescription": "are not a member of eli_399_active_cohort_group", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_399_active_cohort_3", + "CohortGroup": "rsv_eli_399_active_cohort_group_other", + "PositiveDescription": "are a member of eli_399_active_cohort_group_other", + "NegativeDescription": "are not a member of eli_399_active_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_399_active_cohort_1", + "Priority": 100, + "RuleStop:": "Y" + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 4", + "Operator": "=", + "Comparator": "AAB", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CohortLabel": "rsv_eli_399_active_cohort_2", + "Priority": 110 + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 3", + "Operator": "=", + "Comparator": "ZZY", + "AttributeLevel": "PERSON", + "AttributeName": "COMMISSIONING_REGION", + "CohortLabel": "rsv_eli_399_active_cohort_3", + "Priority": 120 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-03.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-03.json new file mode 100644 index 000000000..47322f44c --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-03.json @@ -0,0 +1,186 @@ +{ + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-399-03-Iteration-Config", + "Type": "V", + "Target": "RSV", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "<>", + "EndDate": "<>", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "older-past-iteration-id", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "older-past-iteration", + "IterationDate": "<>", + "IterationNumber": 2, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_399_inactive_cohort_1", + "CohortGroup": "rsv_eli_399_inactive_cohort_group", + "PositiveDescription": "are a member of eli_399_inactive_cohort_group", + "NegativeDescription": "are not a member of eli_399_inactive_cohort_group", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_399_inactive_cohort_2", + "CohortGroup": "rsv_eli_399_inactive_cohort_group", + "PositiveDescription": "are a member of eli_399_inactive_cohort_group", + "NegativeDescription": "are not a member of eli_399_inactive_cohort_group", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_399_inactive_cohort_3", + "CohortGroup": "rsv_eli_399_inactive_cohort_group_other", + "PositiveDescription": "are a member of eli_399_inactive_cohort_group_other", + "NegativeDescription": "are not a member of eli_399_inactive_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_399_inactive_cohort_1", + "Priority": 100, + "RuleStop:": "Y" + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 4", + "Operator": "=", + "Comparator": "AAB", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CohortLabel": "rsv_eli_399_inactive_cohort_2", + "Priority": 110 + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 3", + "Operator": "=", + "Comparator": "ZZY", + "AttributeLevel": "PERSON", + "AttributeName": "COMMISSIONING_REGION", + "CohortLabel": "rsv_eli_399_inactive_cohort_3", + "Priority": 120 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + }, + { + "ID": "active-current-iteration-id", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "active-current-iteration", + "IterationDate": "<>", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_399_active_cohort_1", + "CohortGroup": "rsv_eli_399_active_cohort_group", + "PositiveDescription": "are a member of eli_399_active_cohort_group", + "NegativeDescription": "are not a member of eli_399_active_cohort_group", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_399_active_cohort_2", + "CohortGroup": "rsv_eli_399_active_cohort_group", + "PositiveDescription": "are a member of eli_399_active_cohort_group", + "NegativeDescription": "are not a member of eli_399_active_cohort_group", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_399_active_cohort_3", + "CohortGroup": "rsv_eli_399_active_cohort_group_other", + "PositiveDescription": "are a member of eli_399_active_cohort_group_other", + "NegativeDescription": "are not a member of eli_399_active_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_399_active_cohort_1", + "Priority": 100, + "RuleStop:": "Y" + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 4", + "Operator": "=", + "Comparator": "AAB", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CohortLabel": "rsv_eli_399_active_cohort_2", + "Priority": 110 + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 3", + "Operator": "=", + "Comparator": "ZZY", + "AttributeLevel": "PERSON", + "AttributeName": "COMMISSIONING_REGION", + "CohortLabel": "rsv_eli_399_active_cohort_3", + "Priority": 120 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-04.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-04.json new file mode 100644 index 000000000..89e4bb72b --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-04.json @@ -0,0 +1,186 @@ +{ + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-399-04-Iteration-Config", + "Type": "V", + "Target": "RSV", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "<>", + "EndDate": "<>", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "future-iteration-id", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "future-iteration-iteration", + "IterationDate": "<>", + "IterationNumber": 2, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_399_inactive_cohort_1", + "CohortGroup": "rsv_eli_399_inactive_cohort_group", + "PositiveDescription": "are a member of eli_399_inactive_cohort_group", + "NegativeDescription": "are not a member of eli_399_inactive_cohort_group", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_399_inactive_cohort_2", + "CohortGroup": "rsv_eli_399_inactive_cohort_group", + "PositiveDescription": "are a member of eli_399_inactive_cohort_group", + "NegativeDescription": "are not a member of eli_399_inactive_cohort_group", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_399_inactive_cohort_3", + "CohortGroup": "rsv_eli_399_inactive_cohort_group_other", + "PositiveDescription": "are a member of eli_399_inactive_cohort_group_other", + "NegativeDescription": "are not a member of eli_399_inactive_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_399_inactive_cohort_1", + "Priority": 100, + "RuleStop:": "Y" + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 4", + "Operator": "=", + "Comparator": "AAB", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CohortLabel": "rsv_eli_399_inactive_cohort_2", + "Priority": 110 + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 3", + "Operator": "=", + "Comparator": "ZZY", + "AttributeLevel": "PERSON", + "AttributeName": "COMMISSIONING_REGION", + "CohortLabel": "rsv_eli_399_inactive_cohort_3", + "Priority": 120 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + }, + { + "ID": "more-future-iteration-id", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "more-future-iteration", + "IterationDate": "<>", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_399_active_cohort_1", + "CohortGroup": "rsv_eli_399_active_cohort_group", + "PositiveDescription": "are a member of eli_399_active_cohort_group", + "NegativeDescription": "are not a member of eli_399_active_cohort_group", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_399_active_cohort_2", + "CohortGroup": "rsv_eli_399_active_cohort_group_2", + "PositiveDescription": "are a member of eli_399_active_cohort_group", + "NegativeDescription": "are not a member of eli_399_active_cohort_group", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_399_active_cohort_3", + "CohortGroup": "rsv_eli_399_active_cohort_group_other", + "PositiveDescription": "are a member of eli_399_active_cohort_group_other", + "NegativeDescription": "are not a member of eli_399_active_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_399_active_cohort_1", + "Priority": 100, + "RuleStop:": "Y" + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 4", + "Operator": "=", + "Comparator": "AAB", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CohortLabel": "rsv_eli_399_active_cohort_2", + "Priority": 110 + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 3", + "Operator": "=", + "Comparator": "ZZY", + "AttributeLevel": "PERSON", + "AttributeName": "COMMISSIONING_REGION", + "CohortLabel": "rsv_eli_399_active_cohort_3", + "Priority": 120 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-05.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-05.json new file mode 100644 index 000000000..cd794bc49 --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-05.json @@ -0,0 +1,104 @@ +{ + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-399-05-Iteration-Config", + "Type": "V", + "Target": "RSV", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "<>", + "EndDate": "<>", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "ELI-399-05-Iteration", + "IterationDate": "<>", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_399_cohort_1", + "CohortGroup": "rsv_eli_399_cohort_group", + "PositiveDescription": "are a member of eli_399_cohort_group", + "NegativeDescription": "are not a member of eli_399_cohort_group", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_399_cohort_2", + "CohortGroup": "rsv_eli_399_cohort_group", + "PositiveDescription": "are a member of eli_399_cohort_group", + "NegativeDescription": "are not a member of eli_399_cohort_group", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_399_cohort_3", + "CohortGroup": "rsv_eli_399_cohort_group_other", + "PositiveDescription": "are a member of eli_399_cohort_group_other", + "NegativeDescription": "are not a member of eli_399_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_399_cohort_1", + "Priority": 100, + "RuleStop:": "Y" + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 4", + "Operator": "=", + "Comparator": "AAB", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CohortLabel": "rsv_eli_399_cohort_2", + "Priority": 110 + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 3", + "Operator": "=", + "Comparator": "ZZY", + "AttributeLevel": "PERSON", + "AttributeName": "COMMISSIONING_REGION", + "CohortLabel": "rsv_eli_399_cohort_3", + "Priority": 120 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-06.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-06.json new file mode 100644 index 000000000..6cd224880 --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-06.json @@ -0,0 +1,104 @@ +{ + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-399-06-Iteration-Config", + "Type": "V", + "Target": "RSV", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20300801", + "EndDate": "20350807", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "ELI-399-06-Iteration", + "IterationDate": "20300805", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_399_cohort_1", + "CohortGroup": "rsv_eli_399_cohort_group", + "PositiveDescription": "are a member of eli_399_cohort_group", + "NegativeDescription": "are not a member of eli_399_cohort_group", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_399_cohort_2", + "CohortGroup": "rsv_eli_399_cohort_group", + "PositiveDescription": "are a member of eli_399_cohort_group", + "NegativeDescription": "are not a member of eli_399_cohort_group", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_399_cohort_3", + "CohortGroup": "rsv_eli_399_cohort_group_other", + "PositiveDescription": "are a member of eli_399_cohort_group_other", + "NegativeDescription": "are not a member of eli_399_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_399_cohort_1", + "Priority": 100, + "RuleStop:": "Y" + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 4", + "Operator": "=", + "Comparator": "AAB", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CohortLabel": "rsv_eli_399_cohort_2", + "Priority": 110 + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 3", + "Operator": "=", + "Comparator": "ZZY", + "AttributeLevel": "PERSON", + "AttributeName": "COMMISSIONING_REGION", + "CohortLabel": "rsv_eli_399_cohort_3", + "Priority": 120 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-07.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-07.json new file mode 100644 index 000000000..84ad72a53 --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-07.json @@ -0,0 +1,67 @@ +{ + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-399-07-Iteration-Config", + "Type": "V", + "Target": "COVID", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "<>", + "EndDate": "<>", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "Active Covid Iteration", + "IterationDate": "<>", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_399_cohort_1", + "CohortGroup": "rsv_eli_399_cohort_group", + "PositiveDescription": "are a member of eli_399_cohort_group_10", + "NegativeDescription": "are not a member of eli_399_cohort_group_10", + "Priority": 10 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason 1", + "Description": "Description 1", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_399_cohort_1", + "Priority": 100 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_001.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_001.json new file mode 100644 index 000000000..a591223c8 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_001.json @@ -0,0 +1,46 @@ +{ + "scenario_name": "ELI-373 - Past Booking - Not in Care Home ", + "request_headers": { + "nhs-login-nhs-number": "9900373001" + }, + "config_filenames": [ + "AUTO_RSV_ELI-373-01.json" + ], + "data": [ + { + "NHS_NUMBER": "9900373001", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_75to79", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900373001", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900373001", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_002.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_002.json new file mode 100644 index 000000000..4a16cd1ef --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_002.json @@ -0,0 +1,46 @@ +{ + "scenario_name": "ELI-373 - No Booking - Not in Care Home ", + "request_headers": { + "nhs-login-nhs-number": "9900373002" + }, + "config_filenames": [ + "AUTO_RSV_ELI-373-01.json" + ], + "data": [ + { + "NHS_NUMBER": "9900373002", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_80_since_02_Sept_2024", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900373002", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900373002", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": null, + "BOOKED_APPOINTMENT_PROVIDER": null + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_003.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_003.json new file mode 100644 index 000000000..ff099b84a --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_003.json @@ -0,0 +1,50 @@ +{ + "scenario_name": "ELI-373 - Future Booking - Not in Care Home ", + "request_headers": { + "nhs-login-nhs-number": "9900373003" + }, + "config_filenames": [ + "AUTO_RSV_ELI-373-01.json" + ], + "data": [ + { + "NHS_NUMBER": "9900373003", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_80_since_02_Sept_2024", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_75to79", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900373003", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900373003", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_004.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_004.json new file mode 100644 index 000000000..480b4d2a2 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_004.json @@ -0,0 +1,46 @@ +{ + "scenario_name": "ELI-373 - Past Booking - In Care Home ", + "request_headers": { + "nhs-login-nhs-number": "9900373004" + }, + "config_filenames": [ + "AUTO_RSV_ELI-373-01.json" + ], + "data": [ + { + "NHS_NUMBER": "9900373004", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_75to79", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900373004", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "Y", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900373004", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_005.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_005.json new file mode 100644 index 000000000..c372a1afa --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_005.json @@ -0,0 +1,46 @@ +{ + "scenario_name": "ELI-373 - No Booking - In Care Home ", + "request_headers": { + "nhs-login-nhs-number": "9900373005" + }, + "config_filenames": [ + "AUTO_RSV_ELI-373-01.json" + ], + "data": [ + { + "NHS_NUMBER": "9900373005", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_80_since_02_Sept_2024", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900373005", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "Y", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900373005", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE":null, + "BOOKED_APPOINTMENT_PROVIDER": null + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_006.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_006.json new file mode 100644 index 000000000..c2fc463d5 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_006.json @@ -0,0 +1,50 @@ +{ + "scenario_name": "ELI-373 - Future Booking - In Care Home ", + "request_headers": { + "nhs-login-nhs-number": "9900373006" + }, + "config_filenames": [ + "AUTO_RSV_ELI-373-01.json" + ], + "data": [ + { + "NHS_NUMBER": "9900373006", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_80_since_02_Sept_2024", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_75to79", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900373006", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "Y", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900373006", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_007.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_007.json new file mode 100644 index 000000000..713d3525e --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_007.json @@ -0,0 +1,50 @@ +{ + "scenario_name": "ELI-373 - No Booking - Not in DE", + "request_headers": { + "nhs-login-nhs-number": "9900373007" + }, + "config_filenames": [ + "AUTO_RSV_ELI-373-01.json" + ], + "data": [ + { + "NHS_NUMBER": "9900373007", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_80_since_02_Sept_2024", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_75to79", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900373007", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900373007", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": null, + "BOOKED_APPOINTMENT_PROVIDER": null + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_008.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_008.json new file mode 100644 index 000000000..9271ad755 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_008.json @@ -0,0 +1,50 @@ +{ + "scenario_name": "ELI-373 - No Booking - In Carehome, DE and 13Q", + "request_headers": { + "nhs-login-nhs-number": "9900373008" + }, + "config_filenames": [ + "AUTO_RSV_ELI-373-02.json" + ], + "data": [ + { + "NHS_NUMBER": "9900373008", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_80_since_02_Sept_2024", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_75to79", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900373008", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "Y", + "CARE_HOME_FLAG": "Y", + "DE_FLAG": "Y" + }, + { + "NHS_NUMBER": "9900373008", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": null, + "BOOKED_APPOINTMENT_PROVIDER": null + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-399_001.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-399_001.json new file mode 100644 index 000000000..bb63f3ec2 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-399_001.json @@ -0,0 +1,54 @@ +{ + "scenario_name": "ELI-339 - Inactive Iteration - 1 Iteration - Future Iteration Date", + "request_headers": { + "nhs-login-nhs-number": "9900339001" + }, + "config_filenames": [ + "AUTO_RSV_ELI-399-01.json" + ], + "data": [ + { + "NHS_NUMBER": "9900339001", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_eli_274_cohort_1", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_274_cohort_2", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_274_cohort_3", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900339001", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "AAA", + "COMMISSIONING_REGION": "ZZZ", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "Y" + }, + { + "NHS_NUMBER": "9900339001", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-399_002.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-399_002.json new file mode 100644 index 000000000..42beeecfb --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-399_002.json @@ -0,0 +1,54 @@ +{ + "scenario_name": "ELI-339 - 1 Active Iteration 1 Inactive Future Iteration", + "request_headers": { + "nhs-login-nhs-number": "9990039902" + }, + "config_filenames": [ + "AUTO_RSV_ELI-399-02.json" + ], + "data": [ + { + "NHS_NUMBER": "9990039902", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_eli_274_cohort_1", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_274_cohort_2", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_274_cohort_3", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9990039902", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "AAA", + "COMMISSIONING_REGION": "ZZZ", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "Y" + }, + { + "NHS_NUMBER": "9990039902", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-399_003.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-399_003.json new file mode 100644 index 000000000..86a3f5530 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-399_003.json @@ -0,0 +1,54 @@ +{ + "scenario_name": "ELI-339 - 2 iterations in past - most recent iteration chosen", + "request_headers": { + "nhs-login-nhs-number": "9990039903" + }, + "config_filenames": [ + "AUTO_RSV_ELI-399-03.json" + ], + "data": [ + { + "NHS_NUMBER": "9990039903", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_eli_274_cohort_1", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_274_cohort_2", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_274_cohort_3", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9990039903", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "AAA", + "COMMISSIONING_REGION": "ZZZ", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "Y" + }, + { + "NHS_NUMBER": "9990039903", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-399_004.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-399_004.json new file mode 100644 index 000000000..7051a7011 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-399_004.json @@ -0,0 +1,54 @@ +{ + "scenario_name": "ELI-339 - 2 iterations in future - empty processedsuggestions", + "request_headers": { + "nhs-login-nhs-number": "9990039904" + }, + "config_filenames": [ + "AUTO_RSV_ELI-399-04.json" + ], + "data": [ + { + "NHS_NUMBER": "9990039904", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_eli_274_cohort_1", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_274_cohort_2", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_274_cohort_3", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9990039904", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "AAA", + "COMMISSIONING_REGION": "ZZZ", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "Y" + }, + { + "NHS_NUMBER": "9990039904", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-399_005.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-399_005.json new file mode 100644 index 000000000..3e343141b --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-399_005.json @@ -0,0 +1,54 @@ +{ + "scenario_name": "ELI-339 - past campaign boundaries processedsuggestions", + "request_headers": { + "nhs-login-nhs-number": "9990039905" + }, + "config_filenames": [ + "AUTO_RSV_ELI-399-05.json" + ], + "data": [ + { + "NHS_NUMBER": "9990039905", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_eli_274_cohort_1", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_274_cohort_2", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_274_cohort_3", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9990039905", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "AAA", + "COMMISSIONING_REGION": "ZZZ", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "Y" + }, + { + "NHS_NUMBER": "9990039905", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-399_006.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-399_006.json new file mode 100644 index 000000000..1443aedde --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-399_006.json @@ -0,0 +1,54 @@ +{ + "scenario_name": "ELI-339 - future campaign boundaries processedsuggestions", + "request_headers": { + "nhs-login-nhs-number": "9990039906" + }, + "config_filenames": [ + "AUTO_RSV_ELI-399-06.json" + ], + "data": [ + { + "NHS_NUMBER": "9990039906", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_eli_274_cohort_1", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_274_cohort_2", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_274_cohort_3", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9990039906", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "AAA", + "COMMISSIONING_REGION": "ZZZ", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "Y" + }, + { + "NHS_NUMBER": "9990039906", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-399_007.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-399_007.json new file mode 100644 index 000000000..5e9c9c4fd --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-399_007.json @@ -0,0 +1,55 @@ +{ + "scenario_name": "ELI-339 - Inactive RSV, Active Covid", + "request_headers": { + "nhs-login-nhs-number": "9990039906" + }, + "config_filenames": [ + "AUTO_RSV_ELI-399-01.json", + "AUTO_RSV_ELI-399-07.json" + ], + "data": [ + { + "NHS_NUMBER": "9990039906", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_eli_274_cohort_1", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_274_cohort_2", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_274_cohort_3", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9990039906", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "AAA", + "COMMISSIONING_REGION": "ZZZ", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "Y" + }, + { + "NHS_NUMBER": "9990039906", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-373_001.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-373_001.json new file mode 100644 index 000000000..161e47856 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-373_001.json @@ -0,0 +1,37 @@ +{ + "meta": { + "lastUpdated": "2025-08-11T16:04:27.923796+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "BookLocal", + "actionType": "InfoText", + "description": "##Getting the vaccine\n\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "urlLabel": "", + "urlLink": "" + }, + { + "actionCode": "HelpSupportInfo", + "actionType": "InfoText", + "description": "## CONTENT TBC\n\nBlah blah blah.", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age", + "cohortStatus": "Actionable", + "cohortText": "are aged 75 to 79 years old" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "a09f1caf-8c0b-4448-a7a5-d307924db8b5" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-373_002.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-373_002.json new file mode 100644 index 000000000..fb3f1d028 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-373_002.json @@ -0,0 +1,37 @@ +{ + "meta": { + "lastUpdated": "2025-08-11T16:04:27.923796+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "BookLocal", + "actionType": "InfoText", + "description": "##Getting the vaccine\n\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "urlLabel": "", + "urlLink": "" + }, + { + "actionCode": "HelpSupportInfo", + "actionType": "InfoText", + "description": "## CONTENT TBC\n\nBlah blah blah.", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_catchup", + "cohortStatus": "Actionable", + "cohortText": "turned 80 after 1st September 2024" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "a09f1caf-8c0b-4448-a7a5-d307924db8b5" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-373_003.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-373_003.json new file mode 100644 index 000000000..0807411ba --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-373_003.json @@ -0,0 +1,35 @@ +{ + "meta": { + "lastUpdated": "2025-08-11T16:06:59.787096+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "AmendNBS", + "actionType": "ButtonWithAuthLink", + "description": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", + "urlLabel": "Manage your appointment", + "urlLink": "http://www.nhs.uk/book-rsv" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age", + "cohortStatus": "Actionable", + "cohortText": "are aged 75 to 79 years old" + }, + { + "cohortCode": "rsv_age_catchup", + "cohortStatus": "Actionable", + "cohortText": "turned 80 after 1st September 2024" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "9dc41999-d5e0-4549-bcd8-6265a4284997" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-373_004.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-373_004.json new file mode 100644 index 000000000..bfb84ac34 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-373_004.json @@ -0,0 +1,36 @@ +{ + "meta": { + "lastUpdated": "2025-08-11T16:08:58.770053+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "ManagedSettingInfo", + "actionType": "InfoText", + "description": "## If you think this is incorrect\n\nIf you have already had this vaccination or your personal details are wrong, visit our help and support page. (ADD LINK).", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age", + "cohortStatus": "NotActionable", + "cohortText": "are aged 75 to 79 years old" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "75 to 79 - Other Setting (Care Home) with no future booking", + "ruleText": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "ruleType": "S" + } + ] + } + ], + "responseId": "f35fe552-5b0b-4be1-9a83-61696de605f6" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-373_005.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-373_005.json new file mode 100644 index 000000000..41c3f8730 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-373_005.json @@ -0,0 +1,36 @@ +{ + "meta": { + "lastUpdated": "2025-08-11T16:08:58.770053+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "ManagedSettingInfo", + "actionType": "InfoText", + "description": "## If you think this is incorrect\n\nIf you have already had this vaccination or your personal details are wrong, visit our help and support page. (ADD LINK).", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_catchup", + "cohortStatus": "NotActionable", + "cohortText": "turned 80 after 1st September 2024" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "80 plus - Other Setting (Care Home) with no future booking", + "ruleText": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "ruleType": "S" + } + ] + } + ], + "responseId": "f35fe552-5b0b-4be1-9a83-61696de605f6" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-373_006.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-373_006.json new file mode 100644 index 000000000..321775245 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-373_006.json @@ -0,0 +1,35 @@ +{ + "meta": { + "lastUpdated": "2025-08-11T16:08:19.685910+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "AmendNBS", + "actionType": "ButtonWithAuthLink", + "description": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", + "urlLabel": "Manage your appointment", + "urlLink": "http://www.nhs.uk/book-rsv" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age", + "cohortStatus": "Actionable", + "cohortText": "are aged 75 to 79 years old" + }, + { + "cohortCode": "rsv_age_catchup", + "cohortStatus": "Actionable", + "cohortText": "turned 80 after 1st September 2024" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "bc3da5e2-b3b6-4979-b0fe-e40c48dd9e1c" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-373_007.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-373_007.json new file mode 100644 index 000000000..17669afd9 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-373_007.json @@ -0,0 +1,42 @@ +{ + "meta": { + "lastUpdated": "2025-08-11T16:04:27.923796+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "BookLocal", + "actionType": "InfoText", + "description": "##Getting the vaccine\n\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "urlLabel": "", + "urlLink": "" + }, + { + "actionCode": "HelpSupportInfo", + "actionType": "InfoText", + "description": "## CONTENT TBC\n\nBlah blah blah.", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age", + "cohortStatus": "Actionable", + "cohortText": "are aged 75 to 79 years old" + }, + { + "cohortCode": "rsv_age_catchup", + "cohortStatus": "Actionable", + "cohortText": "turned 80 after 1st September 2024" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "a09f1caf-8c0b-4448-a7a5-d307924db8b5" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-373_008.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-373_008.json new file mode 100644 index 000000000..67c01a813 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-373_008.json @@ -0,0 +1,51 @@ +{ + "meta": { + "lastUpdated": "2025-08-11T16:34:44.019817+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "ManagedSettingInfo", + "actionType": "InfoText", + "description": "## If you think this is incorrect\n\nIf you have already had this vaccination or your personal details are wrong, visit our help and support page. (ADD LINK).", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age", + "cohortStatus": "NotActionable", + "cohortText": "are aged 75 to 79 years old" + }, + { + "cohortCode": "rsv_age_catchup", + "cohortStatus": "NotActionable", + "cohortText": "turned 80 after 1st September 2024" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "75 to 79 - Other Setting (Care Home) with no future booking", + "ruleText": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "ruleType": "S" + }, + { + "ruleCode": "80 plus - Other Setting (Care Home) with no future booking", + "ruleText": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "ruleType": "S" + }, + { + "ruleCode": "80 plus - Other Setting (Detained Estates) with no future booking", + "ruleText": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "ruleType": "S" + } + ] + } + ], + "responseId": "ac49dbde-9adb-49a5-a180-6503b7adcf82" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-399_001.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-399_001.json new file mode 100644 index 000000000..a6ff81f36 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-399_001.json @@ -0,0 +1,7 @@ +{ + "meta": { + "lastUpdated": "2025-08-06T15:15:45.231568+00:00" + }, + "processedSuggestions": [], + "responseId": "f6dfcae4-32c4-493a-a0cd-5faf83f3da64" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-399_002.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-399_002.json new file mode 100644 index 000000000..43ff26a3f --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-399_002.json @@ -0,0 +1,27 @@ +{ + "meta": { + "lastUpdated": "2025-08-07T19:59:03.606275+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_eli_399_active_cohort_group", + "cohortStatus": "NotEligible", + "cohortText": "are not a member of eli_399_active_cohort_group" + }, + { + "cohortCode": "rsv_eli_399_active_cohort_group_other", + "cohortStatus": "NotEligible", + "cohortText": "are not a member of eli_399_active_cohort_group_other" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "7bf9eeeb-4951-4ee9-95ad-64d9a556908d" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-399_003.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-399_003.json new file mode 100644 index 000000000..43ff26a3f --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-399_003.json @@ -0,0 +1,27 @@ +{ + "meta": { + "lastUpdated": "2025-08-07T19:59:03.606275+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_eli_399_active_cohort_group", + "cohortStatus": "NotEligible", + "cohortText": "are not a member of eli_399_active_cohort_group" + }, + { + "cohortCode": "rsv_eli_399_active_cohort_group_other", + "cohortStatus": "NotEligible", + "cohortText": "are not a member of eli_399_active_cohort_group_other" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "7bf9eeeb-4951-4ee9-95ad-64d9a556908d" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-399_004.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-399_004.json new file mode 100644 index 000000000..a6ff81f36 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-399_004.json @@ -0,0 +1,7 @@ +{ + "meta": { + "lastUpdated": "2025-08-06T15:15:45.231568+00:00" + }, + "processedSuggestions": [], + "responseId": "f6dfcae4-32c4-493a-a0cd-5faf83f3da64" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-399_005.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-399_005.json new file mode 100644 index 000000000..a6ff81f36 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-399_005.json @@ -0,0 +1,7 @@ +{ + "meta": { + "lastUpdated": "2025-08-06T15:15:45.231568+00:00" + }, + "processedSuggestions": [], + "responseId": "f6dfcae4-32c4-493a-a0cd-5faf83f3da64" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-399_006.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-399_006.json new file mode 100644 index 000000000..a6ff81f36 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-399_006.json @@ -0,0 +1,7 @@ +{ + "meta": { + "lastUpdated": "2025-08-06T15:15:45.231568+00:00" + }, + "processedSuggestions": [], + "responseId": "f6dfcae4-32c4-493a-a0cd-5faf83f3da64" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-399_007.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-399_007.json new file mode 100644 index 000000000..408f1c61c --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-399_007.json @@ -0,0 +1,22 @@ +{ + "meta": { + "lastUpdated": "" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "COVID", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_eli_399_cohort_group", + "cohortStatus": "NotEligible", + "cohortText": "are not a member of eli_399_cohort_group_10" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "" +} From 8fc6b55dd844cf6ad5d69d88ec88d6c093fc3f60 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Tue, 12 Aug 2025 11:13:58 +0100 Subject: [PATCH 68/72] added ELI-399, 373 regression tests tidying the VITA int tests removed 320-12 as invalid removed old code added Sean handover stuff --- tests/e2e/.gitignore | 2 + tests/e2e/data/audit/AUTO_RSV_ELI-376-01.json | 85 ++++ tests/e2e/data/audit/AUTO_RSV_ELI-376-02.json | 90 ++++ tests/e2e/data/audit/AUTO_RSV_ELI-376-03.json | 96 ++++ tests/e2e/data/audit/AUTO_RSV_ELI-376-04.json | 85 ++++ tests/e2e/data/audit/AUTO_RSV_ELI-376-05.json | 102 ++++ tests/e2e/data/audit/AUTO_RSV_ELI-376-10.json | 76 +++ .../AUTO_RSV_ELI-406-01.json | 108 +++++ .../ELI-220-371/AUTO_RSV_ELI-220-01.json | 88 ++++ .../ELI-220-371/AUTO_RSV_ELI-220-02.json | 88 ++++ .../ELI-220-371/AUTO_RSV_ELI-220-03.json | 87 ++++ .../ELI-220-371/AUTO_RSV_ELI-220-04.json | 87 ++++ .../ELI-220-371/AUTO_RSV_ELI-220-05.json | 87 ++++ .../ELI-220-371/AUTO_RSV_ELI-220-06.json | 87 ++++ .../ELI-274-371/AUTO_RSV_ELI-274-01.json | 103 ++++ .../ELI-274-371/AUTO_RSV_ELI-274-05.json | 103 ++++ .../ELI-274-371/AUTO_RSV_ELI-274-06.json | 103 ++++ .../ELI-274-371/AUTO_RSV_ELI-274-07.json | 104 ++++ .../ELI-376-405/AUTO_RSV_ELI-376-01.json | 119 +++++ .../ELI-376-405/AUTO_RSV_ELI-376-02.json | 118 +++++ .../ELI-376-405/AUTO_RSV_ELI-376-03.json | 118 +++++ .../ELI-376-405/AUTO_RSV_ELI-376-04.json | 119 +++++ .../ELI-376-405/AUTO_RSV_ELI-376-05.json | 161 +++++++ .../ELI-376-405/AUTO_RSV_ELI-376-10.json | 141 ++++++ .../AUTO_RSV_ELI-365v0.5.json | 451 ++++++++++++++++++ .../AUTO_RSV_ELI-406_001.json | 41 ++ .../AUTO_RSV_ELI-406_002.json | 19 + .../AUTO_RSV_ELI-406_003.json | 34 ++ .../ELI-220-371/AUTO_RSV_ELI-220_001.json | 58 +++ .../ELI-220-371/AUTO_RSV_ELI-220_002.json | 58 +++ .../ELI-220-371/AUTO_RSV_ELI-220_003.json | 58 +++ .../ELI-220-371/AUTO_RSV_ELI-220_004.json | 58 +++ .../ELI-220-371/AUTO_RSV_ELI-220_005.json | 58 +++ .../ELI-220-371/AUTO_RSV_ELI-220_006.json | 58 +++ .../ELI-274-371/AUTO_RSV_ELI-274_001.json | 50 ++ .../ELI-274-371/AUTO_RSV_ELI-274_002.json | 50 ++ .../ELI-274-371/AUTO_RSV_ELI-274_003.json | 50 ++ .../ELI-274-371/AUTO_RSV_ELI-274_004.json | 50 ++ .../ELI-274-371/AUTO_RSV_ELI-274_005.json | 50 ++ .../ELI-274-371/AUTO_RSV_ELI-274_006.json | 50 ++ .../ELI-274-371/AUTO_RSV_ELI-274_007.json | 50 ++ .../ELI-376-405/AUTO_RSV_ELI-376_001.json | 58 +++ .../ELI-376-405/AUTO_RSV_ELI-376_002.json | 58 +++ .../ELI-376-405/AUTO_RSV_ELI-376_003.json | 58 +++ .../ELI-376-405/AUTO_RSV_ELI-376_004.json | 58 +++ .../ELI-376-405/AUTO_RSV_ELI-376_005.json | 58 +++ .../ELI-376-405/AUTO_RSV_ELI-376_010.json | 58 +++ tests/e2e/data/generate_dynamo_data.py | 116 ----- .../AUTO_RSV_ELI-406_001.json | 26 + .../AUTO_RSV_ELI-406_002.json | 26 + .../AUTO_RSV_ELI-406_003.json | 26 + .../ELI-220-371/AUTO_RSV_ELI-220_001.json | 27 ++ .../ELI-220-371/AUTO_RSV_ELI-220_002.json | 27 ++ .../ELI-220-371/AUTO_RSV_ELI-220_003.json | 33 ++ .../ELI-220-371/AUTO_RSV_ELI-220_004.json | 33 ++ .../ELI-220-371/AUTO_RSV_ELI-220_005.json | 27 ++ .../ELI-220-371/AUTO_RSV_ELI-220_006.json | 27 ++ .../ELI-274-371/AUTO_RSV_ELI-274_001.json | 27 ++ .../ELI-274-371/AUTO_RSV_ELI-274_002.json | 27 ++ .../ELI-274-371/AUTO_RSV_ELI-274_003.json | 22 + .../ELI-274-371/AUTO_RSV_ELI-274_004.json | 43 ++ .../ELI-274-371/AUTO_RSV_ELI-274_005.json | 38 ++ .../ELI-274-371/AUTO_RSV_ELI-274_006.json | 33 ++ .../ELI-274-371/AUTO_RSV_ELI-274_007.json | 33 ++ .../ELI-376-405/AUTO_RSV_ELI-376_001.json | 35 ++ .../ELI-376-405/AUTO_RSV_ELI-376_002.json | 35 ++ .../ELI-376-405/AUTO_RSV_ELI-376_003.json | 36 ++ .../ELI-376-405/AUTO_RSV_ELI-376_004.json | 30 ++ .../ELI-376-405/AUTO_RSV_ELI-376_005.json | 35 ++ .../ELI-376-405/AUTO_RSV_ELI-376_010.json | 46 ++ .../AUTO_RSV_ELI-216-2.json | 25 +- tests/e2e/features/__init__.py | 0 tests/e2e/features/conftest.py | 32 -- .../eligibility_check.feature | 44 -- tests/e2e/features/environment.py | 189 -------- .../features/steps/eligibility_check_steps.py | 85 ---- 76 files changed, 4664 insertions(+), 467 deletions(-) create mode 100644 tests/e2e/data/audit/AUTO_RSV_ELI-376-01.json create mode 100644 tests/e2e/data/audit/AUTO_RSV_ELI-376-02.json create mode 100644 tests/e2e/data/audit/AUTO_RSV_ELI-376-03.json create mode 100644 tests/e2e/data/audit/AUTO_RSV_ELI-376-04.json create mode 100644 tests/e2e/data/audit/AUTO_RSV_ELI-376-05.json create mode 100644 tests/e2e/data/audit/AUTO_RSV_ELI-376-10.json create mode 100644 tests/e2e/data/configs/inProgressTestConfigs/AUTO_RSV_ELI-406-01.json create mode 100644 tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-01.json create mode 100644 tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-02.json create mode 100644 tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-03.json create mode 100644 tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-04.json create mode 100644 tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-05.json create mode 100644 tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-06.json create mode 100644 tests/e2e/data/configs/inProgressTestConfigs/ELI-274-371/AUTO_RSV_ELI-274-01.json create mode 100644 tests/e2e/data/configs/inProgressTestConfigs/ELI-274-371/AUTO_RSV_ELI-274-05.json create mode 100644 tests/e2e/data/configs/inProgressTestConfigs/ELI-274-371/AUTO_RSV_ELI-274-06.json create mode 100644 tests/e2e/data/configs/inProgressTestConfigs/ELI-274-371/AUTO_RSV_ELI-274-07.json create mode 100644 tests/e2e/data/configs/inProgressTestConfigs/ELI-376-405/AUTO_RSV_ELI-376-01.json create mode 100644 tests/e2e/data/configs/inProgressTestConfigs/ELI-376-405/AUTO_RSV_ELI-376-02.json create mode 100644 tests/e2e/data/configs/inProgressTestConfigs/ELI-376-405/AUTO_RSV_ELI-376-03.json create mode 100644 tests/e2e/data/configs/inProgressTestConfigs/ELI-376-405/AUTO_RSV_ELI-376-04.json create mode 100644 tests/e2e/data/configs/inProgressTestConfigs/ELI-376-405/AUTO_RSV_ELI-376-05.json create mode 100644 tests/e2e/data/configs/inProgressTestConfigs/ELI-376-405/AUTO_RSV_ELI-376-10.json create mode 100644 tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-365v0.5.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-406_001.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-406_002.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-406_003.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/ELI-220-371/AUTO_RSV_ELI-220_001.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/ELI-220-371/AUTO_RSV_ELI-220_002.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/ELI-220-371/AUTO_RSV_ELI-220_003.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/ELI-220-371/AUTO_RSV_ELI-220_004.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/ELI-220-371/AUTO_RSV_ELI-220_005.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/ELI-220-371/AUTO_RSV_ELI-220_006.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_001.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_002.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_003.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_004.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_005.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_006.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_007.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/ELI-376-405/AUTO_RSV_ELI-376_001.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/ELI-376-405/AUTO_RSV_ELI-376_002.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/ELI-376-405/AUTO_RSV_ELI-376_003.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/ELI-376-405/AUTO_RSV_ELI-376_004.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/ELI-376-405/AUTO_RSV_ELI-376_005.json create mode 100644 tests/e2e/data/dynamoDB/inProgressTestData/ELI-376-405/AUTO_RSV_ELI-376_010.json delete mode 100644 tests/e2e/data/generate_dynamo_data.py create mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-406_001.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-406_002.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-406_003.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/ELI-220-371/AUTO_RSV_ELI-220_001.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/ELI-220-371/AUTO_RSV_ELI-220_002.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/ELI-220-371/AUTO_RSV_ELI-220_003.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/ELI-220-371/AUTO_RSV_ELI-220_004.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/ELI-220-371/AUTO_RSV_ELI-220_005.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/ELI-220-371/AUTO_RSV_ELI-220_006.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/ELI-274-371/AUTO_RSV_ELI-274_001.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/ELI-274-371/AUTO_RSV_ELI-274_002.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/ELI-274-371/AUTO_RSV_ELI-274_003.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/ELI-274-371/AUTO_RSV_ELI-274_004.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/ELI-274-371/AUTO_RSV_ELI-274_005.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/ELI-274-371/AUTO_RSV_ELI-274_006.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/ELI-274-371/AUTO_RSV_ELI-274_007.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/ELI-376-405/AUTO_RSV_ELI-376_001.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/ELI-376-405/AUTO_RSV_ELI-376_002.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/ELI-376-405/AUTO_RSV_ELI-376_003.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/ELI-376-405/AUTO_RSV_ELI-376_004.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/ELI-376-405/AUTO_RSV_ELI-376_005.json create mode 100644 tests/e2e/data/responses/inProgressTestResponses/ELI-376-405/AUTO_RSV_ELI-376_010.json delete mode 100644 tests/e2e/features/__init__.py delete mode 100644 tests/e2e/features/conftest.py delete mode 100644 tests/e2e/features/eligibility_check/eligibility_check.feature delete mode 100644 tests/e2e/features/environment.py delete mode 100644 tests/e2e/features/steps/eligibility_check_steps.py diff --git a/tests/e2e/.gitignore b/tests/e2e/.gitignore index 9becdb2de..c9d29fc72 100644 --- a/tests/e2e/.gitignore +++ b/tests/e2e/.gitignore @@ -42,3 +42,5 @@ assets/ # Logs *.log /helpers/output/ +/.hypothesis/unicode_data/15.1.0/charmap.json.gz +/tests/.hypothesis/unicode_data/15.1.0/charmap.json.gz diff --git a/tests/e2e/data/audit/AUTO_RSV_ELI-376-01.json b/tests/e2e/data/audit/AUTO_RSV_ELI-376-01.json new file mode 100644 index 000000000..37a1a8814 --- /dev/null +++ b/tests/e2e/data/audit/AUTO_RSV_ELI-376-01.json @@ -0,0 +1,85 @@ +{ + "request": { + "requestTimestamp": "2025-08-11 07:43:12.954767+00:00", + "headers": { + "xRequestId": null, + "xCorrelationId": null, + "nhsdEndUserOrganisationOds": null, + "nhsdApplicationId": null + }, + "queryParams": { + "category": null, + "conditions": null, + "includeActions": null + }, + "nhsNumber": "9900376001" + }, + "response": { + "responseId": "0300f1e8-6004-4229-a972-29220f08b8e9", + "lastUpdated": "2025-08-11 07:43:13.267973+00:00", + "condition": [ + { + "campaignId": "AUTO_RSV_ELI-376-01-Campaign-ID", + "campaignVersion": 1, + "iterationId": "AUTO_RSV_ELI-376-01-Iteration-ID", + "iterationVersion": 1, + "conditionName": "RSV", + "status": "actionable", + "statusText": "You should have the RSV vaccine", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_eli_376_cohort_1", + "cohortStatus": "actionable" + }, + { + "cohortCode": "rsv_eli_376_cohort_2", + "cohortStatus": "actionable" + }, + { + "cohortCode": "rsv_eli_376_cohort_3", + "cohortStatus": "actionable" + }, + { + "cohortCode": "rsv_eli_376_cohort_4", + "cohortStatus": "actionable" + } + ], + "eligibilityCohortGroups": [ + { + "cohortCode": "rsv_eli_376_cohort_group", + "cohortText": "are a member of eli_376_cohort_group_0", + "cohortStatus": "actionable" + }, + { + "cohortCode": "rsv_eli_376_cohort_group", + "cohortText": "are a member of eli_376_cohort_group_10", + "cohortStatus": "actionable" + }, + { + "cohortCode": "rsv_eli_376_cohort_group", + "cohortText": "are a member of eli_376_cohort_group_20", + "cohortStatus": "actionable" + }, + { + "cohortCode": "rsv_eli_376_cohort_group_other", + "cohortText": "are a member of eli_376_cohort_group_other", + "cohortStatus": "actionable" + } + ], + "filterRules": null, + "suitabilityRules": null, + "actionRule": null, + "actions": [ + { + "internalActionCode": "TEST_ACTION", + "actionType": "ButtonWithAuthLink", + "actionCode": "TestAction", + "actionDescription": "TestAction Description", + "actionUrl": "http://www.nhs.uk/book-rsv", + "actionUrlLabel": "Continue to booking" + } + ] + } + ] + } +} diff --git a/tests/e2e/data/audit/AUTO_RSV_ELI-376-02.json b/tests/e2e/data/audit/AUTO_RSV_ELI-376-02.json new file mode 100644 index 000000000..955390d58 --- /dev/null +++ b/tests/e2e/data/audit/AUTO_RSV_ELI-376-02.json @@ -0,0 +1,90 @@ +{ + "request": { + "requestTimestamp": "2025-08-11 07:43:11.974643+00:00", + "headers": { + "xRequestId": null, + "xCorrelationId": null, + "nhsdEndUserOrganisationOds": null, + "nhsdApplicationId": null + }, + "queryParams": { + "category": null, + "conditions": null, + "includeActions": null + }, + "nhsNumber": "9900376002" + }, + "response": { + "responseId": "f09917f7-9f73-472a-b0f3-6cc0c5c86858", + "lastUpdated": "2025-08-11 07:43:12.287543+00:00", + "condition": [ + { + "campaignId": "AUTO_RSV_ELI-376-02-Campaign-ID", + "campaignVersion": 1, + "iterationId": "AUTO_RSV_ELI-376-02-Iteration-ID", + "iterationVersion": 1, + "conditionName": "RSV", + "status": "not_eligible", + "statusText": "We do not believe you can have it", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_eli_376_cohort_1", + "cohortStatus": "not_eligible" + }, + { + "cohortCode": "rsv_eli_376_cohort_2", + "cohortStatus": "not_eligible" + }, + { + "cohortCode": "rsv_eli_376_cohort_3", + "cohortStatus": "not_eligible" + }, + { + "cohortCode": "rsv_eli_376_cohort_4", + "cohortStatus": "not_eligible" + } + ], + "eligibilityCohortGroups": [ + { + "cohortCode": "rsv_eli_376_cohort_group", + "cohortText": "are not a member of eli_376_cohort_group_0", + "cohortStatus": "not_eligible" + }, + { + "cohortCode": "rsv_eli_376_cohort_group", + "cohortText": "are not a member of eli_376_cohort_group_10", + "cohortStatus": "not_eligible" + }, + { + "cohortCode": "rsv_eli_376_cohort_group", + "cohortText": "are not a member of eli_376_cohort_group_20", + "cohortStatus": "not_eligible" + }, + { + "cohortCode": "rsv_eli_376_cohort_group_other", + "cohortText": "are not a member of eli_376_cohort_group_other", + "cohortStatus": "not_eligible" + } + ], + "filterRules": [ + { + "rulePriority": "100", + "ruleName": "NotEligible Reason 1" + } + ], + "suitabilityRules": null, + "actionRule": null, + "actions": [ + { + "internalActionCode": "TEST_NOT_ELI", + "actionType": "", + "actionCode": "TestNotEli", + "actionDescription": "TestNotEli Description", + "actionUrl": null, + "actionUrlLabel": null + } + ] + } + ] + } +} diff --git a/tests/e2e/data/audit/AUTO_RSV_ELI-376-03.json b/tests/e2e/data/audit/AUTO_RSV_ELI-376-03.json new file mode 100644 index 000000000..7c91d2f68 --- /dev/null +++ b/tests/e2e/data/audit/AUTO_RSV_ELI-376-03.json @@ -0,0 +1,96 @@ +{ + "request": { + "requestTimestamp": "2025-08-11 06:42:52.595081+00:00", + "headers": { + "xRequestId": null, + "xCorrelationId": null, + "nhsdEndUserOrganisationOds": null, + "nhsdApplicationId": null + }, + "queryParams": { + "category": null, + "conditions": null, + "includeActions": null + }, + "nhsNumber": "9900376003" + }, + "response": { + "responseId": "b3032aeb-fafd-48d0-8d32-1459140ea886", + "lastUpdated": "2025-08-11 06:42:52.942561+00:00", + "condition": [ + { + "campaignId": "AUTO_RSV_ELI-376-03-Campaign-ID", + "campaignVersion": 1, + "iterationId": "AUTO_RSV_ELI-376-03-Iteration-ID", + "iterationVersion": 1, + "conditionName": "RSV", + "status": "not_actionable", + "statusText": "You should have the RSV vaccine", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_eli_376_cohort_1", + "cohortStatus": "not_actionable" + }, + { + "cohortCode": "rsv_eli_376_cohort_2", + "cohortStatus": "not_actionable" + }, + { + "cohortCode": "rsv_eli_376_cohort_3", + "cohortStatus": "not_actionable" + }, + { + "cohortCode": "rsv_eli_376_cohort_4", + "cohortStatus": "not_eligible" + } + ], + "eligibilityCohortGroups": [ + { + "cohortCode": "rsv_eli_376_cohort_group", + "cohortText": "are a member of eli_376_cohort_group_0", + "cohortStatus": "not_actionable" + }, + { + "cohortCode": "rsv_eli_376_cohort_group", + "cohortText": "are a member of eli_376_cohort_group_10", + "cohortStatus": "not_actionable" + }, + { + "cohortCode": "rsv_eli_376_cohort_group", + "cohortText": "are a member of eli_376_cohort_group_20", + "cohortStatus": "not_actionable" + }, + { + "cohortCode": "rsv_eli_376_cohort_group_other", + "cohortText": "are not a member of eli_376_cohort_group_other", + "cohortStatus": "not_eligible" + } + ], + "filterRules": [ + { + "rulePriority": "100", + "ruleName": "NotEligible Reason 1" + } + ], + "suitabilityRules": [ + { + "rulePriority": "200", + "ruleName": "NotActionable Reason 1", + "ruleMessage": "NotActionable Description 1" + } + ], + "actionRule": null, + "actions": [ + { + "internalActionCode": "BOOK_NBS", + "actionType": "ButtonWithAuthLink", + "actionCode": "BookNBS", + "actionDescription": null, + "actionUrl": "http://www.nhs.uk/book-rsv", + "actionUrlLabel": "Continue to booking" + } + ] + } + ] + } +} diff --git a/tests/e2e/data/audit/AUTO_RSV_ELI-376-04.json b/tests/e2e/data/audit/AUTO_RSV_ELI-376-04.json new file mode 100644 index 000000000..863f219e5 --- /dev/null +++ b/tests/e2e/data/audit/AUTO_RSV_ELI-376-04.json @@ -0,0 +1,85 @@ +{ + "request": { + "requestTimestamp": "2025-08-11 07:13:25.069391+00:00", + "headers": { + "xRequestId": null, + "xCorrelationId": null, + "nhsdEndUserOrganisationOds": null, + "nhsdApplicationId": null + }, + "queryParams": { + "category": null, + "conditions": null, + "includeActions": null + }, + "nhsNumber": "9900376004" + }, + "response": { + "responseId": "f516123d-e985-45a0-bebe-67fe08328db8", + "lastUpdated": "2025-08-11 07:13:25.470782+00:00", + "condition": [ + { + "campaignId": "AUTO_RSV_ELI-376-04-Campaign-ID", + "campaignVersion": 1, + "iterationId": "AUTO_RSV_ELI-376-04-Iteration-ID", + "iterationVersion": 1, + "conditionName": "RSV", + "status": "actionable", + "statusText": "You should have the RSV vaccine", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_eli_376_cohort_1", + "cohortStatus": "not_eligible" + }, + { + "cohortCode": "rsv_eli_376_cohort_2", + "cohortStatus": "actionable" + }, + { + "cohortCode": "rsv_eli_376_cohort_3", + "cohortStatus": "actionable" + }, + { + "cohortCode": "rsv_eli_376_cohort_4", + "cohortStatus": "not_eligible" + } + ], + "eligibilityCohortGroups": [ + { + "cohortCode": "rsv_eli_376_cohort_group", + "cohortText": "are not a member of eli_376_cohort_group_0", + "cohortStatus": "not_eligible" + }, + { + "cohortCode": "rsv_eli_376_cohort_group", + "cohortText": "are a member of eli_376_cohort_group_10", + "cohortStatus": "actionable" + }, + { + "cohortCode": "rsv_eli_376_cohort_group", + "cohortText": "are a member of eli_376_cohort_group_20", + "cohortStatus": "actionable" + }, + { + "cohortCode": "rsv_eli_376_cohort_group_other", + "cohortText": "are not a member of eli_376_cohort_group_other", + "cohortStatus": "not_eligible" + } + ], + "filterRules": [ + { + "rulePriority": "100", + "ruleName": "NotEligible Reason 1" + }, + { + "rulePriority": "110", + "ruleName": "NotEligible Reason 2" + } + ], + "suitabilityRules": null, + "actionRule": null, + "actions": [] + } + ] + } +} diff --git a/tests/e2e/data/audit/AUTO_RSV_ELI-376-05.json b/tests/e2e/data/audit/AUTO_RSV_ELI-376-05.json new file mode 100644 index 000000000..ec271967e --- /dev/null +++ b/tests/e2e/data/audit/AUTO_RSV_ELI-376-05.json @@ -0,0 +1,102 @@ +{ + "request": { + "requestTimestamp": "2025-08-11 20:46:10.268634+00:00", + "headers": { + "xRequestId": null, + "xCorrelationId": null, + "nhsdEndUserOrganisationOds": null, + "nhsdApplicationId": null + }, + "queryParams": { + "category": null, + "conditions": null, + "includeActions": null + }, + "nhsNumber": "9900376005" + }, + "response": { + "responseId": "762b5ad0-a4fb-4f29-8330-561c98dc60e8", + "lastUpdated": "2025-08-11 20:46:10.693486+00:00", + "condition": [ + { + "campaignId": "AUTO_RSV_ELI-376-05-Campaign-ID", + "campaignVersion": 1, + "iterationId": "AUTO_RSV_ELI-376-05-Iteration-ID", + "iterationVersion": 1, + "conditionName": "RSV", + "status": "not_eligible", + "statusText": "We do not believe you can have it", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_eli_376_cohort_1", + "cohortStatus": "not_eligible" + }, + { + "cohortCode": "rsv_eli_376_cohort_2", + "cohortStatus": "not_eligible" + }, + { + "cohortCode": "rsv_eli_376_cohort_3", + "cohortStatus": "not_eligible" + }, + { + "cohortCode": "rsv_eli_376_cohort_4", + "cohortStatus": "not_eligible" + } + ], + "eligibilityCohortGroups": [ + { + "cohortCode": "rsv_eli_376_cohort_group", + "cohortText": "are not a member of eli_376_cohort_group_0", + "cohortStatus": "not_eligible" + }, + { + "cohortCode": "rsv_eli_376_cohort_group", + "cohortText": "are not a member of eli_376_cohort_group_10", + "cohortStatus": "not_eligible" + }, + { + "cohortCode": "rsv_eli_376_cohort_group", + "cohortText": "are not a member of eli_376_cohort_group_20", + "cohortStatus": "not_eligible" + }, + { + "cohortCode": "rsv_eli_376_cohort_group_other", + "cohortText": "are not a member of eli_376_cohort_group_other", + "cohortStatus": "not_eligible" + } + ], + "filterRules": [ + { + "rulePriority": "100", + "ruleName": "NotEligible Reason 1" + }, + { + "rulePriority": "110", + "ruleName": "NotEligible Reason 2" + }, + { + "rulePriority": "120", + "ruleName": "NotEligible Reason 3" + }, + { + "rulePriority": "130", + "ruleName": "NotEligible Reason 4" + } + ], + "suitabilityRules": null, + "actionRule": null, + "actions": [ + { + "internalActionCode": "TEST_NOT_ELI", + "actionType": "", + "actionCode": "TestNotEli", + "actionDescription": "TestNotEli Description", + "actionUrl": null, + "actionUrlLabel": null + } + ] + } + ] + } +} diff --git a/tests/e2e/data/audit/AUTO_RSV_ELI-376-10.json b/tests/e2e/data/audit/AUTO_RSV_ELI-376-10.json new file mode 100644 index 000000000..21b363c58 --- /dev/null +++ b/tests/e2e/data/audit/AUTO_RSV_ELI-376-10.json @@ -0,0 +1,76 @@ +{ + "request": { + "requestTimestamp": "2025-08-11 07:13:25.069391+00:00", + "headers": { + "xRequestId": null, + "xCorrelationId": null, + "nhsdEndUserOrganisationOds": null, + "nhsdApplicationId": null + }, + "queryParams": { + "category": null, + "conditions": null, + "includeActions": null + }, + "nhsNumber": "9900376004" + }, + "response": { + "responseId": "f516123d-e985-45a0-bebe-67fe08328db8", + "lastUpdated": "2025-08-11 07:13:25.470782+00:00", + "condition": [ + { + "campaignId": "AUTO_RSV_ELI-376-04-Campaign-ID", + "campaignVersion": 1, + "iterationId": "AUTO_RSV_ELI-376-04-Iteration-ID", + "iterationVersion": 1, + "conditionName": "RSV", + "status": "actionable", + "statusText": "You should have the RSV vaccine", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_eli_376_cohort_1", + "cohortStatus": "not_eligible" + }, + { + "cohortCode": "rsv_eli_376_cohort_2", + "cohortStatus": "actionable" + }, + { + "cohortCode": "rsv_eli_376_cohort_3", + "cohortStatus": "actionable" + }, + { + "cohortCode": "rsv_eli_376_cohort_4", + "cohortStatus": "not_eligible" + } + ], + "eligibilityCohortGroups": [ + { + "cohortCode": "rsv_eli_376_cohort_group", + "cohortText": "are not a member of eli_376_cohort_group_0", + "cohortStatus": "not_eligible" + }, + { + "cohortCode": "rsv_eli_376_cohort_group", + "cohortText": "are a member of eli_376_cohort_group_10", + "cohortStatus": "actionable" + }, + { + "cohortCode": "rsv_eli_376_cohort_group", + "cohortText": "are a member of eli_376_cohort_group_20", + "cohortStatus": "actionable" + }, + { + "cohortCode": "rsv_eli_376_cohort_group_other", + "cohortText": "are not a member of eli_376_cohort_group_other", + "cohortStatus": "not_eligible" + } + ], + "filterRules": null, + "suitabilityRules": null, + "actionRule": null, + "actions": [] + } + ] + } +} diff --git a/tests/e2e/data/configs/inProgressTestConfigs/AUTO_RSV_ELI-406-01.json b/tests/e2e/data/configs/inProgressTestConfigs/AUTO_RSV_ELI-406-01.json new file mode 100644 index 000000000..c189d5350 --- /dev/null +++ b/tests/e2e/data/configs/inProgressTestConfigs/AUTO_RSV_ELI-406-01.json @@ -0,0 +1,108 @@ +{ + "CampaignConfig": { + "ID": "AUTO_RSV_ELI-376-01-Campaign-ID", + "Version": 1, + "Name": "ELI-376-01-Iteration-Config-Name", + "Type": "V", + "Target": "RSV", + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "BOOK_NBS", + "Iterations": [ + { + "ID": "AUTO_RSV_ELI-376-01-Iteration-ID", + "DefaultCommsRouting": "TEST_ACTION", + "DefaultNotActionableRouting": "TEST_NOT_ACTION", + "DefaultNotEligibleRouting": "TEST_NOT_ELI", + "Version": 1, + "Name": "ELI-376-01-Iteration-Config-Name", + "IterationDate": "20240808", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_376_cohort_1", + "CohortGroup": "rsv_eli_376_cohort_group", + "PositiveDescription": "are a member of eli_376_cohort_group_0", + "NegativeDescription": "are not a member of eli_376_cohort_group_0", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_376_cohort_2", + "CohortGroup": "rsv_eli_376_cohort_group", + "PositiveDescription": "are a member of eli_376_cohort_group_10", + "NegativeDescription": "are not a member of eli_376_cohort_group_10", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_376_cohort_3", + "CohortGroup": "rsv_eli_376_cohort_group", + "PositiveDescription": "are a member of eli_376_cohort_group_20", + "NegativeDescription": "are not a member of eli_376_cohort_group_20", + "Priority": 20 + }, + { + "CohortLabel": "rsv_eli_376_cohort_4", + "CohortGroup": "rsv_eli_376_cohort_group_other", + "PositiveDescription": "are a member of eli_376_cohort_group_other", + "NegativeDescription": "are not a member of eli_376_cohort_group_other", + "Priority": 30 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "NotEligible Reason 1", + "Description": "NotEligible Description 1", + "Operator": "Y<=", + "Comparator": "-80", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_376_cohort_1", + "Priority": 100 + } + ], + "ActionsMapper": { + "TEST_ACTION": { + "ExternalRoutingCode": "TestAction", + "ActionDescription": "TestAction Description", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + }, + "TEST_NOT_ACTION": { + "ExternalRoutingCode": "TestNotAction", + "ActionDescription": "TestNotAction Description", + "ActionType": "ButtonWithAuthLink", + "UrlLink": null, + "UrlLabel": "" + }, + "TEST_NOT_ELI": { + "ExternalRoutingCode": "TestNotEli", + "ActionDescription": "TestNotEli Description", + "ActionType": "", + "UrlLink": null, + "UrlLabel": "" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-01.json b/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-01.json new file mode 100644 index 000000000..595578249 --- /dev/null +++ b/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-01.json @@ -0,0 +1,88 @@ +{ + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-220-01-Iteration-Config", + "Type": "V", + "Target": "RSV", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "ELI-220-01-Iteration-Config", + "IterationDate": "20240808", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_220_cohort_1", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "are a member of eli_220_cohort_group_10", + "NegativeDescription": "are not a member of eli_220_cohort_group_10", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_220_cohort_2", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "are a member of eli_220_cohort_group_0", + "NegativeDescription": "are not a member of eli_220_cohort_group_0", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_220_cohort_3", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "are a member of eli_220_cohort_group_40", + "NegativeDescription": "are not a member of eli_220_cohort_group_40", + "Priority": 40 + }, + { + "CohortLabel": "rsv_eli_220_cohort_4", + "CohortGroup": "rsv_eli_220_cohort_group_other", + "PositiveDescription": "are a member of eli_220_cohort_group_other", + "NegativeDescription": "are not a member of eli_220_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason 1", + "Description": "Description 1", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_220_cohort_1", + "Priority": 100 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-02.json b/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-02.json new file mode 100644 index 000000000..1fb2b6871 --- /dev/null +++ b/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-02.json @@ -0,0 +1,88 @@ +{ + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-220-01-Iteration-Config", + "Type": "V", + "Target": "RSV", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "ELI-220-01-Iteration-Config", + "IterationDate": "20250717", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_220_cohort_1", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "are a member of eli_220_cohort_group_10", + "NegativeDescription": "are not a member of eli_220_cohort_group_10", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_220_cohort_2", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "", + "NegativeDescription": "are not a member of eli_220_cohort_group_0", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_220_cohort_3", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "are a member of eli_220_cohort_group_40", + "NegativeDescription": "are not a member of eli_220_cohort_group_40", + "Priority": 40 + }, + { + "CohortLabel": "rsv_eli_220_cohort_4", + "CohortGroup": "rsv_eli_220_cohort_group_other", + "PositiveDescription": "are a member of eli_220_cohort_group_other", + "NegativeDescription": "are not a member of eli_220_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason 1", + "Description": "Description 1", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_220_cohort_1", + "Priority": 100 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-03.json b/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-03.json new file mode 100644 index 000000000..d5d8a3570 --- /dev/null +++ b/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-03.json @@ -0,0 +1,87 @@ +{ + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-220-03-Iteration-Config", + "Type": "V", + "Target": "RSV", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "ELI-220-03-Iteration-Config", + "IterationDate": "20250717", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_220_cohort_1", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "are a member of eli_220_cohort_group_10", + "NegativeDescription": "are not a member of eli_220_cohort_group_10", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_220_cohort_2", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "are a member of eli_220_cohort_group_0", + "NegativeDescription": "are not a member of eli_220_cohort_group_0", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_220_cohort_3", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "are a member of eli_220_cohort_group_40", + "NegativeDescription": "are not a member of eli_220_cohort_group_40", + "Priority": 40 + }, + { + "CohortLabel": "rsv_eli_220_cohort_4", + "CohortGroup": "rsv_eli_220_cohort_group_other", + "PositiveDescription": "are a member of eli_220_cohort_group_other", + "NegativeDescription": "are not a member of eli_220_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason 1", + "Description": "Description 1", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Priority": 100 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-04.json b/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-04.json new file mode 100644 index 000000000..fed4ee02a --- /dev/null +++ b/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-04.json @@ -0,0 +1,87 @@ +{ + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-220-04-Iteration-Config", + "Type": "V", + "Target": "RSV", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "ELI-220-04-Iteration-Config", + "IterationDate": "20250717", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_220_cohort_1", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "are a member of eli_220_cohort_group_10", + "NegativeDescription": "are not a member of eli_220_cohort_group_10", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_220_cohort_2", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "", + "NegativeDescription": "are not a member of eli_220_cohort_group_0", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_220_cohort_3", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "are a member of eli_220_cohort_group_40", + "NegativeDescription": "are not a member of eli_220_cohort_group_40", + "Priority": 40 + }, + { + "CohortLabel": "rsv_eli_220_cohort_4", + "CohortGroup": "rsv_eli_220_cohort_group_other", + "PositiveDescription": "are a member of eli_220_cohort_group_other", + "NegativeDescription": "are not a member of eli_220_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason 1", + "Description": "Description 1", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Priority": 100 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-05.json b/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-05.json new file mode 100644 index 000000000..2251bc9c0 --- /dev/null +++ b/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-05.json @@ -0,0 +1,87 @@ +{ + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-220-05-Iteration-Config", + "Type": "V", + "Target": "RSV", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "ELI-220-04-Iteration-Config", + "IterationDate": "20250717", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_220_cohort_1", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "are a member of eli_220_cohort_group_10", + "NegativeDescription": "are not a member of eli_220_cohort_group_10", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_220_cohort_2", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "are a member of eli_220_cohort_group_0", + "NegativeDescription": "are not a member of eli_220_cohort_group_0", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_220_cohort_3", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "are a member of eli_220_cohort_group_40", + "NegativeDescription": "are not a member of eli_220_cohort_group_40", + "Priority": 40 + }, + { + "CohortLabel": "rsv_eli_220_cohort_4", + "CohortGroup": "rsv_eli_220_cohort_group_other", + "PositiveDescription": "are a member of eli_220_cohort_group_other", + "NegativeDescription": "are not a member of eli_220_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "NotActionable Reason 1", + "Description": "Description 1", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Priority": 100 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-06.json b/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-06.json new file mode 100644 index 000000000..31153ebf6 --- /dev/null +++ b/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-06.json @@ -0,0 +1,87 @@ +{ + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-220-06-Iteration-Config", + "Type": "V", + "Target": "RSV", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "ELI-220-04-Iteration-Config", + "IterationDate": "20250717", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_220_cohort_1", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "are a member of eli_220_cohort_group_10", + "NegativeDescription": "are not a member of eli_220_cohort_group_10", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_220_cohort_2", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "are a member of eli_220_cohort_group_0", + "NegativeDescription": "", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_220_cohort_3", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "are a member of eli_220_cohort_group_40", + "NegativeDescription": "are not a member of eli_220_cohort_group_40", + "Priority": 40 + }, + { + "CohortLabel": "rsv_eli_220_cohort_4", + "CohortGroup": "rsv_eli_220_cohort_group_other", + "PositiveDescription": "are a member of eli_220_cohort_group_other", + "NegativeDescription": "are not a member of eli_220_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "NotActionable Reason 1", + "Description": "Description 1", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Priority": 100 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/inProgressTestConfigs/ELI-274-371/AUTO_RSV_ELI-274-01.json b/tests/e2e/data/configs/inProgressTestConfigs/ELI-274-371/AUTO_RSV_ELI-274-01.json new file mode 100644 index 000000000..566f1b1e2 --- /dev/null +++ b/tests/e2e/data/configs/inProgressTestConfigs/ELI-274-371/AUTO_RSV_ELI-274-01.json @@ -0,0 +1,103 @@ +{ + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-274-01-Iteration-Config", + "Type": "V", + "Target": "RSV", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "ELI-274-01-Iteration-Config", + "IterationDate": "20250717", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_274_cohort_1", + "CohortGroup": "rsv_eli_274_cohort_group", + "PositiveDescription": "are a member of eli_274_cohort_group", + "NegativeDescription": "are not a member of eli_274_cohort_group", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_274_cohort_2", + "CohortGroup": "rsv_eli_274_cohort_group", + "PositiveDescription": "are a member of eli_274_cohort_group", + "NegativeDescription": "are not a member of eli_274_cohort_group", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_274_cohort_3", + "CohortGroup": "rsv_eli_274_cohort_group_other", + "PositiveDescription": "are a member of eli_274_cohort_group_other", + "NegativeDescription": "are not a member of eli_274_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason 1", + "Description": "Description 1", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_274_cohort_1", + "Priority": 100 + }, + { + "Type": "S", + "Name": "NotActionable Reason 2", + "Description": "Description 2", + "Operator": "=", + "Comparator": "AAA", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CohortLabel": "rsv_eli_274_cohort_2", + "Priority": 110 + }, + { + "Type": "S", + "Name": "NotActionable Reason 3", + "Description": "Description 3", + "Operator": "=", + "Comparator": "ZZZ", + "AttributeLevel": "PERSON", + "AttributeName": "COMMISSIONING_REGION", + "CohortLabel": "rsv_eli_274_cohort_3", + "Priority": 120 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/inProgressTestConfigs/ELI-274-371/AUTO_RSV_ELI-274-05.json b/tests/e2e/data/configs/inProgressTestConfigs/ELI-274-371/AUTO_RSV_ELI-274-05.json new file mode 100644 index 000000000..7b8d58b68 --- /dev/null +++ b/tests/e2e/data/configs/inProgressTestConfigs/ELI-274-371/AUTO_RSV_ELI-274-05.json @@ -0,0 +1,103 @@ +{ + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-274-05-Iteration-Config", + "Type": "V", + "Target": "RSV", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "ELI-274-05-Iteration-Config", + "IterationDate": "20250717", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_274_cohort_1", + "CohortGroup": "rsv_eli_274_cohort_group", + "PositiveDescription": "are a member of eli_274_cohort_group", + "NegativeDescription": "are not a member of eli_274_cohort_group", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_274_cohort_2", + "CohortGroup": "rsv_eli_274_cohort_group", + "PositiveDescription": "are a member of eli_274_cohort_group", + "NegativeDescription": "are not a member of eli_274_cohort_group", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_274_cohort_3", + "CohortGroup": "rsv_eli_274_cohort_group_other", + "PositiveDescription": "are a member of eli_274_cohort_group_other", + "NegativeDescription": "are not a member of eli_274_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 1", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_274_cohort_1", + "Priority": 100 + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 2", + "Operator": "=", + "Comparator": "AAA", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CohortLabel": "rsv_eli_274_cohort_2", + "Priority": 110 + }, + { + "Type": "S", + "Name": "NotActionable Reason 3", + "Description": "Description 3", + "Operator": "=", + "Comparator": "ZZZ", + "AttributeLevel": "PERSON", + "AttributeName": "COMMISSIONING_REGION", + "CohortLabel": "rsv_eli_274_cohort_3", + "Priority": 120 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/inProgressTestConfigs/ELI-274-371/AUTO_RSV_ELI-274-06.json b/tests/e2e/data/configs/inProgressTestConfigs/ELI-274-371/AUTO_RSV_ELI-274-06.json new file mode 100644 index 000000000..7b41662c0 --- /dev/null +++ b/tests/e2e/data/configs/inProgressTestConfigs/ELI-274-371/AUTO_RSV_ELI-274-06.json @@ -0,0 +1,103 @@ +{ + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-274-06-Iteration-Config", + "Type": "V", + "Target": "RSV", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "ELI-274-06-Iteration-Config", + "IterationDate": "20250717", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_274_cohort_1", + "CohortGroup": "rsv_eli_274_cohort_group", + "PositiveDescription": "are a member of eli_274_cohort_group", + "NegativeDescription": "are not a member of eli_274_cohort_group", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_274_cohort_2", + "CohortGroup": "rsv_eli_274_cohort_group", + "PositiveDescription": "are a member of eli_274_cohort_group", + "NegativeDescription": "are not a member of eli_274_cohort_group", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_274_cohort_3", + "CohortGroup": "rsv_eli_274_cohort_group_other", + "PositiveDescription": "are a member of eli_274_cohort_group_other", + "NegativeDescription": "are not a member of eli_274_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 1", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_274_cohort_1", + "Priority": 100 + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 2", + "Operator": "=", + "Comparator": "AAA", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CohortLabel": "rsv_eli_274_cohort_2", + "Priority": 110 + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 3", + "Operator": "=", + "Comparator": "ZZZ", + "AttributeLevel": "PERSON", + "AttributeName": "COMMISSIONING_REGION", + "CohortLabel": "rsv_eli_274_cohort_3", + "Priority": 120 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/inProgressTestConfigs/ELI-274-371/AUTO_RSV_ELI-274-07.json b/tests/e2e/data/configs/inProgressTestConfigs/ELI-274-371/AUTO_RSV_ELI-274-07.json new file mode 100644 index 000000000..09b2fc893 --- /dev/null +++ b/tests/e2e/data/configs/inProgressTestConfigs/ELI-274-371/AUTO_RSV_ELI-274-07.json @@ -0,0 +1,104 @@ +{ + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-274-07-Iteration-Config", + "Type": "V", + "Target": "RSV", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "ELI-274-07-Iteration-Config", + "IterationDate": "20250717", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_274_cohort_1", + "CohortGroup": "rsv_eli_274_cohort_group", + "PositiveDescription": "are a member of eli_274_cohort_group", + "NegativeDescription": "are not a member of eli_274_cohort_group", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_274_cohort_2", + "CohortGroup": "rsv_eli_274_cohort_group", + "PositiveDescription": "are a member of eli_274_cohort_group", + "NegativeDescription": "are not a member of eli_274_cohort_group", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_274_cohort_3", + "CohortGroup": "rsv_eli_274_cohort_group_other", + "PositiveDescription": "are a member of eli_274_cohort_group_other", + "NegativeDescription": "are not a member of eli_274_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_274_cohort_1", + "Priority": 100, + "RuleStop:": "Y" + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 4", + "Operator": "=", + "Comparator": "AAA", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CohortLabel": "rsv_eli_274_cohort_2", + "Priority": 110 + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 3", + "Operator": "=", + "Comparator": "ZZZ", + "AttributeLevel": "PERSON", + "AttributeName": "COMMISSIONING_REGION", + "CohortLabel": "rsv_eli_274_cohort_3", + "Priority": 120 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/inProgressTestConfigs/ELI-376-405/AUTO_RSV_ELI-376-01.json b/tests/e2e/data/configs/inProgressTestConfigs/ELI-376-405/AUTO_RSV_ELI-376-01.json new file mode 100644 index 000000000..d6516c203 --- /dev/null +++ b/tests/e2e/data/configs/inProgressTestConfigs/ELI-376-405/AUTO_RSV_ELI-376-01.json @@ -0,0 +1,119 @@ +{ + "CampaignConfig": { + "ID": "AUTO_RSV_ELI-376-01-Campaign-ID", + "Version": 1, + "Name": "ELI-376-01-Iteration-Config-Name", + "Type": "V", + "Target": "RSV", + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "BOOK_NBS", + "Iterations": [ + { + "ID": "AUTO_RSV_ELI-376-01-Iteration-ID", + "DefaultCommsRouting": "TEST_ACTION", + "DefaultNotActionableRouting": "TEST_NOT_ACTION", + "DefaultNotEligibleRouting": "TEST_NOT_ELI", + "Version": 1, + "Name": "ELI-376-01-Iteration-Config-Name", + "IterationDate": "20240808", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_376_cohort_1", + "CohortGroup": "rsv_eli_376_cohort_group", + "PositiveDescription": "are a member of eli_376_cohort_group_0", + "NegativeDescription": "are not a member of eli_376_cohort_group_0", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_376_cohort_2", + "CohortGroup": "rsv_eli_376_cohort_group", + "PositiveDescription": "are a member of eli_376_cohort_group_10", + "NegativeDescription": "are not a member of eli_376_cohort_group_10", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_376_cohort_3", + "CohortGroup": "rsv_eli_376_cohort_group", + "PositiveDescription": "are a member of eli_376_cohort_group_20", + "NegativeDescription": "are not a member of eli_376_cohort_group_20", + "Priority": 20 + }, + { + "CohortLabel": "rsv_eli_376_cohort_4", + "CohortGroup": "rsv_eli_376_cohort_group_other", + "PositiveDescription": "are a member of eli_376_cohort_group_other", + "NegativeDescription": "are not a member of eli_376_cohort_group_other", + "Priority": 30 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "NotEligible Reason 1", + "Description": "NotEligible Description 1", + "Operator": "Y<=", + "Comparator": "-80", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_376_cohort_1", + "Priority": 100 + }, + { + "Type": "S", + "Name": "NotActionable Reason 1", + "Description": "NotActionable Description 1", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_376_cohort_1", + "Priority": 200 + } + ], + "ActionsMapper": { + "TEST_ACTION": { + "ExternalRoutingCode": "TestAction", + "ActionDescription": "TestAction Description", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + }, + "TEST_NOT_ACTION": { + "ExternalRoutingCode": "TestNotAction", + "ActionDescription": "TestNotAction Description", + "ActionType": "ButtonWithAuthLink", + "UrlLink": null, + "UrlLabel": "" + }, + "TEST_NOT_ELI": { + "ExternalRoutingCode": "TestNotEli", + "ActionDescription": "TestNotEli Description", + "ActionType": "", + "UrlLink": null, + "UrlLabel": "" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/inProgressTestConfigs/ELI-376-405/AUTO_RSV_ELI-376-02.json b/tests/e2e/data/configs/inProgressTestConfigs/ELI-376-405/AUTO_RSV_ELI-376-02.json new file mode 100644 index 000000000..f3d526bc6 --- /dev/null +++ b/tests/e2e/data/configs/inProgressTestConfigs/ELI-376-405/AUTO_RSV_ELI-376-02.json @@ -0,0 +1,118 @@ +{ + "CampaignConfig": { + "ID": "AUTO_RSV_ELI-376-02-Campaign-ID", + "Version": 1, + "Name": "ELI-376-02-Iteration-Config-Name", + "Type": "V", + "Target": "RSV", + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "BOOK_NBS", + "Iterations": [ + { + "ID": "AUTO_RSV_ELI-376-02-Iteration-ID", + "DefaultCommsRouting": "TEST_ACTION", + "DefaultNotActionableRouting": "TEST_NOT_ACTION", + "DefaultNotEligibleRouting": "TEST_NOT_ELI", + "Version": 1, + "Name": "ELI-376-02-Iteration-Config-Name", + "IterationDate": "20240808", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_376_cohort_1", + "CohortGroup": "rsv_eli_376_cohort_group", + "PositiveDescription": "are a member of eli_376_cohort_group_0", + "NegativeDescription": "are not a member of eli_376_cohort_group_0", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_376_cohort_2", + "CohortGroup": "rsv_eli_376_cohort_group", + "PositiveDescription": "are a member of eli_376_cohort_group_10", + "NegativeDescription": "are not a member of eli_376_cohort_group_10", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_376_cohort_3", + "CohortGroup": "rsv_eli_376_cohort_group", + "PositiveDescription": "are a member of eli_376_cohort_group_20", + "NegativeDescription": "are not a member of eli_376_cohort_group_20", + "Priority": 20 + }, + { + "CohortLabel": "rsv_eli_376_cohort_4", + "CohortGroup": "rsv_eli_376_cohort_group_other", + "PositiveDescription": "are a member of eli_376_cohort_group_other", + "NegativeDescription": "are not a member of eli_376_cohort_group_other", + "Priority": 30 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "NotEligible Reason 1", + "Description": "NotEligible Description 1", + "Operator": "Y<=", + "Comparator": "-80", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Priority": 100 + }, + { + "Type": "S", + "Name": "NotActionable Reason 1", + "Description": "NotActionable Description 1", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_376_cohort_1", + "Priority": 200 + } + ], + "ActionsMapper": { + "TEST_ACTION": { + "ExternalRoutingCode": "TestAction", + "ActionDescription": "TestAction Description", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + }, + "TEST_NOT_ACTION": { + "ExternalRoutingCode": "TestNotAction", + "ActionDescription": "TestNotAction Description", + "ActionType": "ButtonWithAuthLink", + "UrlLink": null, + "UrlLabel": "" + }, + "TEST_NOT_ELI": { + "ExternalRoutingCode": "TestNotEli", + "ActionDescription": "TestNotEli Description", + "ActionType": "", + "UrlLink": null, + "UrlLabel": "" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/inProgressTestConfigs/ELI-376-405/AUTO_RSV_ELI-376-03.json b/tests/e2e/data/configs/inProgressTestConfigs/ELI-376-405/AUTO_RSV_ELI-376-03.json new file mode 100644 index 000000000..1b4aa219a --- /dev/null +++ b/tests/e2e/data/configs/inProgressTestConfigs/ELI-376-405/AUTO_RSV_ELI-376-03.json @@ -0,0 +1,118 @@ +{ + "CampaignConfig": { + "ID": "AUTO_RSV_ELI-376-03-Campaign-ID", + "Version": 1, + "Name": "ELI-376-03-Iteration-Config-Name", + "Type": "V", + "Target": "RSV", + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "BOOK_NBS", + "Iterations": [ + { + "ID": "AUTO_RSV_ELI-376-03-Iteration-ID", + "DefaultCommsRouting": "TEST_ACTION", + "DefaultNotActionableRouting": "TEST_NOT_ACTION", + "DefaultNotEligibleRouting": "TEST_NOT_ELI", + "Version": 1, + "Name": "ELI-376-03-Iteration-Config-Name", + "IterationDate": "20240808", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_376_cohort_1", + "CohortGroup": "rsv_eli_376_cohort_group", + "PositiveDescription": "are a member of eli_376_cohort_group_0", + "NegativeDescription": "are not a member of eli_376_cohort_group_0", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_376_cohort_2", + "CohortGroup": "rsv_eli_376_cohort_group", + "PositiveDescription": "are a member of eli_376_cohort_group_10", + "NegativeDescription": "are not a member of eli_376_cohort_group_10", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_376_cohort_3", + "CohortGroup": "rsv_eli_376_cohort_group", + "PositiveDescription": "are a member of eli_376_cohort_group_20", + "NegativeDescription": "are not a member of eli_376_cohort_group_20", + "Priority": 20 + }, + { + "CohortLabel": "rsv_eli_376_cohort_4", + "CohortGroup": "rsv_eli_376_cohort_group_other", + "PositiveDescription": "are a member of eli_376_cohort_group_other", + "NegativeDescription": "are not a member of eli_376_cohort_group_other", + "Priority": 30 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "NotEligible Reason 1", + "Description": "NotEligible Description 1", + "Operator": "Y<=", + "Comparator": "-80", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_376_cohort_4", + "Priority": 100 + }, + { + "Type": "S", + "Name": "NotActionable Reason 1", + "Description": "NotActionable Description 1", + "Operator": "=", + "Comparator": "ABC", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "Priority": 200 + } + ], + "ActionsMapper": { + "TEST_ACTION": { + "ExternalRoutingCode": "TestAction", + "ActionDescription": "TestAction Description", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + }, + "TEST_NOT_ACTION": { + "ExternalRoutingCode": "TestNotAction", + "ActionDescription": "TestNotAction Description", + "ActionType": "ButtonWithAuthLink", + "UrlLink": null, + "UrlLabel": "" + }, + "TEST_NOT_ELI": { + "ExternalRoutingCode": "TestNotEli", + "ActionDescription": "TestNotEli Description", + "ActionType": "", + "UrlLink": null, + "UrlLabel": "" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/inProgressTestConfigs/ELI-376-405/AUTO_RSV_ELI-376-04.json b/tests/e2e/data/configs/inProgressTestConfigs/ELI-376-405/AUTO_RSV_ELI-376-04.json new file mode 100644 index 000000000..99ed02797 --- /dev/null +++ b/tests/e2e/data/configs/inProgressTestConfigs/ELI-376-405/AUTO_RSV_ELI-376-04.json @@ -0,0 +1,119 @@ +{ + "CampaignConfig": { + "ID": "AUTO_RSV_ELI-376-04-Campaign-ID", + "Version": 1, + "Name": "ELI-376-04-Iteration-Config-Name", + "Type": "V", + "Target": "RSV", + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "", + "Iterations": [ + { + "ID": "AUTO_RSV_ELI-376-04-Iteration-ID", + "DefaultCommsRouting": "TEST_ACTION", + "DefaultNotActionableRouting": "TEST_NOT_ACTION", + "DefaultNotEligibleRouting": "TEST_NOT_ELI", + "Version": 1, + "Name": "ELI-376-04-Iteration-Config-Name", + "IterationDate": "20240808", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_376_cohort_1", + "CohortGroup": "rsv_eli_376_cohort_group", + "PositiveDescription": "are a member of eli_376_cohort_group_0", + "NegativeDescription": "are not a member of eli_376_cohort_group_0", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_376_cohort_2", + "CohortGroup": "rsv_eli_376_cohort_group", + "PositiveDescription": "are a member of eli_376_cohort_group_10", + "NegativeDescription": "are not a member of eli_376_cohort_group_10", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_376_cohort_3", + "CohortGroup": "rsv_eli_376_cohort_group", + "PositiveDescription": "are a member of eli_376_cohort_group_20", + "NegativeDescription": "are not a member of eli_376_cohort_group_20", + "Priority": 20 + }, + { + "CohortLabel": "rsv_eli_376_cohort_4", + "CohortGroup": "rsv_eli_376_cohort_group_other", + "PositiveDescription": "are a member of eli_376_cohort_group_other", + "NegativeDescription": "are not a member of eli_376_cohort_group_other", + "Priority": 30 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "NotEligible Reason 1", + "Description": "NotEligible Description 1", + "Operator": "Y<=", + "Comparator": "-80", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_376_cohort_1", + "Priority": 100 + }, + { + "Type": "F", + "Name": "NotEligible Reason 2", + "Description": "NotEligible Description 2", + "Operator": "=", + "Comparator": "ABC", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CohortLabel": "rsv_eli_376_cohort_4", + "Priority": 110 + } + ], + "ActionsMapper": { + "TEST_ACTION": { + "ExternalRoutingCode": "TestAction", + "ActionDescription": "TestAction Description", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + }, + "TEST_NOT_ACTION": { + "ExternalRoutingCode": "TestNotAction", + "ActionDescription": "TestNotAction Description", + "ActionType": "ButtonWithAuthLink", + "UrlLink": null, + "UrlLabel": "" + }, + "TEST_NOT_ELI": { + "ExternalRoutingCode": "TestNotEli", + "ActionDescription": "TestNotEli Description", + "ActionType": "", + "UrlLink": null, + "UrlLabel": "" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/inProgressTestConfigs/ELI-376-405/AUTO_RSV_ELI-376-05.json b/tests/e2e/data/configs/inProgressTestConfigs/ELI-376-405/AUTO_RSV_ELI-376-05.json new file mode 100644 index 000000000..cccccc457 --- /dev/null +++ b/tests/e2e/data/configs/inProgressTestConfigs/ELI-376-405/AUTO_RSV_ELI-376-05.json @@ -0,0 +1,161 @@ +{ + "CampaignConfig": { + "ID": "AUTO_RSV_ELI-376-05-Campaign-ID", + "Version": 1, + "Name": "ELI-376-05-Iteration-Config-Name", + "Type": "V", + "Target": "RSV", + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "BOOK_NBS", + "Iterations": [ + { + "ID": "AUTO_RSV_ELI-376-05-Iteration-ID", + "DefaultCommsRouting": "TEST_ACTION", + "DefaultNotActionableRouting": "TEST_NOT_ACTION", + "DefaultNotEligibleRouting": "TEST_NOT_ELI", + "Version": 1, + "Name": "ELI-376-05-Iteration-Config-Name", + "IterationDate": "20240808", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_376_cohort_1", + "CohortGroup": "rsv_eli_376_cohort_group", + "PositiveDescription": "are a member of eli_376_cohort_group_0", + "NegativeDescription": "are not a member of eli_376_cohort_group_0", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_376_cohort_2", + "CohortGroup": "rsv_eli_376_cohort_group", + "PositiveDescription": "are a member of eli_376_cohort_group_10", + "NegativeDescription": "are not a member of eli_376_cohort_group_10", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_376_cohort_3", + "CohortGroup": "rsv_eli_376_cohort_group", + "PositiveDescription": "are a member of eli_376_cohort_group_20", + "NegativeDescription": "are not a member of eli_376_cohort_group_20", + "Priority": 20 + }, + { + "CohortLabel": "rsv_eli_376_cohort_4", + "CohortGroup": "rsv_eli_376_cohort_group_other", + "PositiveDescription": "are a member of eli_376_cohort_group_other", + "NegativeDescription": "are not a member of eli_376_cohort_group_other", + "Priority": 30 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "NotEligible Reason 1", + "Description": "NotEligible Description 1", + "Operator": "Y<=", + "Comparator": "-80", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Cohort_label": "rsv_eli_376_cohort_1", + "Priority": 100 + }, + { + "Type": "F", + "Name": "NotEligible Reason 2", + "Description": "NotEligible Description 2", + "Operator": "=", + "Comparator": "ABC", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "Cohort_label": "rsv_eli_376_cohort_1", + "Priority": 110 + }, + { + "Type": "F", + "Name": "NotEligible Reason 3", + "Description": "NotEligible Description 3", + "Operator": "=", + "Comparator": "E02003792", + "AttributeLevel": "PERSON", + "AttributeName": "MSOA", + "Cohort_label": "rsv_eli_376_cohort_3", + "Priority": 120 + }, + { + "Type": "F", + "Name": "NotEligible Reason 4", + "Description": "NotEligible Description 4", + "Operator": "=", + "Comparator": "E01018267", + "AttributeLevel": "PERSON", + "AttributeName": "LSOA", + "Cohort_label": "rsv_eli_376_cohort_4", + "Priority": 130 + }, + { + "Type": "S", + "Name": "NotActionable Reason 1", + "Description": "NotActionable Description 1", + "Operator": "Y<=", + "Comparator": "-80", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Priority": 200 + }, + { + "Type": "S", + "Name": "NotActionable Reason 2", + "Description": "NotActionable Description 2", + "Operator": "=", + "Comparator": "ABC", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "Priority": 210 + } + ], + "ActionsMapper": { + "TEST_ACTION": { + "ExternalRoutingCode": "TestAction", + "ActionDescription": "TestAction Description", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + }, + "TEST_NOT_ACTION": { + "ExternalRoutingCode": "TestNotAction", + "ActionDescription": "TestNotAction Description", + "ActionType": "ButtonWithAuthLink", + "UrlLink": null, + "UrlLabel": "" + }, + "TEST_NOT_ELI": { + "ExternalRoutingCode": "TestNotEli", + "ActionDescription": "TestNotEli Description", + "ActionType": "", + "UrlLink": null, + "UrlLabel": "" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/inProgressTestConfigs/ELI-376-405/AUTO_RSV_ELI-376-10.json b/tests/e2e/data/configs/inProgressTestConfigs/ELI-376-405/AUTO_RSV_ELI-376-10.json new file mode 100644 index 000000000..c2f3b6d6f --- /dev/null +++ b/tests/e2e/data/configs/inProgressTestConfigs/ELI-376-405/AUTO_RSV_ELI-376-10.json @@ -0,0 +1,141 @@ +{ + "CampaignConfig": { + "ID": "AUTO_RSV_ELI-376-10-Campaign-ID", + "Version": 1, + "Name": "ELI-376-10-Iteration-Config-Name", + "Type": "V", + "Target": "RSV", + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "BOOK_NBS", + "Iterations": [ + { + "ID": "AUTO_RSV_ELI-376-10-Iteration-ID", + "DefaultCommsRouting": "TEST_ACTION", + "DefaultNotActionableRouting": "TEST_NOT_ACTION", + "DefaultNotEligibleRouting": "TEST_NOT_ELI", + "Version": 1, + "Name": "ELI-376-05-Iteration-Config-Name", + "IterationDate": "20240808", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_376_cohort_1", + "CohortGroup": "rsv_eli_376_cohort_group", + "PositiveDescription": "are a member of eli_376_cohort_group_0", + "NegativeDescription": "are not a member of eli_376_cohort_group_0", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_376_cohort_2", + "CohortGroup": "rsv_eli_376_cohort_group", + "PositiveDescription": "are a member of eli_376_cohort_group_10", + "NegativeDescription": "are not a member of eli_376_cohort_group_10", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_376_cohort_3", + "CohortGroup": "rsv_eli_376_cohort_group", + "PositiveDescription": "are a member of eli_376_cohort_group_20", + "NegativeDescription": "are not a member of eli_376_cohort_group_20", + "Priority": 20 + }, + { + "CohortLabel": "rsv_eli_376_cohort_4", + "CohortGroup": "rsv_eli_376_cohort_group_other", + "PositiveDescription": "are a member of eli_376_cohort_group_other", + "NegativeDescription": "are not a member of eli_376_cohort_group_other", + "Priority": 30 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "NotEligible Reason 1", + "Description": "NotEligible Description 1", + "Operator": "Y<=", + "Comparator": "-80", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_376_cohort_1", + "Priority": 100 + }, + { + "Type": "F", + "Name": "NotEligible Reason 2", + "Description": "NotEligible Description 2", + "Operator": "=", + "Comparator": "ABC", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CohortLabel": "rsv_eli_376_cohort_2", + "Priority": 110 + }, + { + "Type": "S", + "Name": "NotActionable Reason 1", + "Description": "NotActionable Description 1", + "Operator": "Y<=", + "Comparator": "-80", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_376_cohort_3", + "Priority": 200 + }, + { + "Type": "S", + "Name": "NotActionable Reason 2", + "Description": "NotActionable Description 2", + "Operator": "=", + "Comparator": "ABC", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CohortLabel": "rsv_eli_376_cohort_4", + "Priority": 210 + } + ], + "ActionsMapper": { + "TEST_ACTION": { + "ExternalRoutingCode": "TestAction", + "ActionDescription": "TestAction Description", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + }, + "TEST_NOT_ACTION": { + "ExternalRoutingCode": "TestNotAction", + "ActionDescription": "TestNotAction Description", + "ActionType": "ButtonWithAuthLink", + "UrlLink": null, + "UrlLabel": "" + }, + "TEST_NOT_ELI": { + "ExternalRoutingCode": "TestNotEli", + "ActionDescription": "TestNotEli Description", + "ActionType": "", + "UrlLink": null, + "UrlLabel": "" + } + } + } + ] + } +} diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-365v0.5.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-365v0.5.json new file mode 100644 index 000000000..78f94df3c --- /dev/null +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-365v0.5.json @@ -0,0 +1,451 @@ +{ + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "EliD RSV example config", + "Type": "V", + "Target": "RSV", + "Manager": ["person1@nhs.net"], + "Approver": ["person1@nhs.net"], + "Reviewer": ["person1@nhs.net"], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "BOOK_LOCAL|HELP_SUPPORT", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "CHECK_CORRECT_X", + "Version": 1, + "Name": "EliD RSV example config", + "IterationDate": "20250717", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_75to79", + "CohortGroup": "rsv_age", + "PositiveDescription": "are aged 75 to 79 years old", + "NegativeDescription": "are not aged 75 to 79 years old", + "Priority": 0 + }, + { + "CohortLabel": "rsv_80_since_02_Sept_2024", + "CohortGroup": "rsv_age_catchup", + "PositiveDescription": "turned 80 after 1st September 2024", + "NegativeDescription": "did not turn 80 after 1 September 2024", + "Priority": 10 + }, + { + "CohortLabel": "elid_all_people", + "CohortGroup": "magic_cohort", + "PositiveDescription": "", + "NegativeDescription": "", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "Remove from magic cohort unless already vaccinated or have future booking", + "Description": "Remove anyone NOT already vaccinated within the last 25 years and do not have a future booking from the magic cohort", + "Operator": "Y<=", + "Comparator": "-25[[NVL:18000101]]", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "elid_all_people", + "Priority": 100 + }, + { + "Type": "F", + "Name": "Remove from magic cohort unless already vaccinated or have future booking", + "Description": "Remove anyone without a future booking from magic cohort", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CohortLabel": "elid_all_people", + "Priority": 100 + }, + { + "Type": "F", + "Name": "Remove under 75 Years on day of execution", + "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", + "Priority": 120, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75", + "CohortLabel": "rsv_75to79" + }, + { + "Type": "F", + "Name": "Remove over 80 on day of execution", + "Description": "Exclude anyone who turned 80 before 2nd September 2024", + "Priority": 130, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "<", + "Comparator": "19440902", + "CohortLabel": "rsv_75to79" + }, + { + "Type": "F", + "Name": "Remove under 75 Years on day of execution", + "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", + "Priority": 140, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75", + "CohortLabel": "rsv_80_since_02_Sept_2024" + }, + { + "Type": "F", + "Name": "Remove over 80 on day of execution", + "Description": "Exclude anyone who turned 80 before 2nd September 2024", + "Priority": 150, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "<", + "Comparator": "19440902", + "CohortLabel": "rsv_80_since_02_Sept_2024" + }, + { + "Type": "F", + "Name": "Remove from rsv 80 cohort if already vaccinated", + "Description": "Remove anyone already vaccinated from 80 cohort", + "Operator": "Y>=", + "Comparator": "-25", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "Priority": 160 + }, + { + "Type": "F", + "Name": "Remove from rsv 80 cohort if future booking", + "Description": "Remove anyone with a future booking from RSV 80 cohort", + "Operator": "D>=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "Priority": 170 + }, + { + "Type": "F", + "Name": "Remove from rsv 75-79 cohort if already vaccinated", + "Description": "Remove anyone already vaccinated from 75-79 cohort", + "Operator": "Y>=", + "Comparator": "-25", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "rsv_75to79", + "Priority": 180 + }, + { + "Type": "F", + "Name": "Remove from rsv 75-79 cohort if future booking", + "Description": "Remove anyone with a future booking from RSV 75-79 cohort", + "Operator": "D>=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CohortLabel": "rsv_75to79", + "Priority": 190 + }, + { + "Type": "S", + "Name": "Already Vaccinated", + "Description": "## You've had your RSV vaccination\n\n We believe you had your vaccination.", + "Priority": 200, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "Y>=", + "Comparator": "-25", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Other Setting", + "Description": "## Getting the vaccine\n\n We believe you're living in a setting where care is provided.\n\n Speak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 510, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Other Setting", + "Description": "## Getting the vaccine\n\n We believe you're living in a setting where care is provided.\n\n Speak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 520, + "AttributeLevel": "PERSON", + "AttributeName": "DE_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Other Setting", + "Description": "## Getting the vaccine\n\n We believe you're living in a setting where care is provided.\n\n Speak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 530, + "AttributeLevel": "PERSON", + "AttributeName": "13Q_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Other Setting", + "Description": "## Getting the vaccine\n\n We believe you're living in a setting where care is provided.\n\n Speak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 540, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_75to79", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Other Setting with no future booking", + "Description": "## Getting the vaccine\n\n We believe you're living in a setting where care is provided.\n\n Speak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 550, + "AttributeLevel": "PERSON", + "AttributeName": "DE_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_75to79", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Other Setting", + "Description": "## Getting the vaccine\n\n We believe you're living in a setting where care is provided.\n\n Speak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 560, + "AttributeLevel": "PERSON", + "AttributeName": "13Q_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_75to79", + "RuleStop": "Y" + }, + { + "Type": "R", + "Name": "Actionable Future Booked NBS Appointment", + "Description": "Amend NBS future booking", + "Priority": 1000, + "Operator": "D>=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked NBS Appointment", + "Description": "Amend NBS future booking", + "Priority": 1000, + "Operator": "=", + "Comparator": "NBS", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Local Appointment", + "Description": "Amend local future booking", + "Priority": 1100, + "Operator": "D>=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "MANAGE_LOCAL" + }, + { + "Type": "R", + "Name": "Within CP Expansion ICB not 80 plus", + "Description": "Book an appointment on NBS as within CP expansion", + "Priority": 1200, + "Operator": "in", + "Comparator": "QH8,QJG", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" + }, + { + "Type": "R", + "Name": "Within CP Expansion ICB not 80 plus", + "Description": "Book an appointment on NBS as within CP expansion", + "Priority": 1200, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-80", + "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" + }, + { + "Type": "R", + "Name": "Within CP Expansion Local Authority", + "Description": "Book an appointment on NBS as within CP expansion", + "Priority": 1300, + "Operator": "in", + "Comparator": "E08000028,E08000031,E08000025,E06000016,E06000008,E07000117,E07000120,E08000011,E08000012,E07000122,E07000123,E08000014,E07000126,E08000013,E07000127,E08000015,E07000128", + "AttributeLevel": "PERSON", + "AttributeName": "LOCAL_AUTHORITY", + "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" + }, + { + "Type": "R", + "Name": "Within CP Expansion ICB not 80 plus", + "Description": "Book an appointment on NBS as within CP expansion", + "Priority": 1300, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-80", + "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" + }, + { + "Type": "Y", + "Name": "Already vaccinated default text", + "Description": "Already vaccinated default text", + "Priority": 3000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "Y>=", + "Comparator": "-25", + "CommsRouting": "CHECK_CORRECT_ALREADY_VACCINATED" + }, + { + "Type": "Y", + "Name": "Other setting default text", + "Description": "Other setting default text", + "Priority": 3100, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y", + "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" + }, + { + "Type": "Y", + "Name": "Other setting default text", + "Description": "Other setting default text", + "Priority": 3200, + "AttributeLevel": "PERSON", + "AttributeName": "DE_FLAG", + "Operator": "=", + "Comparator": "Y", + "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" + }, + { + "Type": "Y", + "Name": "Other setting default text", + "Description": "Other setting default text", + "Priority": 3300, + "AttributeLevel": "PERSON", + "AttributeName": "13Q_FLAG", + "Operator": "=", + "Comparator": "Y", + "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + }, + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "## You have an RSV vaccination appointment\n You can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Manage your appointment" + }, + "CONTACT_GP": { + "ExternalRoutingCode": "ContactGP", + "ActionDescription": "Contact your GP", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "BOOK_LOCAL": { + "ExternalRoutingCode": "BookLocal", + "ActionDescription": "## Getting the vaccine\n\n You can get an RSV vaccination at your GP surgery.\n Your GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "MANAGE_LOCAL": { + "ExternalRoutingCode": "ManageLocal", + "ActionDescription": "## You have an RSV vaccination appointment\n\n Contact your healthcare provider to change or cancel your appointment.", + "ActionType": "CardWithText", + "UrlLink": null, + "UrlLabel": "" + }, + "HELP_SUPPORT": { + "ExternalRoutingCode": "HelpSupportInfo", + "ActionDescription": "## If you think this is incorrect\n\n If you have already had this vaccination or your personal details are wrong, visit our [help and support page](https://digital.nhs.uk/services/eligibility-data-product-elid).", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "CHECK_CORRECT_X": { + "ExternalRoutingCode": "HealthcareProInfo", + "ActionDescription": "## If you think this is incorrect\n\n Speak to your healthcare professional if you think you should be offered this vaccine.\n\nFor anything else, visit our [help and support page](https://digital.nhs.uk/services/eligibility-data-product-elid).", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "CHECK_CORRECT_ALREADY_VACCINATED": { + "ExternalRoutingCode": "AlreadyVaccinatedInfo", + "ActionDescription": "## If you think this is incorrect\n\n If you believe you've not been vaccinated against RSV, speak to your healthcare professional.\n\nFor anything else please see our [help and support page](https://digital.nhs.uk/services/eligibility-data-product-elid).", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "CHECK_CORRECT_OTHER_SETTING": { + "ExternalRoutingCode": "ManagedSettingInfo", + "ActionDescription": "## If you think this is incorrect\n\n If you have already had this vaccination or your personal details are wrong, visit our [help and support page](https://digital.nhs.uk/services/eligibility-data-product-elid).", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + } + } + } + ] + } +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-406_001.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-406_001.json new file mode 100644 index 000000000..9fb6c6e64 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-406_001.json @@ -0,0 +1,41 @@ +{ + "scenario_name": "ELI-406 - Missing Person Attribute, Cohort and Target Present", + "request_headers": { + "nhs-login-nhs-number": "9900406001" + }, + "expected_response_code": 404, + "config_filenames": [ + "AUTO_RSV_ELI-406-01.json" + ], + "data": [ + { + "NHS_NUMBER": "9900406001", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_eli_406_cohort_1", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_406_cohort_2", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_406_cohort_3", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_406_cohort_4", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900406001", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-406_002.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-406_002.json new file mode 100644 index 000000000..16e3ef457 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-406_002.json @@ -0,0 +1,19 @@ +{ + "scenario_name": "ELI-406 - Missing Person Attribute, Target Present", + "request_headers": { + "nhs-login-nhs-number": "9900406002" + }, + "expected_response_code": 404, + "config_filenames": [ + "AUTO_RSV_ELI-406-01.json" + ], + "data": [ + { + "NHS_NUMBER": "9900406002", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-406_003.json b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-406_003.json new file mode 100644 index 000000000..49f89d482 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/AUTO_RSV_ELI-406_003.json @@ -0,0 +1,34 @@ +{ + "scenario_name": "ELI-406 - Missing Person Attribute, Cohort Present", + "request_headers": { + "nhs-login-nhs-number": "9900406003" + }, + "expected_response_code": 404, + "config_filenames": [ + "AUTO_RSV_ELI-406-01.json" + ], + "data": [ + { + "NHS_NUMBER": "9900406003", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_eli_406_cohort_1", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_406_cohort_2", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_406_cohort_3", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_406_cohort_4", + "DATE_JOINED": "20231020" + } + ] + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-220-371/AUTO_RSV_ELI-220_001.json b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-220-371/AUTO_RSV_ELI-220_001.json new file mode 100644 index 000000000..0bd1dab97 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-220-371/AUTO_RSV_ELI-220_001.json @@ -0,0 +1,58 @@ +{ + "scenario_name": "ELI-220 - Actionable Cohort Grouping Text - all have descriptions", + "request_headers": { + "nhs-login-nhs-number": "9900220001" + }, + "config_filenames": [ + "AUTO_RSV_ELI-220-01.json" + ], + "data": [ + { + "NHS_NUMBER": "9900220001", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_eli_220_cohort_1", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_220_cohort_2", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_220_cohort_3", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_220_cohort_4", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900220001", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "Y" + }, + { + "NHS_NUMBER": "9900220001", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-220-371/AUTO_RSV_ELI-220_002.json b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-220-371/AUTO_RSV_ELI-220_002.json new file mode 100644 index 000000000..e648620f6 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-220-371/AUTO_RSV_ELI-220_002.json @@ -0,0 +1,58 @@ +{ + "scenario_name": "ELI-220 - Actionable Cohort Grouping Text - highest prio no description", + "request_headers": { + "nhs-login-nhs-number": "9900220002" + }, + "config_filenames": [ + "AUTO_RSV_ELI-220-02.json" + ], + "data": [ + { + "NHS_NUMBER": "9900220002", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_eli_220_cohort_1", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_220_cohort_2", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_220_cohort_3", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_220_cohort_4", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900220002", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "Y" + }, + { + "NHS_NUMBER": "9900220002", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-220-371/AUTO_RSV_ELI-220_003.json b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-220-371/AUTO_RSV_ELI-220_003.json new file mode 100644 index 000000000..c4f0b32c1 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-220-371/AUTO_RSV_ELI-220_003.json @@ -0,0 +1,58 @@ +{ + "scenario_name": "ELI-220 - NotActionable Cohort Grouping Text - all have descriptions", + "request_headers": { + "nhs-login-nhs-number": "9900220003" + }, + "config_filenames": [ + "AUTO_RSV_ELI-220-03.json" + ], + "data": [ + { + "NHS_NUMBER": "9900220003", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_eli_220_cohort_1", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_220_cohort_2", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_220_cohort_3", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_220_cohort_4", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900220003", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "Y" + }, + { + "NHS_NUMBER": "2209900220003", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-220-371/AUTO_RSV_ELI-220_004.json b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-220-371/AUTO_RSV_ELI-220_004.json new file mode 100644 index 000000000..fec870ce3 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-220-371/AUTO_RSV_ELI-220_004.json @@ -0,0 +1,58 @@ +{ + "scenario_name": "ELI-220 - NotActionable Cohort Grouping Text - highest prio has no description", + "request_headers": { + "nhs-login-nhs-number": "9900220004" + }, + "config_filenames": [ + "AUTO_RSV_ELI-220-04.json" + ], + "data": [ + { + "NHS_NUMBER": "9900220004", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_eli_220_cohort_1", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_220_cohort_2", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_220_cohort_3", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_220_cohort_4", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900220004", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "Y" + }, + { + "NHS_NUMBER": "2209900220004", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-220-371/AUTO_RSV_ELI-220_005.json b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-220-371/AUTO_RSV_ELI-220_005.json new file mode 100644 index 000000000..d76c98660 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-220-371/AUTO_RSV_ELI-220_005.json @@ -0,0 +1,58 @@ +{ + "scenario_name": "ELI-220 - NotEligible Cohort Grouping - all have descriptions", + "request_headers": { + "nhs-login-nhs-number": "9900220005" + }, + "config_filenames": [ + "AUTO_RSV_ELI-220-05.json" + ], + "data": [ + { + "NHS_NUMBER": "9900220005", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_eli_220_cohort_1", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_220_cohort_2", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_220_cohort_3", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_220_cohort_4", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900220005", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "Y" + }, + { + "NHS_NUMBER": "2209900220005", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": null, + "BOOKED_APPOINTMENT_PROVIDER": null + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-220-371/AUTO_RSV_ELI-220_006.json b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-220-371/AUTO_RSV_ELI-220_006.json new file mode 100644 index 000000000..473a82191 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-220-371/AUTO_RSV_ELI-220_006.json @@ -0,0 +1,58 @@ +{ + "scenario_name": "ELI-220 - NotEligible Cohort Grouping - highest prio has no description", + "request_headers": { + "nhs-login-nhs-number": "9900220006" + }, + "config_filenames": [ + "AUTO_RSV_ELI-220-06.json" + ], + "data": [ + { + "NHS_NUMBER": "9900220006", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_eli_220_cohort_1", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_220_cohort_2", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_220_cohort_3", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_220_cohort_4", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900220006", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "Y" + }, + { + "NHS_NUMBER": "2209900220006", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": null, + "BOOKED_APPOINTMENT_PROVIDER": null + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_001.json b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_001.json new file mode 100644 index 000000000..7738a29f5 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_001.json @@ -0,0 +1,50 @@ +{ + "scenario_name": "ELI-274 - Create/return Not Actionable reasons - Trigger No Suitability Rules - Actionable", + "request_headers": { + "nhs-login-nhs-number": "9900247001" + }, + "config_filenames": ["AUTO_RSV_ELI-274-01.json"], + "data": [ + { + "NHS_NUMBER": "9900247001", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_eli_274_cohort_1", + "DATE_JOINED": "20231020" + },{ + "COHORT_LABEL": "rsv_eli_274_cohort_2", + "DATE_JOINED": "20231020" + },{ + "COHORT_LABEL": "rsv_eli_274_cohort_3", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900247001", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "Y" + }, + { + "NHS_NUMBER": "9900247001", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE" : "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_002.json b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_002.json new file mode 100644 index 000000000..b835e909b --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_002.json @@ -0,0 +1,50 @@ +{ + "scenario_name": "ELI-274 - Create/return Not Actionable reasons - Trigger 1 Suitability Rule - Actionable", + "request_headers": { + "nhs-login-nhs-number": "9900247002" + }, + "config_filenames": ["AUTO_RSV_ELI-274-01.json"], + "data": [ + { + "NHS_NUMBER": "9900247002", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_eli_274_cohort_1", + "DATE_JOINED": "20231020" + },{ + "COHORT_LABEL": "rsv_eli_274_cohort_2", + "DATE_JOINED": "20231020" + },{ + "COHORT_LABEL": "rsv_eli_274_cohort_3", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900247002", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "Y" + }, + { + "NHS_NUMBER": "9900247002", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE" : "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_003.json b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_003.json new file mode 100644 index 000000000..ac3b7758e --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_003.json @@ -0,0 +1,50 @@ +{ + "scenario_name": "ELI-274 - Create/return Not Actionable reasons - Trigger 2 Suitability Rules - Actionable", + "request_headers": { + "nhs-login-nhs-number": "9900247003" + }, + "config_filenames": ["AUTO_RSV_ELI-274-01.json"], + "data": [ + { + "NHS_NUMBER": "9900247003", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_eli_274_cohort_1", + "DATE_JOINED": "20231020" + },{ + "COHORT_LABEL": "rsv_eli_274_cohort_2", + "DATE_JOINED": "20231020" + },{ + "COHORT_LABEL": "rsv_eli_274_cohort_3", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900247003", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "AAA", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "Y" + }, + { + "NHS_NUMBER": "9900247003", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE" : "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_004.json b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_004.json new file mode 100644 index 000000000..86f7616e5 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_004.json @@ -0,0 +1,50 @@ +{ + "scenario_name": "ELI-274 - Create/return Not Actionable reasons - Trigger 3 Suitability Rules with Different Names - NotActionable", + "request_headers": { + "nhs-login-nhs-number": "9900247004" + }, + "config_filenames": ["AUTO_RSV_ELI-274-01.json"], + "data": [ + { + "NHS_NUMBER": "9900247004", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_eli_274_cohort_1", + "DATE_JOINED": "20231020" + },{ + "COHORT_LABEL": "rsv_eli_274_cohort_2", + "DATE_JOINED": "20231020" + },{ + "COHORT_LABEL": "rsv_eli_274_cohort_3", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900247004", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "AAA", + "COMMISSIONING_REGION": "ZZZ", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "Y" + }, + { + "NHS_NUMBER": "9900247004", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE" : "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_005.json b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_005.json new file mode 100644 index 000000000..527c94b26 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_005.json @@ -0,0 +1,50 @@ +{ + "scenario_name": "ELI-274 - Create/return Not Actionable reasons - Trigger 3 Suitability Rules 2 with same name 1 with different Name - NotActionable", + "request_headers": { + "nhs-login-nhs-number": "9900247005" + }, + "config_filenames": ["AUTO_RSV_ELI-274-05.json"], + "data": [ + { + "NHS_NUMBER": "9900247005", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_eli_274_cohort_1", + "DATE_JOINED": "20231020" + },{ + "COHORT_LABEL": "rsv_eli_274_cohort_2", + "DATE_JOINED": "20231020" + },{ + "COHORT_LABEL": "rsv_eli_274_cohort_3", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900247005", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "AAA", + "COMMISSIONING_REGION": "ZZZ", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "Y" + }, + { + "NHS_NUMBER": "9900247005", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE" : "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_006.json b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_006.json new file mode 100644 index 000000000..5ba02d6a1 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_006.json @@ -0,0 +1,50 @@ +{ + "scenario_name": "ELI-274 - Create/return Not Actionable reasons - Trigger 3 Suitability Rules all with same names - NotActionable", + "request_headers": { + "nhs-login-nhs-number": "9900247006" + }, + "config_filenames": ["AUTO_RSV_ELI-274-06.json"], + "data": [ + { + "NHS_NUMBER": "9900247006", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_eli_274_cohort_1", + "DATE_JOINED": "20231020" + },{ + "COHORT_LABEL": "rsv_eli_274_cohort_2", + "DATE_JOINED": "20231020" + },{ + "COHORT_LABEL": "rsv_eli_274_cohort_3", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900247006", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "AAA", + "COMMISSIONING_REGION": "ZZZ", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "Y" + }, + { + "NHS_NUMBER": "9900247006", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE" : "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_007.json b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_007.json new file mode 100644 index 000000000..6f5c27e22 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_007.json @@ -0,0 +1,50 @@ +{ + "scenario_name": "ELI-274 - Create/return Not Actionable reasons - Trigger 3 Suitability Rules all with same names - 1st No description - NotActionable", + "request_headers": { + "nhs-login-nhs-number": "9900247007" + }, + "config_filenames": ["AUTO_RSV_ELI-274-07.json"], + "data": [ + { + "NHS_NUMBER": "9900247007", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_eli_274_cohort_1", + "DATE_JOINED": "20231020" + },{ + "COHORT_LABEL": "rsv_eli_274_cohort_2", + "DATE_JOINED": "20231020" + },{ + "COHORT_LABEL": "rsv_eli_274_cohort_3", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900247007", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "AAA", + "COMMISSIONING_REGION": "ZZZ", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "Y" + }, + { + "NHS_NUMBER": "9900247007", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE" : "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-376-405/AUTO_RSV_ELI-376_001.json b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-376-405/AUTO_RSV_ELI-376_001.json new file mode 100644 index 000000000..1f085096c --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-376-405/AUTO_RSV_ELI-376_001.json @@ -0,0 +1,58 @@ +{ + "scenario_name": "ELI-376 - Audit Records - No F no S - Actionable", + "request_headers": { + "nhs-login-nhs-number": "9900376001" + }, + "config_filenames": [ + "AUTO_RSV_ELI-376-01.json" + ], + "data": [ + { + "NHS_NUMBER": "9900376001", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_eli_376_cohort_1", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_376_cohort_2", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_376_cohort_3", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_376_cohort_4", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900376001", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "Y" + }, + { + "NHS_NUMBER": "9900376001", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-376-405/AUTO_RSV_ELI-376_002.json b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-376-405/AUTO_RSV_ELI-376_002.json new file mode 100644 index 000000000..492ad0cde --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-376-405/AUTO_RSV_ELI-376_002.json @@ -0,0 +1,58 @@ +{ + "scenario_name": "ELI-376 - Audit Records - Single F Rule - NotEligible", + "request_headers": { + "nhs-login-nhs-number": "9900376002" + }, + "config_filenames": [ + "AUTO_RSV_ELI-376-02.json" + ], + "data": [ + { + "NHS_NUMBER": "9900376002", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_eli_376_cohort_1", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_376_cohort_2", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_376_cohort_3", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_376_cohort_4", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900376002", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QUE", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "Y" + }, + { + "NHS_NUMBER": "9900376002", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-376-405/AUTO_RSV_ELI-376_003.json b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-376-405/AUTO_RSV_ELI-376_003.json new file mode 100644 index 000000000..17c1f6de3 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-376-405/AUTO_RSV_ELI-376_003.json @@ -0,0 +1,58 @@ +{ + "scenario_name": "ELI-376 - Audit Records - One F Rule One S Rule", + "request_headers": { + "nhs-login-nhs-number": "9900376003" + }, + "config_filenames": [ + "AUTO_RSV_ELI-376-03.json" + ], + "data": [ + { + "NHS_NUMBER": "9900376003", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_eli_376_cohort_1", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_376_cohort_2", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_376_cohort_3", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_376_cohort_4", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900376003", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "ABC", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "Y" + }, + { + "NHS_NUMBER": "9900376003", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-376-405/AUTO_RSV_ELI-376_004.json b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-376-405/AUTO_RSV_ELI-376_004.json new file mode 100644 index 000000000..46fd8abfa --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-376-405/AUTO_RSV_ELI-376_004.json @@ -0,0 +1,58 @@ +{ + "scenario_name": "ELI-376 - Audit Records - Multiple F Rules - Actionable", + "request_headers": { + "nhs-login-nhs-number": "9900376004" + }, + "config_filenames": [ + "AUTO_RSV_ELI-376-04.json" + ], + "data": [ + { + "NHS_NUMBER": "9900376004", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_eli_376_cohort_1", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_376_cohort_2", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_376_cohort_3", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_376_cohort_4", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900376004", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "ABC", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "Y" + }, + { + "NHS_NUMBER": "9900376004", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-376-405/AUTO_RSV_ELI-376_005.json b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-376-405/AUTO_RSV_ELI-376_005.json new file mode 100644 index 000000000..e52ce68d4 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-376-405/AUTO_RSV_ELI-376_005.json @@ -0,0 +1,58 @@ +{ + "scenario_name": "ELI-376 - Audit Records - Multiple F Rules - NotEligible", + "request_headers": { + "nhs-login-nhs-number": "9900376005" + }, + "config_filenames": [ + "AUTO_RSV_ELI-376-05.json" + ], + "data": [ + { + "NHS_NUMBER": "9900376005", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_eli_376_cohort_1", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_376_cohort_2", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_376_cohort_3", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_376_cohort_4", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900376005", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "ABC", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "Y" + }, + { + "NHS_NUMBER": "9900376005", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-376-405/AUTO_RSV_ELI-376_010.json b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-376-405/AUTO_RSV_ELI-376_010.json new file mode 100644 index 000000000..85579eb04 --- /dev/null +++ b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-376-405/AUTO_RSV_ELI-376_010.json @@ -0,0 +1,58 @@ +{ + "scenario_name": "ELI-376 - Audit Records - Multiple F & S Rules", + "request_headers": { + "nhs-login-nhs-number": "9900376010" + }, + "config_filenames": [ + "AUTO_RSV_ELI-376-10.json" + ], + "data": [ + { + "NHS_NUMBER": "9900376010", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_eli_376_cohort_1", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_376_cohort_2", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_376_cohort_3", + "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_eli_376_cohort_4", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900376010", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "ABC", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "Y" + }, + { + "NHS_NUMBER": "9900376010", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": "<>", + "BOOKED_APPOINTMENT_PROVIDER": "NBS" + } + ] +} diff --git a/tests/e2e/data/generate_dynamo_data.py b/tests/e2e/data/generate_dynamo_data.py deleted file mode 100644 index ea4e8f599..000000000 --- a/tests/e2e/data/generate_dynamo_data.py +++ /dev/null @@ -1,116 +0,0 @@ -import json -import logging -import os -import re -from datetime import datetime, timedelta, timezone -from pathlib import Path -from typing import Any - -OUTPUT_ROOT = "out" -DATE_FORMAT = "%Y%m%d" -VAR_PATTERN = re.compile(r"<<([^<>]+)>>") -REQUIRED_TOKEN_PARTS = 3 - -logger = logging.getLogger(__name__) -logging.basicConfig(level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(message)s") - - -class DateVariableResolver: - def __init__(self, today: datetime | None = None): - self.today = today or datetime.now(tz=timezone.UTC) - - def resolve(self, token: str) -> str: - logger.debug("Resolving variable: %s", token) - parts = token.split("_") - if len(parts) < REQUIRED_TOKEN_PARTS or parts[0].upper() != "DATE": - msg = f"Unsupported variable format: {token}" - raise ValueError(msg) - _, unit, value = parts[0], parts[1].lower(), parts[2] - try: - offset = int(value) - except ValueError as err: - msg = f"Invalid offset value: {value}" - raise ValueError(msg) from err - if unit == "day": - return (self.today + timedelta(days=offset)).strftime(DATE_FORMAT) - if unit == "week": - return (self.today + timedelta(weeks=offset)).strftime(DATE_FORMAT) - if unit == "year": - return (self.today.replace(year=self.today.year + offset)).strftime(DATE_FORMAT) - if unit == "age": - try: - birth_date = self.today.replace(year=self.today.year - offset) - except ValueError: - birth_date = self.today.replace(month=2, day=28, year=self.today.year - offset) - return birth_date.strftime(DATE_FORMAT) - msg = f"Unsupported calculation unit: {unit}" - raise ValueError(msg) - - -class JsonTestDataProcessor: - def __init__(self, input_dir: Path, output_dir: Path, resolver: DateVariableResolver): - self.input_dir = input_dir - self.output_dir = output_dir - self.resolver = resolver - - def resolve_placeholders(self, obj: Any) -> Any: - if isinstance(obj, dict): - return {k: self.resolve_placeholders(v) for k, v in obj.items()} - if isinstance(obj, list): - return [self.resolve_placeholders(item) for item in obj] - if isinstance(obj, str): - return VAR_PATTERN.sub(self._replace_token, obj) - return obj - - def _replace_token(self, match: re.Match) -> str: - token = match.group(1) - try: - return self.resolver.resolve(token) - except ValueError: - logger.warning("Failed to resolve variable: %s", token) - return match.group(0) - - def process_file(self, file_path: Path): - logger.info("Processing file: %s", file_path) - try: - with file_path.open() as f: - content = json.load(f) - except Exception: - logger.exception("Failed to read file: %s", file_path) - return - try: - resolved = self.resolve_placeholders(content) - except Exception: - logger.exception("Failed to resolve placeholders in file: %s", file_path) - return - if "data" not in resolved: - logger.error("Missing 'data' key in file: %s", file_path) - return - relative_path = file_path.relative_to(self.input_dir) - output_path = self.output_dir / relative_path - output_path.parent.mkdir(parents=True, exist_ok=True) - try: - with output_path.open("w") as f: - json.dump(resolved["data"], f, indent=2) - logger.info("Written resolved file: %s", output_path) - except Exception: - logger.exception("Failed to write output to: %s", output_path) - - -def main(): - input_dir = Path() - output_dir = Path(OUTPUT_ROOT) - resolver = DateVariableResolver() - processor = JsonTestDataProcessor(input_dir, output_dir, resolver) - logger.info("Scanning for JSON files in directory: %s", input_dir) - for root, _, files in os.walk(input_dir): - for file in files: - file_path = Path(root) / file - if file.endswith(".json"): - processor.process_file(file_path) - else: - logger.debug("Skipping non-JSON file: %s", file) - - -if __name__ == "__main__": - main() diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-406_001.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-406_001.json new file mode 100644 index 000000000..390a9f4af --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-406_001.json @@ -0,0 +1,26 @@ +{ + "resourceType": "OperationOutcome", + "id": "e158b107-4283-43ee-8a4e-8e68cd15d26f", + "meta": { + "lastUpdated": "2025-08-12T08:09:01.662728Z" + }, + "issue": [ + { + "severity": "error", + "code": "processing", + "details": { + "coding": [ + { + "system": "https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1", + "code": "REFERENCE_NOT_FOUND", + "display": "The given NHS number was not found in our datasets. This could be because the number is incorrect or some other reason we cannot process that number." + } + ] + }, + "diagnostics": "NHS Number '9900406001' was not recognised by the Eligibility Signposting API", + "location": [ + "parameters/id" + ] + } + ] +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-406_002.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-406_002.json new file mode 100644 index 000000000..fc3bdba0e --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-406_002.json @@ -0,0 +1,26 @@ +{ + "resourceType": "OperationOutcome", + "id": "e158b107-4283-43ee-8a4e-8e68cd15d26f", + "meta": { + "lastUpdated": "2025-08-12T08:09:01.662728Z" + }, + "issue": [ + { + "severity": "error", + "code": "processing", + "details": { + "coding": [ + { + "system": "https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1", + "code": "REFERENCE_NOT_FOUND", + "display": "The given NHS number was not found in our datasets. This could be because the number is incorrect or some other reason we cannot process that number." + } + ] + }, + "diagnostics": "NHS Number '9900406002' was not recognised by the Eligibility Signposting API", + "location": [ + "parameters/id" + ] + } + ] +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-406_003.json b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-406_003.json new file mode 100644 index 000000000..761c1327b --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/AUTO_RSV_ELI-406_003.json @@ -0,0 +1,26 @@ +{ + "resourceType": "OperationOutcome", + "id": "e158b107-4283-43ee-8a4e-8e68cd15d26f", + "meta": { + "lastUpdated": "2025-08-12T08:09:01.662728Z" + }, + "issue": [ + { + "severity": "error", + "code": "processing", + "details": { + "coding": [ + { + "system": "https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1", + "code": "REFERENCE_NOT_FOUND", + "display": "The given NHS number was not found in our datasets. This could be because the number is incorrect or some other reason we cannot process that number." + } + ] + }, + "diagnostics": "NHS Number '9900406003' was not recognised by the Eligibility Signposting API", + "location": [ + "parameters/id" + ] + } + ] +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/ELI-220-371/AUTO_RSV_ELI-220_001.json b/tests/e2e/data/responses/inProgressTestResponses/ELI-220-371/AUTO_RSV_ELI-220_001.json new file mode 100644 index 000000000..230b4da39 --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/ELI-220-371/AUTO_RSV_ELI-220_001.json @@ -0,0 +1,27 @@ +{ + "meta": { + "lastUpdated": "2025-08-06T14:44:56.294924+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_eli_220_cohort_group", + "cohortStatus": "Actionable", + "cohortText": "are a member of eli_220_cohort_group_0" + }, + { + "cohortCode": "rsv_eli_220_cohort_group_other", + "cohortStatus": "Actionable", + "cohortText": "are a member of eli_220_cohort_group_other" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "6e7d0d6a-1a1f-4238-a7ae-2911414c1536" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/ELI-220-371/AUTO_RSV_ELI-220_002.json b/tests/e2e/data/responses/inProgressTestResponses/ELI-220-371/AUTO_RSV_ELI-220_002.json new file mode 100644 index 000000000..944211f08 --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/ELI-220-371/AUTO_RSV_ELI-220_002.json @@ -0,0 +1,27 @@ +{ + "meta": { + "lastUpdated": "2025-08-06T14:44:56.294924+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_eli_220_cohort_group", + "cohortStatus": "Actionable", + "cohortText": "are a member of eli_220_cohort_group_10" + }, + { + "cohortCode": "rsv_eli_220_cohort_group_other", + "cohortStatus": "Actionable", + "cohortText": "are a member of eli_220_cohort_group_other" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "6e7d0d6a-1a1f-4238-a7ae-2911414c1536" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/ELI-220-371/AUTO_RSV_ELI-220_003.json b/tests/e2e/data/responses/inProgressTestResponses/ELI-220-371/AUTO_RSV_ELI-220_003.json new file mode 100644 index 000000000..cf4a88e42 --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/ELI-220-371/AUTO_RSV_ELI-220_003.json @@ -0,0 +1,33 @@ +{ + "meta": { + "lastUpdated": "2025-08-08T06:57:31.196189+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_eli_220_cohort_group", + "cohortStatus": "NotActionable", + "cohortText": "are a member of eli_220_cohort_group_0" + }, + { + "cohortCode": "rsv_eli_220_cohort_group_other", + "cohortStatus": "NotActionable", + "cohortText": "are a member of eli_220_cohort_group_other" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "NotActionable Reason 1", + "ruleText": "Description 1", + "ruleType": "S" + } + ] + } + ], + "responseId": "331c56ff-9c97-47be-8781-34cea44d400b" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/ELI-220-371/AUTO_RSV_ELI-220_004.json b/tests/e2e/data/responses/inProgressTestResponses/ELI-220-371/AUTO_RSV_ELI-220_004.json new file mode 100644 index 000000000..a19d23777 --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/ELI-220-371/AUTO_RSV_ELI-220_004.json @@ -0,0 +1,33 @@ +{ + "meta": { + "lastUpdated": "2025-08-08T06:56:33.768243+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_eli_220_cohort_group", + "cohortStatus": "NotActionable", + "cohortText": "are a member of eli_220_cohort_group_10" + }, + { + "cohortCode": "rsv_eli_220_cohort_group_other", + "cohortStatus": "NotActionable", + "cohortText": "are a member of eli_220_cohort_group_other" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "NotActionable Reason 1", + "ruleText": "Description 1", + "ruleType": "S" + } + ] + } + ], + "responseId": "ec8a1a74-5505-4391-a2ca-093499377d34" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/ELI-220-371/AUTO_RSV_ELI-220_005.json b/tests/e2e/data/responses/inProgressTestResponses/ELI-220-371/AUTO_RSV_ELI-220_005.json new file mode 100644 index 000000000..9e0d07f0a --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/ELI-220-371/AUTO_RSV_ELI-220_005.json @@ -0,0 +1,27 @@ +{ + "meta": { + "lastUpdated": "2025-08-08T06:57:31.196189+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_eli_220_cohort_group", + "cohortStatus": "NotEligible", + "cohortText": "are not a member of eli_220_cohort_group_0" + }, + { + "cohortCode": "rsv_eli_220_cohort_group_other", + "cohortStatus": "NotEligible", + "cohortText": "are not a member of eli_220_cohort_group_other" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "331c56ff-9c97-47be-8781-34cea44d400b" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/ELI-220-371/AUTO_RSV_ELI-220_006.json b/tests/e2e/data/responses/inProgressTestResponses/ELI-220-371/AUTO_RSV_ELI-220_006.json new file mode 100644 index 000000000..8fb9c4a89 --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/ELI-220-371/AUTO_RSV_ELI-220_006.json @@ -0,0 +1,27 @@ +{ + "meta": { + "lastUpdated": "2025-08-08T06:57:31.196189+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_eli_220_cohort_group", + "cohortStatus": "NotEligible", + "cohortText": "are not a member of eli_220_cohort_group_10" + }, + { + "cohortCode": "rsv_eli_220_cohort_group_other", + "cohortStatus": "NotEligible", + "cohortText": "are not a member of eli_220_cohort_group_other" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "331c56ff-9c97-47be-8781-34cea44d400b" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/ELI-274-371/AUTO_RSV_ELI-274_001.json b/tests/e2e/data/responses/inProgressTestResponses/ELI-274-371/AUTO_RSV_ELI-274_001.json new file mode 100644 index 000000000..b04d907a4 --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/ELI-274-371/AUTO_RSV_ELI-274_001.json @@ -0,0 +1,27 @@ +{ + "meta": { + "lastUpdated": "2025-08-06T14:44:56.294924+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_eli_274_cohort_group", + "cohortStatus": "Actionable", + "cohortText": "are a member of eli_274_cohort_group" + }, + { + "cohortCode": "rsv_eli_274_cohort_group_other", + "cohortStatus": "Actionable", + "cohortText": "are a member of eli_274_cohort_group_other" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "6e7d0d6a-1a1f-4238-a7ae-2911414c1536" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/ELI-274-371/AUTO_RSV_ELI-274_002.json b/tests/e2e/data/responses/inProgressTestResponses/ELI-274-371/AUTO_RSV_ELI-274_002.json new file mode 100644 index 000000000..b04d907a4 --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/ELI-274-371/AUTO_RSV_ELI-274_002.json @@ -0,0 +1,27 @@ +{ + "meta": { + "lastUpdated": "2025-08-06T14:44:56.294924+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_eli_274_cohort_group", + "cohortStatus": "Actionable", + "cohortText": "are a member of eli_274_cohort_group" + }, + { + "cohortCode": "rsv_eli_274_cohort_group_other", + "cohortStatus": "Actionable", + "cohortText": "are a member of eli_274_cohort_group_other" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "6e7d0d6a-1a1f-4238-a7ae-2911414c1536" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/ELI-274-371/AUTO_RSV_ELI-274_003.json b/tests/e2e/data/responses/inProgressTestResponses/ELI-274-371/AUTO_RSV_ELI-274_003.json new file mode 100644 index 000000000..134a01f3d --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/ELI-274-371/AUTO_RSV_ELI-274_003.json @@ -0,0 +1,22 @@ +{ + "meta": { + "lastUpdated": "2025-08-06T15:07:37.517048+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_eli_274_cohort_group_other", + "cohortStatus": "Actionable", + "cohortText": "are a member of eli_274_cohort_group_other" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "d07cf694-db14-4289-bd10-3ca365b943d7" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/ELI-274-371/AUTO_RSV_ELI-274_004.json b/tests/e2e/data/responses/inProgressTestResponses/ELI-274-371/AUTO_RSV_ELI-274_004.json new file mode 100644 index 000000000..b2b2e610b --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/ELI-274-371/AUTO_RSV_ELI-274_004.json @@ -0,0 +1,43 @@ +{ + "meta": { + "lastUpdated": "2025-08-06T15:15:45.231568+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_eli_274_cohort_group", + "cohortStatus": "NotActionable", + "cohortText": "are a member of eli_274_cohort_group" + }, + { + "cohortCode": "rsv_eli_274_cohort_group_other", + "cohortStatus": "NotActionable", + "cohortText": "are a member of eli_274_cohort_group_other" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "NotActionable Reason 1", + "ruleText": "Description 1", + "ruleType": "S" + }, + { + "ruleCode": "NotActionable Reason 2", + "ruleText": "Description 2", + "ruleType": "S" + }, + { + "ruleCode": "NotActionable Reason 3", + "ruleText": "Description 3", + "ruleType": "S" + } + ] + } + ], + "responseId": "f6dfcae4-32c4-493a-a0cd-5faf83f3da64" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/ELI-274-371/AUTO_RSV_ELI-274_005.json b/tests/e2e/data/responses/inProgressTestResponses/ELI-274-371/AUTO_RSV_ELI-274_005.json new file mode 100644 index 000000000..67020a81d --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/ELI-274-371/AUTO_RSV_ELI-274_005.json @@ -0,0 +1,38 @@ +{ + "meta": { + "lastUpdated": "2025-08-06T15:15:45.231568+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_eli_274_cohort_group", + "cohortStatus": "NotActionable", + "cohortText": "are a member of eli_274_cohort_group" + }, + { + "cohortCode": "rsv_eli_274_cohort_group_other", + "cohortStatus": "NotActionable", + "cohortText": "are a member of eli_274_cohort_group_other" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "NotActionable Reason", + "ruleText": "Description 1", + "ruleType": "S" + }, + { + "ruleCode": "NotActionable Reason 3", + "ruleText": "Description 3", + "ruleType": "S" + } + ] + } + ], + "responseId": "f6dfcae4-32c4-493a-a0cd-5faf83f3da64" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/ELI-274-371/AUTO_RSV_ELI-274_006.json b/tests/e2e/data/responses/inProgressTestResponses/ELI-274-371/AUTO_RSV_ELI-274_006.json new file mode 100644 index 000000000..03672f8fd --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/ELI-274-371/AUTO_RSV_ELI-274_006.json @@ -0,0 +1,33 @@ +{ + "meta": { + "lastUpdated": "2025-08-06T15:15:45.231568+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_eli_274_cohort_group", + "cohortStatus": "NotActionable", + "cohortText": "are a member of eli_274_cohort_group" + }, + { + "cohortCode": "rsv_eli_274_cohort_group_other", + "cohortStatus": "NotActionable", + "cohortText": "are a member of eli_274_cohort_group_other" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "NotActionable Reason", + "ruleText": "Description 1", + "ruleType": "S" + } + ] + } + ], + "responseId": "f6dfcae4-32c4-493a-a0cd-5faf83f3da64" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/ELI-274-371/AUTO_RSV_ELI-274_007.json b/tests/e2e/data/responses/inProgressTestResponses/ELI-274-371/AUTO_RSV_ELI-274_007.json new file mode 100644 index 000000000..3605be341 --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/ELI-274-371/AUTO_RSV_ELI-274_007.json @@ -0,0 +1,33 @@ +{ + "meta": { + "lastUpdated": "2025-08-06T15:15:45.231568+00:00" + }, + "processedSuggestions": [ + { + "actions": [], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_eli_274_cohort_group", + "cohortStatus": "NotActionable", + "cohortText": "are a member of eli_274_cohort_group" + }, + { + "cohortCode": "rsv_eli_274_cohort_group_other", + "cohortStatus": "NotActionable", + "cohortText": "are a member of eli_274_cohort_group_other" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "NotActionable Reason", + "ruleText": "Description 2", + "ruleType": "S" + } + ] + } + ], + "responseId": "f6dfcae4-32c4-493a-a0cd-5faf83f3da64" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/ELI-376-405/AUTO_RSV_ELI-376_001.json b/tests/e2e/data/responses/inProgressTestResponses/ELI-376-405/AUTO_RSV_ELI-376_001.json new file mode 100644 index 000000000..d611ab02e --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/ELI-376-405/AUTO_RSV_ELI-376_001.json @@ -0,0 +1,35 @@ +{ + "meta": { + "lastUpdated": "2025-08-10T19:50:31.729537+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "TestAction", + "actionType": "ButtonWithAuthLink", + "description": "TestAction Description", + "urlLabel": "Continue to booking", + "urlLink": "http://www.nhs.uk/book-rsv" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_eli_376_cohort_group", + "cohortStatus": "Actionable", + "cohortText": "are a member of eli_376_cohort_group_0" + }, + { + "cohortCode": "rsv_eli_376_cohort_group_other", + "cohortStatus": "Actionable", + "cohortText": "are a member of eli_376_cohort_group_other" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "5e24d72c-04cb-4672-94c6-ce3a2d28671c" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/ELI-376-405/AUTO_RSV_ELI-376_002.json b/tests/e2e/data/responses/inProgressTestResponses/ELI-376-405/AUTO_RSV_ELI-376_002.json new file mode 100644 index 000000000..508175962 --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/ELI-376-405/AUTO_RSV_ELI-376_002.json @@ -0,0 +1,35 @@ +{ + "meta": { + "lastUpdated": "2025-08-10T20:01:50.163464+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "TestNotEli", + "actionType": "", + "description": "TestNotEli Description", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_eli_376_cohort_group", + "cohortStatus": "NotEligible", + "cohortText": "are not a member of eli_376_cohort_group_0" + }, + { + "cohortCode": "rsv_eli_376_cohort_group_other", + "cohortStatus": "NotEligible", + "cohortText": "are not a member of eli_376_cohort_group_other" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "8251a4cd-3d39-4b46-8c32-03597c834c51" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/ELI-376-405/AUTO_RSV_ELI-376_003.json b/tests/e2e/data/responses/inProgressTestResponses/ELI-376-405/AUTO_RSV_ELI-376_003.json new file mode 100644 index 000000000..c8108df45 --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/ELI-376-405/AUTO_RSV_ELI-376_003.json @@ -0,0 +1,36 @@ +{ + "meta": { + "lastUpdated": "2025-08-10T20:50:17.917074+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "TestNotAction", + "actionType": "ButtonWithAuthLink", + "description": "TestNotAction Description", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_eli_376_cohort_group", + "cohortStatus": "NotActionable", + "cohortText": "are a member of eli_376_cohort_group_0" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "NotActionable Reason 1", + "ruleText": "NotActionable Description 1", + "ruleType": "S" + } + ] + } + ], + "responseId": "3726dc78-c69e-491a-8cbc-90e059ba2d49" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/ELI-376-405/AUTO_RSV_ELI-376_004.json b/tests/e2e/data/responses/inProgressTestResponses/ELI-376-405/AUTO_RSV_ELI-376_004.json new file mode 100644 index 000000000..cce80a66f --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/ELI-376-405/AUTO_RSV_ELI-376_004.json @@ -0,0 +1,30 @@ +{ + "meta": { + "lastUpdated": "2025-08-11T06:55:30.261123+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "TestAction", + "actionType": "ButtonWithAuthLink", + "description": "TestAction Description", + "urlLabel": "Continue to booking", + "urlLink": "http://www.nhs.uk/book-rsv" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_eli_376_cohort_group", + "cohortStatus": "Actionable", + "cohortText": "are a member of eli_376_cohort_group_10" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "48d5721e-7c0e-4cc5-9d7a-e7448724340a" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/ELI-376-405/AUTO_RSV_ELI-376_005.json b/tests/e2e/data/responses/inProgressTestResponses/ELI-376-405/AUTO_RSV_ELI-376_005.json new file mode 100644 index 000000000..d49216aad --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/ELI-376-405/AUTO_RSV_ELI-376_005.json @@ -0,0 +1,35 @@ +{ + "meta": { + "lastUpdated": "2025-08-11T20:39:19.034500+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "TestNotEli", + "actionType": "", + "description": "TestNotEli Description", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_eli_376_cohort_group", + "cohortStatus": "NotEligible", + "cohortText": "are not a member of eli_376_cohort_group_0" + }, + { + "cohortCode": "rsv_eli_376_cohort_group_other", + "cohortStatus": "NotEligible", + "cohortText": "are not a member of eli_376_cohort_group_other" + } + ], + "status": "NotEligible", + "statusText": "We do not believe you can have it", + "suitabilityRules": [] + } + ], + "responseId": "9d6c58d5-b36d-4f93-be8b-e5ed5e9da570" +} diff --git a/tests/e2e/data/responses/inProgressTestResponses/ELI-376-405/AUTO_RSV_ELI-376_010.json b/tests/e2e/data/responses/inProgressTestResponses/ELI-376-405/AUTO_RSV_ELI-376_010.json new file mode 100644 index 000000000..d10737c7c --- /dev/null +++ b/tests/e2e/data/responses/inProgressTestResponses/ELI-376-405/AUTO_RSV_ELI-376_010.json @@ -0,0 +1,46 @@ +{ + "meta": { + "lastUpdated": "2025-08-10T20:51:37.488101+00:00" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "TestNotAction", + "actionType": "ButtonWithAuthLink", + "description": "TestNotAction Description", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_eli_376_cohort_group", + "cohortStatus": "NotActionable", + "cohortText": "are a member of eli_376_cohort_group_20" + }, + { + "cohortCode": "rsv_eli_376_cohort_group_other", + "cohortStatus": "NotActionable", + "cohortText": "are a member of eli_376_cohort_group_other" + } + ], + "status": "NotActionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [ + { + "ruleCode": "NotActionable Reason 1", + "ruleText": "NotActionable Description 1", + "ruleType": "S" + }, + { + "ruleCode": "NotActionable Reason 2", + "ruleText": "NotActionable Description 2", + "ruleType": "S" + } + ] + } + ], + "responseId": "7518efc4-1606-43e2-a56e-0a4daefdd229" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-2.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-2.json index d959499f3..4f3339882 100644 --- a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-2.json +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-216-2.json @@ -1,3 +1,26 @@ { - "response_items": "NHS number mismatch" + "resourceType": "OperationOutcome", + "id": "e158b107-4283-43ee-8a4e-8e68cd15d26f", + "meta": { + "lastUpdated": "2025-08-12T08:09:01.662728Z" + }, + "issue": [ + { + "severity": "error", + "code": "processing", + "details": { + "coding": [ + { + "system": "https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1", + "code": "REFERENCE_NOT_FOUND", + "display": "The given NHS number was not found in our datasets. This could be because the number is incorrect or some other reason we cannot process that number." + } + ] + }, + "diagnostics": "NHS Number '9686368' was not recognised by the Eligibility Signposting API", + "location": [ + "parameters/id" + ] + } + ] } diff --git a/tests/e2e/features/__init__.py b/tests/e2e/features/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/e2e/features/conftest.py b/tests/e2e/features/conftest.py deleted file mode 100644 index 0ab0c0301..000000000 --- a/tests/e2e/features/conftest.py +++ /dev/null @@ -1,32 +0,0 @@ -import os - -import pytest -import requests -from dotenv import load_dotenv - -# Load environment variables -load_dotenv() - -# Constants -BASE_URL = os.getenv("BASE_URL", "https://sandbox.api.service.nhs.uk/eligibility-signposting-api") -API_KEY = os.getenv("API_KEY", "") -VALID_NHS_NUMBER = os.getenv("VALID_NHS_NUMBER", "50000000004") -HTTP_STATUS_SERVER_ERROR = 500 - - -@pytest.fixture(scope="session", autouse=False) -def check_api_accessibility(): - """Check if the API is accessible before running tests.""" - try: - response = requests.get( - f"{BASE_URL}/eligibility-check", - params={"patient": VALID_NHS_NUMBER}, - headers={"apikey": API_KEY, "Accept": "application/json"}, - timeout=5, - ) - # If we get a 4xx response, the API is accessible but our request is invalid - # If we get a 5xx response, the API is having issues - if response.status_code >= HTTP_STATUS_SERVER_ERROR: - pytest.skip("API is returning server errors") - except (requests.RequestException, requests.Timeout): - pytest.skip("API is not accessible") diff --git a/tests/e2e/features/eligibility_check/eligibility_check.feature b/tests/e2e/features/eligibility_check/eligibility_check.feature deleted file mode 100644 index e10353c07..000000000 --- a/tests/e2e/features/eligibility_check/eligibility_check.feature +++ /dev/null @@ -1,44 +0,0 @@ - -Feature: Eligibility Check API - As a consumer of the Eligibility Check API - I want to verify the endpoint's response for various NHS numbers and parameters - So that I can ensure the API behaves as expected for all supported scenarios - - Background: - Given the Eligibility Check API base URL is configured - - Scenario Outline: Successful eligibility check returns 2xx and valid response - Given I have the NHS number "" - When I request an eligibility check for the NHS number - Then the response status code should be 2xx - And the response content type should be application/json - And the response should have a JSON body - And the response should match the eligibility check schema - - Examples: - | nhs_number | - | 50000000001 | - | 50000000004 | - | 9876543210 | - - Scenario Outline: Eligibility check with invalid or missing NHS number returns error - Given I have the NHS number "" - When I request an eligibility check for the NHS number - Then the response status code should be 4xx or 404 - - Examples: - | nhs_number | - | 00000000000 | - | | - | patient=ABC | - - Scenario Outline: Eligibility check with custom Accept header - Given I have the NHS number "" - And I set the Accept header to "" - When I request an eligibility check for the NHS number - Then the response content type should contain "" - - Examples: - | nhs_number | accept_header | expected_content_type | - | 9876543210 | application/json | application/json | - | 9876543210 | application/json | application/json | diff --git a/tests/e2e/features/environment.py b/tests/e2e/features/environment.py deleted file mode 100644 index cfb2becac..000000000 --- a/tests/e2e/features/environment.py +++ /dev/null @@ -1,189 +0,0 @@ -import json -import logging -import os -from pathlib import Path - -import boto3 -from botocore.exceptions import BotoCoreError -from dotenv import load_dotenv - -logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s") -logger = logging.getLogger("behave.environment") - - -def _load_environment_variables(context): - load_dotenv(dotenv_path=".env") - context.base_url = os.getenv("BASE_URL") - context.api_key = os.getenv("API_KEY") - context.valid_nhs_number = os.getenv("VALID_NHS_NUMBER", "50000000004") - context.aws_region = os.getenv("AWS_REGION", "eu-west-2") - context.inserted_items = [] - context.abort_on_aws_error = os.getenv("ABORT_ON_AWS_FAILURE", "false").lower() == "true" - context.keep_seed = os.getenv("KEEP_SEED", "false").lower() == "true" - context.s3_bucket = os.getenv("S3_BUCKET_NAME") - context.s3_upload_dir = os.getenv("S3_UPLOAD_DIR", "") - context.s3_data_path = Path(os.getenv("S3_JSON_SOURCE_DIR", "./data/s3")).resolve() - context.dynamodb_table_name = os.getenv("DYNAMODB_TABLE_NAME", "eligibilty_data_store") - context.dynamo_data_path = Path(os.getenv("DYNAMO_JSON_SOURCE_DIR", "./data/out/dynamoDB")).resolve() - logger.info("ABORT_ON_AWS_FAILURE=%s", context.abort_on_aws_error) - logger.info("KEEP_SEED=%s", context.keep_seed) - - -def _connect_to_dynamodb(context): - try: - context.dynamodb = boto3.resource("dynamodb", region_name=context.aws_region) - context.table = context.dynamodb.Table(context.dynamodb_table_name) - _ = context.table.table_status - except (boto3.exceptions.Boto3Error, BotoCoreError): - logger.exception("DynamoDB not accessible") - return False - else: - logger.info("Connected to DynamoDB table: %s", context.dynamodb_table_name) - return True - - -def _get_dynamo_seed_files(context): - if not context.dynamo_data_path.exists() or not context.dynamo_data_path.is_dir(): - logger.error("Seed directory not found: %s", context.dynamo_data_path) - return [] - return list(context.dynamo_data_path.glob("*.json")) - - -def _load_seed_file(file_path: Path): - try: - with file_path.open() as f: - return json.load(f) - except (OSError, json.JSONDecodeError): - logger.exception("Failed to load seed file: %s", file_path) - return [] - - -def _insert_dynamodb_items(context, items): - for item in items: - try: - context.table.put_item(Item=item) - context.inserted_items.append(item) - except (boto3.exceptions.Boto3Error, BotoCoreError): - logger.exception("Failed to insert item %s", item.get("PK", "")) - - -def _setup_dynamodb(context): - if not _connect_to_dynamodb(context): - if context.abort_on_aws_error: - context.abort_all = True - return False - json_files = _get_dynamo_seed_files(context) - if not json_files: - logger.error("No JSON files found in the directory: %s", context.dynamo_data_path) - if context.abort_on_aws_error: - context.abort_all = True - return False - logger.info("Found %d JSON files to insert into DynamoDB", len(json_files)) - for file_path in json_files: - items = _load_seed_file(file_path) - if not items: - if context.abort_on_aws_error: - context.abort_all = True - continue - logger.info("Inserting %d items from %s...", len(items), file_path.name) - _insert_dynamodb_items(context, items) - logger.info("Inserted %d items from %d files", len(context.inserted_items), len(json_files)) - return True - - -def _setup_s3(context): - if not context.s3_bucket: - logger.info("Skipping S3 upload โ€” no S3_BUCKET_NAME set.") - return True - logger.info( - "Uploading JSON files from %s to S3 bucket: %s/%s", - context.s3_data_path, - context.s3_bucket, - context.s3_upload_dir, - ) - try: - s3_client = boto3.client("s3", region_name=context.aws_region) - if not context.s3_data_path.exists(): - logger.error("S3 source directory not found: %s", context.s3_data_path) - return False - json_files = list(context.s3_data_path.glob("*.json")) - upload_success = True - for file_path in json_files: - key = f"{context.s3_upload_dir}/{file_path.name}" if context.s3_upload_dir else file_path.name - try: - s3_client.upload_file(str(file_path), context.s3_bucket, key) - logger.info("Uploaded %s to s3://%s/%s", file_path.name, context.s3_bucket, key) - except (boto3.exceptions.Boto3Error, BotoCoreError): - logger.exception("Failed to upload %s", file_path.name) - upload_success = False - except (boto3.exceptions.Boto3Error, BotoCoreError): - logger.exception("S3 upload setup failed") - if context.abort_on_aws_error: - context.abort_all = True - return False - else: - return upload_success - - -def before_all(context): - logger.info("Loading .env and initializing AWS fixtures...") - _load_environment_variables(context) - _setup_dynamodb(context) - _setup_s3(context) - - -def before_scenario(context, scenario): - if getattr(context, "abort_all", False): - scenario.skip("Skipping scenario due to setup failure") - if "requires_dynamodb" in scenario.tags and not context.inserted_items: - scenario.skip("Skipping due to missing seeded DynamoDB data") - - -def _cleanup_dynamodb(context): - if not context.inserted_items: - logger.info("No items were inserted โ€” skipping DynamoDB cleanup.") - return - logger.info("Cleaning up seeded items from DynamoDB...") - delete_count = 0 - for item in context.inserted_items: - nhs_number = item.get("NHS_NUMBER") - attribute_type = item.get("ATTRIBUTE_TYPE") - if nhs_number and attribute_type: - try: - context.table.delete_item(Key={"NHS_NUMBER": nhs_number, "ATTRIBUTE_TYPE": attribute_type}) - delete_count += 1 - except (boto3.exceptions.Boto3Error, BotoCoreError): - logger.exception("Failed to delete item (%s, %s)", nhs_number, attribute_type) - else: - logger.error("Cannot delete item โ€” missing NHS_NUMBER or ATTRIBUTE_TYPE: %s", item) - logger.info("Deleted %d/%d DynamoDB items", delete_count, len(context.inserted_items)) - - -def _cleanup_s3(context): - if not (context.s3_bucket and context.s3_data_path.exists()): - logger.info("Skipping S3 cleanup โ€” no bucket or source directory not found.") - return - logger.info("Cleaning up uploaded files from S3...") - try: - s3_client = boto3.client("s3", region_name=context.aws_region) - json_files = list(context.s3_data_path.glob("*.json")) - deleted_files = 0 - for file_path in json_files: - key = f"{context.s3_upload_dir}/{file_path.name}" if context.s3_upload_dir else file_path.name - try: - s3_client.delete_object(Bucket=context.s3_bucket, Key=key) - logger.info("Deleted s3://%s/%s", context.s3_bucket, key) - deleted_files += 1 - except (boto3.exceptions.Boto3Error, BotoCoreError): - logger.exception("Failed to delete s3://%s/%s", context.s3_bucket, key) - logger.info("Deleted %d/%d files from S3", deleted_files, len(json_files)) - except (boto3.exceptions.Boto3Error, BotoCoreError): - logger.exception("S3 cleanup failed") - - -def after_all(context): - if getattr(context, "keep_seed", False): - logger.info("KEEP_SEED=true โ€” skipping cleanup.") - return - _cleanup_dynamodb(context) - _cleanup_s3(context) diff --git a/tests/e2e/features/steps/eligibility_check_steps.py b/tests/e2e/features/steps/eligibility_check_steps.py deleted file mode 100644 index 3a0640dab..000000000 --- a/tests/e2e/features/steps/eligibility_check_steps.py +++ /dev/null @@ -1,85 +0,0 @@ -import jsonschema -import pytest -import requests -from behave import given, then, when -from utils.config import API_KEY, BASE_URL, ELIGIBILITY_CHECK_SCHEMA - -# HTTP Status Code Constants -HTTP_STATUS_OK = 200 -HTTP_STATUS_BAD_REQUEST = 400 -HTTP_STATUS_NOT_FOUND = 404 -HTTP_STATUS_SERVER_ERROR = 500 - - -@given("the Eligibility Check API base URL is configured") -def step_impl_base_url(context): - context.base_url = BASE_URL - context.headers = {"apikey": API_KEY} - - -@given('I have the NHS number "{nhs_number}"') -def step_impl_nhs_number(context, nhs_number): - context.nhs_number = nhs_number - - -@given('I have the NHS number ""') -def step_impl_empty_nhs_number(context): - context.nhs_number = "" - - -@given('I set the Accept header to "{accept_header}"') -def step_impl_accept_header(context, accept_header): - context.headers["Accept"] = accept_header - - -@when("I request an eligibility check for the NHS number") -def step_impl_request_eligibility_check(context): - # Use the correct endpoint: /patient-check/{nhs_number} - if context.nhs_number: - url = f"{context.base_url}/patient-check/{context.nhs_number}" - else: - url = f"{context.base_url}/patient-check/" - context.response = requests.get(url, headers=context.headers, timeout=10) - - -@then("the response status code should be 2xx") -def step_impl_status_code_2xx(context): - assert HTTP_STATUS_OK <= context.response.status_code < HTTP_STATUS_BAD_REQUEST, ( - f"Expected 2xx, got {context.response.status_code}" - ) - - -@then("the response status code should be 4xx or 404") -def step_impl_status_code_4xx_or_404(context): - assert ( - HTTP_STATUS_BAD_REQUEST <= context.response.status_code < HTTP_STATUS_SERVER_ERROR - ) or context.response.status_code == HTTP_STATUS_NOT_FOUND, ( - f"Expected 4xx or 404, got {context.response.status_code}" - ) - - -@then("the response content type should be application/json") -def step_impl_content_type_json(context): - assert "application/json" in context.response.headers.get("Content-Type", ""), ( - f"Content-Type is not application/json, got {context.response.headers.get('Content-Type', '')}" - ) - - -@then("the response should have a JSON body") -def step_impl_has_json_body(context): - try: - context.response.json() - except (ValueError, TypeError) as e: - pytest.fail(f"Response does not have a JSON body: {e}") - - -@then("the response should match the eligibility check schema") -def step_impl_schema(context): - jsonschema.validate(instance=context.response.json(), schema=ELIGIBILITY_CHECK_SCHEMA) - - -@then('the response content type should contain "{expected_content_type}"') -def step_impl_content_type_contains(context, expected_content_type): - assert expected_content_type in context.response.headers.get("Content-Type", ""), ( - f"Content-Type does not contain {expected_content_type}, got {context.response.headers.get('Content-Type', '')}" - ) From e5a55875130f81fc0997136f014003edb872e52e Mon Sep 17 00:00:00 2001 From: Sean Steberis <103416906+seansteberisal@users.noreply.github.com> Date: Tue, 12 Aug 2025 11:29:18 +0100 Subject: [PATCH 69/72] ELI-365 - over 80s (#288) * ELI-318: Adds content-type in 404 and 500 error response (#232) * ELI-294: Personalised/customised status text (#234) * ELI-294: Personalised/customised status text * ELI-294: Personalised/customised status text * ELI-294: Adds unit tests for Status Enum * ELI-294: Fix sonar issues * ELI-318: Adds application/fhir+json as valid mime type in Mangum (#235) * ELI-331: Mask PII/GDPR info (#239) * ELI-331: Mask PII/GDPR info * ELI-331: Fix formatting * ELI-331: Fix format and lint * Revert "ELI-331: Mask PII/GDPR info (#239)" (#240) This reverts commit 14d6a5ec9cc71b193e68ffc7169fdb3e3a1c2ee6. * Feature/eli 295 generic text for not eligible xrules (#238) * WIP: drafting out X and Y rules. * WIP: updated config with x and y rule idea. * WIP: Stub out x and y rules impl * WIP: stubbing out impl. * Refactored action support functions and renamed vars * WIP: Added X/Y Rule logic and test. * Added tests for eligible and actionable actions. * WIP: Added more tests for X and Y rule scenarios. * WIP: flaky tests. * WIP: Fixed failing tests for empty actions. * WIP: added audit record check to tests. * WIP: file format and added audit rule priority and name test. * Working tests. Refactored some audit logic. * Minor refactor * Addressed linting issues * WIP: fixed failing unit tests. * Format. * Added tests. * File format --------- Co-authored-by: Robert * Bump polyfactory from 2.21.0 to 2.22.1 --- updated-dependencies: - dependency-name: polyfactory dependency-version: 2.22.1 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Revert "Bump polyfactory from 2.21.0 to 2.22.1" (#241) * Bump aiohttp from 3.12.13 to 3.12.14 --- updated-dependencies: - dependency-name: aiohttp dependency-version: 3.12.14 dependency-type: direct:development ... Signed-off-by: dependabot[bot] * provided appropriate values factory methods in tests (#245) * Refactor package structure (#247) * eli-343 following on from suggestions from AWS Security Hub, restricting access to public internet via Internet Gateway, and adding table protection in Prod for DynamoDB * add lambda request id in logs events (#248) * add lambda request id in logs events * linting fix * revoked custom formatter and reapplied JsonFormatter * revoked formatting * added wrapper for registering requestid * Adds unit tests for log format --------- Co-authored-by: Shweta <216860557+shweta-nhs@users.noreply.github.com> * Feature/rgjb aa eli 329 add xray tracing for lambda (#243) * Added xray permissions policy for lambda * Add xray vpc endpoint * Added xray to the permissions boundary * Added xray to the assumed role permissions boundary * Testing permission boundary. * testing perm bound. --------- Co-authored-by: ayeshalshukri1-nhs <112615598+ayeshalshukri1-nhs@users.noreply.github.com> * bugfix - Github action needs permission to modify public access block * bugfix - changing permission to be wildcard resource, as it's an account level permission * Added api gateway request id, moved request id logging to app.py (#252) * ELI-351 and ELI-342: Refactors and fixes Cohort Schema Mismatch (#253) * Adds campaign_evaluator and tests * Adds person_data_reader and tests * Injects person data reader and campaign processor into eligibility calculator * ELI-342 Dynamo Cohort Schema Mismatch * ELI-342: Fixes usage of person cohorts method and tests * Feature/eli 369 dynamodb x ray tracing (#256) * handled none headers from request * x-ray tracing setup for dynamo, s3, firehose * enable_xray_patching env variable for lambda * sonar fixes --------- Co-authored-by: karthikeyannhs <174426205+Karthikeyannhs@users.noreply.github.com> * Bump slackapi/slack-github-action from 2.1.0 to 2.1.1 Bumps [slackapi/slack-github-action](https://github.com/slackapi/slack-github-action) from 2.1.0 to 2.1.1. - [Release notes](https://github.com/slackapi/slack-github-action/releases) - [Commits](https://github.com/slackapi/slack-github-action/compare/v2.1.0...v2.1.1) --- updated-dependencies: - dependency-name: slackapi/slack-github-action dependency-version: 2.1.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * ELI-351: Refactor (#254) * ELI-351: Renames rules model to campaign_config * ELI-351: Extracts Person data class * ELI-351: Adds rule processor * ELI-351: Adds tests for rule processor * ELI-351: Moves get_cohort_group_results to rule processor * ELI-351: Adds tests for rule processor * ELI-351: Adds cohort handler using Chain of responsibility pattern * ELI-351: Renames * ELI-351: Fixes lint * ELI-351: Renames evaluate_eligibility to get_eligibility_status * ELI-351: Refactoring to get better readability for chaining * ELI-351: Fix lint * Bump botocore from 1.38.42 to 1.38.46 Bumps [botocore](https://github.com/boto/botocore) from 1.38.42 to 1.38.46. - [Commits](https://github.com/boto/botocore/compare/1.38.42...1.38.46) --- updated-dependencies: - dependency-name: botocore dependency-version: 1.38.46 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * Bump moto from 5.1.6 to 5.1.9 Bumps [moto](https://github.com/getmoto/moto) from 5.1.6 to 5.1.9. - [Release notes](https://github.com/getmoto/moto/releases) - [Changelog](https://github.com/getmoto/moto/blob/master/CHANGELOG.md) - [Commits](https://github.com/getmoto/moto/compare/5.1.6...5.1.9) --- updated-dependencies: - dependency-name: moto dependency-version: 5.1.9 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * Bump localstack from 4.5.0 to 4.6.0 Bumps [localstack](https://github.com/localstack/localstack) from 4.5.0 to 4.6.0. - [Release notes](https://github.com/localstack/localstack/releases) - [Commits](https://github.com/localstack/localstack/compare/v4.5.0...v4.6.0) --- updated-dependencies: - dependency-name: localstack dependency-version: 4.6.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Bump pytest-asyncio from 1.0.0 to 1.1.0 --- updated-dependencies: - dependency-name: pytest-asyncio dependency-version: 1.1.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Bump pytest-docker from 3.2.2 to 3.2.3 Bumps [pytest-docker](https://github.com/avast/pytest-docker) from 3.2.2 to 3.2.3. - [Release notes](https://github.com/avast/pytest-docker/releases) - [Changelog](https://github.com/avast/pytest-docker/blob/master/CHANGELOG.md) - [Commits](https://github.com/avast/pytest-docker/compare/v3.2.2...v3.2.3) --- updated-dependencies: - dependency-name: pytest-docker dependency-version: 3.2.3 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * ELI-351: Refactor (#258) * ELI-351: Refactor * ELI-351: Adds tests for action rule handler * ELI-351: Renames and fixes tests * ELI-351: Renames and fixes tests * Bump asgiref from 3.8.1 to 3.9.1 Bumps [asgiref](https://github.com/django/asgiref) from 3.8.1 to 3.9.1. - [Changelog](https://github.com/django/asgiref/blob/main/CHANGELOG.txt) - [Commits](https://github.com/django/asgiref/compare/3.8.1...3.9.1) --- updated-dependencies: - dependency-name: asgiref dependency-version: 3.9.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Bump gitpython from 3.1.44 to 3.1.45 Bumps [gitpython](https://github.com/gitpython-developers/GitPython) from 3.1.44 to 3.1.45. - [Release notes](https://github.com/gitpython-developers/GitPython/releases) - [Changelog](https://github.com/gitpython-developers/GitPython/blob/main/CHANGES) - [Commits](https://github.com/gitpython-developers/GitPython/compare/3.1.44...3.1.45) --- updated-dependencies: - dependency-name: gitpython dependency-version: 3.1.45 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * Bump pyright from 1.1.402 to 1.1.403 Bumps [pyright](https://github.com/RobertCraigie/pyright-python) from 1.1.402 to 1.1.403. - [Release notes](https://github.com/RobertCraigie/pyright-python/releases) - [Commits](https://github.com/RobertCraigie/pyright-python/compare/v1.1.402...v1.1.403) --- updated-dependencies: - dependency-name: pyright dependency-version: 1.1.403 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * ELI-351: Moves/deletes tests after refactoring (#265) * ELI-351: Moves/deletes tests after refactoring * ELI-351: Extracts EligibilityResultBuilder and adds tests * ELI-351: De-extracts EligibilityResultBuilder and moves tests to Eligibility Calculator tests * ELI-351: Removes duplicated tests * ELI-351: Removes duplicated tests #2 * ELI-351: Adds validation and audit layer to Readme * Bump aiohttp from 3.12.14 to 3.12.15 --- updated-dependencies: - dependency-name: aiohttp dependency-version: 3.12.15 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * trying an approach to ensure correct version of python used in lambda build (#271) * ELI-311: Campaign Config Data Type Changes (#269) * eli-285 and eli-349 adding cloudwatch alarms for a) security and b) ops - API Gateway and Lambda execution * eli-285 - disabling action on API calls as our internal security are triggering this * eli-285 and 349 adding kms for sns, checkov skip for disabled alarms * work in progress * eli-285 enable kms key rotation * eli-285 get rid of false flag gitleak * eli-388 adding access log permissions for audit buckets * eli-386 blocking s3 public access at account level * ELI-376: Audit record should log multiple F and S rules (#275) * ELI-376: Audit record should log multiple F and S rules * ELI-376: Fixing int test * ELI-150: campaign config validation (#264) * validations - wip * iteration validation * iteration rules * campaign config validation * made BUC tests bit more clear * Renaming for clarity. * lint and formatting fixes. * wip * Integration Rules Test * Actions mapper validator * Iterations BUC * available_actions tests * lint fixes * lint fixes * Bump asgiref from 3.8.1 to 3.9.1 Bumps [asgiref](https://github.com/django/asgiref) from 3.8.1 to 3.9.1. - [Changelog](https://github.com/django/asgiref/blob/main/CHANGELOG.txt) - [Commits](https://github.com/django/asgiref/compare/3.8.1...3.9.1) --- updated-dependencies: - dependency-name: asgiref dependency-version: 3.9.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Bump gitpython from 3.1.44 to 3.1.45 Bumps [gitpython](https://github.com/gitpython-developers/GitPython) from 3.1.44 to 3.1.45. - [Release notes](https://github.com/gitpython-developers/GitPython/releases) - [Changelog](https://github.com/gitpython-developers/GitPython/blob/main/CHANGES) - [Commits](https://github.com/gitpython-developers/GitPython/compare/3.1.44...3.1.45) --- updated-dependencies: - dependency-name: gitpython dependency-version: 3.1.45 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * Bump pyright from 1.1.402 to 1.1.403 Bumps [pyright](https://github.com/RobertCraigie/pyright-python) from 1.1.402 to 1.1.403. - [Release notes](https://github.com/RobertCraigie/pyright-python/releases) - [Commits](https://github.com/RobertCraigie/pyright-python/compare/v1.1.402...v1.1.403) --- updated-dependencies: - dependency-name: pyright dependency-version: 1.1.403 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * ELI-351: Moves/deletes tests after refactoring (#265) * ELI-351: Moves/deletes tests after refactoring * ELI-351: Extracts EligibilityResultBuilder and adds tests * ELI-351: De-extracts EligibilityResultBuilder and moves tests to Eligibility Calculator tests * ELI-351: Removes duplicated tests * ELI-351: Removes duplicated tests #2 * ELI-351: Adds validation and audit layer to Readme * wip - has failing tests * test fixed and lint error fixed * warning fixed * rules validation added * test commit * tests updated w.r.t to datatype changes from main * updated output message * arguments added to app.py * sonar fix * sonar fix * sonar fix --------- Signed-off-by: dependabot[bot] Co-authored-by: karthikeyannhs <174426205+Karthikeyannhs@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Shweta <216860557+shweta-nhs@users.noreply.github.com> * eli-386 adding github permissions to make account level public access block changes * all tests passing * Revert "eli-386 blocking s3 public access at account level" * extracting method for readability * ELI-399: Fixing Future Iteration.StartDate Resulting in 500 Error (#282) * ELI-399: Fixing Future Iteration.StartDate Resulting in 500 Error * ELI-399: Adds empty rules to fix flakiness * applying to filter rules and adding test * ELI-397: Fixing nhs number missing from path error to be FHIR compliant (#284) * Added new tests updated poetry version * ELI-399: Fixing start date validation (#287) * ELI-399: Fixing start date validation * ELI-399: Fixing annotation * ELI-328: cohort validations (#281) * test action mapper doesn't accept invalid actions * Attribute level and name relations when it is cohort * added iteration_cohorts_validation * chainging the validations * fix * fix * fix lint * fix lint * lint fixes * lint fixes * test fixes * lint fixes * Removed defaultcomms from iteration level of test config. * Reorder feilds in config. * Update to config. * default comm routing validation * unit tests default comm routing validation * default comm routing validation in rules * lint fixes * test data fixed * grouped model validators --------- Co-authored-by: ayeshalshukri1-nhs <112615598+ayeshalshukri1-nhs@users.noreply.github.com> * fixed new tests * ELI-404: Fix Error message returned for authorisation failure (#289) * ELI-404: Fix Error message returned for authorisation failure * ELI-404: Fix sonar * update packages * Bugfix to change response grouping from name to priority (#286) * changed grouping from name to priority * changed grouping from name to priority, type * sonar code complexity fix * sonar code complexity fix * updated names in the code for better clarity * regroup the suitability tests * fix - ordering regroup the suitability tests * test for cohort groups --------- Co-authored-by: karthikeyannhs <174426205+Karthikeyannhs@users.noreply.github.com> * ELI-397: AWS api gateway to handle bad request param error (#291) * Changes to tests added 1 more test fixed 365 config to work with all test cases * linting and formatting update package updates --------- Signed-off-by: dependabot[bot] Co-authored-by: Shweta <216860557+shweta-nhs@users.noreply.github.com> Co-authored-by: ayeshalshukri1-nhs <112615598+ayeshalshukri1-nhs@users.noreply.github.com> Co-authored-by: Robert Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: eddalmond1 Co-authored-by: Karthikeyannhs <174426205+Karthikeyannhs@users.noreply.github.com> Co-authored-by: eddalmond1 <102675624+eddalmond1@users.noreply.github.com> Co-authored-by: TOEL2 --- .github/workflows/cicd-2-publish.yaml | 2 +- .tool-versions | 3 +- README.md | 28 +- infrastructure/modules/dynamodb/dynamodb.tf | 1 + infrastructure/modules/lambda/lambda.tf | 1 + infrastructure/modules/lambda/variables.tf | 5 + infrastructure/modules/s3/s3.tf | 45 +- .../assumed_role_permissions_boundary.tf | 3 +- .../stacks/api-layer/cloudwatch_alarms.tf | 420 +++ .../stacks/api-layer/iam_policies.tf | 105 +- infrastructure/stacks/api-layer/lambda.tf | 1 + .../stacks/api-layer/patient_check.tf | 51 +- .../github_actions_policies.tf | 51 + .../iams_permissions_boundary.tf | 3 +- infrastructure/stacks/networking/locals.tf | 2 + infrastructure/stacks/networking/vpc.tf | 5 + poetry.lock | 853 ++---- pyproject.toml | 22 +- scripts/config/gitleaks.toml | 2 +- scripts/dependencies.sh | 14 +- src/eligibility_signposting_api/app.py | 18 +- .../audit/audit_context.py | 122 +- .../audit/audit_models.py | 10 +- .../{services/rules => common}/__init__.py | 0 .../common/api-error-response-readme.md | 88 + .../{ => common}/api_error_response.py | 28 +- .../{ => common}/error_handler.py | 4 +- .../request_validator.py} | 15 +- .../config/config.py | 25 +- .../logging/__init__.py | 0 .../logging/logs_helper.py | 29 + .../logging/logs_manager.py | 48 + .../logging/tracing_helper.py | 21 + .../model/{rules.py => campaign_config.py} | 29 +- .../{eligibility.py => eligibility_status.py} | 43 +- .../model/person.py | 7 + .../repos/campaign_repo.py | 2 +- .../repos/person_repo.py | 8 +- .../calculators/eligibility_calculator.py | 500 +--- .../services/calculators/rule_calculator.py | 73 +- .../services/eligibility_services.py | 8 +- .../services/operators/__init__.py | 0 .../{rules => operators}/operators.py | 2 +- .../services/processors/__init__.py | 0 .../processors/action_rule_handler.py | 103 + .../services/processors/campaign_evaluator.py | 43 + .../services/processors/cohort_handler.py | 110 + .../services/processors/person_data_reader.py | 25 + .../services/processors/rule_processor.py | 184 ++ .../views/eligibility.py | 94 +- ...eligibility.py => eligibility_response.py} | 0 src/rules_validation_api/README.md | 26 + src/rules_validation_api/__init__.py | 0 src/rules_validation_api/app.py | 29 + .../validators/__init__.py | 0 .../validators/actions_mapper_validator.py | 13 + .../validators/available_action_validator.py | 5 + .../validators/campaign_config_validator.py | 29 + .../validators/iteration_cohort_validator.py | 5 + .../validators/iteration_rules_validator.py | 18 + .../validators/iteration_validator.py | 152 + .../validators/rules_validator.py | 11 + .../unicode_data/15.1.0/charmap.json.gz | Bin 0 -> 21735 bytes .../storyTestConfigs/AUTO_RSV_ELI-365.json | 22 + .../storyTestData/AUTO_RSV_ELI-320-12.json | 60 - .../storyTestData/AUTO_RSV_ELI-365_023.json | 10 +- .../storyTestData/AUTO_RSV_ELI-365_024.json | 47 + .../storyTestData/AUTO_RSV_ELI-365_025.json | 47 + .../storyTestData/AUTO_RSV_ELI-365_026.json | 47 + .../storyTestData/AUTO_RSV_ELI-365_027.json | 47 + .../storyTestData/AUTO_RSV_ELI-365_028.json | 47 + .../AUTO_RSV_ELI-320-12.json | 78 - .../AUTO_RSV_ELI-365_023.json | 5 + .../AUTO_RSV_ELI-365_024.json | 44 + .../AUTO_RSV_ELI-365_025.json | 44 + .../AUTO_RSV_ELI-365_026.json | 37 + .../AUTO_RSV_ELI-365_027.json | 37 + .../AUTO_RSV_ELI-365_028.json | 37 + tests/e2e/tests/conftest.py | 3 +- tests/e2e/tests/test_in_progress.py | 4 +- tests/e2e/tests/test_regression_tests.py | 4 +- tests/e2e/tests/test_smoke_tests.py | 4 +- tests/e2e/tests/test_story_tests.py | 4 +- .../e2e/tests/test_vita_integration_tests.py | 4 +- tests/fixtures/builders/model/eligibility.py | 33 +- tests/fixtures/builders/model/rule.py | 183 +- tests/fixtures/builders/repos/person.py | 16 +- .../views/response_model/eligibility.py | 12 +- tests/fixtures/matchers/eligibility.py | 8 +- tests/fixtures/matchers/rules.py | 2 +- tests/integration/conftest.py | 168 +- .../in_process/test_eligibility_endpoint.py | 79 +- .../lambda/test_app_running_as_lambda.py | 131 +- tests/integration/repo/test_campaign_repo.py | 2 +- tests/integration/repo/test_person_repo.py | 4 +- tests/test_data/test_config/test_config.json | 68 +- tests/unit/audit/test_audit_context.py | 66 +- tests/unit/common/__init__.py | 0 tests/unit/common/test_request_validator.py | 387 +++ tests/unit/config/__init__.py | 0 tests/unit/{ => config}/test_config.py | 0 tests/unit/logging/__init__.py | 0 tests/unit/logging/test_logs_helper.py | 70 + tests/unit/logging/test_logs_manager.py | 120 + ...{test_rules.py => test_campaign_config.py} | 36 +- tests/unit/model/test_status.py | 47 + .../test_eligibility_calculator.py | 2573 ++++------------- .../calculators/test_rule_calculator.py | 24 +- .../unit/services/operators/test_operators.py | 4 +- tests/unit/services/processors/__init__.py | 0 .../processors/test_action_rule_handler.py | 805 ++++++ .../processors/test_campaign_evaluator.py | 117 + .../processors/test_cohort_handler.py | 122 + .../processors/test_person_data_reader.py | 102 + .../processors/test_rule_processor.py | 657 +++++ .../services/test_eligibility_services.py | 2 +- tests/unit/test_wrapper.py | 313 -- tests/unit/validation/__init__.py | 0 tests/unit/validation/conftest.py | 61 + .../test_actions_mapper_validator.py | 64 + .../test_available_action_validator.py | 55 + .../test_campaign_config_validator.py | 235 ++ .../test_iteration_cohorts_validator.py | 65 + .../test_iteration_rules_validator.py | 247 ++ .../validation/test_iteration_validator.py | 408 +++ tests/unit/validation/test_rule_validator.py | 17 + tests/unit/views/test_eligibility.py | 279 +- 127 files changed, 7536 insertions(+), 4222 deletions(-) create mode 100644 infrastructure/stacks/api-layer/cloudwatch_alarms.tf rename src/eligibility_signposting_api/{services/rules => common}/__init__.py (100%) create mode 100644 src/eligibility_signposting_api/common/api-error-response-readme.md rename src/eligibility_signposting_api/{ => common}/api_error_response.py (85%) rename src/eligibility_signposting_api/{ => common}/error_handler.py (85%) rename src/eligibility_signposting_api/{wrapper.py => common/request_validator.py} (88%) create mode 100644 src/eligibility_signposting_api/logging/__init__.py create mode 100644 src/eligibility_signposting_api/logging/logs_helper.py create mode 100644 src/eligibility_signposting_api/logging/logs_manager.py create mode 100644 src/eligibility_signposting_api/logging/tracing_helper.py rename src/eligibility_signposting_api/model/{rules.py => campaign_config.py} (90%) rename src/eligibility_signposting_api/model/{eligibility.py => eligibility_status.py} (64%) create mode 100644 src/eligibility_signposting_api/model/person.py create mode 100644 src/eligibility_signposting_api/services/operators/__init__.py rename src/eligibility_signposting_api/services/{rules => operators}/operators.py (99%) create mode 100644 src/eligibility_signposting_api/services/processors/__init__.py create mode 100644 src/eligibility_signposting_api/services/processors/action_rule_handler.py create mode 100644 src/eligibility_signposting_api/services/processors/campaign_evaluator.py create mode 100644 src/eligibility_signposting_api/services/processors/cohort_handler.py create mode 100644 src/eligibility_signposting_api/services/processors/person_data_reader.py create mode 100644 src/eligibility_signposting_api/services/processors/rule_processor.py rename src/eligibility_signposting_api/views/response_model/{eligibility.py => eligibility_response.py} (100%) create mode 100644 src/rules_validation_api/README.md create mode 100644 src/rules_validation_api/__init__.py create mode 100644 src/rules_validation_api/app.py create mode 100644 src/rules_validation_api/validators/__init__.py create mode 100644 src/rules_validation_api/validators/actions_mapper_validator.py create mode 100644 src/rules_validation_api/validators/available_action_validator.py create mode 100644 src/rules_validation_api/validators/campaign_config_validator.py create mode 100644 src/rules_validation_api/validators/iteration_cohort_validator.py create mode 100644 src/rules_validation_api/validators/iteration_rules_validator.py create mode 100644 src/rules_validation_api/validators/iteration_validator.py create mode 100644 src/rules_validation_api/validators/rules_validator.py create mode 100644 tests/e2e/.hypothesis/unicode_data/15.1.0/charmap.json.gz delete mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-12.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_024.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_025.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_026.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_027.json create mode 100644 tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_028.json delete mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-12.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_024.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_025.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_026.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_027.json create mode 100644 tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_028.json create mode 100644 tests/unit/common/__init__.py create mode 100644 tests/unit/common/test_request_validator.py create mode 100644 tests/unit/config/__init__.py rename tests/unit/{ => config}/test_config.py (100%) create mode 100644 tests/unit/logging/__init__.py create mode 100644 tests/unit/logging/test_logs_helper.py create mode 100644 tests/unit/logging/test_logs_manager.py rename tests/unit/model/{test_rules.py => test_campaign_config.py} (81%) create mode 100644 tests/unit/model/test_status.py create mode 100644 tests/unit/services/processors/__init__.py create mode 100644 tests/unit/services/processors/test_action_rule_handler.py create mode 100644 tests/unit/services/processors/test_campaign_evaluator.py create mode 100644 tests/unit/services/processors/test_cohort_handler.py create mode 100644 tests/unit/services/processors/test_person_data_reader.py create mode 100644 tests/unit/services/processors/test_rule_processor.py delete mode 100644 tests/unit/test_wrapper.py create mode 100644 tests/unit/validation/__init__.py create mode 100644 tests/unit/validation/conftest.py create mode 100644 tests/unit/validation/test_actions_mapper_validator.py create mode 100644 tests/unit/validation/test_available_action_validator.py create mode 100644 tests/unit/validation/test_campaign_config_validator.py create mode 100644 tests/unit/validation/test_iteration_cohorts_validator.py create mode 100644 tests/unit/validation/test_iteration_rules_validator.py create mode 100644 tests/unit/validation/test_iteration_validator.py create mode 100644 tests/unit/validation/test_rule_validator.py diff --git a/.github/workflows/cicd-2-publish.yaml b/.github/workflows/cicd-2-publish.yaml index d41b1de16..979cd9832 100644 --- a/.github/workflows/cicd-2-publish.yaml +++ b/.github/workflows/cicd-2-publish.yaml @@ -141,7 +141,7 @@ jobs: # asset_name: lambda-${{ needs.metadata.outputs.version }}.zip # asset_content_type: application/zip - name: "Notify Slack on PR merge" - uses: slackapi/slack-github-action@v2.1.0 + uses: slackapi/slack-github-action@v2.1.1 with: webhook: ${{ secrets.SLACK_WEBHOOK_URL }} webhook-type: webhook-trigger diff --git a/.tool-versions b/.tool-versions index 4ee8707ce..2bb2a6b76 100644 --- a/.tool-versions +++ b/.tool-versions @@ -3,8 +3,9 @@ terraform 1.12.1 pre-commit 4.2.0 vale 3.11.2 -poetry 2.1.3 +poetry 2.1.4 act 0.2.77 +nodejs 22.18.0 # ============================================================================== # The section below is reserved for Docker image versions. diff --git a/README.md b/README.md index 28b9197d3..d27ef9560 100644 --- a/README.md +++ b/README.md @@ -189,15 +189,25 @@ graph TB direction TB App["app.py (WireUp DI)"] Config["config.py, error_handler.py"] + subgraph "Audit Layer" + direction TB + Audit["audit/audit_service.py"] + AuditModels["audit/audit_models.py"] + end + subgraph "Validation Layer" + direction TB + Validator["common/request_validator.py"] + ApiErrResp["common/api_error_response.py"] + end subgraph "Presentation Layer" direction TB View["views/eligibility.py"] - ResponseModel["views/response_model/eligibility.py"] + ResponseModel["views/response_model/eligibility_response.py"] end subgraph "Business Logic Layer" direction TB Service["services/eligibility_services.py"] - Operators["services/rules/operators.py"] + Operators["services/operators/operators.py"] end subgraph "Data Access Layer" direction TB @@ -207,24 +217,30 @@ graph TB end subgraph "Models" direction TB - ModelElig["model/eligibility.py"] - ModelRules["model/rules.py"] + ModelElig["model/eligibility_status.py"] + ModelRules["model/campaign_config.py"] end end Lambda -->|"loads"| App App -->|injects| View View -->|calls| Service + View -->|validates via| Validator + View -->|audits via| Audit + View -->|uses| RespModel + Audit -->|uses| AuditModels + Validator -->|uses| ApiErrResp + Service -->|calls| Operators Service -->|calls| PersonRepo Service -->|calls| CampaignRepo PersonRepo -->|uses| DynamoDB CampaignRepo -->|uses| S3Bucket - View -->|uses| ResponseModel App -->|reads| Config + App -->|wires| Factory + Service -->|uses| ModelElig Operators -->|uses| ModelRules - App -->|wires| Factory ``` diff --git a/infrastructure/modules/dynamodb/dynamodb.tf b/infrastructure/modules/dynamodb/dynamodb.tf index 6f8f39a80..4730d2f8d 100644 --- a/infrastructure/modules/dynamodb/dynamodb.tf +++ b/infrastructure/modules/dynamodb/dynamodb.tf @@ -2,6 +2,7 @@ resource "aws_dynamodb_table" "dynamodb_table" { name = "${terraform.workspace == "default" ? "" : "${terraform.workspace}-"}${var.project_name}-${var.environment}-${var.table_name_suffix}" billing_mode = "PAY_PER_REQUEST" hash_key = var.partition_key + deletion_protection_enabled = var.environment == "prod" attribute { name = var.partition_key diff --git a/infrastructure/modules/lambda/lambda.tf b/infrastructure/modules/lambda/lambda.tf index 9013c8386..f31a6e762 100644 --- a/infrastructure/modules/lambda/lambda.tf +++ b/infrastructure/modules/lambda/lambda.tf @@ -22,6 +22,7 @@ resource "aws_lambda_function" "eligibility_signposting_lambda" { KINESIS_AUDIT_STREAM_TO_S3 = var.kinesis_audit_stream_to_s3_name ENV = var.environment LOG_LEVEL = var.log_level + ENABLE_XRAY_PATCHING = var.enable_xray_patching } } diff --git a/infrastructure/modules/lambda/variables.tf b/infrastructure/modules/lambda/variables.tf index ca6d9b95d..229c1fbb4 100644 --- a/infrastructure/modules/lambda/variables.tf +++ b/infrastructure/modules/lambda/variables.tf @@ -47,3 +47,8 @@ variable "log_level" { description = "log level" type = string } + +variable "enable_xray_patching"{ + description = "flag to enable xray tracing, which puts an entry for dynamodb, s3 and firehose in trace map" + type = string +} diff --git a/infrastructure/modules/s3/s3.tf b/infrastructure/modules/s3/s3.tf index e0138c065..8dc3c8744 100644 --- a/infrastructure/modules/s3/s3.tf +++ b/infrastructure/modules/s3/s3.tf @@ -105,6 +105,49 @@ data "aws_iam_policy_document" "access_logs_s3_bucket_policy" { variable = "aws:SecureTransport" } } + + # Allow S3 Log Delivery service to write access logs + statement { + sid = "S3ServerAccessLogsPolicy" + effect = "Allow" + principals { + type = "Service" + identifiers = ["logging.s3.amazonaws.com"] + } + actions = [ + "s3:PutObject" + ] + resources = [ + "${aws_s3_bucket.storage_bucket_access_logs.arn}/*" + ] + condition { + test = "ArnEquals" + variable = "aws:SourceArn" + values = [aws_s3_bucket.storage_bucket.arn] + } + } + + # Allow S3 Log Delivery service to check bucket location and get bucket ACL + statement { + sid = "S3ServerAccessLogsDeliveryRootAccess" + effect = "Allow" + principals { + type = "Service" + identifiers = ["logging.s3.amazonaws.com"] + } + actions = [ + "s3:GetBucketAcl", + "s3:ListBucket" + ] + resources = [ + aws_s3_bucket.storage_bucket_access_logs.arn + ] + condition { + test = "ArnEquals" + variable = "aws:SourceArn" + values = [aws_s3_bucket.storage_bucket.arn] + } + } } resource "aws_s3_bucket_server_side_encryption_configuration" "storage_bucket_access_logs_server_side_encryption_config" { @@ -112,7 +155,7 @@ resource "aws_s3_bucket_server_side_encryption_configuration" "storage_bucket_ac rule { apply_server_side_encryption_by_default { - sse_algorithm = "aws:kms" + sse_algorithm = "aws:kms" kms_master_key_id = aws_kms_key.storage_bucket_cmk.arn } } diff --git a/infrastructure/stacks/api-layer/assumed_role_permissions_boundary.tf b/infrastructure/stacks/api-layer/assumed_role_permissions_boundary.tf index 2fd4e8454..980bf8e61 100644 --- a/infrastructure/stacks/api-layer/assumed_role_permissions_boundary.tf +++ b/infrastructure/stacks/api-layer/assumed_role_permissions_boundary.tf @@ -33,7 +33,8 @@ data "aws_iam_policy_document" "assumed_role_permissions_boundary" { "support:*", "sqs:*", "tag:*", - "trustedadvisor:*" + "trustedadvisor:*", + "xray:*" ] resources = ["*"] diff --git a/infrastructure/stacks/api-layer/cloudwatch_alarms.tf b/infrastructure/stacks/api-layer/cloudwatch_alarms.tf new file mode 100644 index 000000000..4c252a38d --- /dev/null +++ b/infrastructure/stacks/api-layer/cloudwatch_alarms.tf @@ -0,0 +1,420 @@ +locals { + # Security alarms based on CloudTrail custom metrics + cloudwatch_alarm_config = { + UnauthorizedApiCalls = { + threshold = 1 + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 300 + statistic = "Sum" + alarm_description = "Unauthorized API calls detected - immediate alert on any occurrence" + actions_enabled = false # Disabling as cloudhealth role is triggering this alarm + } + ConsoleAuthenticationFailures = { + threshold = 3 + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 300 + statistic = "Sum" + alarm_description = "Multiple console authentication failures detected within 5 minutes" + actions_enabled = true + } + CloudTrailConfigChanges = { + threshold = 1 + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 300 + statistic = "Sum" + alarm_description = "CloudTrail configuration changes detected - immediate alert" + actions_enabled = true + } + VPCChanges = { + threshold = 1 + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 300 + statistic = "Sum" + alarm_description = "VPC configuration changes detected" + actions_enabled = true + } + AWSConfigChanges = { + threshold = 1 + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 300 + statistic = "Sum" + alarm_description = "AWS Config service changes detected" + actions_enabled = true + } + ModificationOfCMKs = { + threshold = 1 + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 300 + statistic = "Sum" + alarm_description = "KMS Customer Managed Key modifications detected - critical security alert" + actions_enabled = true + } + UnsuccessfulSwitchRole = { + threshold = 5 + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 900 + statistic = "Sum" + alarm_description = "Multiple unsuccessful role switch attempts detected within 15 minutes" + actions_enabled = true + } + ConsoleLoginNoMFA = { + threshold = 1 + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 300 + statistic = "Sum" + alarm_description = "Console login without MFA detected - security policy violation" + actions_enabled = true + } + RootAccountUsage = { + threshold = 1 + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 300 + statistic = "Sum" + alarm_description = "Root account usage detected - immediate critical alert" + actions_enabled = true + } + SecurityGroupChange = { + threshold = 1 + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 300 + statistic = "Sum" + alarm_description = "Security group changes detected" + actions_enabled = true + } + RouteTableChanges = { + threshold = 1 + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 300 + statistic = "Sum" + alarm_description = "Route table changes detected" + actions_enabled = true + } + IAMPolicyChanges = { + threshold = 1 + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 300 + statistic = "Sum" + alarm_description = "IAM policy changes detected - immediate security alert" + actions_enabled = true + } + s3BucketPolicyChanges = { + threshold = 1 + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 300 + statistic = "Sum" + alarm_description = "S3 bucket policy changes detected" + actions_enabled = true + } + ChangesToNetworkGateways = { + threshold = 1 + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 300 + statistic = "Sum" + alarm_description = "Network gateway changes detected" + actions_enabled = true + } + ChangesToNACLs = { + threshold = 1 + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 300 + statistic = "Sum" + alarm_description = "Network ACL changes detected" + actions_enabled = true + } + KMSKeyPolicyChanges = { + threshold = 1 + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 300 + statistic = "Sum" + alarm_description = "KMS key policy changes detected - critical security alert" + actions_enabled = true + } + s3PublicAccessChanges = { + threshold = 1 + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 300 + statistic = "Sum" + alarm_description = "S3 public access changes detected - potential data exposure risk" + actions_enabled = true + } + CloudWatchAlarmChanges = { + threshold = 1 + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 300 + statistic = "Sum" + alarm_description = "CloudWatch alarm configuration changes detected" + actions_enabled = true + } + LambdaFunctionChanges = { + threshold = 2 + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 600 + statistic = "Sum" + alarm_description = "Multiple Lambda function changes detected within 10 minutes" + actions_enabled = true + } + } + + # API Gateway alarm configuration + api_gateway_alarm_config = { + "5XXError" = { + metric_name = "5XXError" + namespace = "AWS/ApiGateway" + statistic = "Sum" + threshold = 0 + comparison_operator = "GreaterThanThreshold" + evaluation_periods = 1 + period = 300 + alarm_description = "API Gateway 5XX errors detected - critical server-side issues" + severity = "critical" + treat_missing_data = "notBreaching" + } + "4XXError" = { + metric_name = "4XXError" + namespace = "AWS/ApiGateway" + statistic = "Sum" + threshold = 50 # Adjust based on expected traffic + comparison_operator = "GreaterThanThreshold" + evaluation_periods = 2 + period = 300 + alarm_description = "High rate of API Gateway 4XX errors - client-side issues or auth problems" + severity = "high" + treat_missing_data = "notBreaching" + } + "LatencyP95" = { + metric_name = "Latency" + namespace = "AWS/ApiGateway" + statistic = "Average" # Use Average for ExtendedStatistic + extended_statistic = "p95" + threshold = 1000 + comparison_operator = "GreaterThanThreshold" + evaluation_periods = 1 + period = 300 + alarm_description = "API Gateway P95 latency > 1000ms - performance degradation" + severity = "high" + treat_missing_data = "notBreaching" + } + "IntegrationLatencyP95" = { + metric_name = "IntegrationLatency" + namespace = "AWS/ApiGateway" + statistic = "Average" # Use Average for ExtendedStatistic + extended_statistic = "p95" + threshold = 900 + comparison_operator = "GreaterThanThreshold" + evaluation_periods = 1 + period = 300 + alarm_description = "API Gateway backend (Lambda) P95 latency > 900ms - backend performance issues" + severity = "high" + treat_missing_data = "notBreaching" + } + "CountDrop" = { + metric_name = "Count" + namespace = "AWS/ApiGateway" + statistic = "Sum" + threshold = 10 # Minimum expected requests per 5min - adjust when live + comparison_operator = "LessThanThreshold" + evaluation_periods = 2 + period = 300 + alarm_description = "API Gateway request volume drop - possible outage (enable when service is live)" + severity = "high" + treat_missing_data = "breaching" # Missing data could indicate outage + actions_enabled = false # Disable until service is live + } + } + + # Lambda alarm configuration + lambda_alarm_config = { + "Errors" = { + metric_name = "Errors" + namespace = "AWS/Lambda" + statistic = "Sum" + threshold = 0 + comparison_operator = "GreaterThanThreshold" + evaluation_periods = 1 + period = 300 + alarm_description = "Lambda invocation errors detected - critical function failures" + severity = "critical" + treat_missing_data = "notBreaching" + } + "Throttles" = { + metric_name = "Throttles" + namespace = "AWS/Lambda" + statistic = "Sum" + threshold = 0 + comparison_operator = "GreaterThanThreshold" + evaluation_periods = 1 + period = 300 + alarm_description = "Lambda throttling detected - concurrency limits reached" + severity = "critical" + treat_missing_data = "notBreaching" + } + "Duration" = { + metric_name = "Duration" + namespace = "AWS/Lambda" + statistic = "Average" + threshold = 27000 # 90% of 30s timeout (adjust based on actual timeout) + comparison_operator = "GreaterThanThreshold" + evaluation_periods = 2 + period = 300 + alarm_description = "Lambda duration approaching timeout - function performance warning" + severity = "warning" + treat_missing_data = "notBreaching" + } + "InvocationsDrop" = { + metric_name = "Invocations" + namespace = "AWS/Lambda" + statistic = "Sum" + threshold = 5 # Minimum expected invocations per 5min - adjust when live + comparison_operator = "LessThanThreshold" + evaluation_periods = 2 + period = 300 + alarm_description = "Lambda invocation volume drop - possible outage (enable when service is live)" + severity = "high" + treat_missing_data = "breaching" # Missing data could indicate outage + actions_enabled = false # Disable until service is live + } + } +} + +# SNS Topic for CloudWatch Alarms +resource "aws_sns_topic" "cloudwatch_alarms" { + name = "cloudwatch-security-alarms" + + kms_master_key_id = aws_kms_key.sns_encryption_key.id + + tags = { + Environment = var.environment + Purpose = "security-alerting" + ManagedBy = "terraform" + } +} + +resource "aws_kms_key" "sns_encryption_key" { + description = "KMS key for encrypting CloudWatch alarms SNS topic" + deletion_window_in_days = 7 + enable_key_rotation = true + + + tags = { + Name = "cloudwatch-alarms-sns-encryption-key" + Environment = var.environment + Purpose = "sns-encryption" + ManagedBy = "terraform" + } +} + +# Security Alarms (CloudTrail-based) +resource "aws_cloudwatch_metric_alarm" "cloudtrail_custom_metric_alarms" { + # checkov:skip=CKV_AWS_319: Disabling some alarms until service is live + for_each = local.cloudwatch_alarm_config + + alarm_name = "SecurityAlert-${each.key}" + alarm_description = each.value.alarm_description + actions_enabled = each.value.actions_enabled + metric_name = each.key + namespace = "security" + statistic = each.value.statistic + period = each.value.period + evaluation_periods = each.value.evaluation_periods + threshold = each.value.threshold + comparison_operator = each.value.comparison_operator + + # Treat missing data as not breaching (common for security metrics) + treat_missing_data = "notBreaching" + + # Add standard tags for organization + tags = { + Environment = "production" + AlertType = "security" + Severity = contains(["RootAccountUsage", "ModificationOfCMKs", "KMSKeyPolicyChanges", "ConsoleLoginNoMFA"], each.key) ? "critical" : "high" + ManagedBy = "terraform" + } + + alarm_actions = [aws_sns_topic.cloudwatch_alarms.arn] +} + +# API Gateway CloudWatch Alarms +resource "aws_cloudwatch_metric_alarm" "api_gateway_alarms" { + # checkov:skip=CKV_AWS_319: Disabling some alarms until service is live + for_each = local.api_gateway_alarm_config + + alarm_name = "APIGateway-${each.key}" + alarm_description = each.value.alarm_description + actions_enabled = lookup(each.value, "actions_enabled", true) + metric_name = each.value.metric_name + namespace = each.value.namespace + statistic = lookup(each.value, "extended_statistic", null) == null ? each.value.statistic : null + extended_statistic = lookup(each.value, "extended_statistic", null) + period = each.value.period + evaluation_periods = each.value.evaluation_periods + threshold = each.value.threshold + comparison_operator = each.value.comparison_operator + treat_missing_data = each.value.treat_missing_data + + # Add dimensions for API Gateway + dimensions = { + ApiName = "eligibility-signposting-api" + } + + tags = { + Environment = var.environment + AlertType = "performance" + Service = "api-gateway" + Severity = each.value.severity + ManagedBy = "terraform" + } + + alarm_actions = [aws_sns_topic.cloudwatch_alarms.arn] +} + +# Lambda CloudWatch Alarms +resource "aws_cloudwatch_metric_alarm" "lambda_alarms" { + # checkov:skip=CKV_AWS_319: Disabling some alarms until service is live + for_each = local.lambda_alarm_config + + alarm_name = "Lambda-${each.key}" + alarm_description = each.value.alarm_description + actions_enabled = lookup(each.value, "actions_enabled", true) + metric_name = each.value.metric_name + namespace = each.value.namespace + statistic = each.value.statistic + period = each.value.period + evaluation_periods = each.value.evaluation_periods + threshold = each.value.threshold + comparison_operator = each.value.comparison_operator + treat_missing_data = each.value.treat_missing_data + + # Add dimensions for Lambda + dimensions = { + FunctionName = module.eligibility_signposting_lambda_function.aws_lambda_function_name + } + + tags = { + Environment = var.environment + AlertType = "performance" + Service = "lambda" + Severity = each.value.severity + ManagedBy = "terraform" + } + + alarm_actions = [aws_sns_topic.cloudwatch_alarms.arn] +} diff --git a/infrastructure/stacks/api-layer/iam_policies.tf b/infrastructure/stacks/api-layer/iam_policies.tf index 00b5d914f..5f384895c 100644 --- a/infrastructure/stacks/api-layer/iam_policies.tf +++ b/infrastructure/stacks/api-layer/iam_policies.tf @@ -1,7 +1,7 @@ # Read-only policy for DynamoDB data "aws_iam_policy_document" "dynamodb_read_policy_doc" { statement { - actions = ["dynamodb:GetItem", "dynamodb:Query", "dynamodb:Scan"] + actions = ["dynamodb:GetItem", "dynamodb:Query", "dynamodb:Scan"] resources = [module.eligibility_status_table.arn] } } @@ -16,7 +16,7 @@ resource "aws_iam_role_policy" "lambda_dynamodb_read_policy" { # Write-only policy for DynamoDB data "aws_iam_policy_document" "dynamodb_write_policy_doc" { statement { - actions = ["dynamodb:PutItem", "dynamodb:UpdateItem", "dynamodb:DeleteItem", "dynamodb:BatchWriteItem"] + actions = ["dynamodb:PutItem", "dynamodb:UpdateItem", "dynamodb:DeleteItem", "dynamodb:BatchWriteItem"] resources = [module.eligibility_status_table.arn] } } @@ -37,7 +37,7 @@ data "aws_iam_policy_document" "dynamo_kms_access_policy_doc" { # Attach dynamoDB write policy to external write role resource "aws_iam_role_policy" "external_dynamodb_write_policy" { - count = length(aws_iam_role.write_access_role) + count = length(aws_iam_role.write_access_role) name = "DynamoDBWriteAccess" role = aws_iam_role.write_access_role[count.index].id policy = data.aws_iam_policy_document.dynamodb_write_policy_doc.json @@ -45,7 +45,7 @@ resource "aws_iam_role_policy" "external_dynamodb_write_policy" { # Attach dynamo KMS policy to external write role resource "aws_iam_role_policy" "external_kms_access_policy" { - count = length(aws_iam_role.write_access_role) + count = length(aws_iam_role.write_access_role) name = "KMSAccessForDynamoDB" role = aws_iam_role.write_access_role[count.index].id policy = data.aws_iam_policy_document.dynamo_kms_access_policy_doc.json @@ -65,7 +65,7 @@ data "aws_iam_policy_document" "s3_rules_bucket_policy" { ] condition { test = "Bool" - values = ["true"] + values = ["true"] variable = "aws:SecureTransport" } } @@ -90,7 +90,7 @@ data "aws_iam_policy_document" "rules_s3_bucket_policy" { "${module.s3_rules_bucket.storage_bucket_arn}/*", ] principals { - type = "*" + type = "*" identifiers = ["*"] } condition { @@ -121,7 +121,7 @@ data "aws_iam_policy_document" "audit_s3_bucket_policy" { "${module.s3_audit_bucket.storage_bucket_arn}/*", ] principals { - type = "*" + type = "*" identifiers = ["*"] } condition { @@ -192,7 +192,7 @@ resource "aws_iam_role_policy_attachment" "lambda_logs_policy_attachment" { # Policy doc for S3 Audit bucket data "aws_iam_policy_document" "s3_audit_bucket_policy" { statement { - sid = "AllowSSLRequestsOnly" + sid = "AllowSSLRequestsOnly" actions = ["s3:*"] resources = [ module.s3_audit_bucket.storage_bucket_arn, @@ -200,7 +200,7 @@ data "aws_iam_policy_document" "s3_audit_bucket_policy" { ] condition { test = "Bool" - values = ["true"] + values = ["true"] variable = "aws:SecureTransport" } } @@ -222,10 +222,10 @@ data "aws_iam_policy_document" "dynamodb_kms_key_policy" { sid = "EnableIamUserPermissions" effect = "Allow" principals { - type = "AWS" + type = "AWS" identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"] } - actions = ["kms:*"] + actions = ["kms:*"] resources = ["*"] } @@ -233,7 +233,7 @@ data "aws_iam_policy_document" "dynamodb_kms_key_policy" { sid = "AllowLambdaDecrypt" effect = "Allow" principals { - type = "AWS" + type = "AWS" identifiers = [aws_iam_role.eligibility_lambda_role.arn] } actions = [ @@ -260,10 +260,10 @@ data "aws_iam_policy_document" "s3_rules_kms_key_policy" { sid = "EnableIamUserPermissions" effect = "Allow" principals { - type = "AWS" + type = "AWS" identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"] } - actions = ["kms:*"] + actions = ["kms:*"] resources = ["*"] } @@ -271,10 +271,10 @@ data "aws_iam_policy_document" "s3_rules_kms_key_policy" { sid = "AllowLambdaDecrypt" effect = "Allow" principals { - type = "AWS" + type = "AWS" identifiers = [aws_iam_role.eligibility_lambda_role.arn] } - actions = ["kms:Decrypt"] + actions = ["kms:Decrypt"] resources = ["*"] } } @@ -293,17 +293,17 @@ data "aws_iam_policy_document" "s3_audit_kms_key_policy" { sid = "EnableIamUserPermissions" effect = "Allow" principals { - type = "AWS" + type = "AWS" identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"] } - actions = ["kms:*"] + actions = ["kms:*"] resources = ["*"] } statement { sid = "AllowLambdaFullWrite" effect = "Allow" principals { - type = "AWS" + type = "AWS" identifiers = [aws_iam_role.eligibility_lambda_role.arn, aws_iam_role.eligibility_audit_firehose_role.arn] } actions = [ @@ -340,3 +340,70 @@ resource "aws_iam_role_policy" "lambda_firehose_policy" { role = aws_iam_role.eligibility_lambda_role.id policy = data.aws_iam_policy_document.lambda_firehose_write_policy.json } + +data "aws_iam_policy_document" "lambda_xray_tracing_permissions_policy" { + statement { + sid = "AllowLambdaToPutToXRay" + effect = "Allow" + actions = [ + "xray:PutTraceSegments", + "xray:PutTelemetryRecords" + ] + resources = ["*"] + } +} + +resource "aws_iam_role_policy" "lambda_xray_tracing_policy" { + name = "LambdaXRayWritePolicy" + role = aws_iam_role.eligibility_lambda_role.id + policy = data.aws_iam_policy_document.lambda_xray_tracing_permissions_policy.json +} + +# KMS Key Policy for SNS encryption +resource "aws_kms_key_policy" "sns_encryption_key_policy" { + key_id = aws_kms_key.sns_encryption_key.id + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Sid = "EnableIAMRootPermissions" + Effect = "Allow" + Principal = { + AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root" + } + Action = "kms:*" + Resource = "*" + }, + { + Sid = "AllowCloudWatchAlarmsAccess" + Effect = "Allow" + Principal = { + Service = "cloudwatch.amazonaws.com" + } + Action = [ + "kms:Encrypt", + "kms:Decrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*", + "kms:DescribeKey" + ] + Resource = "*" + }, + { + Sid = "AllowSNSServiceAccess" + Effect = "Allow" + Principal = { + Service = "sns.amazonaws.com" + } + Action = [ + "kms:Encrypt", + "kms:Decrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*", + "kms:DescribeKey" + ] + Resource = "*" + } + ] + }) +} diff --git a/infrastructure/stacks/api-layer/lambda.tf b/infrastructure/stacks/api-layer/lambda.tf index 09f56ac03..68885b6d7 100644 --- a/infrastructure/stacks/api-layer/lambda.tf +++ b/infrastructure/stacks/api-layer/lambda.tf @@ -24,5 +24,6 @@ module "eligibility_signposting_lambda_function" { eligibility_status_table_name = module.eligibility_status_table.table_name kinesis_audit_stream_to_s3_name = module.eligibility_audit_firehose_delivery_stream.firehose_stream_name log_level = "INFO" + enable_xray_patching = "true" stack_name = local.stack_name } diff --git a/infrastructure/stacks/api-layer/patient_check.tf b/infrastructure/stacks/api-layer/patient_check.tf index a0cf18a8e..030e69a65 100644 --- a/infrastructure/stacks/api-layer/patient_check.tf +++ b/infrastructure/stacks/api-layer/patient_check.tf @@ -1,4 +1,3 @@ - resource "aws_api_gateway_request_validator" "patient_check_validator" { rest_api_id = module.eligibility_signposting_api_gateway.rest_api_id name = "validate-path-params" @@ -27,12 +26,12 @@ resource "aws_api_gateway_method" "get_patient_check" { } resource "aws_api_gateway_integration" "get_patient_check" { - rest_api_id = module.eligibility_signposting_api_gateway.rest_api_id - resource_id = aws_api_gateway_resource.patient.id - http_method = aws_api_gateway_method.get_patient_check.http_method + rest_api_id = module.eligibility_signposting_api_gateway.rest_api_id + resource_id = aws_api_gateway_resource.patient.id + http_method = aws_api_gateway_method.get_patient_check.http_method integration_http_method = "POST" # Needed for lambda proxy integration - type = "AWS_PROXY" - uri = module.eligibility_signposting_lambda_function.aws_lambda_invoke_arn + type = "AWS_PROXY" + uri = module.eligibility_signposting_lambda_function.aws_lambda_invoke_arn depends_on = [ aws_api_gateway_method.get_patient_check @@ -47,3 +46,43 @@ resource "aws_lambda_permission" "get_patient_check" { source_arn = "${module.eligibility_signposting_api_gateway.execution_arn}/*/*" } + +resource "aws_api_gateway_gateway_response" "bad_request_parameters" { + rest_api_id = module.eligibility_signposting_api_gateway.rest_api_id + response_type = "BAD_REQUEST_PARAMETERS" + status_code = "400" + + response_templates = { + "application/json" = jsonencode({ + resourceType = "OperationOutcome" + id = "$context.requestId" + meta = { + lastUpdated = "$context.requestTime" + } + issue = [ + { + severity = "error" + code = "invalid" + details = { + coding = [ + { + system = "https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1", + code = "BAD_REQUEST", + display = "Bad Request" + } + ] + } + diagnostics = "Missing required NHS Number from path parameters", + location = [ + "parameters/id" + ] + } + ] + }) + } + + response_parameters = { + "gatewayresponse.header.Access-Control-Allow-Origin" = "'*'" + "gatewayresponse.header.Content-Type" = "'application/fhir+json'" + } +} diff --git a/infrastructure/stacks/iams-developer-roles/github_actions_policies.tf b/infrastructure/stacks/iams-developer-roles/github_actions_policies.tf index 78e909d86..6b69b5025 100644 --- a/infrastructure/stacks/iams-developer-roles/github_actions_policies.tf +++ b/infrastructure/stacks/iams-developer-roles/github_actions_policies.tf @@ -190,6 +190,7 @@ resource "aws_iam_policy" "api_infrastructure" { "ssm:DescribeParameters", "ec2:Describe*", "ec2:DescribeVpcs", + "ec2:ModifyVpcBlockPublicAccessOptions", # API Gateway domain and deployment "apigateway:*", # ACM for certs @@ -204,6 +205,7 @@ resource "aws_iam_policy" "api_infrastructure" { "logs:PutLogEvents", # IAM PassRole for logging role association (if needed) "iam:PassRole" + ], Resource = "*" #checkov:skip=CKV_AWS_289: Actions require wildcard resource @@ -473,6 +475,50 @@ resource "aws_iam_policy" "firehose_readonly" { tags = merge(local.tags, { Name = "firehose-describe-access" }) } +resource "aws_iam_policy" "cloudwatch_alarms" { + name = "cloudwatch-alarms-management" + description = "Allow GitHub Actions to manage CloudWatch alarms and SNS topics" + path = "/service-policies/" + + policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Effect = "Allow", + Action = [ + # CloudWatch Alarms management + "cloudwatch:PutMetricAlarm", + "cloudwatch:DeleteAlarms", + "cloudwatch:DescribeAlarms", + "cloudwatch:DescribeAlarmsForMetric", + "cloudwatch:ListTagsForResource", + "cloudwatch:TagResource", + "cloudwatch:UntagResource", + # SNS Topic management for alarm notifications + "sns:CreateTopic", + "sns:DeleteTopic", + "sns:GetTopicAttributes", + "sns:SetTopicAttributes", + "sns:ListTopics", + "sns:ListTagsForResource", + "sns:TagResource", + "sns:UntagResource", + "sns:Subscribe", + "sns:Unsubscribe", + "sns:ListSubscriptions", + "sns:ListSubscriptionsByTopic" + ], + Resource = [ + "arn:aws:cloudwatch:${var.default_aws_region}:${data.aws_caller_identity.current.account_id}:alarm:*", + "arn:aws:sns:${var.default_aws_region}:${data.aws_caller_identity.current.account_id}:cloudwatch-security-alarms*" + ] + } + ] + }) + + tags = merge(local.tags, { Name = "cloudwatch-alarms-management" }) +} + # Attach the policies to the role resource "aws_iam_role_policy_attachment" "terraform_state" { role = aws_iam_role.github_actions.name @@ -518,3 +564,8 @@ resource "aws_iam_role_policy_attachment" "firehose_readonly_attach" { role = aws_iam_role.github_actions.name policy_arn = aws_iam_policy.firehose_readonly.arn } + +resource "aws_iam_role_policy_attachment" "cloudwatch_alarms" { + role = aws_iam_role.github_actions.name + policy_arn = aws_iam_policy.cloudwatch_alarms.arn +} diff --git a/infrastructure/stacks/iams-developer-roles/iams_permissions_boundary.tf b/infrastructure/stacks/iams-developer-roles/iams_permissions_boundary.tf index 27909c885..8d7940668 100644 --- a/infrastructure/stacks/iams-developer-roles/iams_permissions_boundary.tf +++ b/infrastructure/stacks/iams-developer-roles/iams_permissions_boundary.tf @@ -33,7 +33,8 @@ data "aws_iam_policy_document" "permissions_boundary" { "support:*", "sqs:*", "tag:*", - "trustedadvisor:*" + "trustedadvisor:*", + "xray:*" ] resources = ["*"] diff --git a/infrastructure/stacks/networking/locals.tf b/infrastructure/stacks/networking/locals.tf index 5acda7626..9114c1144 100644 --- a/infrastructure/stacks/networking/locals.tf +++ b/infrastructure/stacks/networking/locals.tf @@ -22,6 +22,8 @@ locals { sts = "com.amazonaws.${local.region}.sts" sqs = "com.amazonaws.${local.region}.sqs" kinesis-firehose = "com.amazonaws.${local.region}.kinesis-firehose" + xray = "com.amazonaws.${local.region}.xray" + } # VPC Gateway Endpoints diff --git a/infrastructure/stacks/networking/vpc.tf b/infrastructure/stacks/networking/vpc.tf index a3ad8a04d..e88ce7894 100644 --- a/infrastructure/stacks/networking/vpc.tf +++ b/infrastructure/stacks/networking/vpc.tf @@ -21,3 +21,8 @@ resource "aws_default_security_group" "default_vpc" { } ) } + +# EC2.172 - block internet gateway access at the account level +resource "aws_vpc_block_public_access_options" "default_vpc" { + internet_gateway_block_mode = "block-bidirectional" +} diff --git a/poetry.lock b/poetry.lock index 963595b81..641bd9b07 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.4 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -14,103 +14,103 @@ files = [ [[package]] name = "aiohttp" -version = "3.12.13" +version = "3.12.15" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "aiohttp-3.12.13-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5421af8f22a98f640261ee48aae3a37f0c41371e99412d55eaf2f8a46d5dad29"}, - {file = "aiohttp-3.12.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fcda86f6cb318ba36ed8f1396a6a4a3fd8f856f84d426584392083d10da4de0"}, - {file = "aiohttp-3.12.13-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cd71c9fb92aceb5a23c4c39d8ecc80389c178eba9feab77f19274843eb9412d"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34ebf1aca12845066c963016655dac897651e1544f22a34c9b461ac3b4b1d3aa"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:893a4639694c5b7edd4bdd8141be296042b6806e27cc1d794e585c43010cc294"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:663d8ee3ffb3494502ebcccb49078faddbb84c1d870f9c1dd5a29e85d1f747ce"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0f8f6a85a0006ae2709aa4ce05749ba2cdcb4b43d6c21a16c8517c16593aabe"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1582745eb63df267c92d8b61ca655a0ce62105ef62542c00a74590f306be8cb5"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d59227776ee2aa64226f7e086638baa645f4b044f2947dbf85c76ab11dcba073"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06b07c418bde1c8e737d8fa67741072bd3f5b0fb66cf8c0655172188c17e5fa6"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:9445c1842680efac0f81d272fd8db7163acfcc2b1436e3f420f4c9a9c5a50795"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:09c4767af0b0b98c724f5d47f2bf33395c8986995b0a9dab0575ca81a554a8c0"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f3854fbde7a465318ad8d3fc5bef8f059e6d0a87e71a0d3360bb56c0bf87b18a"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2332b4c361c05ecd381edb99e2a33733f3db906739a83a483974b3df70a51b40"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1561db63fa1b658cd94325d303933553ea7d89ae09ff21cc3bcd41b8521fbbb6"}, - {file = "aiohttp-3.12.13-cp310-cp310-win32.whl", hash = "sha256:a0be857f0b35177ba09d7c472825d1b711d11c6d0e8a2052804e3b93166de1ad"}, - {file = "aiohttp-3.12.13-cp310-cp310-win_amd64.whl", hash = "sha256:fcc30ad4fb5cb41a33953292d45f54ef4066746d625992aeac33b8c681173178"}, - {file = "aiohttp-3.12.13-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7c229b1437aa2576b99384e4be668af1db84b31a45305d02f61f5497cfa6f60c"}, - {file = "aiohttp-3.12.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:04076d8c63471e51e3689c93940775dc3d12d855c0c80d18ac5a1c68f0904358"}, - {file = "aiohttp-3.12.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:55683615813ce3601640cfaa1041174dc956d28ba0511c8cbd75273eb0587014"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:921bc91e602d7506d37643e77819cb0b840d4ebb5f8d6408423af3d3bf79a7b7"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e72d17fe0974ddeae8ed86db297e23dba39c7ac36d84acdbb53df2e18505a013"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0653d15587909a52e024a261943cf1c5bdc69acb71f411b0dd5966d065a51a47"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a77b48997c66722c65e157c06c74332cdf9c7ad00494b85ec43f324e5c5a9b9a"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6946bae55fd36cfb8e4092c921075cde029c71c7cb571d72f1079d1e4e013bc"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f95db8c8b219bcf294a53742c7bda49b80ceb9d577c8e7aa075612b7f39ffb7"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:03d5eb3cfb4949ab4c74822fb3326cd9655c2b9fe22e4257e2100d44215b2e2b"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6383dd0ffa15515283c26cbf41ac8e6705aab54b4cbb77bdb8935a713a89bee9"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6548a411bc8219b45ba2577716493aa63b12803d1e5dc70508c539d0db8dbf5a"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:81b0fcbfe59a4ca41dc8f635c2a4a71e63f75168cc91026c61be665945739e2d"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6a83797a0174e7995e5edce9dcecc517c642eb43bc3cba296d4512edf346eee2"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5734d8469a5633a4e9ffdf9983ff7cdb512524645c7a3d4bc8a3de45b935ac3"}, - {file = "aiohttp-3.12.13-cp311-cp311-win32.whl", hash = "sha256:fef8d50dfa482925bb6b4c208b40d8e9fa54cecba923dc65b825a72eed9a5dbd"}, - {file = "aiohttp-3.12.13-cp311-cp311-win_amd64.whl", hash = "sha256:9a27da9c3b5ed9d04c36ad2df65b38a96a37e9cfba6f1381b842d05d98e6afe9"}, - {file = "aiohttp-3.12.13-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0aa580cf80558557285b49452151b9c69f2fa3ad94c5c9e76e684719a8791b73"}, - {file = "aiohttp-3.12.13-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b103a7e414b57e6939cc4dece8e282cfb22043efd0c7298044f6594cf83ab347"}, - {file = "aiohttp-3.12.13-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78f64e748e9e741d2eccff9597d09fb3cd962210e5b5716047cbb646dc8fe06f"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c955989bf4c696d2ededc6b0ccb85a73623ae6e112439398935362bacfaaf6"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d640191016763fab76072c87d8854a19e8e65d7a6fcfcbf017926bdbbb30a7e5"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4dc507481266b410dede95dd9f26c8d6f5a14315372cc48a6e43eac652237d9b"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8a94daa873465d518db073bd95d75f14302e0208a08e8c942b2f3f1c07288a75"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:177f52420cde4ce0bb9425a375d95577fe082cb5721ecb61da3049b55189e4e6"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f7df1f620ec40f1a7fbcb99ea17d7326ea6996715e78f71a1c9a021e31b96b8"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3062d4ad53b36e17796dce1c0d6da0ad27a015c321e663657ba1cc7659cfc710"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:8605e22d2a86b8e51ffb5253d9045ea73683d92d47c0b1438e11a359bdb94462"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:54fbbe6beafc2820de71ece2198458a711e224e116efefa01b7969f3e2b3ddae"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:050bd277dfc3768b606fd4eae79dd58ceda67d8b0b3c565656a89ae34525d15e"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2637a60910b58f50f22379b6797466c3aa6ae28a6ab6404e09175ce4955b4e6a"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e986067357550d1aaa21cfe9897fa19e680110551518a5a7cf44e6c5638cb8b5"}, - {file = "aiohttp-3.12.13-cp312-cp312-win32.whl", hash = "sha256:ac941a80aeea2aaae2875c9500861a3ba356f9ff17b9cb2dbfb5cbf91baaf5bf"}, - {file = "aiohttp-3.12.13-cp312-cp312-win_amd64.whl", hash = "sha256:671f41e6146a749b6c81cb7fd07f5a8356d46febdaaaf07b0e774ff04830461e"}, - {file = "aiohttp-3.12.13-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d4a18e61f271127465bdb0e8ff36e8f02ac4a32a80d8927aa52371e93cd87938"}, - {file = "aiohttp-3.12.13-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:532542cb48691179455fab429cdb0d558b5e5290b033b87478f2aa6af5d20ace"}, - {file = "aiohttp-3.12.13-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d7eea18b52f23c050ae9db5d01f3d264ab08f09e7356d6f68e3f3ac2de9dfabb"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad7c8e5c25f2a26842a7c239de3f7b6bfb92304593ef997c04ac49fb703ff4d7"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6af355b483e3fe9d7336d84539fef460120c2f6e50e06c658fe2907c69262d6b"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a95cf9f097498f35c88e3609f55bb47b28a5ef67f6888f4390b3d73e2bac6177"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8ed8c38a1c584fe99a475a8f60eefc0b682ea413a84c6ce769bb19a7ff1c5ef"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a0b9170d5d800126b5bc89d3053a2363406d6e327afb6afaeda2d19ee8bb103"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:372feeace612ef8eb41f05ae014a92121a512bd5067db8f25101dd88a8db11da"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a946d3702f7965d81f7af7ea8fb03bb33fe53d311df48a46eeca17e9e0beed2d"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a0c4725fae86555bbb1d4082129e21de7264f4ab14baf735278c974785cd2041"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9b28ea2f708234f0a5c44eb6c7d9eb63a148ce3252ba0140d050b091b6e842d1"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d4f5becd2a5791829f79608c6f3dc745388162376f310eb9c142c985f9441cc1"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:60f2ce6b944e97649051d5f5cc0f439360690b73909230e107fd45a359d3e911"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:69fc1909857401b67bf599c793f2183fbc4804717388b0b888f27f9929aa41f3"}, - {file = "aiohttp-3.12.13-cp313-cp313-win32.whl", hash = "sha256:7d7e68787a2046b0e44ba5587aa723ce05d711e3a3665b6b7545328ac8e3c0dd"}, - {file = "aiohttp-3.12.13-cp313-cp313-win_amd64.whl", hash = "sha256:5a178390ca90419bfd41419a809688c368e63c86bd725e1186dd97f6b89c2706"}, - {file = "aiohttp-3.12.13-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:36f6c973e003dc9b0bb4e8492a643641ea8ef0e97ff7aaa5c0f53d68839357b4"}, - {file = "aiohttp-3.12.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6cbfc73179bd67c229eb171e2e3745d2afd5c711ccd1e40a68b90427f282eab1"}, - {file = "aiohttp-3.12.13-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1e8b27b2d414f7e3205aa23bb4a692e935ef877e3a71f40d1884f6e04fd7fa74"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eabded0c2b2ef56243289112c48556c395d70150ce4220d9008e6b4b3dd15690"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:003038e83f1a3ff97409999995ec02fe3008a1d675478949643281141f54751d"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b6f46613031dbc92bdcaad9c4c22c7209236ec501f9c0c5f5f0b6a689bf50f3"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c332c6bb04650d59fb94ed96491f43812549a3ba6e7a16a218e612f99f04145e"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fea41a2c931fb582cb15dc86a3037329e7b941df52b487a9f8b5aa960153cbd"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:846104f45d18fb390efd9b422b27d8f3cf8853f1218c537f36e71a385758c896"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d6c85ac7dd350f8da2520bac8205ce99df4435b399fa7f4dc4a70407073e390"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5a1ecce0ed281bec7da8550da052a6b89552db14d0a0a45554156f085a912f48"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:5304d74867028cca8f64f1cc1215eb365388033c5a691ea7aa6b0dc47412f495"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:64d1f24ee95a2d1e094a4cd7a9b7d34d08db1bbcb8aa9fb717046b0a884ac294"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:119c79922a7001ca6a9e253228eb39b793ea994fd2eccb79481c64b5f9d2a055"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:bb18f00396d22e2f10cd8825d671d9f9a3ba968d708a559c02a627536b36d91c"}, - {file = "aiohttp-3.12.13-cp39-cp39-win32.whl", hash = "sha256:0022de47ef63fd06b065d430ac79c6b0bd24cdae7feaf0e8c6bac23b805a23a8"}, - {file = "aiohttp-3.12.13-cp39-cp39-win_amd64.whl", hash = "sha256:29e08111ccf81b2734ae03f1ad1cb03b9615e7d8f616764f22f71209c094f122"}, - {file = "aiohttp-3.12.13.tar.gz", hash = "sha256:47e2da578528264a12e4e3dd8dd72a7289e5f812758fe086473fab037a10fcce"}, + {file = "aiohttp-3.12.15-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b6fc902bff74d9b1879ad55f5404153e2b33a82e72a95c89cec5eb6cc9e92fbc"}, + {file = "aiohttp-3.12.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:098e92835b8119b54c693f2f88a1dec690e20798ca5f5fe5f0520245253ee0af"}, + {file = "aiohttp-3.12.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:40b3fee496a47c3b4a39a731954c06f0bd9bd3e8258c059a4beb76ac23f8e421"}, + {file = "aiohttp-3.12.15-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ce13fcfb0bb2f259fb42106cdc63fa5515fb85b7e87177267d89a771a660b79"}, + {file = "aiohttp-3.12.15-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3beb14f053222b391bf9cf92ae82e0171067cc9c8f52453a0f1ec7c37df12a77"}, + {file = "aiohttp-3.12.15-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c39e87afe48aa3e814cac5f535bc6199180a53e38d3f51c5e2530f5aa4ec58c"}, + {file = "aiohttp-3.12.15-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5f1b4ce5bc528a6ee38dbf5f39bbf11dd127048726323b72b8e85769319ffc4"}, + {file = "aiohttp-3.12.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1004e67962efabbaf3f03b11b4c43b834081c9e3f9b32b16a7d97d4708a9abe6"}, + {file = "aiohttp-3.12.15-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8faa08fcc2e411f7ab91d1541d9d597d3a90e9004180edb2072238c085eac8c2"}, + {file = "aiohttp-3.12.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fe086edf38b2222328cdf89af0dde2439ee173b8ad7cb659b4e4c6f385b2be3d"}, + {file = "aiohttp-3.12.15-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:79b26fe467219add81d5e47b4a4ba0f2394e8b7c7c3198ed36609f9ba161aecb"}, + {file = "aiohttp-3.12.15-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b761bac1192ef24e16706d761aefcb581438b34b13a2f069a6d343ec8fb693a5"}, + {file = "aiohttp-3.12.15-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e153e8adacfe2af562861b72f8bc47f8a5c08e010ac94eebbe33dc21d677cd5b"}, + {file = "aiohttp-3.12.15-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:fc49c4de44977aa8601a00edbf157e9a421f227aa7eb477d9e3df48343311065"}, + {file = "aiohttp-3.12.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2776c7ec89c54a47029940177e75c8c07c29c66f73464784971d6a81904ce9d1"}, + {file = "aiohttp-3.12.15-cp310-cp310-win32.whl", hash = "sha256:2c7d81a277fa78b2203ab626ced1487420e8c11a8e373707ab72d189fcdad20a"}, + {file = "aiohttp-3.12.15-cp310-cp310-win_amd64.whl", hash = "sha256:83603f881e11f0f710f8e2327817c82e79431ec976448839f3cd05d7afe8f830"}, + {file = "aiohttp-3.12.15-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d3ce17ce0220383a0f9ea07175eeaa6aa13ae5a41f30bc61d84df17f0e9b1117"}, + {file = "aiohttp-3.12.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:010cc9bbd06db80fe234d9003f67e97a10fe003bfbedb40da7d71c1008eda0fe"}, + {file = "aiohttp-3.12.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3f9d7c55b41ed687b9d7165b17672340187f87a773c98236c987f08c858145a9"}, + {file = "aiohttp-3.12.15-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc4fbc61bb3548d3b482f9ac7ddd0f18c67e4225aaa4e8552b9f1ac7e6bda9e5"}, + {file = "aiohttp-3.12.15-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7fbc8a7c410bb3ad5d595bb7118147dfbb6449d862cc1125cf8867cb337e8728"}, + {file = "aiohttp-3.12.15-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:74dad41b3458dbb0511e760fb355bb0b6689e0630de8a22b1b62a98777136e16"}, + {file = "aiohttp-3.12.15-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b6f0af863cf17e6222b1735a756d664159e58855da99cfe965134a3ff63b0b0"}, + {file = "aiohttp-3.12.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5b7fe4972d48a4da367043b8e023fb70a04d1490aa7d68800e465d1b97e493b"}, + {file = "aiohttp-3.12.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6443cca89553b7a5485331bc9bedb2342b08d073fa10b8c7d1c60579c4a7b9bd"}, + {file = "aiohttp-3.12.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6c5f40ec615e5264f44b4282ee27628cea221fcad52f27405b80abb346d9f3f8"}, + {file = "aiohttp-3.12.15-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:2abbb216a1d3a2fe86dbd2edce20cdc5e9ad0be6378455b05ec7f77361b3ab50"}, + {file = "aiohttp-3.12.15-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:db71ce547012a5420a39c1b744d485cfb823564d01d5d20805977f5ea1345676"}, + {file = "aiohttp-3.12.15-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ced339d7c9b5030abad5854aa5413a77565e5b6e6248ff927d3e174baf3badf7"}, + {file = "aiohttp-3.12.15-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:7c7dd29c7b5bda137464dc9bfc738d7ceea46ff70309859ffde8c022e9b08ba7"}, + {file = "aiohttp-3.12.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:421da6fd326460517873274875c6c5a18ff225b40da2616083c5a34a7570b685"}, + {file = "aiohttp-3.12.15-cp311-cp311-win32.whl", hash = "sha256:4420cf9d179ec8dfe4be10e7d0fe47d6d606485512ea2265b0d8c5113372771b"}, + {file = "aiohttp-3.12.15-cp311-cp311-win_amd64.whl", hash = "sha256:edd533a07da85baa4b423ee8839e3e91681c7bfa19b04260a469ee94b778bf6d"}, + {file = "aiohttp-3.12.15-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:802d3868f5776e28f7bf69d349c26fc0efadb81676d0afa88ed00d98a26340b7"}, + {file = "aiohttp-3.12.15-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f2800614cd560287be05e33a679638e586a2d7401f4ddf99e304d98878c29444"}, + {file = "aiohttp-3.12.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8466151554b593909d30a0a125d638b4e5f3836e5aecde85b66b80ded1cb5b0d"}, + {file = "aiohttp-3.12.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e5a495cb1be69dae4b08f35a6c4579c539e9b5706f606632102c0f855bcba7c"}, + {file = "aiohttp-3.12.15-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6404dfc8cdde35c69aaa489bb3542fb86ef215fc70277c892be8af540e5e21c0"}, + {file = "aiohttp-3.12.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3ead1c00f8521a5c9070fcb88f02967b1d8a0544e6d85c253f6968b785e1a2ab"}, + {file = "aiohttp-3.12.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6990ef617f14450bc6b34941dba4f12d5613cbf4e33805932f853fbd1cf18bfb"}, + {file = "aiohttp-3.12.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd736ed420f4db2b8148b52b46b88ed038d0354255f9a73196b7bbce3ea97545"}, + {file = "aiohttp-3.12.15-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c5092ce14361a73086b90c6efb3948ffa5be2f5b6fbcf52e8d8c8b8848bb97c"}, + {file = "aiohttp-3.12.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aaa2234bb60c4dbf82893e934d8ee8dea30446f0647e024074237a56a08c01bd"}, + {file = "aiohttp-3.12.15-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6d86a2fbdd14192e2f234a92d3b494dd4457e683ba07e5905a0b3ee25389ac9f"}, + {file = "aiohttp-3.12.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a041e7e2612041a6ddf1c6a33b883be6a421247c7afd47e885969ee4cc58bd8d"}, + {file = "aiohttp-3.12.15-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5015082477abeafad7203757ae44299a610e89ee82a1503e3d4184e6bafdd519"}, + {file = "aiohttp-3.12.15-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:56822ff5ddfd1b745534e658faba944012346184fbfe732e0d6134b744516eea"}, + {file = "aiohttp-3.12.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b2acbbfff69019d9014508c4ba0401822e8bae5a5fdc3b6814285b71231b60f3"}, + {file = "aiohttp-3.12.15-cp312-cp312-win32.whl", hash = "sha256:d849b0901b50f2185874b9a232f38e26b9b3d4810095a7572eacea939132d4e1"}, + {file = "aiohttp-3.12.15-cp312-cp312-win_amd64.whl", hash = "sha256:b390ef5f62bb508a9d67cb3bba9b8356e23b3996da7062f1a57ce1a79d2b3d34"}, + {file = "aiohttp-3.12.15-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9f922ffd05034d439dde1c77a20461cf4a1b0831e6caa26151fe7aa8aaebc315"}, + {file = "aiohttp-3.12.15-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2ee8a8ac39ce45f3e55663891d4b1d15598c157b4d494a4613e704c8b43112cd"}, + {file = "aiohttp-3.12.15-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3eae49032c29d356b94eee45a3f39fdf4b0814b397638c2f718e96cfadf4c4e4"}, + {file = "aiohttp-3.12.15-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b97752ff12cc12f46a9b20327104448042fce5c33a624f88c18f66f9368091c7"}, + {file = "aiohttp-3.12.15-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:894261472691d6fe76ebb7fcf2e5870a2ac284c7406ddc95823c8598a1390f0d"}, + {file = "aiohttp-3.12.15-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5fa5d9eb82ce98959fc1031c28198b431b4d9396894f385cb63f1e2f3f20ca6b"}, + {file = "aiohttp-3.12.15-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0fa751efb11a541f57db59c1dd821bec09031e01452b2b6217319b3a1f34f3d"}, + {file = "aiohttp-3.12.15-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5346b93e62ab51ee2a9d68e8f73c7cf96ffb73568a23e683f931e52450e4148d"}, + {file = "aiohttp-3.12.15-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:049ec0360f939cd164ecbfd2873eaa432613d5e77d6b04535e3d1fbae5a9e645"}, + {file = "aiohttp-3.12.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b52dcf013b57464b6d1e51b627adfd69a8053e84b7103a7cd49c030f9ca44461"}, + {file = "aiohttp-3.12.15-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:9b2af240143dd2765e0fb661fd0361a1b469cab235039ea57663cda087250ea9"}, + {file = "aiohttp-3.12.15-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ac77f709a2cde2cc71257ab2d8c74dd157c67a0558a0d2799d5d571b4c63d44d"}, + {file = "aiohttp-3.12.15-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:47f6b962246f0a774fbd3b6b7be25d59b06fdb2f164cf2513097998fc6a29693"}, + {file = "aiohttp-3.12.15-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:760fb7db442f284996e39cf9915a94492e1896baac44f06ae551974907922b64"}, + {file = "aiohttp-3.12.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad702e57dc385cae679c39d318def49aef754455f237499d5b99bea4ef582e51"}, + {file = "aiohttp-3.12.15-cp313-cp313-win32.whl", hash = "sha256:f813c3e9032331024de2eb2e32a88d86afb69291fbc37a3a3ae81cc9917fb3d0"}, + {file = "aiohttp-3.12.15-cp313-cp313-win_amd64.whl", hash = "sha256:1a649001580bdb37c6fdb1bebbd7e3bc688e8ec2b5c6f52edbb664662b17dc84"}, + {file = "aiohttp-3.12.15-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:691d203c2bdf4f4637792efbbcdcd157ae11e55eaeb5e9c360c1206fb03d4d98"}, + {file = "aiohttp-3.12.15-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8e995e1abc4ed2a454c731385bf4082be06f875822adc4c6d9eaadf96e20d406"}, + {file = "aiohttp-3.12.15-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bd44d5936ab3193c617bfd6c9a7d8d1085a8dc8c3f44d5f1dcf554d17d04cf7d"}, + {file = "aiohttp-3.12.15-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46749be6e89cd78d6068cdf7da51dbcfa4321147ab8e4116ee6678d9a056a0cf"}, + {file = "aiohttp-3.12.15-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0c643f4d75adea39e92c0f01b3fb83d57abdec8c9279b3078b68a3a52b3933b6"}, + {file = "aiohttp-3.12.15-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0a23918fedc05806966a2438489dcffccbdf83e921a1170773b6178d04ade142"}, + {file = "aiohttp-3.12.15-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:74bdd8c864b36c3673741023343565d95bfbd778ffe1eb4d412c135a28a8dc89"}, + {file = "aiohttp-3.12.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a146708808c9b7a988a4af3821379e379e0f0e5e466ca31a73dbdd0325b0263"}, + {file = "aiohttp-3.12.15-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7011a70b56facde58d6d26da4fec3280cc8e2a78c714c96b7a01a87930a9530"}, + {file = "aiohttp-3.12.15-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3bdd6e17e16e1dbd3db74d7f989e8af29c4d2e025f9828e6ef45fbdee158ec75"}, + {file = "aiohttp-3.12.15-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:57d16590a351dfc914670bd72530fd78344b885a00b250e992faea565b7fdc05"}, + {file = "aiohttp-3.12.15-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:bc9a0f6569ff990e0bbd75506c8d8fe7214c8f6579cca32f0546e54372a3bb54"}, + {file = "aiohttp-3.12.15-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:536ad7234747a37e50e7b6794ea868833d5220b49c92806ae2d7e8a9d6b5de02"}, + {file = "aiohttp-3.12.15-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:f0adb4177fa748072546fb650d9bd7398caaf0e15b370ed3317280b13f4083b0"}, + {file = "aiohttp-3.12.15-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:14954a2988feae3987f1eb49c706bff39947605f4b6fa4027c1d75743723eb09"}, + {file = "aiohttp-3.12.15-cp39-cp39-win32.whl", hash = "sha256:b784d6ed757f27574dca1c336f968f4e81130b27595e458e69457e6878251f5d"}, + {file = "aiohttp-3.12.15-cp39-cp39-win_amd64.whl", hash = "sha256:86ceded4e78a992f835209e236617bffae649371c4a50d5e5a3987f237db84b8"}, + {file = "aiohttp-3.12.15.tar.gz", hash = "sha256:4fc61385e9c98d72fcdf47e6dd81833f47b2f77c114c29cd64a361be57a763a2"}, ] [package.dependencies] aiohappyeyeballs = ">=2.5.0" -aiosignal = ">=1.1.2" +aiosignal = ">=1.4.0" attrs = ">=17.3.0" frozenlist = ">=1.1.1" multidict = ">=4.5,<7.0" @@ -122,14 +122,14 @@ speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (> [[package]] name = "aiosignal" -version = "1.3.2" +version = "1.4.0" description = "aiosignal: a list of registered asynchronous callbacks" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"}, - {file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"}, + {file = "aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e"}, + {file = "aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7"}, ] [package.dependencies] @@ -168,40 +168,20 @@ doc = ["Sphinx (>=8.2,<9.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", test = ["anyio[trio]", "blockbuster (>=1.5.23)", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1) ; python_version >= \"3.10\"", "uvloop (>=0.21) ; platform_python_implementation == \"CPython\" and platform_system != \"Windows\" and python_version < \"3.14\""] trio = ["trio (>=0.26.1)"] -[[package]] -name = "arrow" -version = "1.3.0" -description = "Better dates & times for Python" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "arrow-1.3.0-py3-none-any.whl", hash = "sha256:c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80"}, - {file = "arrow-1.3.0.tar.gz", hash = "sha256:d4540617648cb5f895730f1ad8c82a65f2dad0166f57b75f3ca54759c4d67a85"}, -] - -[package.dependencies] -python-dateutil = ">=2.7.0" -types-python-dateutil = ">=2.8.10" - -[package.extras] -doc = ["doc8", "sphinx (>=7.0.0)", "sphinx-autobuild", "sphinx-autodoc-typehints", "sphinx_rtd_theme (>=1.3.0)"] -test = ["dateparser (==1.*)", "pre-commit", "pytest", "pytest-cov", "pytest-mock", "pytz (==2021.1)", "simplejson (==3.*)"] - [[package]] name = "asgiref" -version = "3.8.1" +version = "3.9.1" description = "ASGI specs, helper code, and adapters" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47"}, - {file = "asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590"}, + {file = "asgiref-3.9.1-py3-none-any.whl", hash = "sha256:f3bba7092a48005b5f5bacd747d36ee4a5a61f4a269a6df590b43144355ebd2c"}, + {file = "asgiref-3.9.1.tar.gz", hash = "sha256:a5ab6582236218e5ef1648f242fd9f10626cfd4de8dc377db215d5d5098e3142"}, ] [package.extras] -tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] +tests = ["mypy (>=1.14.0)", "pytest", "pytest-asyncio"] [[package]] name = "attrs" @@ -209,7 +189,7 @@ version = "25.3.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"}, {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"}, @@ -238,20 +218,36 @@ files = [ [package.dependencies] cryptography = "*" +[[package]] +name = "aws-xray-sdk" +version = "2.14.0" +description = "The AWS X-Ray SDK for Python (the SDK) enables Python developers to record and emit information from within their applications to the AWS X-Ray service." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "aws_xray_sdk-2.14.0-py2.py3-none-any.whl", hash = "sha256:cfbe6feea3d26613a2a869d14c9246a844285c97087ad8f296f901633554ad94"}, + {file = "aws_xray_sdk-2.14.0.tar.gz", hash = "sha256:aab843c331af9ab9ba5cefb3a303832a19db186140894a523edafc024cc0493c"}, +] + +[package.dependencies] +botocore = ">=1.11.3" +wrapt = "*" + [[package]] name = "awscli" -version = "1.40.41" +version = "1.40.45" description = "Universal Command Line Environment for AWS." optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "awscli-1.40.41-py3-none-any.whl", hash = "sha256:d75cc6c654418ac4d30eb996081033e90024fa7a661db8ab40de4b5a545eaa79"}, - {file = "awscli-1.40.41.tar.gz", hash = "sha256:553c3a3ba7879be18c5db219f9a710daf90d750044eb604297b25805b05ebc42"}, + {file = "awscli-1.40.45-py3-none-any.whl", hash = "sha256:017cdb820e9d1a1ff72abd968b27eea8c36f5d0a2f30dad555d027a8c53c18fe"}, + {file = "awscli-1.40.45.tar.gz", hash = "sha256:41c06b168de2bb4e573804de8034f061e1d856bd0a362609badef8f47cd33bed"}, ] [package.dependencies] -botocore = "1.38.42" +botocore = "1.38.46" colorama = ">=0.2.5,<0.4.7" docutils = ">=0.18.1,<=0.19" PyYAML = ">=3.10,<6.1" @@ -275,18 +271,6 @@ localstack-client = "*" [package.extras] ver1 = ["awscli"] -[[package]] -name = "backoff" -version = "2.2.1" -description = "Function decoration for backoff and retry" -optional = false -python-versions = ">=3.7,<4.0" -groups = ["main"] -files = [ - {file = "backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8"}, - {file = "backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba"}, -] - [[package]] name = "beautifulsoup4" version = "4.13.4" @@ -365,14 +349,14 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.38.42" +version = "1.38.46" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.9" groups = ["main", "dev"] files = [ - {file = "botocore-1.38.42-py3-none-any.whl", hash = "sha256:fbbeac30c045b5c19f1c3bb063ea2b6315ce2d6fcb3d898e87d1c1846297961c"}, - {file = "botocore-1.38.42.tar.gz", hash = "sha256:3a14188e48f6e26be561164373d34150fa9cb39f7ad32cc745dcd3ab05f43683"}, + {file = "botocore-1.38.46-py3-none-any.whl", hash = "sha256:89ca782ffbf2e8769ca9c89234cfa5ca577f1987d07d913ee3c68c4776b1eb5b"}, + {file = "botocore-1.38.46.tar.gz", hash = "sha256:8798e5a418c27cf93195b077153644aea44cb171fcd56edc1ecebaa1e49e226e"}, ] [package.dependencies] @@ -547,25 +531,13 @@ files = [ [package.dependencies] pycparser = "*" -[[package]] -name = "chardet" -version = "5.2.0" -description = "Universal encoding detector for Python 3" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"}, - {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, -] - [[package]] name = "charset-normalizer" version = "3.4.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941"}, {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd"}, @@ -687,6 +659,7 @@ files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +markers = {main = "platform_system == \"Windows\""} [[package]] name = "coverage" @@ -1025,18 +998,6 @@ werkzeug = ">=3.1.0" async = ["asgiref (>=3.2)"] dotenv = ["python-dotenv"] -[[package]] -name = "fqdn" -version = "1.5.1" -description = "Validates fully-qualified domain names against RFC 1123, so that they are acceptable to modern bowsers" -optional = false -python-versions = ">=2.7, !=3.0, !=3.1, !=3.2, !=3.3, !=3.4, <4" -groups = ["main"] -files = [ - {file = "fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014"}, - {file = "fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f"}, -] - [[package]] name = "freezegun" version = "1.5.2" @@ -1199,14 +1160,14 @@ smmap = ">=3.0.1,<6" [[package]] name = "gitpython" -version = "3.1.44" +version = "3.1.45" description = "GitPython is a Python library used to interact with Git repositories" optional = false python-versions = ">=3.7" groups = ["dev"] files = [ - {file = "GitPython-3.1.44-py3-none-any.whl", hash = "sha256:9e0e10cda9bed1ee64bc9a6de50e7e38a9c9943241cd7f585f6df3ed28011110"}, - {file = "gitpython-3.1.44.tar.gz", hash = "sha256:c87e30b26253bf5418b01b0660f818967f3c503193838337fe5e573331249269"}, + {file = "gitpython-3.1.45-py3-none-any.whl", hash = "sha256:8908cb2e02fb3b93b7eb0f2827125cb699869470432cc885f019b8fd0fccff77"}, + {file = "gitpython-3.1.45.tar.gz", hash = "sha256:85b0ee964ceddf211c41b9f27a49086010a190fd8132a24e21f362a4b36a791c"}, ] [package.dependencies] @@ -1216,18 +1177,6 @@ gitdb = ">=4.0.1,<5" doc = ["sphinx (>=7.1.2,<7.2)", "sphinx-autodoc-typehints", "sphinx_rtd_theme"] test = ["coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock ; python_version < \"3.8\"", "mypy", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar", "typing-extensions ; python_version < \"3.11\""] -[[package]] -name = "graphql-core" -version = "3.2.6" -description = "GraphQL implementation for Python, a port of GraphQL.js, the JavaScript reference implementation for GraphQL." -optional = false -python-versions = "<4,>=3.6" -groups = ["main"] -files = [ - {file = "graphql_core-3.2.6-py3-none-any.whl", hash = "sha256:78b016718c161a6fb20a7d97bbf107f331cd1afe53e45566c59f776ed7f0b45f"}, - {file = "graphql_core-3.2.6.tar.gz", hash = "sha256:c08eec22f9e40f0bd61d805907e3b3b1b9a320bc606e23dc145eebca07c8fbab"}, -] - [[package]] name = "h11" version = "0.16.0" @@ -1240,24 +1189,6 @@ files = [ {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, ] -[[package]] -name = "harfile" -version = "0.3.0" -description = "Writer for HTTP Archive (HAR) files" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "harfile-0.3.0-py3-none-any.whl", hash = "sha256:ac11177e06c88c9553c8c73c16ab20428a176d1d2ebe00b41ce527ff0bdc47e6"}, - {file = "harfile-0.3.0.tar.gz", hash = "sha256:23be8037e1296bb4787a15543a37835ed91f408c8296988f9ba022a44accad9e"}, -] - -[package.extras] -bench = ["pytest-codspeed (==2.2.1)"] -cov = ["coverage-enable-subprocess", "coverage[toml] (>=7)"] -dev = ["coverage (>=7)", "coverage-enable-subprocess", "coverage[toml] (>=7)", "hypothesis (>=6)", "hypothesis-jsonschema (>=0.23.1)", "jsonschema (>=4.18.0)", "pytest (>=6.2.0,<8)", "pytest-codspeed (==2.2.1)"] -tests = ["coverage (>=7)", "hypothesis (>=6)", "hypothesis-jsonschema (>=0.23.1)", "jsonschema (>=4.18.0)", "pytest (>=6.2.0,<8)"] - [[package]] name = "httpcore" version = "1.0.9" @@ -1305,77 +1236,6 @@ http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] zstd = ["zstandard (>=0.18.0)"] -[[package]] -name = "hypothesis" -version = "6.137.1" -description = "A library for property-based testing" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "hypothesis-6.137.1-py3-none-any.whl", hash = "sha256:7cbda6a98ed4d32aad31a5fc5bff5e119b9275fe2579a7b08863cba313a4b9be"}, - {file = "hypothesis-6.137.1.tar.gz", hash = "sha256:b086e644456da79ad460fdaf8fbf90a41a661e8a4076232dd4ea64cfbc0d0529"}, -] - -[package.dependencies] -attrs = ">=22.2.0" -sortedcontainers = ">=2.1.0,<3.0.0" - -[package.extras] -all = ["black (>=19.10b0)", "click (>=7.0)", "crosshair-tool (>=0.0.93)", "django (>=4.2)", "dpcontracts (>=0.4)", "hypothesis-crosshair (>=0.0.24)", "lark (>=0.10.1)", "libcst (>=0.3.16)", "numpy (>=1.19.3)", "pandas (>=1.1)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "rich (>=9.0.0)", "tzdata (>=2025.2) ; sys_platform == \"win32\" or sys_platform == \"emscripten\"", "watchdog (>=4.0.0)"] -cli = ["black (>=19.10b0)", "click (>=7.0)", "rich (>=9.0.0)"] -codemods = ["libcst (>=0.3.16)"] -crosshair = ["crosshair-tool (>=0.0.93)", "hypothesis-crosshair (>=0.0.24)"] -dateutil = ["python-dateutil (>=1.4)"] -django = ["django (>=4.2)"] -dpcontracts = ["dpcontracts (>=0.4)"] -ghostwriter = ["black (>=19.10b0)"] -lark = ["lark (>=0.10.1)"] -numpy = ["numpy (>=1.19.3)"] -pandas = ["pandas (>=1.1)"] -pytest = ["pytest (>=4.6)"] -pytz = ["pytz (>=2014.1)"] -redis = ["redis (>=3.0.0)"] -watchdog = ["watchdog (>=4.0.0)"] -zoneinfo = ["tzdata (>=2025.2) ; sys_platform == \"win32\" or sys_platform == \"emscripten\""] - -[[package]] -name = "hypothesis-graphql" -version = "0.11.1" -description = "Hypothesis strategies for GraphQL queries" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "hypothesis_graphql-0.11.1-py3-none-any.whl", hash = "sha256:a6968f703bcdc31fbe1b26be69185aa2c824eb3b478057a66aa85967c81cadca"}, - {file = "hypothesis_graphql-0.11.1.tar.gz", hash = "sha256:bd49ab6804a3f488ecab2e39c20dba6dfc2101525c6742f5831cfa9eff95285a"}, -] - -[package.dependencies] -graphql-core = ">=3.1.0,<3.3.0" -hypothesis = ">=6.84.3,<7.0" - -[package.extras] -cov = ["coverage-enable-subprocess", "coverage[toml] (>=7)"] -dev = ["coverage (>=7)", "coverage-enable-subprocess", "coverage[toml] (>=7)", "pytest (>=6.2.0,<8)", "pytest-xdist (>=2.5,<3.0)"] -tests = ["coverage (>=7)", "pytest (>=6.2.0,<8)", "pytest-xdist (>=2.5,<3.0)"] - -[[package]] -name = "hypothesis-jsonschema" -version = "0.23.1" -description = "Generate test data from JSON schemata with Hypothesis" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "hypothesis-jsonschema-0.23.1.tar.gz", hash = "sha256:f4ac032024342a4149a10253984f5a5736b82b3fe2afb0888f3834a31153f215"}, - {file = "hypothesis_jsonschema-0.23.1-py3-none-any.whl", hash = "sha256:a4d74d9516dd2784fbbae82e009f62486c9104ac6f4e3397091d98a1d5ee94a2"}, -] - -[package.dependencies] -hypothesis = ">=6.84.3" -jsonschema = ">=4.18.0" - [[package]] name = "idna" version = "3.10" @@ -1397,27 +1257,12 @@ version = "2.1.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, ] -[[package]] -name = "isoduration" -version = "20.11.0" -description = "Operations with ISO 8601 durations" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "isoduration-20.11.0-py3-none-any.whl", hash = "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042"}, - {file = "isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9"}, -] - -[package.dependencies] -arrow = ">=0.15.0" - [[package]] name = "itsdangerous" version = "2.2.0" @@ -1476,47 +1321,27 @@ decorator = "*" ply = "*" six = "*" -[[package]] -name = "jsonpointer" -version = "3.0.0" -description = "Identify specific nodes in a JSON document (RFC 6901)" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942"}, - {file = "jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef"}, -] - [[package]] name = "jsonschema" -version = "4.25.0" +version = "4.24.0" description = "An implementation of JSON Schema validation for Python" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] +groups = ["dev"] files = [ - {file = "jsonschema-4.25.0-py3-none-any.whl", hash = "sha256:24c2e8da302de79c8b9382fee3e76b355e44d2a4364bb207159ce10b517bd716"}, - {file = "jsonschema-4.25.0.tar.gz", hash = "sha256:e63acf5c11762c0e6672ffb61482bdf57f0876684d8d249c0fe2d730d48bc55f"}, + {file = "jsonschema-4.24.0-py3-none-any.whl", hash = "sha256:a462455f19f5faf404a7902952b6f0e3ce868f3ee09a359b05eca6673bd8412d"}, + {file = "jsonschema-4.24.0.tar.gz", hash = "sha256:0b4e8069eb12aedfa881333004bccaec24ecef5a8a6a4b6df142b2cc9599d196"}, ] [package.dependencies] attrs = ">=22.2.0" -fqdn = {version = "*", optional = true, markers = "extra == \"format\""} -idna = {version = "*", optional = true, markers = "extra == \"format\""} -isoduration = {version = "*", optional = true, markers = "extra == \"format\""} -jsonpointer = {version = ">1.13", optional = true, markers = "extra == \"format\""} jsonschema-specifications = ">=2023.03.6" referencing = ">=0.28.4" -rfc3339-validator = {version = "*", optional = true, markers = "extra == \"format\""} -rfc3987 = {version = "*", optional = true, markers = "extra == \"format\""} rpds-py = ">=0.7.1" -uri-template = {version = "*", optional = true, markers = "extra == \"format\""} -webcolors = {version = ">=1.11", optional = true, markers = "extra == \"format\""} [package.extras] format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] -format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "rfc3987-syntax (>=1.1.0)", "uri-template", "webcolors (>=24.6.0)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=24.6.0)"] [[package]] name = "jsonschema-path" @@ -1542,7 +1367,7 @@ version = "2025.4.1" description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "jsonschema_specifications-2025.4.1-py3-none-any.whl", hash = "sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af"}, {file = "jsonschema_specifications-2025.4.1.tar.gz", hash = "sha256:630159c9f4dbea161a6a2205c3011cc4f18ff381b189fff48bb39b9bf26ae608"}, @@ -1551,21 +1376,6 @@ files = [ [package.dependencies] referencing = ">=0.31.0" -[[package]] -name = "junit-xml" -version = "1.9" -description = "Creates JUnit XML test result documents that can be read by tools such as Jenkins" -optional = false -python-versions = "*" -groups = ["main"] -files = [ - {file = "junit-xml-1.9.tar.gz", hash = "sha256:de16a051990d4e25a3982b2dd9e89d671067548718866416faec14d9de56db9f"}, - {file = "junit_xml-1.9-py2.py3-none-any.whl", hash = "sha256:ec5ca1a55aefdd76d28fcc0b135251d156c7106fa979686a4b48d62b761b4732"}, -] - -[package.dependencies] -six = "*" - [[package]] name = "lazy-object-proxy" version = "1.11.0" @@ -1592,21 +1402,21 @@ files = [ [[package]] name = "localstack" -version = "4.5.0" +version = "4.6.0" description = "LocalStack - A fully functional local Cloud stack" optional = false python-versions = "*" groups = ["dev"] files = [ - {file = "localstack-4.5.0.tar.gz", hash = "sha256:f8ebf3a9af1826c595cfe4196c6d52792152db374e437e1a574ac52aedc53a18"}, + {file = "localstack-4.6.0.tar.gz", hash = "sha256:2b0ba609816241dba507d7d7b20ca44ee598b4c730f6c56bed3e6f5472dc6c7d"}, ] [package.dependencies] localstack-core = "*" -localstack-ext = "4.5.0" +localstack-ext = "4.6.0" [package.extras] -runtime = ["localstack-core[runtime]", "localstack-ext[runtime] (==4.5.0)"] +runtime = ["localstack-core[runtime]", "localstack-ext[runtime] (==4.6.0)"] [[package]] name = "localstack-client" @@ -1627,14 +1437,14 @@ test = ["black", "coverage", "flake8", "isort", "localstack", "pytest"] [[package]] name = "localstack-core" -version = "4.5.0" +version = "4.6.0" description = "The core library and runtime of LocalStack" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "localstack_core-4.5.0-py3-none-any.whl", hash = "sha256:ab0099d840ff9e718a268315bd9965152f92241dfaf3124b7eaa1305dd58b0f6"}, - {file = "localstack_core-4.5.0.tar.gz", hash = "sha256:2930c0a67dad7f88d2690c0b8720a8b605774c6edf819593ab0328fd14a3e395"}, + {file = "localstack_core-4.6.0-py3-none-any.whl", hash = "sha256:98c1b8b6406f7b72a3906434dfb9fcf3644ff9191211a607073dd76f6f6e51a4"}, + {file = "localstack_core-4.6.0.tar.gz", hash = "sha256:e324a8ece8acb1b15a8961ea1a103a50e24e61bbbd30e608b9f5a03ff3b0b0ad"}, ] [package.dependencies] @@ -1655,21 +1465,21 @@ semver = ">=2.10" tailer = ">=0.4.1" [package.extras] -base-runtime = ["Werkzeug (>=3.1.3)", "awscrt (>=0.13.14,!=0.27.1)", "boto3 (==1.38.27)", "botocore (==1.38.27)", "cbor2 (>=5.5.0)", "dnspython (>=1.16.0)", "docker (>=6.1.1)", "hypercorn (>=0.14.4)", "jsonpatch (>=1.24)", "localstack-twisted (>=23.0)", "openapi-core (>=0.19.2)", "pyopenssl (>=23.0.0)", "readerwriterlock (>=1.0.7)", "requests-aws4auth (>=1.0)", "rolo (>=0.7)", "urllib3 (>=2.0.7)", "xmltodict (>=0.13.0)"] +base-runtime = ["Werkzeug (>=3.1.3)", "awscrt (>=0.13.14,!=0.27.1)", "boto3 (==1.38.46)", "botocore (==1.38.46)", "cbor2 (>=5.5.0)", "dnspython (>=1.16.0)", "docker (>=6.1.1)", "hypercorn (>=0.14.4)", "jsonpatch (>=1.24)", "localstack-twisted (>=23.0)", "openapi-core (>=0.19.2)", "pyopenssl (>=23.0.0)", "readerwriterlock (>=1.0.7)", "requests-aws4auth (>=1.0)", "rolo (>=0.7)", "urllib3 (>=2.0.7)", "xmltodict (>=0.13.0)"] dev = ["Cython", "coveralls (>=3.3.1)", "localstack-core[test]", "mypy", "networkx (>=2.8.4)", "openapi-spec-validator (>=0.7.1)", "pandoc", "pre-commit (>=3.5.0)", "pypandoc", "rstr (>=3.2.0)", "ruff (>=0.3.3)"] -runtime = ["airspeed-ext (>=0.6.3)", "antlr4-python3-runtime (==4.13.2)", "apispec (>=5.1.1)", "aws-sam-translator (>=1.15.1)", "awscli (>=1.37.0)", "crontab (>=0.22.6)", "cryptography (>=41.0.5)", "jpype1-ext (>=0.0.1)", "json5 (>=0.9.11)", "jsonpath-ng (>=1.6.1)", "jsonpath-rw (>=1.4.0)", "kclpy-ext (>=3.0.0)", "localstack-core[base-runtime]", "moto-ext[all] (==5.1.5.post1)", "opensearch-py (>=2.4.1)", "pymongo (>=4.2.0)", "pyopenssl (>=23.0.0)"] +runtime = ["airspeed-ext (>=0.6.3)", "antlr4-python3-runtime (==4.13.2)", "apispec (>=5.1.1)", "aws-sam-translator (>=1.15.1)", "awscli (>=1.37.0)", "crontab (>=0.22.6)", "cryptography (>=41.0.5)", "jpype1-ext (>=0.0.1)", "json5 (>=0.9.11)", "jsonpath-ng (>=1.6.1)", "jsonpath-rw (>=1.4.0)", "kclpy-ext (>=3.0.0)", "localstack-core[base-runtime]", "moto-ext[all] (==5.1.6.post2)", "opensearch-py (>=2.4.1)", "pymongo (>=4.2.0)", "pyopenssl (>=23.0.0)"] test = ["aws-cdk-lib (>=2.88.0)", "coverage[toml] (>=5.5)", "deepdiff (>=6.4.1)", "httpx[http2] (>=0.25)", "localstack-core[runtime]", "localstack-snapshot (>=0.1.1)", "pluggy (>=1.3.0)", "pytest (>=7.4.2)", "pytest-httpserver (>=1.1.2)", "pytest-rerunfailures (>=12.0)", "pytest-split (>=0.8.0)", "pytest-tinybird (>=0.5.0)", "websocket-client (>=1.7.0)"] typehint = ["boto3-stubs[acm,acm-pca,amplify,apigateway,apigatewayv2,appconfig,appconfigdata,application-autoscaling,appsync,athena,autoscaling,backup,batch,ce,cloudcontrol,cloudformation,cloudfront,cloudtrail,cloudwatch,codebuild,codecommit,codeconnections,codedeploy,codepipeline,codestar-connections,cognito-identity,cognito-idp,dms,docdb,dynamodb,dynamodbstreams,ec2,ecr,ecs,efs,eks,elasticache,elasticbeanstalk,elbv2,emr,emr-serverless,es,events,firehose,fis,glacier,glue,iam,identitystore,iot,iot-data,iotanalytics,iotwireless,kafka,kinesis,kinesisanalytics,kinesisanalyticsv2,kms,lakeformation,lambda,logs,managedblockchain,mediaconvert,mediastore,mq,mwaa,neptune,opensearch,organizations,pi,pinpoint,pipes,qldb,qldb-session,rds,rds-data,redshift,redshift-data,resource-groups,resourcegroupstaggingapi,route53,route53resolver,s3,s3control,sagemaker,sagemaker-runtime,secretsmanager,serverlessrepo,servicediscovery,ses,sesv2,sns,sqs,ssm,sso-admin,stepfunctions,sts,timestream-query,timestream-write,transcribe,verifiedpermissions,wafv2,xray]", "localstack-core[dev]"] [[package]] name = "localstack-ext" -version = "4.5.0" +version = "4.6.0" description = "Extensions for LocalStack" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "localstack_ext-4.5.0.tar.gz", hash = "sha256:7d7c30ce3edbe822a5ff3db063d323ae3360c346d6719c83447fbd9188462556"}, + {file = "localstack_ext-4.6.0.tar.gz", hash = "sha256:7b9fcd712877bd5678b394bb24e8ebdb1df9fb4c669177049c0d97e005c8615c"}, ] [package.dependencies] @@ -1677,7 +1487,7 @@ build = "*" dill = ">=0.3.2" dnslib = ">=0.9.10" dnspython = ">=1.16.0" -localstack-core = "4.5.0" +localstack-core = "4.6.0" packaging = "*" plux = ">=1.10.0" PyJWT = {version = ">=1.7.0", extras = ["crypto"]} @@ -1689,8 +1499,8 @@ windows-curses = {version = "*", markers = "platform_system == \"Windows\""} [package.extras] package = ["python-minifier (<2.11.3)"] -runtime = ["Whoosh (>=2.7.4)", "amazon.ion (>=0.9.3)", "avro (>=1.11.0)", "aws-encryption-sdk (>=3.1.0)", "aws-json-term-matcher (>=0.1.5)", "cedarpy (>=4.1.0)", "confluent-kafka", "dirtyjson (>=1.0.7)", "distro", "dulwich (>=0.19.16)", "graphql-core (>=3.0.3)", "janus (>=0.5.0)", "jsonpatch (>=1.32)", "kafka-python", "kubernetes (>=21.7.0)", "libvirt-python", "localstack-core[runtime] (==4.5.0)", "mysql-replication", "paho-mqtt (>=1.5)", "parquet[snappy] (>=1.3.1)", "parse (>=1.19.0)", "pg8000 (>=1.10)", "postgres (>=2.2.2)", "postgresql-proxy (>=0.2.0)", "pproxy-ext (>=2.7.9)", "presto-python-client (>=0.7.0)", "pure-sasl (>=0.6.2)", "pycdlib (>=1.14.0)", "pycognito (>=2024.5.1)", "pyftpdlib (>=1.5.6)", "pyhive[hive-pure-sasl] (>=0.7.0)", "pyion2json (>=0.0.2)", "pymysql", "pyqldb (>=3.2,<4.0)", "python-dxf (>=12.1.1)", "python-snappy (>=0.6)", "readerwriterlock (>=1.0.7)", "redis (>=5.0)", "rsa (>=4.0)", "sql-metadata (>=2.6.0)", "sqlglot[rs]", "srp-ext (>=1.0.7.1)", "testing.common.database (>=1.1.0)", "thrift (>=0.10.0)", "thrift_sasl (>=0.1.0)", "tornado (>=6.0)", "websockets (>=8.1,<14)"] -test = ["PyAthena[pandas]", "aiohttp", "async-timeout", "aws-cdk-lib (>=2.88.0)", "aws-cdk.aws-cognito-identitypool-alpha", "aws_cdk.aws_neptune_alpha", "aws_cdk.aws_redshift_alpha", "aws_xray_sdk (>=2.4.2)", "awsiotsdk", "awsiotsdk", "awswrangler (>=3.5.2)", "coverage[toml] (>=5.0.0)", "deepdiff (>=5.5.0)", "gremlinpython", "jws (>=0.1.3)", "localstack-core[test] (==4.5.0)", "localstack-ext[runtime]", "msal", "msal-extensions", "msrest", "mysql-connector-python", "neo4j", "nest-asyncio (>=1.4.1)", "paramiko", "playwright", "portalocker", "pre-commit (>=3.5.0)", "pyarrow", "pymongo", "pymssql (>=2.2.8)", "pytest-httpserver (>=1.0.1)", "pytest-instafail (>=0.4.2)", "pytest-mock (>=3.14.0)", "pytest-playwright", "python-terraform", "redshift_connector", "ruff (>=0.1.0)", "stomp.py (>=8.0.1)"] +runtime = ["Whoosh (>=2.7.4)", "amazon.ion (>=0.9.3)", "avro (>=1.11.0)", "aws-encryption-sdk (>=3.1.0)", "aws-json-term-matcher (>=0.1.5)", "cedarpy (>=4.1.0)", "confluent-kafka", "dirtyjson (>=1.0.7)", "distro", "dulwich (>=0.19.16)", "graphql-core (>=3.0.3)", "janus (>=0.5.0)", "javascript", "jsonpatch (>=1.32)", "kafka-python", "kubernetes (>=21.7.0)", "libvirt-python", "localstack-core[runtime] (==4.6.0)", "mysql-replication", "paho-mqtt (>=1.5)", "parquet[snappy] (>=1.3.1)", "parse (>=1.19.0)", "pg8000 (>=1.10)", "postgres (>=2.2.2)", "postgresql-proxy (>=0.2.0)", "pproxy-ext (>=2.7.9)", "presto-python-client (>=0.7.0)", "pure-sasl (>=0.6.2)", "pycdlib (>=1.14.0)", "pycognito (>=2024.5.1)", "pyftpdlib (>=1.5.6)", "pyhive[hive-pure-sasl] (>=0.7.0)", "pyion2json (>=0.0.2)", "pymysql", "pyqldb (>=3.2,<4.0)", "python-dxf (>=12.1.1)", "python-snappy (>=0.6)", "readerwriterlock (>=1.0.7)", "redis (>=5.0)", "rsa (>=4.0)", "sql-metadata (>=2.6.0)", "sqlglot[rs]", "srp-ext (>=1.0.7.1)", "testing.common.database (>=1.1.0)", "thrift (>=0.10.0)", "thrift_sasl (>=0.1.0)", "tornado (>=6.0)", "websockets (>=8.1,<14)"] +test = ["PyAthena[pandas]", "aiohttp", "async-timeout", "aws-cdk-lib (>=2.88.0)", "aws-cdk.aws-cognito-identitypool-alpha", "aws_cdk.aws_neptune_alpha", "aws_cdk.aws_redshift_alpha", "aws_xray_sdk (>=2.4.2)", "awsiotsdk", "awsiotsdk", "awswrangler (>=3.5.2)", "coverage[toml] (>=5.0.0)", "deepdiff (>=5.5.0)", "gremlinpython", "jws (>=0.1.3)", "localstack-core[test] (==4.6.0)", "localstack-ext[runtime]", "msal", "msal-extensions", "msrest", "mysql-connector-python", "neo4j", "nest-asyncio (>=1.4.1)", "paramiko", "playwright", "portalocker", "pre-commit (>=3.5.0)", "pyarrow", "pymongo", "pymssql (>=2.2.8)", "pytest-httpserver (>=1.0.1)", "pytest-instafail (>=0.4.2)", "pytest-mock (>=3.14.0)", "pytest-playwright", "python-terraform", "redshift_connector", "ruff (>=0.1.0)", "stomp.py (>=8.0.1)"] typehint = ["boto3-stubs[acm,amplify,apigateway,apigatewayv2,appconfig,appsync,athena,autoscaling,backup,batch,bedrock,bedrock-runtime,ce,cloudcontrol,cloudformation,cloudfront,cloudtrail,cloudwatch,codecommit,cognito-identity,cognito-idp,dms,docdb,dynamodb,dynamodbstreams,ec2,ecr,ecs,efs,eks,elasticache,elasticbeanstalk,elbv2,emr,emr-serverless,es,events,firehose,fis,glacier,glue,iam,iot,iot-data,iotanalytics,iotwireless,kafka,kinesis,kinesisanalytics,kinesisanalyticsv2,kms,lakeformation,lambda,logs,mediaconvert,mediastore,mq,mwaa,neptune,opensearch,organizations,pi,qldb,qldb-session,rds,rds-data,redshift,redshift-data,resource-groups,resourcegroupstaggingapi,route53,route53resolver,s3,s3control,sagemaker,sagemaker-runtime,secretsmanager,serverlessrepo,servicediscovery,ses,sesv2,sns,sqs,ssm,sso-admin,stepfunctions,sts,timestream-query,timestream-write,transcribe,xray]", "localstack-ext[test]"] [[package]] @@ -1779,8 +1589,11 @@ files = [ {file = "lxml-5.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:7ce1a171ec325192c6a636b64c94418e71a1964f56d002cc28122fceff0b6121"}, {file = "lxml-5.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:795f61bcaf8770e1b37eec24edf9771b307df3af74d1d6f27d812e15a9ff3872"}, {file = "lxml-5.4.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:29f451a4b614a7b5b6c2e043d7b64a15bd8304d7e767055e8ab68387a8cacf4e"}, + {file = "lxml-5.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:891f7f991a68d20c75cb13c5c9142b2a3f9eb161f1f12a9489c82172d1f133c0"}, {file = "lxml-5.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4aa412a82e460571fad592d0f93ce9935a20090029ba08eca05c614f99b0cc92"}, + {file = "lxml-5.4.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:ac7ba71f9561cd7d7b55e1ea5511543c0282e2b6450f122672a2694621d63b7e"}, {file = "lxml-5.4.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:c5d32f5284012deaccd37da1e2cd42f081feaa76981f0eaa474351b68df813c5"}, + {file = "lxml-5.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:ce31158630a6ac85bddd6b830cffd46085ff90498b397bd0a259f59d27a12188"}, {file = "lxml-5.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:31e63621e073e04697c1b2d23fcb89991790eef370ec37ce4d5d469f40924ed6"}, {file = "lxml-5.4.0-cp37-cp37m-win32.whl", hash = "sha256:be2ba4c3c5b7900246a8f866580700ef0d538f2ca32535e991027bdaba944063"}, {file = "lxml-5.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:09846782b1ef650b321484ad429217f5154da4d6e786636c38e434fa32e94e49"}, @@ -1860,7 +1673,7 @@ version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, @@ -1956,7 +1769,7 @@ version = "0.1.2" description = "Markdown URL utilities" optional = false python-versions = ">=3.7" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, @@ -1964,14 +1777,14 @@ files = [ [[package]] name = "moto" -version = "5.1.6" +version = "5.1.9" description = "A library that allows you to easily mock out tests based on AWS infrastructure" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "moto-5.1.6-py3-none-any.whl", hash = "sha256:e4a3092bc8fe9139caa77cd34cdcbad804de4d9671e2270ea3b4d53f5c645047"}, - {file = "moto-5.1.6.tar.gz", hash = "sha256:baf7afa9d4a92f07277b29cf466d0738f25db2ed2ee12afcb1dc3f2c540beebd"}, + {file = "moto-5.1.9-py3-none-any.whl", hash = "sha256:e9ba7e4764a6088ccc34e3cc846ae719861ca202409fa865573de40a3e805b9b"}, + {file = "moto-5.1.9.tar.gz", hash = "sha256:0c4f0387b06b5d24c0ce90f8f89f31a565cc05789189c5d59b5df02594f2e371"}, ] [package.dependencies] @@ -2196,7 +2009,7 @@ version = "25.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, @@ -2253,7 +2066,7 @@ version = "1.6.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, @@ -2315,32 +2128,6 @@ odmantic = ["odmantic (<1.0.0)", "pydantic[email]"] pydantic = ["pydantic[email] (>=1.10)"] sqlalchemy = ["sqlalchemy (>=1.4.29)"] -[[package]] -name = "prance" -version = "25.4.8.0" -description = "Resolving Swagger/OpenAPI 2.0 and 3.0.0 Parser" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "prance-25.4.8.0-py3-none-any.whl", hash = "sha256:d3c362036d625b12aeee495621cb1555fd50b2af3632af3d825176bfb50e073b"}, - {file = "prance-25.4.8.0.tar.gz", hash = "sha256:2f72d2983d0474b6f53fd604eb21690c1ebdb00d79a6331b7ec95fb4f25a1f65"}, -] - -[package.dependencies] -chardet = ">=5.2" -packaging = ">=24.2" -requests = ">=2.32.3" -"ruamel.yaml" = ">=0.18.10" - -[package.extras] -cli = ["click (>=8.1.8)"] -dev = ["bumpversion (>=0.6.0)", "pytest (>=8.3.5)", "pytest-cov (>=6.0)", "sphinx (>=8.1.3)", "towncrier (>=24.8)", "tox (>=4.23.2)"] -flex = ["flex (>=6.14.1,<6.15.0)"] -icu = ["PyICU (>=2.14,<3.0)"] -osv = ["openapi-spec-validator (>=0.7.1,<0.8.0)"] -ssv = ["swagger-spec-validator (>=3.0.4,<3.1.0)"] - [[package]] name = "propcache" version = "0.3.2" @@ -2713,7 +2500,7 @@ version = "2.19.1" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, @@ -2788,32 +2575,16 @@ files = [ {file = "pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8"}, ] -[[package]] -name = "pyrate-limiter" -version = "3.9.0" -description = "Python Rate-Limiter using Leaky-Bucket Algorithm" -optional = false -python-versions = "<4.0,>=3.8" -groups = ["main"] -files = [ - {file = "pyrate_limiter-3.9.0-py3-none-any.whl", hash = "sha256:77357840c8cf97a36d67005d4e090787043f54000c12c2b414ff65657653e378"}, - {file = "pyrate_limiter-3.9.0.tar.gz", hash = "sha256:6b882e2c77cda07a241d3730975daea4258344b39c878f1dd8849df73f70b0ce"}, -] - -[package.extras] -all = ["filelock (>=3.0)", "psycopg[pool] (>=3.1.18,<4.0.0)", "redis (>=5.0.0,<6.0.0)"] -docs = ["furo (>=2022.3.4,<2023.0.0)", "myst-parser (>=0.17)", "sphinx (>=4.3.0,<5.0.0)", "sphinx-autodoc-typehints (>=1.17,<2.0)", "sphinx-copybutton (>=0.5)", "sphinxcontrib-apidoc (>=0.3,<0.4)"] - [[package]] name = "pyright" -version = "1.1.402" +version = "1.1.403" description = "Command line wrapper for pyright" optional = false python-versions = ">=3.7" groups = ["dev"] files = [ - {file = "pyright-1.1.402-py3-none-any.whl", hash = "sha256:2c721f11869baac1884e846232800fe021c33f1b4acb3929cff321f7ea4e2982"}, - {file = "pyright-1.1.402.tar.gz", hash = "sha256:85a33c2d40cd4439c66aa946fd4ce71ab2f3f5b8c22ce36a623f59ac22937683"}, + {file = "pyright-1.1.403-py3-none-any.whl", hash = "sha256:c0eeca5aa76cbef3fcc271259bbd785753c7ad7bcac99a9162b4c4c7daed23b3"}, + {file = "pyright-1.1.403.tar.gz", hash = "sha256:3ab69b9f41c67fb5bbb4d7a36243256f0d549ed3608678d381d5f51863921104"}, ] [package.dependencies] @@ -2831,7 +2602,7 @@ version = "8.4.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7"}, {file = "pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c"}, @@ -2849,14 +2620,14 @@ dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests [[package]] name = "pytest-asyncio" -version = "1.0.0" +version = "1.1.0" description = "Pytest support for asyncio" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "pytest_asyncio-1.0.0-py3-none-any.whl", hash = "sha256:4f024da9f1ef945e680dc68610b52550e36590a67fd31bb3b4943979a1f90ef3"}, - {file = "pytest_asyncio-1.0.0.tar.gz", hash = "sha256:d15463d13f4456e1ead2594520216b225a16f781e144f8fdf6c5bb4667c48b3f"}, + {file = "pytest_asyncio-1.1.0-py3-none-any.whl", hash = "sha256:5fe2d69607b0bd75c656d1211f969cadba035030156745ee09e7d71740e58ecf"}, + {file = "pytest_asyncio-1.1.0.tar.gz", hash = "sha256:796aa822981e01b68c12e4827b8697108f7205020f24b5793b3c41555dab68ea"}, ] [package.dependencies] @@ -2888,14 +2659,14 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] [[package]] name = "pytest-docker" -version = "3.2.2" +version = "3.2.3" description = "Simple pytest fixtures for Docker and Docker Compose based tests" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "pytest_docker-3.2.2-py3-none-any.whl", hash = "sha256:2926033d48a10de611070fce17f6e67b9e81af2d8ccc59debbbf39872b8ebef9"}, - {file = "pytest_docker-3.2.2.tar.gz", hash = "sha256:58ce79f3173209634bfff8ccaed2ce5593463d5272325c912e1b52a53154f452"}, + {file = "pytest_docker-3.2.3-py3-none-any.whl", hash = "sha256:f973c35e6f2b674c8fc87e8b3354b02c15866a21994c0841a338c240a05de1eb"}, + {file = "pytest_docker-3.2.3.tar.gz", hash = "sha256:26a1c711d99ef01e86e7c9c007f69641552c1554df4fccb065b35581cca24206"}, ] [package.dependencies] @@ -2949,22 +2720,6 @@ toml = ">=0.10.2,<0.11.0" typing-extensions = ">=4.11.0,<5.0.0" wheel = ">=0.37.1,<0.39.0" -[[package]] -name = "pytest-subtests" -version = "0.14.2" -description = "unittest subTest() support and subtests fixture" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pytest_subtests-0.14.2-py3-none-any.whl", hash = "sha256:8da0787c994ab372a13a0ad7d390533ad2e4385cac167b3ac501258c885d0b66"}, - {file = "pytest_subtests-0.14.2.tar.gz", hash = "sha256:7154a8665fd528ee70a76d00216a44d139dc3c9c83521a0f779f7b0ad4f800de"}, -] - -[package.dependencies] -attrs = ">=19.2.0" -pytest = ">=7.4" - [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -3016,7 +2771,7 @@ version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, @@ -3079,7 +2834,7 @@ version = "0.36.2" description = "JSON Referencing + Python" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0"}, {file = "referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa"}, @@ -3095,7 +2850,7 @@ version = "2.32.4" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c"}, {file = "requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422"}, @@ -3137,7 +2892,7 @@ version = "0.1.4" description = "A pure python RFC3339 validator" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa"}, {file = "rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b"}, @@ -3146,25 +2901,13 @@ files = [ [package.dependencies] six = "*" -[[package]] -name = "rfc3987" -version = "1.3.8" -description = "Parsing and validation of URIs (RFC 3986) and IRIs (RFC 3987)" -optional = false -python-versions = "*" -groups = ["main"] -files = [ - {file = "rfc3987-1.3.8-py2.py3-none-any.whl", hash = "sha256:10702b1e51e5658843460b189b185c0366d2cf4cff716f13111b0ea9fd2dce53"}, - {file = "rfc3987-1.3.8.tar.gz", hash = "sha256:d3c4d257a560d544e9826b38bc81db676890c79ab9d7ac92b39c7a253d5ca733"}, -] - [[package]] name = "rich" version = "14.0.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.8.0" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0"}, {file = "rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725"}, @@ -3183,7 +2926,7 @@ version = "0.25.1" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "rpds_py-0.25.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f4ad628b5174d5315761b67f212774a32f5bad5e61396d38108bd801c0a8f5d9"}, {file = "rpds_py-0.25.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8c742af695f7525e559c16f1562cf2323db0e3f0fbdcabdf6865b095256b2d40"}, @@ -3319,82 +3062,6 @@ files = [ [package.dependencies] pyasn1 = ">=0.1.3" -[[package]] -name = "ruamel-yaml" -version = "0.18.14" -description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "ruamel.yaml-0.18.14-py3-none-any.whl", hash = "sha256:710ff198bb53da66718c7db27eec4fbcc9aa6ca7204e4c1df2f282b6fe5eb6b2"}, - {file = "ruamel.yaml-0.18.14.tar.gz", hash = "sha256:7227b76aaec364df15936730efbf7d72b30c0b79b1d578bbb8e3dcb2d81f52b7"}, -] - -[package.dependencies] -"ruamel.yaml.clib" = {version = ">=0.2.7", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.14\""} - -[package.extras] -docs = ["mercurial (>5.7)", "ryd"] -jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] - -[[package]] -name = "ruamel-yaml-clib" -version = "0.2.12" -description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" -optional = false -python-versions = ">=3.9" -groups = ["main"] -markers = "platform_python_implementation == \"CPython\" and python_version < \"3.14\"" -files = [ - {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:11f891336688faf5156a36293a9c362bdc7c88f03a8a027c2c1d8e0bcde998e5"}, - {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a606ef75a60ecf3d924613892cc603b154178ee25abb3055db5062da811fd969"}, - {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd5415dded15c3822597455bc02bcd66e81ef8b7a48cb71a33628fc9fdde39df"}, - {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f66efbc1caa63c088dead1c4170d148eabc9b80d95fb75b6c92ac0aad2437d76"}, - {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:22353049ba4181685023b25b5b51a574bce33e7f51c759371a7422dcae5402a6"}, - {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:932205970b9f9991b34f55136be327501903f7c66830e9760a8ffb15b07f05cd"}, - {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a52d48f4e7bf9005e8f0a89209bf9a73f7190ddf0489eee5eb51377385f59f2a"}, - {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-win32.whl", hash = "sha256:3eac5a91891ceb88138c113f9db04f3cebdae277f5d44eaa3651a4f573e6a5da"}, - {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-win_amd64.whl", hash = "sha256:ab007f2f5a87bd08ab1499bdf96f3d5c6ad4dcfa364884cb4549aa0154b13a28"}, - {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:4a6679521a58256a90b0d89e03992c15144c5f3858f40d7c18886023d7943db6"}, - {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:d84318609196d6bd6da0edfa25cedfbabd8dbde5140a0a23af29ad4b8f91fb1e"}, - {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb43a269eb827806502c7c8efb7ae7e9e9d0573257a46e8e952f4d4caba4f31e"}, - {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:811ea1594b8a0fb466172c384267a4e5e367298af6b228931f273b111f17ef52"}, - {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cf12567a7b565cbf65d438dec6cfbe2917d3c1bdddfce84a9930b7d35ea59642"}, - {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7dd5adc8b930b12c8fc5b99e2d535a09889941aa0d0bd06f4749e9a9397c71d2"}, - {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1492a6051dab8d912fc2adeef0e8c72216b24d57bd896ea607cb90bb0c4981d3"}, - {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-win32.whl", hash = "sha256:bd0a08f0bab19093c54e18a14a10b4322e1eacc5217056f3c063bd2f59853ce4"}, - {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-win_amd64.whl", hash = "sha256:a274fb2cb086c7a3dea4322ec27f4cb5cc4b6298adb583ab0e211a4682f241eb"}, - {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632"}, - {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:943f32bc9dedb3abff9879edc134901df92cfce2c3d5c9348f172f62eb2d771d"}, - {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95c3829bb364fdb8e0332c9931ecf57d9be3519241323c5274bd82f709cebc0c"}, - {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd"}, - {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31"}, - {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680"}, - {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b82a7c94a498853aa0b272fd5bc67f29008da798d4f93a2f9f289feb8426a58d"}, - {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-win32.whl", hash = "sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5"}, - {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-win_amd64.whl", hash = "sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4"}, - {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4c8c5d82f50bb53986a5e02d1b3092b03622c02c2eb78e29bec33fd9593bae1a"}, - {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:e7e3736715fbf53e9be2a79eb4db68e4ed857017344d697e8b9749444ae57475"}, - {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7e75b4965e1d4690e93021adfcecccbca7d61c7bddd8e22406ef2ff20d74ef"}, - {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96777d473c05ee3e5e3c3e999f5d23c6f4ec5b0c38c098b3a5229085f74236c6"}, - {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:3bc2a80e6420ca8b7d3590791e2dfc709c88ab9152c00eeb511c9875ce5778bf"}, - {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e188d2699864c11c36cdfdada94d781fd5d6b0071cd9c427bceb08ad3d7c70e1"}, - {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f6f3eac23941b32afccc23081e1f50612bdbe4e982012ef4f5797986828cd01"}, - {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-win32.whl", hash = "sha256:6442cb36270b3afb1b4951f060eccca1ce49f3d087ca1ca4563a6eb479cb3de6"}, - {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-win_amd64.whl", hash = "sha256:e5b8daf27af0b90da7bb903a876477a9e6d7270be6146906b276605997c7e9a3"}, - {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:fc4b630cd3fa2cf7fce38afa91d7cfe844a9f75d7f0f36393fa98815e911d987"}, - {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bc5f1e1c28e966d61d2519f2a3d451ba989f9ea0f2307de7bc45baa526de9e45"}, - {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a0e060aace4c24dcaf71023bbd7d42674e3b230f7e7b97317baf1e953e5b519"}, - {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2f1c3765db32be59d18ab3953f43ab62a761327aafc1594a2a1fbe038b8b8a7"}, - {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d85252669dc32f98ebcd5d36768f5d4faeaeaa2d655ac0473be490ecdae3c285"}, - {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e143ada795c341b56de9418c58d028989093ee611aa27ffb9b7f609c00d813ed"}, - {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2c59aa6170b990d8d2719323e628aaf36f3bfbc1c26279c0eeeb24d05d2d11c7"}, - {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-win32.whl", hash = "sha256:beffaed67936fbbeffd10966a4eb53c402fafd3d6833770516bf7314bc6ffa12"}, - {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-win_amd64.whl", hash = "sha256:040ae85536960525ea62868b642bdb0c2cc6021c9f9d507810c0c604e66f5a7b"}, - {file = "ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f"}, -] - [[package]] name = "ruff" version = "0.11.13" @@ -3441,47 +3108,6 @@ botocore = ">=1.37.4,<2.0a.0" [package.extras] crt = ["botocore[crt] (>=1.37.4,<2.0a.0)"] -[[package]] -name = "schemathesis" -version = "4.0.21" -description = "Property-based testing framework for Open API and GraphQL based apps" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "schemathesis-4.0.21-py3-none-any.whl", hash = "sha256:329ad303f0d119e3d8f187e48479027c9de27c6d04cb7112fc15867e5486c08d"}, - {file = "schemathesis-4.0.21.tar.gz", hash = "sha256:2004f6b0bba6508ec48377d00d22aca2eeaa0d1fc9613b6f0c8dd1f52c69c1a8"}, -] - -[package.dependencies] -backoff = ">=2.1.2,<3.0" -click = ">=8.0,<9" -colorama = ">=0.4,<1.0" -harfile = ">=0.3.0,<1.0" -httpx = ">=0.22.0,<1.0" -hypothesis = ">=6.108.0,<7" -hypothesis-graphql = ">=0.11.1,<1" -hypothesis-jsonschema = ">=0.23.1,<0.24" -jsonschema = {version = ">=4.18.0,<5.0", extras = ["format"]} -junit-xml = ">=1.9,<2.0" -pyrate-limiter = ">=3.0,<4.0" -pytest = ">=8,<9" -pytest-subtests = ">=0.11,<0.15.0" -pyyaml = ">=5.1,<7.0" -requests = ">=2.22,<3" -rich = ">=13.9.4" -starlette-testclient = ">=0.4.1,<1" -tomli = ">=2.2.1" -typing-extensions = ">=4.12.2" -werkzeug = ">=0.16.0,<4" - -[package.extras] -bench = ["pytest-codspeed (==2.2.1)"] -cov = ["coverage-enable-subprocess", "coverage[toml] (>=5.3)"] -dev = ["aiohttp (>=3.9.1,<4.0)", "coverage (>=6)", "coverage-enable-subprocess", "coverage[toml] (>=5.3)", "fastapi (>=0.86.0)", "flask (>=2.1.1,<3.0)", "hypothesis-openapi (>=0.2,<1) ; python_version >= \"3.10\"", "mkdocs-material", "mkdocstrings[python]", "pydantic (>=1.10.2)", "pytest-asyncio (>=0.18.0,<1.0)", "pytest-codspeed (==2.2.1)", "pytest-httpserver (>=1.0,<2.0)", "pytest-mock (>=3.7.0,<4.0)", "pytest-trio (>=0.8,<1.0)", "pytest-xdist (>=3,<4.0)", "strawberry-graphql[fastapi] (>=0.109.0)", "syrupy (>=2,<5.0)", "tomli-w (>=1.2.0)", "trustme (>=0.9.0,<1.0)"] -docs = ["mkdocs-material", "mkdocstrings[python]"] -tests = ["aiohttp (>=3.9.1,<4.0)", "coverage (>=6)", "fastapi (>=0.86.0)", "flask (>=2.1.1,<3.0)", "hypothesis-openapi (>=0.2,<1) ; python_version >= \"3.10\"", "pydantic (>=1.10.2)", "pytest-asyncio (>=0.18.0,<1.0)", "pytest-httpserver (>=1.0,<2.0)", "pytest-mock (>=3.7.0,<4.0)", "pytest-trio (>=0.8,<1.0)", "pytest-xdist (>=3,<4.0)", "strawberry-graphql[fastapi] (>=0.109.0)", "syrupy (>=2,<5.0)", "tomli-w (>=1.2.0)", "trustme (>=0.9.0,<1.0)"] - [[package]] name = "semver" version = "3.0.4" @@ -3530,18 +3156,6 @@ files = [ {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, ] -[[package]] -name = "sortedcontainers" -version = "2.4.0" -description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" -optional = false -python-versions = "*" -groups = ["main"] -files = [ - {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, - {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, -] - [[package]] name = "soupsieve" version = "2.7" @@ -3575,40 +3189,6 @@ docs = ["furo", "myst-parser", "prometheus-client", "sphinx (>=7.2.2)", "sphinx- tests = ["anyio", "dirty-equals", "pytest"] typing = ["mypy (>=1.4)"] -[[package]] -name = "starlette" -version = "0.47.2" -description = "The little ASGI library that shines." -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "starlette-0.47.2-py3-none-any.whl", hash = "sha256:c5847e96134e5c5371ee9fac6fdf1a67336d5815e09eb2a01fdb57a351ef915b"}, - {file = "starlette-0.47.2.tar.gz", hash = "sha256:6ae9aa5db235e4846decc1e7b79c4f346adf41e9777aebeb49dfd09bbd7023d8"}, -] - -[package.dependencies] -anyio = ">=3.6.2,<5" - -[package.extras] -full = ["httpx (>=0.27.0,<0.29.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.18)", "pyyaml"] - -[[package]] -name = "starlette-testclient" -version = "0.4.1" -description = "A backport of Starlette TestClient using requests! โช๏ธ" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "starlette_testclient-0.4.1-py3-none-any.whl", hash = "sha256:dcf0eb237dc47f062ef5925f98330af46f67e547cb587119c9ae78c17ae6c1d1"}, - {file = "starlette_testclient-0.4.1.tar.gz", hash = "sha256:9e993ffe12fab45606116257813986612262fe15c1bb6dc9e39cc68693ac1fc5"}, -] - -[package.dependencies] -requests = "*" -starlette = ">=0.20.1" - [[package]] name = "tabulate" version = "0.9.0" @@ -3663,60 +3243,6 @@ files = [ {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] -[[package]] -name = "tomli" -version = "2.2.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, - {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, - {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, - {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, - {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, - {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, - {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, - {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, - {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, - {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, - {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, - {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, - {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, - {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, -] - -[[package]] -name = "types-python-dateutil" -version = "2.9.0.20250708" -description = "Typing stubs for python-dateutil" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "types_python_dateutil-2.9.0.20250708-py3-none-any.whl", hash = "sha256:4d6d0cc1cc4d24a2dc3816024e502564094497b713f7befda4d5bc7a8e3fd21f"}, - {file = "types_python_dateutil-2.9.0.20250708.tar.gz", hash = "sha256:ccdbd75dab2d6c9696c350579f34cffe2c281e4c5f27a585b2a2438dd1d5c8ab"}, -] - [[package]] name = "typing-extensions" version = "4.14.0" @@ -3756,21 +3282,6 @@ files = [ {file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"}, ] -[[package]] -name = "uri-template" -version = "1.3.0" -description = "RFC 6570 URI Template Processor" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "uri-template-1.3.0.tar.gz", hash = "sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7"}, - {file = "uri_template-1.3.0-py3-none-any.whl", hash = "sha256:a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363"}, -] - -[package.extras] -dev = ["flake8", "flake8-annotations", "flake8-bandit", "flake8-bugbear", "flake8-commas", "flake8-comprehensions", "flake8-continuation", "flake8-datetimez", "flake8-docstrings", "flake8-import-order", "flake8-literal", "flake8-modern-annotations", "flake8-noqa", "flake8-pyproject", "flake8-requirements", "flake8-typechecking-import", "flake8-use-fstring", "mypy", "pep8-naming", "types-PyYAML"] - [[package]] name = "urllib3" version = "2.5.0" @@ -3789,18 +3300,6 @@ h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] -[[package]] -name = "webcolors" -version = "24.11.1" -description = "A library for working with the color formats defined by HTML and CSS." -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "webcolors-24.11.1-py3-none-any.whl", hash = "sha256:515291393b4cdf0eb19c155749a096f779f7d909f7cceea072791cb9095b92e9"}, - {file = "webcolors-24.11.1.tar.gz", hash = "sha256:ecb3d768f32202af770477b8b65f318fa4f566c22948673a977b00d589dd80f6"}, -] - [[package]] name = "werkzeug" version = "3.1.3" @@ -3882,7 +3381,7 @@ version = "1.17.2" description = "Module for decorators, wrappers and monkey patching." optional = false python-versions = ">=3.8" -groups = ["dev"] +groups = ["main", "dev"] files = [ {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984"}, {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22"}, @@ -4099,4 +3598,4 @@ propcache = ">=0.2.1" [metadata] lock-version = "2.1" python-versions = "^3.13" -content-hash = "8a3beb3f554d833b708cd02fe37440bb95158f42a995715351e2293e124ad087" +content-hash = "8e6e98116871c55e44eddc6f7312d0e01b268dfdb0b410d55d9a30035ac98a07" diff --git a/pyproject.toml b/pyproject.toml index 9931737ff..2de1b730d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,9 +25,9 @@ flask = {extras = ["async"], version = "^3.1.1"} httpx = "^0.28.1" yarl = "^1.18.3" pydantic = "^2.11.7" -asgiref = "^3.8.1" +asgiref = "^3.9.1" boto3 = "^1.37.3" -botocore = "^1.38.40" +botocore = "^1.38.46" eval-type-backport = "^0.2.2" mangum = "^0.19.0" wireup = "^2.0.0" @@ -35,31 +35,29 @@ python-json-logger = "^3.3.0" fhir-resources = "^8.0.0" python-dateutil = "^2.9.0" pyhamcrest = "^2.1.0" -schemathesis = "^4.0.21" -prance = "^25.4.8.0" -jsonschema = "^4.25.0" +aws-xray-sdk = "2.14.0" [tool.poetry.group.dev.dependencies] ruff = "^0.11.13" docopt = "^0.6.2" jsonpath-rw = "^1.4.0" semver = "^3.0.4" -gitpython = "^3.1.44" +gitpython = "^3.1.45" pytest = "^8.4.1" -pytest-asyncio = "^1.0.0" +pytest-asyncio = "^1.1.0" pytest-cov = "^6.0.0" pytest-nhsd-apim = "^5.0.0" -aiohttp = "^3.12.13" +aiohttp = "^3.12.15" awscli = "^1.37.24" awscli-local = "^0.22.0" polyfactory = "^2.20.0" -pyright = "^1.1.394" +pyright = "^1.1.403" brunns-matchers = "^2.9.0" -localstack = "^4.1.1" -pytest-docker = "^3.2.0" +localstack = "^4.6.0" +pytest-docker = "^3.2.3" stamina = "^25.1.0" pytest-freezer = "^0.4.9" -moto = "^5.1.5" +moto = "^5.1.9" requests = "^2.31.0" jsonschema = "^4.24.0" behave = "^1.2.6" diff --git a/scripts/config/gitleaks.toml b/scripts/config/gitleaks.toml index 175e20678..66a3d7e94 100644 --- a/scripts/config/gitleaks.toml +++ b/scripts/config/gitleaks.toml @@ -17,4 +17,4 @@ regexes = [ [allowlist] paths = ['''.terraform.lock.hcl''', '''poetry.lock''', '''yarn.lock'''] -stopwords = ['''dummy_key''', '''dummy_secret''', '''192.0.0.1'''] +stopwords = ['''dummy_key''', '''dummy_secret''', '''192.0.0.1''', '''prance = "^25.4.8.0"''', '''25.4.8.0'''] diff --git a/scripts/dependencies.sh b/scripts/dependencies.sh index 1977ab67a..9a35eb4e2 100755 --- a/scripts/dependencies.sh +++ b/scripts/dependencies.sh @@ -2,11 +2,19 @@ set -euo pipefail +# Use the python from PATH (set by setup-python) +PYTHON_BIN="${PYTHON_BIN:-python}" + + if ! [ -x "$(command -v poetry)" ]; then if ! [ -x "$(command -v pipx)" ]; then - python -m pip install --user pipx --isolated - python -m pipx ensurepath + $PYTHON_BIN -m pip install --user pipx --isolated + $PYTHON_BIN -m pipx ensurepath fi - pipx install poetry + pipx install poetry --python $PYTHON_BIN fi + +# Ensure poetry uses the correct python environment +poetry env use $PYTHON_BIN + poetry self add poetry-plugin-lambda-build@2.1.0 poetry-plugin-export@1.9.0 diff --git a/src/eligibility_signposting_api/app.py b/src/eligibility_signposting_api/app.py index 9073bb779..ffa3cd14b 100644 --- a/src/eligibility_signposting_api/app.py +++ b/src/eligibility_signposting_api/app.py @@ -1,17 +1,25 @@ import logging +import os from typing import Any import wireup.integration.flask from asgiref.wsgi import WsgiToAsgi +from aws_xray_sdk.core import patch_all from flask import Flask from mangum import Mangum from mangum.types import LambdaContext, LambdaEvent from eligibility_signposting_api import audit, repos, services -from eligibility_signposting_api.config.config import config, init_logging -from eligibility_signposting_api.error_handler import handle_exception +from eligibility_signposting_api.common.error_handler import handle_exception +from eligibility_signposting_api.common.request_validator import validate_request_params +from eligibility_signposting_api.config.config import config +from eligibility_signposting_api.logging.logs_helper import log_request_ids_from_headers +from eligibility_signposting_api.logging.logs_manager import add_lambda_request_id_to_logger, init_logging +from eligibility_signposting_api.logging.tracing_helper import tracing_setup from eligibility_signposting_api.views import eligibility_blueprint -from eligibility_signposting_api.wrapper import validate_request_params + +if os.getenv("ENABLE_XRAY_PATCHING"): + patch_all() init_logging() logger = logging.getLogger(__name__) @@ -23,12 +31,16 @@ def main() -> None: # pragma: no cover app.run(debug=config()["log_level"] == logging.DEBUG) +@add_lambda_request_id_to_logger() +@tracing_setup() +@log_request_ids_from_headers() @validate_request_params() def lambda_handler(event: LambdaEvent, context: LambdaContext) -> dict[str, Any]: # pragma: no cover """Run the Flask app as an AWS Lambda.""" app = create_app() app.debug = config()["log_level"] == logging.DEBUG handler = Mangum(WsgiToAsgi(app), lifespan="off") + handler.config["text_mime_types"].append("application/fhir+json") return handler(event, context) diff --git a/src/eligibility_signposting_api/audit/audit_context.py b/src/eligibility_signposting_api/audit/audit_context.py index b52edbe9b..58bb49578 100644 --- a/src/eligibility_signposting_api/audit/audit_context.py +++ b/src/eligibility_signposting_api/audit/audit_context.py @@ -18,14 +18,16 @@ RequestAuditQueryParams, ) from eligibility_signposting_api.audit.audit_service import AuditService -from eligibility_signposting_api.model.eligibility import ( +from eligibility_signposting_api.model.eligibility_status import ( + BestIterationResult, CohortGroupResult, ConditionName, IterationResult, + MatchedActionDetail, + Reason, Status, SuggestedAction, ) -from eligibility_signposting_api.model.rules import CampaignID, CampaignVersion, Iteration, RuleName, RulePriority logger = logging.getLogger(__name__) @@ -59,17 +61,15 @@ def add_request_details(request: Request) -> None: @staticmethod def append_audit_condition( - suggested_actions: list[SuggestedAction] | None, condition_name: ConditionName, - best_results: tuple[Iteration | None, IterationResult | None, dict[str, CohortGroupResult] | None], - campaign_details: tuple[CampaignID | None, CampaignVersion | None], - redirect_rule_details: tuple[RulePriority | None, RuleName | None], + best_iteration_result: BestIterationResult, + action_detail: MatchedActionDetail, + cohort_results: list[CohortGroupResult], ) -> None: - audit_eligibility_cohorts, audit_eligibility_cohort_groups = [], [] - audit_filter_rule, audit_suitability_rule, audit_redirect_rule = None, None, None - best_active_iteration = best_results[0] - best_candidate = best_results[1] - best_cohort_results = best_results[2] + audit_eligibility_cohorts, audit_eligibility_cohort_groups, audit_actions = [], [], [] + best_active_iteration = best_iteration_result.active_iteration + best_candidate = best_iteration_result.iteration_result + best_cohort_results = best_iteration_result.cohort_results if best_cohort_results: for cohort_label, result in sorted(best_cohort_results.items(), key=lambda item: item[1].cohort_code): @@ -84,35 +84,53 @@ def append_audit_condition( ) ) - if result.audit_rules and best_candidate: - audit_filter_rule = AuditContext.create_audit_filter_rule(best_candidate, result) - audit_suitability_rule = AuditContext.create_audit_suitability_rule(best_candidate, result) + filter_audit_rules, suitability_audit_rules = [], [] + for result in cohort_results: + if result.status.name == Status.not_eligible.name: + filter_audit_rules.extend(result.audit_rules) + if result.status.name == Status.not_actionable.name: + suitability_audit_rules.extend(result.audit_rules) - if best_candidate and best_candidate.status and best_candidate.status.name == Status.actionable.name: - audit_redirect_rule = AuditRedirectRule( - rule_priority=str(redirect_rule_details[0]), rule_name=redirect_rule_details[1] - ) + audit_filter_rule = AuditContext.create_audit_filter_rule(filter_audit_rules) + audit_suitability_rule = AuditContext.create_audit_suitability_rule(suitability_audit_rules) + + audit_action_rule = AuditContext.add_rule_name_and_priority_to_audit(best_candidate, action_detail) - audit_actions = AuditContext.create_audit_actions(suggested_actions) + audit_actions = AuditContext.create_audit_actions(action_detail.actions) audit_condition = AuditCondition( - campaign_id=campaign_details[0], - campaign_version=campaign_details[1], + campaign_id=best_iteration_result.campaign_id, + campaign_version=best_iteration_result.campaign_version, iteration_id=best_active_iteration.id if best_active_iteration else None, iteration_version=best_active_iteration.version if best_active_iteration else None, condition_name=condition_name, status=best_candidate.status.name if best_candidate and best_candidate.status else None, - status_text=best_candidate.status.name if best_candidate and best_candidate.status else None, + status_text=best_candidate.status.get_status_text(condition_name) if best_candidate else None, eligibility_cohorts=audit_eligibility_cohorts, eligibility_cohort_groups=audit_eligibility_cohort_groups, filter_rules=audit_filter_rule, suitability_rules=audit_suitability_rule, - action_rule=audit_redirect_rule, + action_rule=audit_action_rule, actions=audit_actions, ) g.audit_log.response.condition.append(audit_condition) + @staticmethod + def add_rule_name_and_priority_to_audit( + best_candidate: IterationResult | None, + action_detail: MatchedActionDetail, + ) -> AuditRedirectRule | None: + audit_action_rule = None + if best_candidate and best_candidate.status: + if action_detail.rule_priority is None and action_detail.rule_name is None: + audit_action_rule = None + else: + audit_action_rule = AuditRedirectRule( + rule_priority=str(action_detail.rule_priority), rule_name=action_detail.rule_name + ) + return audit_action_rule + @staticmethod def add_response_details(response_id: UUID, last_updated: datetime) -> None: g.audit_log.response.response_id = response_id @@ -142,24 +160,46 @@ def create_audit_actions(suggested_actions: list[SuggestedAction] | None) -> lis return audit_actions @staticmethod - def create_audit_suitability_rule( - best_candidate: IterationResult, result: CohortGroupResult - ) -> AuditSuitabilityRule | None: - audit_suitability_rule = None - if best_candidate.status and best_candidate.status.name == Status.not_actionable.name: - audit_suitability_rule = AuditSuitabilityRule( - rule_priority=result.audit_rules[0].rule_priority, - rule_name=result.audit_rules[0].rule_name, - rule_message=result.audit_rules[0].rule_description, + def create_audit_suitability_rule(reasons: list[Reason]) -> list[AuditSuitabilityRule] | None: + unique_reasons = AuditContext.deduplicate_reasons(reasons) + + suitability_audit = [ + AuditSuitabilityRule( + rule_priority=rule.rule_priority, + rule_name=rule.rule_name, + rule_message=rule.rule_description, ) - return audit_suitability_rule + for rule in unique_reasons + ] + + return suitability_audit if suitability_audit else None @staticmethod - def create_audit_filter_rule(best_candidate: IterationResult, result: CohortGroupResult) -> AuditFilterRule | None: - audit_filter_rule = None - if best_candidate.status and best_candidate.status.name == Status.not_eligible.name: - audit_filter_rule = AuditFilterRule( - rule_priority=result.audit_rules[0].rule_priority, - rule_name=result.audit_rules[0].rule_name, - ) - return audit_filter_rule + def create_audit_filter_rule(reasons: list[Reason]) -> list[AuditFilterRule] | None: + unique_reasons = AuditContext.deduplicate_reasons(reasons) + + filter_audit = [ + AuditFilterRule(rule_priority=rule.rule_priority, rule_name=rule.rule_name) for rule in unique_reasons + ] + + return filter_audit if len(filter_audit) > 0 else None + + @staticmethod + def deduplicate_reasons(reasons: list[Reason]) -> list[Reason]: + unique_rule_codes = set() + deduplicated_reasons = [] + + for reason in reasons: + if reason.rule_name not in unique_rule_codes and reason.rule_description: + unique_rule_codes.add(reason.rule_name) + deduplicated_reasons.append( + Reason( + reason.rule_type, + reason.rule_name, + reason.rule_priority, + reason.rule_description, + reason.matcher_matched, + ) + ) + + return deduplicated_reasons diff --git a/src/eligibility_signposting_api/audit/audit_models.py b/src/eligibility_signposting_api/audit/audit_models.py index 17467130f..2f1b0ee2d 100644 --- a/src/eligibility_signposting_api/audit/audit_models.py +++ b/src/eligibility_signposting_api/audit/audit_models.py @@ -70,23 +70,23 @@ class AuditAction(CamelCaseBaseModel): class AuditCondition(CamelCaseBaseModel): campaign_id: str | None = None - campaign_version: str | None = None + campaign_version: int | None = None iteration_id: str | None = None - iteration_version: str | None = None + iteration_version: int | None = None condition_name: str | None = None status: str | None = None status_text: str | None = None eligibility_cohorts: list[AuditEligibilityCohorts] | None = None eligibility_cohort_groups: list[AuditEligibilityCohortGroups] | None = None - filter_rules: AuditFilterRule | None = None - suitability_rules: AuditSuitabilityRule | None = None + filter_rules: list[AuditFilterRule] | None = None + suitability_rules: list[AuditSuitabilityRule] | None = None action_rule: AuditRedirectRule | None = None actions: list[AuditAction] | None = Field(default_factory=list) class ResponseAuditData(CamelCaseBaseModel): response_id: UUID | None = None - last_updated: str | None = None + last_updated: datetime | None = None condition: list[AuditCondition] = Field(default_factory=list) diff --git a/src/eligibility_signposting_api/services/rules/__init__.py b/src/eligibility_signposting_api/common/__init__.py similarity index 100% rename from src/eligibility_signposting_api/services/rules/__init__.py rename to src/eligibility_signposting_api/common/__init__.py diff --git a/src/eligibility_signposting_api/common/api-error-response-readme.md b/src/eligibility_signposting_api/common/api-error-response-readme.md new file mode 100644 index 000000000..08473002c --- /dev/null +++ b/src/eligibility_signposting_api/common/api-error-response-readme.md @@ -0,0 +1,88 @@ +# How to Use the API Error Response Module + +This document outlines how to use the `api_error_response.py` module for standardized error handling within the Eligibility Signposting API. The module ensures that all API errors are consistent, logged appropriately, and conform to the FHIR `OperationOutcome` standard. + +## Core Concepts + +The error handling mechanism is built around the class `APIErrorResponse`. + +1. **`APIErrorResponse` Class**: This class is a constructor for a specific type of error. An instance of this class holds configuration for an error, such as the `HTTPStatus`, severity, and various FHIR-specific codes. +2. **Pre-defined Error Instances**: The module defines several singleton instances of for common, application-specific errors. Examples include: + - `INVALID_CATEGORY_ERROR` + - `NHS_NUMBER_MISMATCH_ERROR` + - `INTERNAL_SERVER_ERROR` +3. **`log_and_generate_response()` Method**: This is the primary method to be used. When called on an `APIErrorResponse` instance, it performs two actions: + - Logs the error with a detailed internal message. + - Generates a complete HTTP response dictionary (`statusCode`, `headers`, `body`) containing a FHIR-compliant `OperationOutcome` payload. + +## How to Use + +The primary way to handle errors is to import a pre-defined error object from `api_error_response.py` and call its `log_and_generate_response()` method. + +### 1. Handling Specific, Known Errors + +For handling validation failures or other expected error conditions, use one of the pre-defined error instances. +The `wrapper.py` module uses this pattern to validate query parameters. If a parameter is invalid, it calls the corresponding error function. + +#### Example: Invalid "category" parameter + +``` python +# wrapper.py + +from eligibility_signposting_api.api_error_response import INVALID_CATEGORY_ERROR + +def get_category_error_response(category: str) -> dict[str, Any]: + """Generates an error response for an invalid category.""" + + return INVALID_CATEGORY_ERROR.log_and_generate_response( + log_message=f"Invalid category query param: '{category}'", + diagnostics=f"{category} is not a category that is supported by the API", + location_param="category" + ) +``` + +#### Key Parameters for `log_and_generate_response()` + +- `log_message`: A detailed message for internal logging. This should contain specific information useful for debugging. +- `diagnostics`: The user-facing error message that will be included in the API response body. +- `location_param` (optional): The name of the parameter that caused the error. This helps pinpoint the issue for API consumers. + +### 2. Handling Unexpected Exceptions (Global Error Handler) + +For unexpected errors, a global exception handler in `error_handler.py` catches any unhandled exception and returns a generic 500 Internal Server Error. This prevents sensitive information from leaking in stack traces. + +``` python +# error_handler.py + +from eligibility_signposting_api.api_error_response import INTERNAL_SERVER_ERROR + +def handle_exception(e: Exception) -> ResponseReturnValue | HTTPException: + # Generate a generic, safe response for the client + response = INTERNAL_SERVER_ERROR.log_and_generate_response( + log_message=f"An unexpected error occurred: {traceback.format_exception(e)}", + diagnostics="An unexpected error occurred." + ) + return make_response(response.get("body"), response.get("statusCode"), response.get("headers")) +``` + +### 3. Creating New Error Types + +If a new, reusable error condition is identified, you should add a new instance of `APIErrorResponse` to `api_error_response.py` +Follow the existing pattern: + +``` python +# api_error_response.py + +# ... (other error definitions) + +SOME_NEW_ERROR = APIErrorResponse( + status_code=HTTPStatus.BAD_REQUEST, + fhir_issue_code=FHIRIssueCode.VALUE, + fhir_issue_severity=FHIRIssueSeverity.ERROR, + fhir_coding_system=FHIR_SPINE_ERROR_CODE_SYSTEM, + fhir_error_code=FHIRSpineErrorCode.INVALID_PARAMETER, + fhir_display_message="A new specific error message for display", +) +``` + +By centralizing error definitions, we ensure that the API provides a consistent and predictable experience for its consumers. diff --git a/src/eligibility_signposting_api/api_error_response.py b/src/eligibility_signposting_api/common/api_error_response.py similarity index 85% rename from src/eligibility_signposting_api/api_error_response.py rename to src/eligibility_signposting_api/common/api_error_response.py index 9b81a740c..afdbfe962 100644 --- a/src/eligibility_signposting_api/api_error_response.py +++ b/src/eligibility_signposting_api/common/api_error_response.py @@ -22,29 +22,30 @@ class FHIRIssueCode(str, Enum): FORBIDDEN = "forbidden" PROCESSING = "processing" VALUE = "value" + INVALID = "invalid" class FHIRSpineErrorCode(str, Enum): - INVALID_NHS_NUMBER = "INVALID_NHS_NUMBER" + ACCESS_DENIED = "ACCESS_DENIED" INVALID_PARAMETER = "INVALID_PARAMETER" + BAD_REQUEST = "BAD_REQUEST" INTERNAL_SERVER_ERROR = "INTERNAL_SERVER_ERROR" REFERENCE_NOT_FOUND = "REFERENCE_NOT_FOUND" class APIErrorResponse: - def __init__( # noqa: PLR0913 + def __init__( self, status_code: HTTPStatus, fhir_issue_code: FHIRIssueCode, fhir_issue_severity: FHIRIssueSeverity, - fhir_coding_system: str, fhir_error_code: str, fhir_display_message: str, ) -> None: self.status_code = status_code self.fhir_issue_code = fhir_issue_code self.fhir_issue_severity = fhir_issue_severity - self.fhir_coding_system = fhir_coding_system + self.fhir_coding_system = "https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1" self.fhir_error_code = fhir_error_code self.fhir_display_message = fhir_display_message @@ -94,7 +95,6 @@ def log_and_generate_response( status_code=HTTPStatus.UNPROCESSABLE_ENTITY, fhir_issue_code=FHIRIssueCode.VALUE, fhir_issue_severity=FHIRIssueSeverity.ERROR, - fhir_coding_system="https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1", fhir_error_code=FHIRSpineErrorCode.INVALID_PARAMETER, fhir_display_message="The supplied value was not recognised by the API.", ) @@ -103,7 +103,6 @@ def log_and_generate_response( status_code=HTTPStatus.UNPROCESSABLE_ENTITY, fhir_issue_code=FHIRIssueCode.VALUE, fhir_issue_severity=FHIRIssueSeverity.ERROR, - fhir_coding_system="https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1", fhir_error_code=FHIRSpineErrorCode.INVALID_PARAMETER, fhir_display_message="The supplied category was not recognised by the API.", ) @@ -112,7 +111,6 @@ def log_and_generate_response( status_code=HTTPStatus.BAD_REQUEST, fhir_issue_code=FHIRIssueCode.VALUE, fhir_issue_severity=FHIRIssueSeverity.ERROR, - fhir_coding_system="https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1", fhir_error_code=FHIRSpineErrorCode.INVALID_PARAMETER, fhir_display_message="The given conditions were not in the expected format.", ) @@ -121,7 +119,6 @@ def log_and_generate_response( status_code=HTTPStatus.NOT_FOUND, fhir_issue_code=FHIRIssueCode.PROCESSING, fhir_issue_severity=FHIRIssueSeverity.ERROR, - fhir_coding_system="https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1", fhir_error_code=FHIRSpineErrorCode.REFERENCE_NOT_FOUND, fhir_display_message="The given NHS number was not found in our datasets. " "This could be because the number is incorrect or " @@ -132,7 +129,6 @@ def log_and_generate_response( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, fhir_issue_code=FHIRIssueCode.PROCESSING, fhir_issue_severity=FHIRIssueSeverity.ERROR, - fhir_coding_system="https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1", fhir_error_code=FHIRSpineErrorCode.INTERNAL_SERVER_ERROR, fhir_display_message="An unexpected internal server error occurred.", ) @@ -141,7 +137,15 @@ def log_and_generate_response( status_code=HTTPStatus.FORBIDDEN, fhir_issue_code=FHIRIssueCode.FORBIDDEN, fhir_issue_severity=FHIRIssueSeverity.ERROR, - fhir_coding_system="https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1", - fhir_error_code=FHIRSpineErrorCode.INVALID_NHS_NUMBER, - fhir_display_message="The provided NHS number does not match the record.", + fhir_error_code=FHIRSpineErrorCode.ACCESS_DENIED, + fhir_display_message="Access has been denied to process this request.", +) + + +NHS_NUMBER_MISSING_ERROR = APIErrorResponse( + status_code=HTTPStatus.BAD_REQUEST, + fhir_issue_code=FHIRIssueCode.INVALID, + fhir_issue_severity=FHIRIssueSeverity.ERROR, + fhir_error_code=FHIRSpineErrorCode.BAD_REQUEST, + fhir_display_message="Bad Request", ) diff --git a/src/eligibility_signposting_api/error_handler.py b/src/eligibility_signposting_api/common/error_handler.py similarity index 85% rename from src/eligibility_signposting_api/error_handler.py rename to src/eligibility_signposting_api/common/error_handler.py index 5ff156d5b..662e8bdda 100644 --- a/src/eligibility_signposting_api/error_handler.py +++ b/src/eligibility_signposting_api/common/error_handler.py @@ -5,7 +5,7 @@ from flask.typing import ResponseReturnValue from werkzeug.exceptions import HTTPException -from eligibility_signposting_api.api_error_response import INTERNAL_SERVER_ERROR +from eligibility_signposting_api.common.api_error_response import INTERNAL_SERVER_ERROR logger = logging.getLogger(__name__) @@ -21,4 +21,4 @@ def handle_exception(e: Exception) -> ResponseReturnValue | HTTPException: response = INTERNAL_SERVER_ERROR.log_and_generate_response( log_message=f"An unexpected error occurred: {full_traceback}", diagnostics="An unexpected error occurred." ) - return make_response(response.get("body"), response.get("statusCode")) + return make_response(response.get("body"), response.get("statusCode"), response.get("headers")) diff --git a/src/eligibility_signposting_api/wrapper.py b/src/eligibility_signposting_api/common/request_validator.py similarity index 88% rename from src/eligibility_signposting_api/wrapper.py rename to src/eligibility_signposting_api/common/request_validator.py index c3437afea..9375fd729 100644 --- a/src/eligibility_signposting_api/wrapper.py +++ b/src/eligibility_signposting_api/common/request_validator.py @@ -6,17 +6,18 @@ from mangum.types import LambdaContext, LambdaEvent -from eligibility_signposting_api.api_error_response import ( +from eligibility_signposting_api.common.api_error_response import ( INVALID_CATEGORY_ERROR, INVALID_CONDITION_FORMAT_ERROR, INVALID_INCLUDE_ACTIONS_ERROR, NHS_NUMBER_MISMATCH_ERROR, + NHS_NUMBER_MISSING_ERROR, ) from eligibility_signposting_api.config.contants import NHS_NUMBER_HEADER logger = logging.getLogger(__name__) -condition_pattern = re.compile(r"^\s*[a-zA-Z0-9]+\s*$", re.IGNORECASE) +condition_pattern = re.compile(r"^\s*[a-z0-9]+\s*$", re.IGNORECASE) category_pattern = re.compile(r"^\s*(VACCINATIONS|SCREENING|ALL)\s*$", re.IGNORECASE) include_actions_pattern = re.compile(r"^\s*([YN])\s*$", re.IGNORECASE) @@ -59,12 +60,16 @@ def wrapper(event: LambdaEvent, context: LambdaContext) -> dict[str, Any] | None path_nhs_no = event.get("pathParameters", {}).get("id") header_nhs_no = event.get("headers", {}).get(NHS_NUMBER_HEADER) - if not validate_nhs_number(path_nhs_no, header_nhs_no): - message = f"NHS Number {path_nhs_no or ''} does not match the header NHS Number {header_nhs_no or ''}" - return NHS_NUMBER_MISMATCH_ERROR.log_and_generate_response( + if not path_nhs_no: + message = "Missing required NHS Number from path parameters" + return NHS_NUMBER_MISSING_ERROR.log_and_generate_response( log_message=message, diagnostics=message, location_param="id" ) + if not validate_nhs_number(path_nhs_no, header_nhs_no): + message = "You are not authorised to request information for the supplied NHS Number" + return NHS_NUMBER_MISMATCH_ERROR.log_and_generate_response(log_message=message, diagnostics=message) + query_params = event.get("queryStringParameters") if query_params: is_valid, problem = validate_query_params(query_params) diff --git a/src/eligibility_signposting_api/config/config.py b/src/eligibility_signposting_api/config/config.py index 722e90133..58be70258 100644 --- a/src/eligibility_signposting_api/config/config.py +++ b/src/eligibility_signposting_api/config/config.py @@ -1,10 +1,8 @@ import logging import os -from collections.abc import Sequence from functools import cache from typing import Any, NewType -from pythonjsonlogger.json import JsonFormatter from yarl import URL from eligibility_signposting_api.repos.campaign_repo import BucketName @@ -24,6 +22,7 @@ def config() -> dict[str, Any]: rules_bucket_name = BucketName(os.getenv("RULES_BUCKET_NAME", "test-rules-bucket")) audit_bucket_name = BucketName(os.getenv("AUDIT_BUCKET_NAME", "test-audit-bucket")) aws_default_region = AwsRegion(os.getenv("AWS_DEFAULT_REGION", "eu-west-1")) + enable_xray_patching = bool(os.getenv("ENABLE_XRAY_PATCHING", "false")) kinesis_audit_stream_to_s3 = AwsKinesisFirehoseStreamName( os.getenv("KINESIS_AUDIT_STREAM_TO_S3", "test_kinesis_audit_stream_to_s3") ) @@ -41,32 +40,22 @@ def config() -> dict[str, Any]: "audit_bucket_name": audit_bucket_name, "firehose_endpoint": None, "kinesis_audit_stream_to_s3": kinesis_audit_stream_to_s3, + "enable_xray_patching": enable_xray_patching, "log_level": log_level, } + local_stack_endpoint = "http://localhost:4566" return { "aws_access_key_id": AwsAccessKey(os.getenv("AWS_ACCESS_KEY_ID", "dummy_key")), "aws_default_region": aws_default_region, "aws_secret_access_key": AwsSecretAccessKey(os.getenv("AWS_SECRET_ACCESS_KEY", "dummy_secret")), - "dynamodb_endpoint": URL(os.getenv("DYNAMODB_ENDPOINT", "http://localhost:4566")), + "dynamodb_endpoint": URL(os.getenv("DYNAMODB_ENDPOINT", local_stack_endpoint)), "person_table_name": person_table_name, - "s3_endpoint": URL(os.getenv("S3_ENDPOINT", "http://localhost:4566")), + "s3_endpoint": URL(os.getenv("S3_ENDPOINT", local_stack_endpoint)), "rules_bucket_name": rules_bucket_name, "audit_bucket_name": audit_bucket_name, - "firehose_endpoint": URL(os.getenv("FIREHOSE_ENDPOINT", "http://localhost:4566")), + "firehose_endpoint": URL(os.getenv("FIREHOSE_ENDPOINT", local_stack_endpoint)), "kinesis_audit_stream_to_s3": kinesis_audit_stream_to_s3, + "enable_xray_patching": enable_xray_patching, "log_level": log_level, } - - -def init_logging(quieten: Sequence[str] = ("asyncio", "botocore", "boto3", "mangum", "urllib3")) -> None: - log_format = "%(asctime)s %(levelname)-8s %(name)s %(module)s.py:%(funcName)s():%(lineno)d %(message)s" - formatter = JsonFormatter(log_format) - handler = logging.StreamHandler() - handler.setFormatter(formatter) - logging.root.handlers = [] # Clear any existing handlers - logging.root.setLevel(LOG_LEVEL) # Set log level - logging.root.addHandler(handler) # Add handler - - for q in quieten: - logging.getLogger(q).setLevel(logging.WARNING) diff --git a/src/eligibility_signposting_api/logging/__init__.py b/src/eligibility_signposting_api/logging/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/eligibility_signposting_api/logging/logs_helper.py b/src/eligibility_signposting_api/logging/logs_helper.py new file mode 100644 index 000000000..12d8a48db --- /dev/null +++ b/src/eligibility_signposting_api/logging/logs_helper.py @@ -0,0 +1,29 @@ +import logging +from collections.abc import Callable +from functools import wraps +from typing import Any + +from mangum.types import LambdaContext, LambdaEvent + +logger = logging.getLogger(__name__) + + +def log_request_ids_from_headers() -> Callable: + def decorator(func: Callable) -> Callable: + @wraps(func) + def wrapper(event: LambdaEvent, context: LambdaContext) -> dict[str, Any] | None: + gateway_request_id = (event.get("requestContext") or {}).get("requestId") + headers = event.get("headers") or {} + logger.info( + "request trace metadata", + extra={ + "x_request_id": headers.get("X-Request-ID"), + "x_correlation_id": headers.get("X-Correlation-ID"), + "gateway_request_id": gateway_request_id, + }, + ) + return func(event, context) + + return wrapper + + return decorator diff --git a/src/eligibility_signposting_api/logging/logs_manager.py b/src/eligibility_signposting_api/logging/logs_manager.py new file mode 100644 index 000000000..6ca253ef2 --- /dev/null +++ b/src/eligibility_signposting_api/logging/logs_manager.py @@ -0,0 +1,48 @@ +import logging +from collections.abc import Callable, Sequence +from contextvars import ContextVar +from functools import wraps +from typing import Any + +from mangum.types import LambdaContext, LambdaEvent +from pythonjsonlogger.json import JsonFormatter + +from eligibility_signposting_api.config.config import LOG_LEVEL + +request_id_context_var: ContextVar[str | None] = ContextVar("request_id", default=None) + +LOG_FORMAT = "%(asctime)s %(levelname)-8s %(name)s %(module)s.py:%(funcName)s():%(lineno)d %(message)s" + + +def add_lambda_request_id_to_logger() -> Callable: + def decorator(func: Callable) -> Callable: + @wraps(func) + def wrapper(event: LambdaEvent, context: LambdaContext) -> dict[str, Any] | None: + aws_request_id = request_id_context_var.set(context.aws_request_id) + try: + return func(event, context) + finally: + request_id_context_var.reset(aws_request_id) + + return wrapper + + return decorator + + +class EnrichedJsonFormatter(JsonFormatter): + def add_fields(self, log_record: dict[str, Any], record: logging.LogRecord, message_dict: dict[str, Any]) -> None: + log_record["request_id"] = request_id_context_var.get() or "-" + super().add_fields(log_record, record, message_dict) + + +def init_logging(quieten: Sequence[str] = ("asyncio", "botocore", "boto3", "mangum", "urllib3")) -> None: + formatter = EnrichedJsonFormatter(LOG_FORMAT) + handler = logging.StreamHandler() + handler.setFormatter(formatter) + + logging.root.handlers = [] # Remove default handlers + logging.root.setLevel(LOG_LEVEL) + logging.root.addHandler(handler) + + for q in quieten: + logging.getLogger(q).setLevel(logging.WARNING) diff --git a/src/eligibility_signposting_api/logging/tracing_helper.py b/src/eligibility_signposting_api/logging/tracing_helper.py new file mode 100644 index 000000000..888adc507 --- /dev/null +++ b/src/eligibility_signposting_api/logging/tracing_helper.py @@ -0,0 +1,21 @@ +from collections.abc import Callable +from functools import wraps +from typing import Any + +from aws_xray_sdk.core import xray_recorder +from mangum.types import LambdaContext, LambdaEvent + + +def tracing_setup() -> Callable: + def decorator(func: Callable) -> Callable: + @wraps(func) + def wrapper(event: LambdaEvent, context: LambdaContext) -> dict[str, Any] | None: + xray_recorder.begin_subsegment("Lambda") + try: + return func(event, context) + finally: + xray_recorder.end_subsegment() + + return wrapper + + return decorator diff --git a/src/eligibility_signposting_api/model/rules.py b/src/eligibility_signposting_api/model/campaign_config.py similarity index 90% rename from src/eligibility_signposting_api/model/rules.py rename to src/eligibility_signposting_api/model/campaign_config.py index 541db6263..989f2e53d 100644 --- a/src/eligibility_signposting_api/model/rules.py +++ b/src/eligibility_signposting_api/model/campaign_config.py @@ -17,10 +17,10 @@ from pydantic import SerializationInfo CampaignName = NewType("CampaignName", str) -CampaignVersion = NewType("CampaignVersion", str) +CampaignVersion = NewType("CampaignVersion", int) CampaignID = NewType("CampaignID", str) IterationName = NewType("IterationName", str) -IterationVersion = NewType("IterationVersion", str) +IterationVersion = NewType("IterationVersion", int) IterationID = NewType("IterationID", str) IterationDate = NewType("IterationDate", date) RuleName = NewType("RuleName", str) @@ -42,6 +42,8 @@ class RuleType(StrEnum): filter = "F" suppression = "S" redirect = "R" + not_eligible_actions = "X" + not_actionable_actions = "Y" class RuleOperator(StrEnum): @@ -153,6 +155,8 @@ class Iteration(BaseModel): approval_maximum: int | None = Field(None, alias="ApprovalMaximum") type: Literal["A", "M", "S", "O"] = Field(..., alias="Type") default_comms_routing: str = Field(..., alias="DefaultCommsRouting") + default_not_eligible_routing: str = Field(..., alias="DefaultNotEligibleRouting") + default_not_actionable_routing: str = Field(..., alias="DefaultNotActionableRouting") iteration_cohorts: list[IterationCohort] = Field(..., alias="IterationCohorts") iteration_rules: list[IterationRule] = Field(..., alias="IterationRules") actions_mapper: ActionsMapper = Field(..., alias="ActionsMapper") @@ -181,9 +185,9 @@ class CampaignConfig(BaseModel): name: CampaignName = Field(..., alias="Name") type: Literal["V", "S"] = Field(..., alias="Type") target: Literal["COVID", "FLU", "MMR", "RSV"] = Field(..., alias="Target") - manager: str | None = Field(None, alias="Manager") - approver: str | None = Field(None, alias="Approver") - reviewer: str | None = Field(None, alias="Reviewer") + manager: list[str] | None = Field(None, alias="Manager") + approver: list[str] | None = Field(None, alias="Approver") + reviewer: list[str] | None = Field(None, alias="Reviewer") iteration_frequency: Literal["X", "D", "W", "M", "Q", "A"] = Field(..., alias="IterationFrequency") iteration_type: Literal["A", "M", "S", "O"] = Field(..., alias="IterationType") iteration_time: str | None = Field(None, alias="IterationTime") @@ -224,21 +228,6 @@ def check_no_overlapping_iterations(self) -> typing.Self: raise ValueError(message) return self - @model_validator(mode="after") - def check_has_iteration_from_start(self) -> typing.Self: - iterations_by_date = sorted(self.iterations, key=attrgetter("iteration_date")) - if first_iteration := next(iter(iterations_by_date), None): - if first_iteration.iteration_date > self.start_date: - message = ( - f"campaign {self.id} starts on {self.start_date}, " - f"1st iteration starts later - {first_iteration.iteration_date}" - ) - raise ValueError(message) - return self - # Should never happen, since we are constraining self.iterations with a min_length of 1 - message = f"campaign {self.id} has no iterations." - raise ValueError(message) - @cached_property def campaign_live(self) -> bool: today = datetime.now(tz=UTC).date() diff --git a/src/eligibility_signposting_api/model/eligibility.py b/src/eligibility_signposting_api/model/eligibility_status.py similarity index 64% rename from src/eligibility_signposting_api/model/eligibility.py rename to src/eligibility_signposting_api/model/eligibility_status.py index bad948361..9cc8809fc 100644 --- a/src/eligibility_signposting_api/model/eligibility.py +++ b/src/eligibility_signposting_api/model/eligibility_status.py @@ -4,10 +4,14 @@ from datetime import date from enum import Enum, StrEnum, auto from functools import total_ordering -from typing import NewType, Self +from typing import TYPE_CHECKING, NewType, Self from pydantic import HttpUrl +if TYPE_CHECKING: + from eligibility_signposting_api.model import campaign_config + from eligibility_signposting_api.model.campaign_config import CampaignID, CampaignVersion, CohortLabel, Iteration + NHSNumber = NewType("NHSNumber", str) DateOfBirth = NewType("DateOfBirth", date) Postcode = NewType("Postcode", str) @@ -24,11 +28,15 @@ UrlLink = NewType("UrlLink", HttpUrl) UrlLabel = NewType("UrlLabel", str) +StatusText = NewType("StatusText", str) + class RuleType(StrEnum): filter = "F" suppression = "S" redirect = "R" + not_eligible_actions = "X" + not_actionable_actions = "Y" @total_ordering @@ -65,6 +73,21 @@ def best(*statuses: Status) -> Status: """ return max(statuses) + def get_status_text(self, condition_name: ConditionName) -> StatusText: + status_to_text_mapping = { + self.not_eligible: lambda: StatusText("We do not believe you can have it"), + self.not_actionable: lambda: StatusText(f"You should have the {condition_name} vaccine"), + self.actionable: lambda: StatusText(f"You should have the {condition_name} vaccine"), + } + return status_to_text_mapping.get(self, lambda: StatusText("Unknown status provided"))() + + def get_action_rule_type(self) -> RuleType: + return { + self.not_eligible: RuleType.not_eligible_actions, + self.not_actionable: RuleType.not_actionable_actions, + self.actionable: RuleType.redirect, + }[self] + @dataclass class Reason: @@ -90,6 +113,8 @@ class Condition: condition_name: ConditionName status: Status cohort_results: list[CohortGroupResult] + suitability_rules: list[Reason] + status_text: StatusText actions: list[SuggestedAction] | None = None @@ -109,6 +134,22 @@ class IterationResult: actions: list[SuggestedAction] | None +@dataclass +class BestIterationResult: + iteration_result: IterationResult + active_iteration: Iteration | None = None + campaign_id: CampaignID | None = None + campaign_version: CampaignVersion | None = None + cohort_results: dict[CohortLabel, CohortGroupResult] | None = None + + +@dataclass +class MatchedActionDetail: + rule_name: campaign_config.RuleName | None = None + rule_priority: campaign_config.RulePriority | None = None + actions: list[SuggestedAction] | None = None + + @dataclass class EligibilityStatus: """Represents a person's eligibility for vaccination.""" diff --git a/src/eligibility_signposting_api/model/person.py b/src/eligibility_signposting_api/model/person.py new file mode 100644 index 000000000..eaaff6c64 --- /dev/null +++ b/src/eligibility_signposting_api/model/person.py @@ -0,0 +1,7 @@ +from dataclasses import dataclass +from typing import Any + + +@dataclass +class Person: + data: list[dict[str, Any]] diff --git a/src/eligibility_signposting_api/repos/campaign_repo.py b/src/eligibility_signposting_api/repos/campaign_repo.py index 8a2a212fd..26b701962 100644 --- a/src/eligibility_signposting_api/repos/campaign_repo.py +++ b/src/eligibility_signposting_api/repos/campaign_repo.py @@ -5,7 +5,7 @@ from botocore.client import BaseClient from wireup import Inject, service -from eligibility_signposting_api.model.rules import CampaignConfig, Rules +from eligibility_signposting_api.model.campaign_config import CampaignConfig, Rules BucketName = NewType("BucketName", str) diff --git a/src/eligibility_signposting_api/repos/person_repo.py b/src/eligibility_signposting_api/repos/person_repo.py index 41ea20745..9867b2844 100644 --- a/src/eligibility_signposting_api/repos/person_repo.py +++ b/src/eligibility_signposting_api/repos/person_repo.py @@ -5,7 +5,8 @@ from boto3.resources.base import ServiceResource from wireup import Inject, service -from eligibility_signposting_api.model.eligibility import NHSNumber +from eligibility_signposting_api.model.eligibility_status import NHSNumber +from eligibility_signposting_api.model.person import Person from eligibility_signposting_api.repos.exceptions import NotFoundError logger = logging.getLogger(__name__) @@ -35,7 +36,7 @@ def __init__(self, table: Annotated[Any, Inject(qualifier="person_table")]) -> N super().__init__() self.table = table - def get_eligibility_data(self, nhs_number: NHSNumber) -> list[dict[str, Any]]: + def get_eligibility_data(self, nhs_number: NHSNumber) -> Person: response = self.table.query(KeyConditionExpression=Key("NHS_NUMBER").eq(nhs_number)) logger.debug("response %r for %r", response, nhs_number, extra={"response": response, "nhs_number": nhs_number}) @@ -44,4 +45,5 @@ def get_eligibility_data(self, nhs_number: NHSNumber) -> list[dict[str, Any]]: raise NotFoundError(message) logger.debug("returning items %s", items, extra={"items": items}) - return items + + return Person(data=items) diff --git a/src/eligibility_signposting_api/services/calculators/eligibility_calculator.py b/src/eligibility_signposting_api/services/calculators/eligibility_calculator.py index a64a8b878..66cba4664 100644 --- a/src/eligibility_signposting_api/services/calculators/eligibility_calculator.py +++ b/src/eligibility_signposting_api/services/calculators/eligibility_calculator.py @@ -1,101 +1,68 @@ from __future__ import annotations -from _operator import attrgetter +import logging from collections import defaultdict -from collections.abc import Collection, Iterable, Iterator, Mapping from dataclasses import dataclass, field -from itertools import groupby -from typing import TYPE_CHECKING, Any - -from eligibility_signposting_api.audit.audit_context import AuditContext - -if TYPE_CHECKING: - from eligibility_signposting_api.model.rules import ( - ActionsMapper, - CampaignID, - CampaignVersion, - Iteration, - IterationCohort, - RuleName, - RulePriority, - ) +from itertools import chain +from typing import TYPE_CHECKING from wireup import service -from eligibility_signposting_api.model import eligibility, rules -from eligibility_signposting_api.model.eligibility import ( - ActionCode, - ActionDescription, - ActionType, +from eligibility_signposting_api.audit.audit_context import AuditContext +from eligibility_signposting_api.model import eligibility_status +from eligibility_signposting_api.model.eligibility_status import ( + BestIterationResult, CohortGroupResult, Condition, ConditionName, - InternalActionCode, + EligibilityStatus, IterationResult, + Reason, Status, - SuggestedAction, - UrlLabel, - UrlLink, -) -from eligibility_signposting_api.services.calculators.rule_calculator import ( - RuleCalculator, ) +from eligibility_signposting_api.services.processors.action_rule_handler import ActionRuleHandler +from eligibility_signposting_api.services.processors.campaign_evaluator import CampaignEvaluator +from eligibility_signposting_api.services.processors.rule_processor import RuleProcessor -Row = Collection[Mapping[str, Any]] +if TYPE_CHECKING: + from collections.abc import Collection + + from eligibility_signposting_api.model.campaign_config import ( + CampaignConfig, + CohortLabel, + IterationName, + ) + from eligibility_signposting_api.model.person import Person + +logger = logging.getLogger(__name__) @service class EligibilityCalculatorFactory: @staticmethod - def get(person_data: Row, campaign_configs: Collection[rules.CampaignConfig]) -> EligibilityCalculator: - return EligibilityCalculator(person_data=person_data, campaign_configs=campaign_configs) + def get(person: Person, campaign_configs: Collection[CampaignConfig]) -> EligibilityCalculator: + return EligibilityCalculator(person=person, campaign_configs=campaign_configs) @dataclass class EligibilityCalculator: - person_data: Row - campaign_configs: Collection[rules.CampaignConfig] - - results: list[eligibility.Condition] = field(default_factory=list) + person: Person + campaign_configs: Collection[CampaignConfig] - @property - def active_campaigns(self) -> list[rules.CampaignConfig]: - return [cc for cc in self.campaign_configs if cc.campaign_live] + campaign_evaluator: CampaignEvaluator = field(default_factory=CampaignEvaluator) + rule_processor: RuleProcessor = field(default_factory=RuleProcessor) + action_rule_handler: ActionRuleHandler = field(default_factory=ActionRuleHandler) - def campaigns_grouped_by_condition_name( - self, conditions: list[str], category: str - ) -> Iterator[tuple[eligibility.ConditionName, list[rules.CampaignConfig]]]: - """Generator that yields campaign groups filtered by condition names and campaign category.""" - - allowed_types = ( - {"V", "S"} if category == "ALL" else {category[0]} if category in {"VACCINATIONS", "SCREENING"} else set() - ) - filter_all_conditions = "ALL" in conditions - - for condition_name, campaign_group in groupby( - sorted(self.active_campaigns, key=attrgetter("target")), - key=attrgetter("target"), - ): - campaigns = list(campaign_group) - if campaigns[0].type in allowed_types and (filter_all_conditions or str(condition_name) in conditions): - yield condition_name, campaigns - - @property - def person_cohorts(self) -> set[str]: - cohorts_row: Mapping[str, dict[str, dict[str, dict[str, Any]]]] = next( - (row for row in self.person_data if row.get("ATTRIBUTE_TYPE") == "COHORTS"), - {}, - ) - return set(cohorts_row.get("COHORT_MAP", {}).get("cohorts", {}).get("M", {}).keys()) + results: list[eligibility_status.Condition] = field(default_factory=list) @staticmethod def get_the_best_cohort_memberships( - cohort_results: dict[str, CohortGroupResult], + cohort_results: dict[CohortLabel, CohortGroupResult], ) -> tuple[Status, list[CohortGroupResult]]: if not cohort_results: - return eligibility.Status.not_eligible, [] + return eligibility_status.Status.not_eligible, [] - best_status = eligibility.Status.best(*[result.status for result in cohort_results.values()]) + best_status = eligibility_status.Status.best(*[result.status for result in cohort_results.values()]) best_cohorts = [result for result in cohort_results.values() if result.status == best_status] best_cohorts = [ @@ -111,310 +78,137 @@ def get_the_best_cohort_memberships( return best_status, best_cohorts - @staticmethod - def get_exclusion_rules( - cohort: IterationCohort, filter_rules: Iterable[rules.IterationRule] - ) -> Iterator[rules.IterationRule]: - return ( - ir - for ir in filter_rules - if ir.cohort_label is None - or cohort.cohort_label == ir.cohort_label - or (isinstance(ir.cohort_label, (list, set, tuple)) and cohort.cohort_label in ir.cohort_label) - ) + def get_eligibility_status(self, include_actions: str, conditions: list[str], category: str) -> EligibilityStatus: + include_actions_flag = include_actions.upper() == "Y" + condition_results: dict[ConditionName, IterationResult] = {} + final_result = [] - @staticmethod - def get_rules_by_type( - active_iteration: Iteration, - ) -> tuple[tuple[rules.IterationRule, ...], tuple[rules.IterationRule, ...]]: - filter_rules, suppression_rules = ( - tuple(rule for rule in active_iteration.iteration_rules if attrgetter("type")(rule) == rule_type) - for rule_type in (rules.RuleType.filter, rules.RuleType.suppression) + requested_grouped_campaigns = self.campaign_evaluator.get_requested_grouped_campaigns( + self.campaign_configs, conditions, category ) - return filter_rules, suppression_rules + for condition_name, campaign_group in requested_grouped_campaigns: + best_iteration_result = self.get_best_iteration_result(campaign_group) - @staticmethod - def get_redirect_rules( - active_iteration: Iteration, - ) -> tuple[tuple[rules.IterationRule, ...], ActionsMapper, str]: - redirect_rules = tuple( - rule for rule in active_iteration.iteration_rules if rule.type in rules.RuleType.redirect - ) - default_comms = active_iteration.default_comms_routing - action_mapper = active_iteration.actions_mapper - return redirect_rules, action_mapper, default_comms + if best_iteration_result is None: + continue - def evaluate_eligibility( - self, include_actions: str, conditions: list[str], category: str - ) -> eligibility.EligibilityStatus: - include_actions_flag = include_actions.upper() == "Y" - condition_results: dict[ConditionName, IterationResult] = {} - actions: list[SuggestedAction] | None = [] - redirect_rule_priority, redirect_rule_name = None, None - - for condition_name, campaign_group in self.campaigns_grouped_by_condition_name(conditions, category): - best_active_iteration: Iteration | None - best_candidate: IterationResult - best_campaign_id: CampaignID | None - best_campaign_version: CampaignVersion | None - best_cohort_results: dict[str, CohortGroupResult] | None + matched_action_detail = self.action_rule_handler.get_actions( + self.person, + best_iteration_result.active_iteration, + best_iteration_result.iteration_result, + include_actions_flag=include_actions_flag, + ) - iteration_results: dict[ - str, tuple[Iteration, IterationResult, CampaignID, CampaignVersion, dict[str, CohortGroupResult]] - ] = {} + condition_results[condition_name] = best_iteration_result.iteration_result + condition_results[condition_name].actions = matched_action_detail.actions - for cc in campaign_group: - active_iteration = cc.current_iteration - cohort_results: dict[str, CohortGroupResult] = self.get_cohort_results(active_iteration) - - # Determine Result between cohorts - get the best - status, best_cohorts = self.get_the_best_cohort_memberships(cohort_results) - iteration_results[active_iteration.name] = ( - active_iteration, - IterationResult(status, best_cohorts, actions), - cc.id, - cc.version, - cohort_results, - ) + condition: Condition = self.build_condition( + iteration_result=condition_results[condition_name], condition_name=condition_name + ) + final_result.append(condition) - # Determine results between iterations - get the best - if iteration_results: - ( - best_iteration_name, - ( - best_active_iteration, - best_candidate, - best_campaign_id, - best_campaign_version, - best_cohort_results, - ), - ) = max(iteration_results.items(), key=lambda item: item[1][1].status.value) - else: - best_candidate = IterationResult(eligibility.Status.not_eligible, [], actions) - best_campaign_id = None - best_campaign_version = None - best_active_iteration = None - best_cohort_results = None - - condition_results[condition_name] = best_candidate - - if best_candidate.status == Status.actionable and best_active_iteration is not None: - if include_actions_flag: - actions, matched_r_rule_priority, matched_r_rule_name = self.handle_redirect_rules( - best_active_iteration - ) - redirect_rule_name = matched_r_rule_name - redirect_rule_priority = matched_r_rule_priority - else: - actions = None - - if best_candidate.status in (Status.not_eligible, Status.not_actionable) and not include_actions_flag: - actions = None - - # add actions to condition results - condition_results[condition_name].actions = actions - # reset actions for the next condition - actions: list[SuggestedAction] | None = [] - - # add audit data AuditContext.append_audit_condition( - condition_results[condition_name].actions, condition_name, - (best_active_iteration, best_candidate, best_cohort_results), - (best_campaign_id, best_campaign_version), - (redirect_rule_priority, redirect_rule_name), + best_iteration_result, + matched_action_detail, + condition_results[condition_name].cohort_results, ) # Consolidate all the results and return - final_result = self.build_condition_results(condition_results) - return eligibility.EligibilityStatus(conditions=final_result) - - def handle_redirect_rules( - self, best_active_iteration: Iteration - ) -> tuple[list[SuggestedAction] | None, RulePriority | None, RuleName | None]: - redirect_rules, action_mapper, default_comms = self.get_redirect_rules(best_active_iteration) - priority_getter = attrgetter("priority") - sorted_rules_by_priority = sorted(redirect_rules, key=priority_getter) - - actions: list[SuggestedAction] | None = self.get_actions_from_comms(action_mapper, default_comms) - matched_redirect_rule_priority, matched_redirect_rule_name = None, None - for _, rule_group in groupby(sorted_rules_by_priority, key=priority_getter): - rule_group_list = list(rule_group) - matcher_matched_list = [ - RuleCalculator(person_data=self.person_data, rule=rule).evaluate_exclusion()[1].matcher_matched - for rule in rule_group_list - ] - - comms_routing = rule_group_list[0].comms_routing - if comms_routing and all(matcher_matched_list): - rule_actions = self.get_actions_from_comms(action_mapper, comms_routing) - if rule_actions and len(rule_actions) > 0: - actions = rule_actions - matched_redirect_rule_priority = rule_group_list[0].priority - matched_redirect_rule_name = rule_group_list[0].name - break - - return actions, matched_redirect_rule_priority, matched_redirect_rule_name - - def get_cohort_results(self, active_iteration: rules.Iteration) -> dict[str, CohortGroupResult]: - cohort_results: dict[str, CohortGroupResult] = {} - filter_rules, suppression_rules = self.get_rules_by_type(active_iteration) - for cohort in sorted(active_iteration.iteration_cohorts, key=attrgetter("priority")): - # Base Eligibility - check - if cohort.cohort_label in self.person_cohorts or cohort.is_magic_cohort: - # Eligibility - check - if self.is_eligible_by_filter_rules(cohort, cohort_results, filter_rules): - # Actionability - evaluation - self.evaluate_suppression_rules(cohort, cohort_results, suppression_rules) - - # Not base eligible - elif cohort.cohort_label is not None: - cohort_results[cohort.cohort_label] = CohortGroupResult( - cohort.cohort_group, - Status.not_eligible, - [], - cohort.negative_description, - [], - ) - return cohort_results + return eligibility_status.EligibilityStatus(conditions=final_result) + + def get_best_iteration_result(self, campaign_group: list[CampaignConfig]) -> BestIterationResult | None: + iteration_results = self.get_iteration_results(campaign_group) + + if not iteration_results: + return None + + (best_iteration_name, best_iteration_result) = max( + iteration_results.items(), + key=lambda item: next(iter(item[1].cohort_results.values())).status.value + # Below handles the case where there are no cohort results + if item[1].cohort_results + else -1, + ) + + return best_iteration_result + + def get_iteration_results(self, campaign_group: list[CampaignConfig]) -> dict[IterationName, BestIterationResult]: + iteration_results: dict[IterationName, BestIterationResult] = {} + + for cc in campaign_group: + try: + active_iteration = cc.current_iteration + except StopIteration: + logger.info("Skipping campaign ID %s as no active iteration was found.", cc.id) + continue + cohort_results: dict[CohortLabel, CohortGroupResult] = self.rule_processor.get_cohort_group_results( + self.person, active_iteration + ) + + # Determine Result between cohorts - get the best + status, best_cohorts = self.get_the_best_cohort_memberships(cohort_results) + iteration_results[active_iteration.name] = BestIterationResult( + IterationResult(status, best_cohorts, []), active_iteration, cc.id, cc.version, cohort_results + ) + return iteration_results + + @staticmethod + def build_condition(iteration_result: IterationResult, condition_name: ConditionName) -> Condition: + grouped_cohort_results = defaultdict(list) + + for cohort_result in iteration_result.cohort_results: + if iteration_result.status == cohort_result.status: + grouped_cohort_results[cohort_result.cohort_code].append(cohort_result) + + deduplicated_cohort_results: list[CohortGroupResult] = EligibilityCalculator.deduplicate_cohort_results( + grouped_cohort_results + ) + + overall_deduplicated_reasons_for_condition = EligibilityCalculator.deduplicate_reasons( + deduplicated_cohort_results + ) + + return Condition( + condition_name=condition_name, + status=iteration_result.status, + cohort_results=list(deduplicated_cohort_results), + suitability_rules=list(overall_deduplicated_reasons_for_condition), + actions=iteration_result.actions, + status_text=iteration_result.status.get_status_text(condition_name), + ) @staticmethod - def build_condition_results(condition_results: dict[ConditionName, IterationResult]) -> list[Condition]: - conditions: list[Condition] = [] - # iterate over conditions - for condition_name, active_iteration_result in condition_results.items(): - grouped_cohort_results = defaultdict(list) - # iterate over cohorts and group them by status and cohort_group - for cohort_result in active_iteration_result.cohort_results: - if active_iteration_result.status == cohort_result.status: - grouped_cohort_results[cohort_result.cohort_code].append(cohort_result) - - # deduplicate grouped cohort results by cohort_code - deduplicated_cohort_results = [ + def deduplicate_cohort_results( + grouped_cohort_results: dict[str, list[CohortGroupResult]], + ) -> list[CohortGroupResult]: + results = [] + + for cohort_code, group_results in grouped_cohort_results.items(): + if not group_results: + continue + + deduped_reasons: list[Reason] = EligibilityCalculator.deduplicate_reasons(group_results) + + description = next((c.description for c in group_results if c.description), group_results[0].description) + + results.append( CohortGroupResult( - cohort_code=group_cohort_code, - status=group[0].status, - # Flatten all reasons from the group - reasons=[reason for cohort in group for reason in cohort.reasons], - # get the first nonempty description - description=next((c.description for c in group if c.description), group[0].description), + cohort_code=cohort_code, + status=group_results[0].status, + reasons=list(deduped_reasons), + description=description, audit_rules=[], ) - for group_cohort_code, group in grouped_cohort_results.items() - if group - ] - - # return condition with cohort results - conditions.append( - Condition( - condition_name=condition_name, - status=active_iteration_result.status, - cohort_results=list(deduplicated_cohort_results), - actions=condition_results[condition_name].actions, - ) - ) - return conditions - - def is_eligible_by_filter_rules( - self, - cohort: IterationCohort, - cohort_results: dict[str, CohortGroupResult], - filter_rules: Iterable[rules.IterationRule], - ) -> bool: - is_eligible = True - priority_getter = attrgetter("priority") - sorted_rules_by_priority = sorted(self.get_exclusion_rules(cohort, filter_rules), key=priority_getter) - - for _, rule_group in groupby(sorted_rules_by_priority, key=priority_getter): - status, group_inclusion_reasons, group_exclusion_reasons, rule_stop = self.evaluate_rules_priority_group( - rule_group - ) - if status.is_exclusion: - if cohort.cohort_label is not None: - cohort_results[cohort.cohort_label] = CohortGroupResult( - (cohort.cohort_group), - Status.not_eligible, - [], - cohort.negative_description, - group_exclusion_reasons, - ) - is_eligible = False - break - return is_eligible - - def evaluate_suppression_rules( - self, - cohort: IterationCohort, - cohort_results: dict[str, CohortGroupResult], - suppression_rules: Iterable[rules.IterationRule], - ) -> None: - is_actionable: bool = True - priority_getter = attrgetter("priority") - suppression_reasons = [] - - sorted_rules_by_priority = sorted(self.get_exclusion_rules(cohort, suppression_rules), key=priority_getter) - - for _, rule_group in groupby(sorted_rules_by_priority, key=priority_getter): - status, group_inclusion_reasons, group_exclusion_reasons, rule_stop = self.evaluate_rules_priority_group( - rule_group ) - if status.is_exclusion: - is_actionable = False - suppression_reasons.extend(group_exclusion_reasons) - if rule_stop: - break - - if cohort.cohort_label is not None: - key = cohort.cohort_label - if is_actionable: - cohort_results[key] = CohortGroupResult( - cohort.cohort_group, Status.actionable, [], cohort.positive_description, suppression_reasons - ) - else: - cohort_results[key] = CohortGroupResult( - cohort.cohort_group, - Status.not_actionable, - suppression_reasons, - cohort.positive_description, - suppression_reasons, - ) - def evaluate_rules_priority_group( - self, rules_group: Iterator[rules.IterationRule] - ) -> tuple[eligibility.Status, list[eligibility.Reason], list[eligibility.Reason], bool]: - is_rule_stop = False - inclusion_reasons, exclusion_reasons = [], [] - best_status = eligibility.Status.not_eligible - - for rule in rules_group: - is_rule_stop = rule.rule_stop or is_rule_stop - rule_calculator = RuleCalculator(person_data=self.person_data, rule=rule) - status, reason = rule_calculator.evaluate_exclusion() - if status.is_exclusion: - best_status = eligibility.Status.best(status, best_status) - exclusion_reasons.append(reason) - else: - best_status = eligibility.Status.actionable - inclusion_reasons.append(reason) - - return best_status, inclusion_reasons, exclusion_reasons, is_rule_stop + return results @staticmethod - def get_actions_from_comms(action_mapper: ActionsMapper, comms: str) -> list[SuggestedAction] | None: - suggested_actions: list[SuggestedAction] = [] - for comm in comms.split("|"): - action = action_mapper.get(comm) - if action is not None: - suggested_actions.append( - SuggestedAction( - internal_action_code=InternalActionCode(comm), - action_type=ActionType(action.action_type), - action_code=ActionCode(action.action_code), - action_description=ActionDescription(action.action_description) - if action.action_description - else None, - url_link=UrlLink(action.url_link) if action.url_link else None, - url_label=UrlLabel(action.url_label) if action.url_label else None, - ) - ) - return suggested_actions + def deduplicate_reasons(group_results: list[CohortGroupResult]) -> list[Reason]: + all_reasons = chain.from_iterable(group_result.reasons for group_result in group_results) + deduped = {} + for reason in all_reasons: + key = (reason.rule_type, reason.rule_priority) + deduped.setdefault(key, reason) + return list(deduped.values()) diff --git a/src/eligibility_signposting_api/services/calculators/rule_calculator.py b/src/eligibility_signposting_api/services/calculators/rule_calculator.py index 145a1e89f..c9fd7f41b 100644 --- a/src/eligibility_signposting_api/services/calculators/rule_calculator.py +++ b/src/eligibility_signposting_api/services/calculators/rule_calculator.py @@ -1,31 +1,37 @@ from __future__ import annotations -from collections.abc import Collection, Mapping -from dataclasses import dataclass -from typing import Any +from dataclasses import dataclass, field +from typing import TYPE_CHECKING from hamcrest.core.string_description import StringDescription -from eligibility_signposting_api.model import eligibility, rules -from eligibility_signposting_api.services.rules.operators import OperatorRegistry +from eligibility_signposting_api.model import eligibility_status +from eligibility_signposting_api.model.campaign_config import IterationRule, RuleAttributeLevel, RuleType +from eligibility_signposting_api.services.operators.operators import OperatorRegistry +from eligibility_signposting_api.services.processors.person_data_reader import PersonDataReader -Row = Collection[Mapping[str, Any]] +if TYPE_CHECKING: + from collections.abc import Mapping + + from eligibility_signposting_api.model.person import Person @dataclass class RuleCalculator: - person_data: Row - rule: rules.IterationRule + person: Person + rule: IterationRule + + person_data_reader: PersonDataReader = field(default_factory=PersonDataReader) - def evaluate_exclusion(self) -> tuple[eligibility.Status, eligibility.Reason]: + def evaluate_exclusion(self) -> tuple[eligibility_status.Status, eligibility_status.Reason]: """Evaluate if a particular rule excludes this person. Return the result, and the reason for the result.""" attribute_value = self.get_attribute_value() status, reason, matcher_matched = self.evaluate_rule(attribute_value) - reason = eligibility.Reason( - rule_name=eligibility.RuleName(self.rule.name), - rule_type=eligibility.RuleType(self.rule.type), - rule_priority=eligibility.RulePriority(str(self.rule.priority)), - rule_description=eligibility.RuleDescription(self.rule.description), + reason = eligibility_status.Reason( + rule_name=eligibility_status.RuleName(self.rule.name), + rule_type=eligibility_status.RuleType(self.rule.type), + rule_priority=eligibility_status.RulePriority(str(self.rule.priority)), + rule_description=eligibility_status.RuleDescription(self.rule.description), matcher_matched=matcher_matched, ) return status, reason @@ -33,32 +39,24 @@ def evaluate_exclusion(self) -> tuple[eligibility.Status, eligibility.Reason]: def get_attribute_value(self) -> str | None: """Pull out the correct attribute for a rule from the person's data.""" match self.rule.attribute_level: - case rules.RuleAttributeLevel.PERSON: + case RuleAttributeLevel.PERSON: person: Mapping[str, str | None] | None = next( - (r for r in self.person_data if r.get("ATTRIBUTE_TYPE", "") == "PERSON"), None + (r for r in self.person.data if r.get("ATTRIBUTE_TYPE", "") == "PERSON"), None ) attribute_value = person.get(str(self.rule.attribute_name)) if person else None - case rules.RuleAttributeLevel.COHORT: + case RuleAttributeLevel.COHORT: cohorts: Mapping[str, str | None] | None = next( - (r for r in self.person_data if r.get("ATTRIBUTE_TYPE", "") == "COHORTS"), None + (r for r in self.person.data if r.get("ATTRIBUTE_TYPE", "") == "COHORTS"), None ) if cohorts: - attr_name = ( - "COHORT_MAP" - if not self.rule.attribute_name or self.rule.attribute_name == "COHORT_LABEL" - else self.rule.attribute_name - ) - cohort_map = self.get_value(cohorts, attr_name) - cohorts_dict = self.get_value(cohort_map, "cohorts") - m_dict = self.get_value(cohorts_dict, "M") - person_cohorts: set[str] = set(m_dict.keys()) + person_cohorts = self.person_data_reader.get_person_cohorts(self.person) attribute_value = ",".join(person_cohorts) else: attribute_value = None - case rules.RuleAttributeLevel.TARGET: + case RuleAttributeLevel.TARGET: target: Mapping[str, str | None] | None = next( - (r for r in self.person_data if r.get("ATTRIBUTE_TYPE", "") == self.rule.attribute_target), None + (r for r in self.person.data if r.get("ATTRIBUTE_TYPE", "") == self.rule.attribute_target), None ) attribute_value = target.get(str(self.rule.attribute_name)) if target else None case _: # pragma: no cover @@ -66,12 +64,7 @@ def get_attribute_value(self) -> str | None: raise NotImplementedError(msg) return attribute_value - @staticmethod - def get_value(dictionary: Mapping[str, Any] | None, key: str) -> dict: - v = dictionary.get(key, {}) if isinstance(dictionary, dict) else {} - return v if isinstance(v, dict) else {} - - def evaluate_rule(self, attribute_value: str | None) -> tuple[eligibility.Status, str, bool]: + def evaluate_rule(self, attribute_value: str | None) -> tuple[eligibility_status.Status, str, bool]: """Evaluate a rule against a person data attribute. Return the result, and the reason for the result.""" matcher_class = OperatorRegistry.get(self.rule.operator) matcher = matcher_class(rule_value=self.rule.comparator) @@ -81,10 +74,12 @@ def evaluate_rule(self, attribute_value: str | None) -> tuple[eligibility.Status if matcher_matched: matcher.describe_match(attribute_value, reason) status = { - rules.RuleType.filter: eligibility.Status.not_eligible, - rules.RuleType.suppression: eligibility.Status.not_actionable, - rules.RuleType.redirect: eligibility.Status.actionable, + RuleType.filter: eligibility_status.Status.not_eligible, + RuleType.suppression: eligibility_status.Status.not_actionable, + RuleType.redirect: eligibility_status.Status.actionable, + RuleType.not_eligible_actions: eligibility_status.Status.not_eligible, + RuleType.not_actionable_actions: eligibility_status.Status.not_actionable, }[self.rule.type] return status, str(reason), matcher_matched matcher.describe_mismatch(attribute_value, reason) - return eligibility.Status.actionable, str(reason), matcher_matched + return eligibility_status.Status.actionable, str(reason), matcher_matched diff --git a/src/eligibility_signposting_api/services/eligibility_services.py b/src/eligibility_signposting_api/services/eligibility_services.py index 48586290b..465f73b08 100644 --- a/src/eligibility_signposting_api/services/eligibility_services.py +++ b/src/eligibility_signposting_api/services/eligibility_services.py @@ -2,7 +2,7 @@ from wireup import service -from eligibility_signposting_api.model import eligibility +from eligibility_signposting_api.model import eligibility_status from eligibility_signposting_api.repos import CampaignRepo, NotFoundError, PersonRepo from eligibility_signposting_api.services.calculators import eligibility_calculator as calculator @@ -32,11 +32,11 @@ def __init__( def get_eligibility_status( self, - nhs_number: eligibility.NHSNumber, + nhs_number: eligibility_status.NHSNumber, include_actions: str, conditions: list[str], category: str, - ) -> eligibility.EligibilityStatus: + ) -> eligibility_status.EligibilityStatus: """Calculate a person's eligibility for vaccination given an NHS number.""" if nhs_number: try: @@ -55,6 +55,6 @@ def get_eligibility_status( raise UnknownPersonError from e else: calc: calculator.EligibilityCalculator = self.calculator_factory.get(person_data, campaign_configs) - return calc.evaluate_eligibility(include_actions, conditions, category) + return calc.get_eligibility_status(include_actions, conditions, category) raise UnknownPersonError # pragma: no cover diff --git a/src/eligibility_signposting_api/services/operators/__init__.py b/src/eligibility_signposting_api/services/operators/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/eligibility_signposting_api/services/rules/operators.py b/src/eligibility_signposting_api/services/operators/operators.py similarity index 99% rename from src/eligibility_signposting_api/services/rules/operators.py rename to src/eligibility_signposting_api/services/operators/operators.py index 1f9c4af85..565503093 100644 --- a/src/eligibility_signposting_api/services/rules/operators.py +++ b/src/eligibility_signposting_api/services/operators/operators.py @@ -11,7 +11,7 @@ from hamcrest.core.base_matcher import BaseMatcher from hamcrest.core.description import Description -from eligibility_signposting_api.model.rules import RuleOperator +from eligibility_signposting_api.model.campaign_config import RuleOperator logger = logging.getLogger(__name__) diff --git a/src/eligibility_signposting_api/services/processors/__init__.py b/src/eligibility_signposting_api/services/processors/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/eligibility_signposting_api/services/processors/action_rule_handler.py b/src/eligibility_signposting_api/services/processors/action_rule_handler.py new file mode 100644 index 000000000..6ff38bd05 --- /dev/null +++ b/src/eligibility_signposting_api/services/processors/action_rule_handler.py @@ -0,0 +1,103 @@ +from itertools import groupby +from operator import attrgetter + +from eligibility_signposting_api.model.campaign_config import ( + ActionsMapper, + Iteration, + IterationRule, +) +from eligibility_signposting_api.model.eligibility_status import ( + ActionCode, + ActionDescription, + ActionType, + InternalActionCode, + IterationResult, + MatchedActionDetail, + RuleType, + SuggestedAction, + UrlLabel, + UrlLink, +) +from eligibility_signposting_api.model.person import Person +from eligibility_signposting_api.services.calculators.rule_calculator import RuleCalculator + + +class ActionRuleHandler: + def get_actions( + self, + person: Person, + active_iteration: Iteration | None, + best_iteration_result: IterationResult, + *, + include_actions_flag: bool, + ) -> MatchedActionDetail: + action_detail = MatchedActionDetail() + + if active_iteration is not None and include_actions_flag: + rule_type = best_iteration_result.status.get_action_rule_type() + action_detail = self._handle(person, active_iteration, rule_type) + + return action_detail + + def _handle(self, person: Person, best_active_iteration: Iteration, rule_type: RuleType) -> MatchedActionDetail: + action_rules, action_mapper, default_comms = self._get_action_rules_components(best_active_iteration, rule_type) + + priority_getter = attrgetter("priority") + sorted_rules_by_priority = sorted(action_rules, key=priority_getter) + + actions: list[SuggestedAction] | None = self._get_actions_from_comms(action_mapper, default_comms) # pyright: ignore[reportArgumentType] + + matched_action_rule_priority, matched_action_rule_name = None, None + for _, rule_group in groupby(sorted_rules_by_priority, key=priority_getter): + rule_group_list = list(rule_group) + matcher_matched_list = [ + RuleCalculator(person=person, rule=rule).evaluate_exclusion()[1].matcher_matched + for rule in rule_group_list + ] + + comms_routing = rule_group_list[0].comms_routing + if comms_routing and all(matcher_matched_list): + rule_actions = self._get_actions_from_comms(action_mapper, comms_routing) + if rule_actions and len(rule_actions) > 0: + actions = rule_actions + matched_action_rule_priority = rule_group_list[0].priority + matched_action_rule_name = rule_group_list[0].name + break + + return MatchedActionDetail(matched_action_rule_name, matched_action_rule_priority, actions) + + @staticmethod + def _get_action_rules_components( + active_iteration: Iteration, rule_type: RuleType + ) -> tuple[tuple[IterationRule, ...], ActionsMapper, str | None]: + action_rules = tuple(rule for rule in active_iteration.iteration_rules if rule.type in rule_type) + + routing_map = { + RuleType.redirect: active_iteration.default_comms_routing, + RuleType.not_eligible_actions: active_iteration.default_not_eligible_routing, + RuleType.not_actionable_actions: active_iteration.default_not_actionable_routing, + } + + default_comms = routing_map.get(rule_type) + action_mapper = active_iteration.actions_mapper + return action_rules, action_mapper, default_comms + + @staticmethod + def _get_actions_from_comms(action_mapper: ActionsMapper, comms: str) -> list[SuggestedAction] | None: + suggested_actions: list[SuggestedAction] = [] + for comm in comms.split("|"): + action = action_mapper.get(comm) + if action is not None: + suggested_actions.append( + SuggestedAction( + internal_action_code=InternalActionCode(comm), + action_type=ActionType(action.action_type), + action_code=ActionCode(action.action_code), + action_description=ActionDescription(action.action_description) + if action.action_description + else None, + url_link=UrlLink(action.url_link) if action.url_link else None, + url_label=UrlLabel(action.url_label) if action.url_label else None, + ) + ) + return suggested_actions diff --git a/src/eligibility_signposting_api/services/processors/campaign_evaluator.py b/src/eligibility_signposting_api/services/processors/campaign_evaluator.py new file mode 100644 index 000000000..864d45c8c --- /dev/null +++ b/src/eligibility_signposting_api/services/processors/campaign_evaluator.py @@ -0,0 +1,43 @@ +from collections.abc import Collection, Iterator +from itertools import groupby +from operator import attrgetter + +from wireup import service + +from eligibility_signposting_api.model import eligibility_status +from eligibility_signposting_api.model.campaign_config import CampaignConfig + + +@service +class CampaignEvaluator: + """Filters and groups campaign configurations.""" + + def get_active_campaigns(self, campaign_configs: Collection[CampaignConfig]) -> list[CampaignConfig]: + return [cc for cc in campaign_configs if cc.campaign_live] + + def get_requested_grouped_campaigns( + self, campaign_configs: Collection[CampaignConfig], conditions: list[str], category: str + ) -> Iterator[tuple[eligibility_status.ConditionName, list[CampaignConfig]]]: + mapping = { + "ALL": {"V", "S"}, + "VACCINATIONS": {"V"}, + "SCREENING": {"S"}, + } + + allowed_types = mapping.get(category, set()) + + filter_all_conditions = "ALL" in conditions + + active_campaigns = self.get_active_campaigns(campaign_configs) + + for condition_name, campaign_group in groupby( + sorted(active_campaigns, key=attrgetter("target")), + key=attrgetter("target"), + ): + campaigns = list(campaign_group) + if ( + campaigns + and campaigns[0].type in allowed_types + and (filter_all_conditions or str(condition_name) in conditions) + ): + yield condition_name, campaigns diff --git a/src/eligibility_signposting_api/services/processors/cohort_handler.py b/src/eligibility_signposting_api/services/processors/cohort_handler.py new file mode 100644 index 000000000..d5848e52b --- /dev/null +++ b/src/eligibility_signposting_api/services/processors/cohort_handler.py @@ -0,0 +1,110 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING + +from eligibility_signposting_api.model.eligibility_status import CohortGroupResult, Status + +if TYPE_CHECKING: + from collections.abc import Iterable + + from eligibility_signposting_api.model.campaign_config import CohortLabel, IterationCohort, IterationRule + from eligibility_signposting_api.model.person import Person + from eligibility_signposting_api.services.processors.rule_processor import RuleProcessor + + +class CohortEligibilityHandler(ABC): + """Abstract base class for eligibility/actionability handlers.""" + + def __init__(self, next_handler: CohortEligibilityHandler | None = None) -> None: + self.next_handler = next_handler + + @abstractmethod + def handle( + self, + person: Person, + cohort: IterationCohort, + cohort_results: dict[CohortLabel, CohortGroupResult], + rules_processor: RuleProcessor, + ) -> None: + """Handles a part of the eligibility/actionability check or passes to the next handler.""" + + def next(self, next_handler: CohortEligibilityHandler) -> CohortEligibilityHandler: + """Sets the next handler in the chain and returns this handler for chaining.""" + self.next_handler = next_handler + return next_handler + + def pass_to_next( + self, + person: Person, + cohort: IterationCohort, + cohort_results: dict[CohortLabel, CohortGroupResult], + rules_processor: RuleProcessor, + ) -> None: + """Passes the request to the next handler in the chain if one exists.""" + if self.next_handler: + self.next_handler.handle(person, cohort, cohort_results, rules_processor) + + +class BaseEligibilityHandler(CohortEligibilityHandler): + """Handles the base eligibility check (person in cohort or magic cohort).""" + + def handle( + self, + person: Person, + cohort: IterationCohort, + cohort_results: dict[CohortLabel, CohortGroupResult], + rules_processor: RuleProcessor, + ) -> None: + if not rules_processor.is_base_eligible(person, cohort): + cohort_results[cohort.cohort_label] = CohortGroupResult( + cohort.cohort_group, + Status.not_eligible, + [], + cohort.negative_description, + [], + ) + return + + self.pass_to_next(person, cohort, cohort_results, rules_processor) + + +class FilterRuleHandler(CohortEligibilityHandler): + """Handles the eligibility check based on filter rules.""" + + def __init__( + self, filter_rules: Iterable[IterationRule], next_handler: CohortEligibilityHandler | None = None + ) -> None: + super().__init__(next_handler) + self.filter_rules = filter_rules + + def handle( + self, + person: Person, + cohort: IterationCohort, + cohort_results: dict[CohortLabel, CohortGroupResult], + rules_processor: RuleProcessor, + ) -> None: + if not rules_processor.is_eligible(person, cohort, cohort_results, self.filter_rules): + return + + self.pass_to_next(person, cohort, cohort_results, rules_processor) + + +class SuppressionRuleHandler(CohortEligibilityHandler): + """Handles the actionability check based on suppression rules.""" + + def __init__( + self, suppression_rules: Iterable[IterationRule], next_handler: CohortEligibilityHandler | None = None + ) -> None: + super().__init__(next_handler) + self.suppression_rules = suppression_rules + + def handle( + self, + person: Person, + cohort: IterationCohort, + cohort_results: dict[CohortLabel, CohortGroupResult], + rules_processor: RuleProcessor, + ) -> None: + rules_processor.is_actionable(person, cohort, cohort_results, self.suppression_rules) diff --git a/src/eligibility_signposting_api/services/processors/person_data_reader.py b/src/eligibility_signposting_api/services/processors/person_data_reader.py new file mode 100644 index 000000000..6b365ae7f --- /dev/null +++ b/src/eligibility_signposting_api/services/processors/person_data_reader.py @@ -0,0 +1,25 @@ +from __future__ import annotations + +from wireup import service + +from eligibility_signposting_api.model.person import Person + + +@service +class PersonDataReader: + """Handles extracting and interpreting person data.""" + + def get_person_cohorts(self, person: Person) -> set[str]: + cohorts_row: Person = Person([]) + for data in person.data: + if data.get("ATTRIBUTE_TYPE") == "COHORTS": + cohorts_row.data.append(data) + + person_cohorts = set() + + if cohorts_row.data: + for membership in cohorts_row.data[0].get("COHORT_MEMBERSHIPS", []): + if membership.get("COHORT_LABEL"): + person_cohorts.add(membership.get("COHORT_LABEL")) + + return person_cohorts diff --git a/src/eligibility_signposting_api/services/processors/rule_processor.py b/src/eligibility_signposting_api/services/processors/rule_processor.py new file mode 100644 index 000000000..8b49d2778 --- /dev/null +++ b/src/eligibility_signposting_api/services/processors/rule_processor.py @@ -0,0 +1,184 @@ +from __future__ import annotations + +from dataclasses import dataclass, field +from itertools import groupby +from operator import attrgetter +from typing import TYPE_CHECKING + +from wireup import service + +from eligibility_signposting_api.model import eligibility_status +from eligibility_signposting_api.model.campaign_config import ( + CohortLabel, + Iteration, + IterationCohort, + IterationRule, + RuleType, +) +from eligibility_signposting_api.model.eligibility_status import CohortGroupResult, Status +from eligibility_signposting_api.services.calculators.rule_calculator import RuleCalculator +from eligibility_signposting_api.services.processors.cohort_handler import ( + BaseEligibilityHandler, + FilterRuleHandler, + SuppressionRuleHandler, +) +from eligibility_signposting_api.services.processors.person_data_reader import PersonDataReader + +if TYPE_CHECKING: + from collections.abc import Iterable, Iterator + + from eligibility_signposting_api.model.person import Person + + +@service +@dataclass +class RuleProcessor: + """Handles the processing and evaluation of different rules (filter, suppression) against person data.""" + + person_data_reader: PersonDataReader = field(default_factory=PersonDataReader) + + def is_base_eligible(self, person: Person, cohort: IterationCohort) -> bool: + person_cohorts = self.person_data_reader.get_person_cohorts(person) + return cohort.cohort_label in person_cohorts or cohort.is_magic_cohort + + def is_eligible( + self, + person: Person, + cohort: IterationCohort, + cohort_results: dict[CohortLabel, CohortGroupResult], + filter_rules: Iterable[IterationRule], + ) -> bool: + is_eligible = True + priority_getter = attrgetter("priority") + sorted_rules_by_priority = sorted(filter_rules, key=priority_getter) + + for _, rule_group in groupby(sorted_rules_by_priority, key=priority_getter): + group_rules = list(rule_group) + if self._should_skip_rule_group(cohort, group_rules): + continue + status, group_exclusion_reasons, _ = self.evaluate_rules_priority_group(person, iter(group_rules)) + if status.is_exclusion: + if cohort.cohort_label is not None: + cohort_results[cohort.cohort_label] = CohortGroupResult( + cohort.cohort_group, + Status.not_eligible, + [], + cohort.negative_description, + group_exclusion_reasons, + ) + is_eligible = False + break + + return is_eligible + + def is_actionable( + self, + person: Person, + cohort: IterationCohort, + cohort_results: dict[CohortLabel, CohortGroupResult], + suppression_rules: Iterable[IterationRule], + ) -> None: + is_actionable: bool = True + priority_getter = attrgetter("priority") + suppression_reasons = [] + + sorted_rules_by_priority = sorted(suppression_rules, key=priority_getter) + + for _, rule_group in groupby(sorted_rules_by_priority, key=priority_getter): + group_rules = list(rule_group) + if self._should_skip_rule_group(cohort, group_rules): + continue + + status, group_exclusion_reasons, rule_stop = self.evaluate_rules_priority_group(person, iter(group_rules)) + if status.is_exclusion: + is_actionable = False + suppression_reasons.extend(group_exclusion_reasons) + if rule_stop: + break + + if cohort.cohort_label is not None: + key = cohort.cohort_label + if is_actionable: + cohort_results[key] = CohortGroupResult( + cohort.cohort_group, Status.actionable, [], cohort.positive_description, suppression_reasons + ) + else: + cohort_results[key] = CohortGroupResult( + cohort.cohort_group, + Status.not_actionable, + suppression_reasons, + cohort.positive_description, + suppression_reasons, + ) + + @staticmethod + def _should_skip_rule_group(cohort: IterationCohort, group_rules: list[IterationRule]) -> bool: + cohort_specific_rules = [rule for rule in group_rules if rule.cohort_label is not None] + matching_specific_rules = [rule for rule in cohort_specific_rules if rule.cohort_label == cohort.cohort_label] + return bool(cohort_specific_rules and not matching_specific_rules) + + def evaluate_rules_priority_group( + self, person: Person, rules_group: Iterator[IterationRule] + ) -> tuple[eligibility_status.Status, list[eligibility_status.Reason], bool]: + is_rule_stop = False + exclusion_reasons = [] + best_status = eligibility_status.Status.not_eligible + + for rule in rules_group: + is_rule_stop = rule.rule_stop or is_rule_stop + rule_calculator = RuleCalculator(person=person, rule=rule) + status, reason = rule_calculator.evaluate_exclusion() + if status.is_exclusion: + best_status = eligibility_status.Status.best(status, best_status) + exclusion_reasons.append(reason) + else: + best_status = eligibility_status.Status.actionable + + return best_status, exclusion_reasons, is_rule_stop + + @staticmethod + def get_exclusion_rules(cohort: IterationCohort, rules: Iterable[IterationRule]) -> Iterator[IterationRule]: + return ( + ir + for ir in rules + if ir.cohort_label is None + or cohort.cohort_label == ir.cohort_label + or (isinstance(ir.cohort_label, (list, set, tuple)) and cohort.cohort_label in ir.cohort_label) + ) + + def get_cohort_group_results( + self, person: Person, active_iteration: Iteration + ) -> dict[CohortLabel, CohortGroupResult]: + cohort_results: dict[CohortLabel, CohortGroupResult] = {} + filter_rules, suppression_rules = self.get_rules_by_type(active_iteration) + + cohort_base_handler = BaseEligibilityHandler() + filter_rule_handler = FilterRuleHandler(filter_rules=filter_rules) + suppression_rule_handler = SuppressionRuleHandler(suppression_rules=suppression_rules) + + cohort_base_handler.next(filter_rule_handler).next(suppression_rule_handler) + + for cohort in sorted(active_iteration.iteration_cohorts, key=attrgetter("priority")): + cohort_base_handler.handle(person, cohort, cohort_results, self) + + return cohort_results + + def get_not_base_eligible_results( + self, cohort: IterationCohort, cohort_results: dict[str, CohortGroupResult] + ) -> dict[str, CohortGroupResult]: + cohort_results[cohort.cohort_label] = CohortGroupResult( + cohort.cohort_group, + Status.not_eligible, + [], + cohort.negative_description, + [], + ) + return cohort_results + + @staticmethod + def get_rules_by_type(active_iteration: Iteration) -> tuple[tuple[IterationRule, ...], tuple[IterationRule, ...]]: + filter_rules, suppression_rules = ( + tuple(rule for rule in active_iteration.iteration_rules if attrgetter("type")(rule) == rule_type) + for rule_type in (RuleType.filter, RuleType.suppression) + ) + return filter_rules, suppression_rules diff --git a/src/eligibility_signposting_api/views/eligibility.py b/src/eligibility_signposting_api/views/eligibility.py index 0f507f65c..383d73d13 100644 --- a/src/eligibility_signposting_api/views/eligibility.py +++ b/src/eligibility_signposting_api/views/eligibility.py @@ -8,18 +8,18 @@ from flask.typing import ResponseReturnValue from wireup import Injected -from eligibility_signposting_api.api_error_response import NHS_NUMBER_NOT_FOUND_ERROR from eligibility_signposting_api.audit.audit_context import AuditContext from eligibility_signposting_api.audit.audit_service import AuditService -from eligibility_signposting_api.model.eligibility import Condition, EligibilityStatus, NHSNumber, Status +from eligibility_signposting_api.common.api_error_response import NHS_NUMBER_NOT_FOUND_ERROR +from eligibility_signposting_api.model.eligibility_status import Condition, EligibilityStatus, NHSNumber, Status from eligibility_signposting_api.services import EligibilityService, UnknownPersonError -from eligibility_signposting_api.views.response_model import eligibility -from eligibility_signposting_api.views.response_model.eligibility import ProcessedSuggestion +from eligibility_signposting_api.views.response_model import eligibility_response +from eligibility_signposting_api.views.response_model.eligibility_response import ProcessedSuggestion STATUS_MAPPING = { - Status.actionable: eligibility.Status.actionable, - Status.not_actionable: eligibility.Status.not_actionable, - Status.not_eligible: eligibility.Status.not_eligible, + Status.actionable: eligibility_response.Status.actionable, + Status.not_actionable: eligibility_response.Status.not_actionable, + Status.not_eligible: eligibility_response.Status.not_eligible, } logger = logging.getLogger(__name__) @@ -29,13 +29,6 @@ @eligibility_blueprint.before_request def before_request() -> None: - logger.info( - "request details", - extra={ - "X-Request-ID": request.headers.get("X-Request-ID"), - "X-Correlation-ID": request.headers.get("X-Correlation-ID"), - }, - ) AuditContext.add_request_details(request) @@ -56,11 +49,9 @@ def check_eligibility( except UnknownPersonError: return handle_unknown_person_error(nhs_number) else: - eligibility_response: eligibility.EligibilityResponse = build_eligibility_response(eligibility_status) + response: eligibility_response.EligibilityResponse = build_eligibility_response(eligibility_status) AuditContext.write_to_firehose(audit_service) - return make_response( - eligibility_response.model_dump(by_alias=True, mode="json", exclude_none=True), HTTPStatus.OK - ) + return make_response(response.model_dump(by_alias=True, mode="json", exclude_none=True), HTTPStatus.OK) def get_or_default_query_params() -> dict[str, Any]: @@ -100,10 +91,10 @@ def handle_unknown_person_error(nhs_number: NHSNumber) -> ResponseReturnValue: response = NHS_NUMBER_NOT_FOUND_ERROR.log_and_generate_response( log_message=diagnostics, diagnostics=diagnostics, location_param="id" ) - return make_response(response.get("body"), response.get("statusCode")) + return make_response(response.get("body"), response.get("statusCode"), response.get("headers")) -def build_eligibility_response(eligibility_status: EligibilityStatus) -> eligibility.EligibilityResponse: +def build_eligibility_response(eligibility_status: EligibilityStatus) -> eligibility_response.EligibilityResponse: """Return an object representing the API response we are going to send, given an evaluation of the person's eligibility.""" @@ -111,9 +102,9 @@ def build_eligibility_response(eligibility_status: EligibilityStatus) -> eligibi for condition in eligibility_status.conditions: suggestions = ProcessedSuggestion( # pyright: ignore[reportCallIssue] - condition=eligibility.ConditionName(condition.condition_name), # pyright: ignore[reportCallIssue] + condition=eligibility_response.ConditionName(condition.condition_name), # pyright: ignore[reportCallIssue] status=STATUS_MAPPING[condition.status], - statusText=eligibility.StatusText(f"{condition.status}"), # pyright: ignore[reportCallIssue] + statusText=eligibility_response.StatusText(condition.status_text), # pyright: ignore[reportCallIssue] eligibilityCohorts=build_eligibility_cohorts(condition), # pyright: ignore[reportCallIssue] suitabilityRules=build_suitability_results(condition), # pyright: ignore[reportCallIssue] actions=build_actions(condition), @@ -122,27 +113,29 @@ def build_eligibility_response(eligibility_status: EligibilityStatus) -> eligibi processed_suggestions.append(suggestions) response_id = uuid.uuid4() - updated = eligibility.LastUpdated(datetime.now(tz=UTC)) + updated = eligibility_response.LastUpdated(datetime.now(tz=UTC)) AuditContext.add_response_details(response_id, updated) - return eligibility.EligibilityResponse( # pyright: ignore[reportCallIssue] + return eligibility_response.EligibilityResponse( # pyright: ignore[reportCallIssue] responseId=response_id, # pyright: ignore[reportCallIssue] - meta=eligibility.Meta(lastUpdated=updated), + meta=eligibility_response.Meta(lastUpdated=updated), # pyright: ignore[reportCallIssue] processedSuggestions=processed_suggestions, ) -def build_actions(condition: Condition) -> list[eligibility.Action] | None: +def build_actions(condition: Condition) -> list[eligibility_response.Action] | None: if condition.actions is not None: return [ - eligibility.Action( - actionType=eligibility.ActionType(action.action_type), - actionCode=eligibility.ActionCode(action.action_code), - description=eligibility.Description(action.action_description or ""), - urlLabel=eligibility.UrlLabel(action.url_label or ""), - urlLink=eligibility.UrlLink(str(action.url_link)) if action.url_link else eligibility.UrlLink(""), + eligibility_response.Action( + actionType=eligibility_response.ActionType(action.action_type), + actionCode=eligibility_response.ActionCode(action.action_code), + description=eligibility_response.Description(action.action_description or ""), + urlLabel=eligibility_response.UrlLabel(action.url_label or ""), + urlLink=eligibility_response.UrlLink(str(action.url_link)) + if action.url_link + else eligibility_response.UrlLink(""), ) for action in condition.actions ] @@ -150,13 +143,13 @@ def build_actions(condition: Condition) -> list[eligibility.Action] | None: return None -def build_eligibility_cohorts(condition: Condition) -> list[eligibility.EligibilityCohort]: +def build_eligibility_cohorts(condition: Condition) -> list[eligibility_response.EligibilityCohort]: """Group Iteration cohorts and make only one entry per cohort group""" return [ - eligibility.EligibilityCohort( - cohortCode=eligibility.CohortCode(cohort_result.cohort_code), - cohortText=eligibility.CohortText(cohort_result.description), + eligibility_response.EligibilityCohort( + cohortCode=eligibility_response.CohortCode(cohort_result.cohort_code), + cohortText=eligibility_response.CohortText(cohort_result.description), cohortStatus=STATUS_MAPPING[cohort_result.status], ) for cohort_result in condition.cohort_results @@ -164,25 +157,16 @@ def build_eligibility_cohorts(condition: Condition) -> list[eligibility.Eligibil ] -def build_suitability_results(condition: Condition) -> list[eligibility.SuitabilityRule]: - """Make only one entry if there are duplicate rules""" +def build_suitability_results(condition: Condition) -> list[eligibility_response.SuitabilityRule]: if condition.status != Status.not_actionable: return [] - unique_rule_codes = set() - suitability_results = [] - - for cohort_result in condition.cohort_results: - if cohort_result.status == Status.not_actionable: - for reason in cohort_result.reasons: - if reason.rule_name not in unique_rule_codes and reason.rule_description: - unique_rule_codes.add(reason.rule_name) - suitability_results.append( - eligibility.SuitabilityRule( - ruleType=eligibility.RuleType(reason.rule_type.value), - ruleCode=eligibility.RuleCode(reason.rule_name), - ruleText=eligibility.RuleText(reason.rule_description), - ) - ) - - return suitability_results + return [ + eligibility_response.SuitabilityRule( + ruleType=eligibility_response.RuleType(reason.rule_type.value), + ruleCode=eligibility_response.RuleCode(reason.rule_name), + ruleText=eligibility_response.RuleText(reason.rule_description), + ) + for reason in condition.suitability_rules + if reason.rule_description + ] diff --git a/src/eligibility_signposting_api/views/response_model/eligibility.py b/src/eligibility_signposting_api/views/response_model/eligibility_response.py similarity index 100% rename from src/eligibility_signposting_api/views/response_model/eligibility.py rename to src/eligibility_signposting_api/views/response_model/eligibility_response.py diff --git a/src/rules_validation_api/README.md b/src/rules_validation_api/README.md new file mode 100644 index 000000000..68d314d1a --- /dev/null +++ b/src/rules_validation_api/README.md @@ -0,0 +1,26 @@ +# ๐Ÿงช Campaign-config Validation + +This Python script is designed to validate a campaign configuration JSON file. + +## ๐Ÿ›  Requirements + +- Python 3.13 +- `rules_validation_api` must be installed and accessible +- Campaign configuration JSON file to verify + +## Steps to verify + +- Get to the `rules_validation_api` folder +- Run `python app.py --config_path ` + +## Results + +- `On success`: + + ```text + "Valid config" is printed + +- `On Failure`: + + ```text + "Errors" is printed diff --git a/src/rules_validation_api/__init__.py b/src/rules_validation_api/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/rules_validation_api/app.py b/src/rules_validation_api/app.py new file mode 100644 index 000000000..feb9464ba --- /dev/null +++ b/src/rules_validation_api/app.py @@ -0,0 +1,29 @@ +import argparse +import json +import sys +from pathlib import Path + +from rules_validation_api.validators.rules_validator import RulesValidation + +GREEN = "\033[92m" # pragma: no cover +RESET = "\033[0m" # pragma: no cover +YELLOW = "\033[93m" # pragma: no cover +RED = "\033[91m" # pragma: no cover + + +def main() -> None: # pragma: no cover + parser = argparse.ArgumentParser(description="Validate campaign configuration.") + parser.add_argument("--config_path", required=True, help="Path to the campaign config JSON file") + args = parser.parse_args() + + try: + with Path(args.config_path).open() as file: + json_data = json.load(file) + RulesValidation(**json_data) + sys.stdout.write(f"{GREEN}Valid Config{RESET}\n") + except ValueError as e: + sys.stderr.write(f"{YELLOW}Validation Error:{RESET} {RED}{e}{RESET}\n") + + +if __name__ == "__main__": # pragma: no cover + main() diff --git a/src/rules_validation_api/validators/__init__.py b/src/rules_validation_api/validators/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/rules_validation_api/validators/actions_mapper_validator.py b/src/rules_validation_api/validators/actions_mapper_validator.py new file mode 100644 index 000000000..200b056d1 --- /dev/null +++ b/src/rules_validation_api/validators/actions_mapper_validator.py @@ -0,0 +1,13 @@ +from pydantic import model_validator + +from eligibility_signposting_api.model.campaign_config import ActionsMapper + + +class ActionsMapperValidation(ActionsMapper): + @model_validator(mode="after") + def validate_keys(self) -> "ActionsMapperValidation": + invalid_keys = [key for key in self.root if key is None or key == ""] + if invalid_keys: + msg = f"Invalid keys found in ActionsMapper: {invalid_keys}" + raise ValueError(msg) + return self diff --git a/src/rules_validation_api/validators/available_action_validator.py b/src/rules_validation_api/validators/available_action_validator.py new file mode 100644 index 000000000..c8bf788d4 --- /dev/null +++ b/src/rules_validation_api/validators/available_action_validator.py @@ -0,0 +1,5 @@ +from eligibility_signposting_api.model.campaign_config import AvailableAction + + +class AvailableActionValidation(AvailableAction): + pass diff --git a/src/rules_validation_api/validators/campaign_config_validator.py b/src/rules_validation_api/validators/campaign_config_validator.py new file mode 100644 index 000000000..94b007346 --- /dev/null +++ b/src/rules_validation_api/validators/campaign_config_validator.py @@ -0,0 +1,29 @@ +import typing +from operator import attrgetter + +from pydantic import field_validator, model_validator + +from eligibility_signposting_api.model.campaign_config import CampaignConfig, Iteration +from rules_validation_api.validators.iteration_validator import IterationValidation + + +class CampaignConfigValidation(CampaignConfig): + @field_validator("iterations") + @classmethod + def validate_iterations(cls, iterations: list[Iteration]) -> list[IterationValidation]: + return [IterationValidation(**i.model_dump()) for i in iterations] + + @model_validator(mode="after") + def check_has_iteration_from_start(self) -> typing.Self: + iterations_by_date = sorted(self.iterations, key=attrgetter("iteration_date")) + if first_iteration := next(iter(iterations_by_date), None): + if first_iteration.iteration_date > self.start_date: + message = ( + f"campaign {self.id} starts on {self.start_date}, " + f"1st iteration starts later - {first_iteration.iteration_date}" + ) + raise ValueError(message) + return self + # Should never happen, since we are constraining self.iterations with a min_length of 1 + message = f"campaign {self.id} has no iterations." + raise ValueError(message) diff --git a/src/rules_validation_api/validators/iteration_cohort_validator.py b/src/rules_validation_api/validators/iteration_cohort_validator.py new file mode 100644 index 000000000..32e1a4b3a --- /dev/null +++ b/src/rules_validation_api/validators/iteration_cohort_validator.py @@ -0,0 +1,5 @@ +from eligibility_signposting_api.model.campaign_config import IterationCohort + + +class IterationCohortValidation(IterationCohort): + pass diff --git a/src/rules_validation_api/validators/iteration_rules_validator.py b/src/rules_validation_api/validators/iteration_rules_validator.py new file mode 100644 index 000000000..341a08c1f --- /dev/null +++ b/src/rules_validation_api/validators/iteration_rules_validator.py @@ -0,0 +1,18 @@ +from typing import Self + +from pydantic import model_validator + +from eligibility_signposting_api.model.campaign_config import IterationRule, RuleAttributeLevel, RuleAttributeName + + +class IterationRuleValidation(IterationRule): + @model_validator(mode="after") + def check_cohort_attribute_name(self) -> Self: + if ( + self.attribute_level == RuleAttributeLevel.COHORT + and self.attribute_name + and self.attribute_name != RuleAttributeName("COHORT_LABEL") + ): + msg = "When attribute_level is COHORT, attribute_name must be COHORT_LABEL or None (default:COHORT_LABEL)" + raise ValueError(msg) + return self diff --git a/src/rules_validation_api/validators/iteration_validator.py b/src/rules_validation_api/validators/iteration_validator.py new file mode 100644 index 000000000..c16286ab2 --- /dev/null +++ b/src/rules_validation_api/validators/iteration_validator.py @@ -0,0 +1,152 @@ +import typing + +from pydantic import Field, ValidationError, field_validator, model_validator +from pydantic_core import InitErrorDetails + +from eligibility_signposting_api.model.campaign_config import ( + ActionsMapper, + Iteration, + IterationCohort, + IterationRule, + RuleType, +) +from rules_validation_api.validators.actions_mapper_validator import ActionsMapperValidation +from rules_validation_api.validators.iteration_cohort_validator import IterationCohortValidation +from rules_validation_api.validators.iteration_rules_validator import IterationRuleValidation + + +class IterationValidation(Iteration): + iteration_cohorts: list[IterationCohort] = Field(..., alias="IterationCohorts") + iteration_rules: list[IterationRule] = Field(..., alias="IterationRules") + actions_mapper: ActionsMapper = Field(..., alias="ActionsMapper") + + @field_validator("iteration_rules") + @classmethod + def validate_iteration_rules(cls, iteration_rules: list[IterationRule]) -> list[IterationRuleValidation]: + return [IterationRuleValidation(**i.model_dump()) for i in iteration_rules] + + @field_validator("iteration_cohorts") + @classmethod + def validate_iteration_cohorts(cls, iteration_cohorts: list[IterationCohort]) -> list[IterationCohortValidation]: + return [IterationCohortValidation(**i.model_dump()) for i in iteration_cohorts] + + @field_validator("actions_mapper", mode="after") + @classmethod + def transform_actions_mapper(cls, action_mapper: ActionsMapper) -> ActionsMapper: + ActionsMapperValidation.model_validate(action_mapper.model_dump()) + return action_mapper + + @model_validator(mode="after") + def action_mapper_validation(self) -> typing.Self: + all_errors = [] + + for validator in [ + self.validate_default_comms_routing_in_actions_mapper, + self.validate_default_not_eligible_routing_in_actions_mapper, + self.validate_default_not_actionable_routing_in_actions_mapper, + self.validate_iteration_rules_against_actions_mapper, + ]: + try: + validator() + except ValidationError as ve: + all_errors.extend(ve.errors(include_input=False)) + + if all_errors: + raise ValidationError.from_exception_data(title="IterationValidation", line_errors=all_errors) + + return self + + def validate_default_comms_routing_in_actions_mapper(self) -> typing.Self: + default_routes = self.default_comms_routing + actions_keys = list(self.actions_mapper.root.keys()) + line_errors = [] + + for routing in default_routes.split("|"): + cleaned_routing = routing.strip() + if cleaned_routing and (not actions_keys or cleaned_routing not in actions_keys): + error = InitErrorDetails( + type="value_error", + loc=("actions_mapper",), + input=actions_keys, + ctx={"error": f"Missing entry for DefaultCommsRouting '{cleaned_routing}' in ActionsMapper"}, + ) + line_errors.append(error) + + if line_errors: + raise ValidationError.from_exception_data(title="IterationValidation", line_errors=line_errors) + + return self + + def validate_default_not_eligible_routing_in_actions_mapper(self) -> typing.Self: + default_not_eligibile_routes = self.default_not_eligible_routing + actions_keys = list(self.actions_mapper.root.keys()) + line_errors = [] + + for routing in default_not_eligibile_routes.split("|"): + cleaned_routing = routing.strip() + if cleaned_routing and (not actions_keys or cleaned_routing not in actions_keys): + error = InitErrorDetails( + type="value_error", + loc=("actions_mapper",), + input=actions_keys, + ctx={"error": f"Missing entry for DefaultNotEligibleRouting '{cleaned_routing}' in ActionsMapper"}, + ) + line_errors.append(error) + + if line_errors: + raise ValidationError.from_exception_data(title="IterationValidation", line_errors=line_errors) + + return self + + def validate_default_not_actionable_routing_in_actions_mapper(self) -> typing.Self: + default_not_actionable_routes = self.default_not_actionable_routing + actions_keys = list(self.actions_mapper.root.keys()) + line_errors = [] + + for routing in default_not_actionable_routes.split("|"): + cleaned_routing = routing.strip() + if cleaned_routing and (not actions_keys or cleaned_routing not in actions_keys): + error = InitErrorDetails( + type="value_error", + loc=("actions_mapper",), + input=actions_keys, + ctx={ + "error": f"Missing entry for DefaultNotActionableRouting '{cleaned_routing}' in ActionsMapper" + }, + ) + line_errors.append(error) + + if line_errors: + raise ValidationError.from_exception_data(title="IterationValidation", line_errors=line_errors) + + return self + + def validate_iteration_rules_against_actions_mapper(self) -> typing.Self: + actions_keys = list(self.actions_mapper.root.keys()) + line_errors = [] + + for rule in self.iteration_rules: + if ( + rule.type + in [ + RuleType.redirect, + RuleType.not_actionable_actions, + RuleType.not_eligible_actions, + ] + and rule.comms_routing + ): + for routing in rule.comms_routing.split("|"): + cleaned_routing = routing.strip() + if cleaned_routing and (not actions_keys or cleaned_routing not in actions_keys): + error = InitErrorDetails( + type="value_error", + loc=("iteration_rules",), + input=actions_keys, + ctx={"error": f"Missing entry for CommsRouting '{cleaned_routing}' in ActionsMapper"}, + ) + line_errors.append(error) + + if line_errors: + raise ValidationError.from_exception_data(title="IterationValidation", line_errors=line_errors) + + return self diff --git a/src/rules_validation_api/validators/rules_validator.py b/src/rules_validation_api/validators/rules_validator.py new file mode 100644 index 000000000..cacb143d0 --- /dev/null +++ b/src/rules_validation_api/validators/rules_validator.py @@ -0,0 +1,11 @@ +from pydantic import field_validator + +from eligibility_signposting_api.model.campaign_config import CampaignConfig, Rules +from rules_validation_api.validators.campaign_config_validator import CampaignConfigValidation + + +class RulesValidation(Rules): + @field_validator("campaign_config") + @classmethod + def validate_campaign_config(cls, campaign_config: CampaignConfig) -> CampaignConfig: + return CampaignConfigValidation(**campaign_config.model_dump()) diff --git a/tests/e2e/.hypothesis/unicode_data/15.1.0/charmap.json.gz b/tests/e2e/.hypothesis/unicode_data/15.1.0/charmap.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..d9cff3e81732ee78adb53b4e70ca6bd601d90db4 GIT binary patch literal 21735 zcmbT7Wl&vBw5D+hZh_!#!QI`1y9N*LPOuQ%Ik>wgxVr@n?s{sd_}jf4aZ1qB83=I-F^$^FgL+1AAyO37%nda&K)B?YktC_!A+ zddGtd<83wYnGPY?8)<9rZ9o9FhPO~*L#cu$>eu#sh3NA(Z_mq|Q^3nUM&Luhi)Ewe zX4m5GlJTlQ;Ip}W&*ORCrAxF+{7^6N;__|ED7Z%W%PNlgucyZiiLsjE4(5uMxxklw z$C)o+mq490fTzLZDQgom1H?}kDfxNSpI(#K;Y`pqHl z?0jrNjuhHwOEJ+0WE?FN`}RBb>=s{m_bW;OGpR0ms{Yv03UO$(pslmv!(AaNDT_r>Y0g z(7g58dc0I!dR0jhewnh{^>I+(lRQDCt4#Y|b#c?}lQ+GgtZ<79^1S44(VohfXiR5C4Sv1v=7d zxiz&T#hv_8`AplY)j%F)7Lc59GZFW--M0e2!zr$04P8zHJAn6gW$;sJwdHn}om~H8 z^AoC}-bt2s4=?a9sj>W}-fDt(TH)96RrSiW+U8`ouM$hxI6YZUzrIDTs;}}5EPmqY zZo8d`<+N^;+41rN`|gZgnX|5(czJz6p5l2V-Dvie-)I;V{<6{VE2FwO*>fYUBPBV( z9pSJ0rzfV%iTOutD?S0(q31{XoOtXhXMLlU4TaT&n@^i@oIYWkIM`DG7lU!t*Jp${ zvQyx#ToR+hzKBi(dDz*65GPwd(oU^h6lDbJ^^MDgjjlE6ip}f}5&(?4>dC{mB`a>6 zjmYOBu8S2-R-&HWlvvjH{;WI@?NE-D7n%Uaehwx^yJw;xV$IpcM)JC}=8K6Y8WOn^ zw<4A9mX?i#2QvM64_ixOW-EEW%%WZW_Ue+|9^C&SSzx)KJ==xj=A7E@5FTQe(eWzjK;s}M zULj-QKm$)55Vo-6xbkI7Ut%fvyJyQ8Cw5+mh~UH7QG*-#C7EUH)av^Vdw2h`(lk(L zJ(<^{s}}HBNEZcrYE-U#Bh3X@qTWxkPMGM(w=UHe->>sfZ0m#i0|Hyzb5ITq1P1#5 z2DHdm$ZQBax17#D*K^HPYux{8p8neN6xfu)^<^hwy*2f>UE7!hYoybIBWyblFnj|B zxJR(wwOCihc;Qtf^SGg&Me0!lPM!V5`+g?B<@meiY~Qh9#;BOn2lB{dCPD; z6>gotsY~^CitEgxYbD!`Hk{v=6u-ATACM5~%p$FL$Ag+RD(gD9X+gce8p=TG=yFdO zde_gXpq|U(mfRbXbe&4(V(n`^uGgNU5(nUUspjh3#*R^>ny&@w-6wPL9o;B3w<)df z&dktgBDBNm+4``c)WZP*|CYw^Zo*No&lM@~#fJfO2y*k!PSH+RV~$w~*0TP$K#kLL ze|}&2XW5mLiuqLaW8tO}&Fhto*|5GGx!+kUFzQEMx5Tzzj>UFfI-~HIgjR#^+3j^* zo;L`ps8^|VHUwxcR}SR-hRQNGpEX)5Em~z{Lxy$nQ6L<6n#G;2$FBbehczw z(MK(=WUDTgmW-#D%Hx8}WWS3Sj~oBjH_#i9>Y{CP+cO39)MMmya`rp2iPJ08Pz%cQ6_-JGN{edX)_=Iebeq+^ry-JWU!0-aP5R7VmZ6_zY``p8L12 zGIp~nwQF-TJ5IQ&qK}8yFG(=fF6e$u_UMj;x1H=_kd&{I`MkH%+P!=Fqpd0J;Gv_T z*4nmnfiTLl;nB_ed|HlFSC0D?q|7jAeMUD|p9Q#lt11wJ1*YNegD%``j+>H__pP!J zmjp{PnD{g*XTaNH;^r9vZPnK`u9sRxr$#t0cHOOl+_^JyBFR%pk1s`FaM~z8@7ryV7}nbMsY=XJ8Lv?n${VYPDDOH}OF^lN7ee(t>TUev$5@Nbzy;j{a7!Rr ztH>h}2tB#gwtTK{G-&85_!q?IuEU$|UrnC@ z5xsn^wPz6G5=(PBNS0!Dyr)T2RTpy)=25XLT~*Ag@kFrn@7aFTp=!H^YygebJF@nr z`DxOUc6kV$bZqtx+j~~nH|xtLc`x2-;>uqUL1|5ccEo-Uug~*ugV(LGuT$#}FQ|cJ z_jF6c5HBxh;qj#&0g)DF0GnG)bKyM=f8JgL2llo9m|$zt{hCW?$IsgxYvB$-9G3_d z$Z=&AnT0RF)=K^E-lfY5F7Gg-^=)_;@>fak{&Z5~;br!vZ4v${O~e`4fj(y?sEn;E z>QwY<60DOHS48hvPa-Qnc7g$#UJG%k7<^MQp6Y|B##+RHi!Qy55A}hL0}veJsz=1w zz1cFSf%{{SeL(lEPt9qM(ul95AyVv~Z-6zE)0n~HX87w+*{jS(kL?Yl@0E)z&1hsQ zUL=ofZ}igLi?I4$Su*vft3qyTE`Qd(P@7n&X#N!H2gc69T}`JjgxlVNdK68F!CNgU3dGyw)7e0IpLe21xlB))c}- z7>w;Tb90;PYwS4icn4A(yB^J|ufCr)C1+JXCmsWV^WO&EN48A1)N$lT)(#9uf8ir` zi56Vn4z|v-j7eJ?k(@d4M_rTMugV{ncx#zsp9u$f9eSVD{{o|fywYBLyx4L(4#GQr zzG=i-2pJC>?{r%|1Uxj48GmIw1p8^8k3!|b6wSQ@oD4gmFGx4 z%2mIg_C0Z*hwDsW|K0k=%CjE?lJ(-`A9srpyRW2|OAHJB~}vUmxi(YED6#}7mv-u+w?j{HXlFrPE7mU&bLIS*OJ2&<p?nEb_-#yQ*jofo#b#{-l*MKXPV9^uSu29d>mwor0s<>P3XtM?K zer2yp?vb7g0t5get~XUw#?xKed|ur`+f<040`M}~O-`S2&I~Ydl)mu+>`XEP4dMVt=`vWP?5xn}z9jIsA{@oQScmK1HGlNzY-EwvFU~UU|IVaco za0*gJ7avD#yuBg>Sve(uUT^AXvTRi49OVfP=8d=e07OcKJBC)Yc3;pY*8?SbKMoTtyj9*?b%6{1zKL3+Ig#& zy#PV)|M2XgI$HVKuxikn+rdT8@L z$>mC9wZthEv4=r3^<8^hXF&VYG1yS@kg(>k&cmXIs)FhjKf7qUo9e`;JK)=qTqo6Y znO&DLv(dJ%PV4-X?+cUBlg(lx07$0pWB-OCg5iQwUz>9J5??X1lB{}sfr9;Sq~kgS(N138FYVtAfGctB zOx+BZ-;f2`$2!%E%Qf9^lC~R+H_k6cd}SXLIoI86KPpk=fHt>(3$`u40ogBO2&@N4qk+o&60TjK{i62qa_ znEkTr{+@nVV^i3|z-SB0R?`Ui2<>IpgDTa& zm>%2Y=f5Q)+tsA9m*c^|AItRPQUew?GK+%paxm1sJpWo`CASfb(=~T`Oo`qpc4)PO z{$e>8x7!{A=W=N3@`la?`ar+`-P&HqI9+t6@8Q_J>L>U|;aea2#{{;8scE{mD||oK z)`#oK1t_f7M%VzxdD|a1)=rd!)1lK;7W29)z5Jmwa3&h`ieen0eZO8XVQ`=n%}7sM z`mdnJ=%|euyo_|Ef|MmAfdivB^73?!<(Xh2P5}jtxF-S?*bl+_Fyb%v7W4f1$Oyj~V_IbU7HSDcq~x71Z6AO8O0omkxp>~+CI*kH|QV) zPzC&uhLE4*?0Fj%!;nm}<r22D47zKb8}-gzP_hx+q? zwXNevI^*+a42wZ$olwngsJu#LC=XgTt;RmJkgI~mG&uXqQA<>-28KkVd|fGW@egMN zF)US#;3&qI#PI^s4nDd?IosXF=2c?oeQZ^ z40sxehX+0I749pvST~gvF|G^QukReVkvupxI+>;rl;_-6w6V1(?vPdv4)7 zog|;X6{7@*LbZn+R*EF~OSC2VX9xMXWJB$fQ=e3nXgwh06QK_?t`p2MC>yP9H$iI# zr~?_mlr`s2BQ&B0vGS&_`47a^tU>Z0Xnbt_7+HWp>Gl$esl0I_{Qtyfa4#lTc*C8> zx?fF<0u40?4JULa3cjKYn>8G)2>5XskrH}CWj3F0JErlcji&LqG$PR9C&wow%qE5OZp`2?`*|8@HcB_%xvy{FyGXNsX4-R z$y0_ne7cnB&AN05Y5CM8bCFqR8&WAx={(WO-M<=ROLe5wHBI)-iNHVq4n@Mv(MH8= z+KxW*t2AWuNUK35c2f`nI{MiFMDJQ2Rsa_NM)v){H7Vdv+pKaV{B*Wa82>ky`RF>& zw}9-=SKOGs!`*_szOM)m8Xd0f?azjHHhtA~H9vUDe;eu3Hh-~RSpkvUFn-z&Z&-7Z zt=YI{nQ1A06U!R>I9kj)l1uIzeI zKSr=@_gK?xT(GAa$GLpQyun+}E%RrM%ndb<&c17GvQ z*sRghV59N+%r0f${!3=_U4y;NABDnwn=i?WI}M~Zf~(5K8}vkVecu}Y@R6g*H`1k1 z?-AQJO1wDE_7LG-dR!LgA5;f1)8&#^~5ku5vm@`ov?^`vB3jk?Gcs}SRHf&^nEo$uvjZp^{d_|ODKNFB^4+5*(1 z_nxJ7wx=qYX~f`(!F=KZ8_ksUNIO8fm4GY(DKe4DxIH}y>+ zI})p^1B0}A`uk!b-(nw0ufRczNrm`A8`9vOO9x2NdtwLOg;CvJu!q*$!PJ(nDoC&r zb_79%`>C}^|6^$^zPFrNhS%f^N&Ya1AjvP#LXv>%F|@1{YqSR-g7#X2mG3e+D;;9# zL#U`jp?4+lNg(K`sLLYgh>}163`n+ZLyugkz!97Ca(hAtay+pl&=9hQ`>Cqu5&W!R zPygR!NS0j4$Uy!`+MJCYLBkwVuYzKI#bG9T$ixh_bcmb+S_TfP7H`ZL%_88R6EH)d z7$Z*6;TZju3jxPmHKY2UVgHO|w;qI6j7(t0VxyHdGZU#($=|=UJJApT6<3_-; zLt7#J&nL?xyva#P$GYNwvU_tOU~PZ+(*jf1y{hm*j^bEMot5Bcl+VV;E^AcMn7$(R zh-DLc4sV0^h4p4bh#w+1l^Nz2C-^CAj>+DnDs|p`Bz;X+2x&?Q`N|atGe#`o3TO6Q zaJD5zEr2@^p^+G#@LM|2ZcIdBS&X5_4@NoZY7ko}6SxTG;Q&ae7vir(U2K#I-AAUZ z5X?%w8jbL#U;U8^7UpU8(TE#=~eRl-gQi=o`Sl+uQ4nH6b&<;RA^O~lX=epA{_wQ^7BIR z0qbQBLax{$A7C1V?U&>rOiT17^jI}3vc9G8Ar3V4BF3#ZN%L{-(%%rfvzoS^q1ViR z4bg8ZN(Sdi-yn?}A~vlm2B4%;NaMMPO{a zN>l=F_3Fj2GcT@?J8Yy%&2ueG)L^rB^;2^AzVxHND1G?8q~4|r-1>~}x4`Y!;FnMc z${jw4b=kPJ$0G3fH9hChz7d^_nsEG#b;G#6$2~CorR%JF;57~+cs8|ia$4K7`-%?H zxFb5&Dt*+lwNfQb#kag|$#7_eB>i;N6-nNBGkKfTmb#;Rcj469)At$%A^OJ{eJ%Yb z`q%!y8V2u96Z#-z-EaTO-@DVvdhq`&|3@YI+2kO26q3^o{8vS{k|4d@y zh5K%4-PrtJ|lN_O;-UYLyLkgA}AZl?R6kjEMaC5*ue)#n9=DQWXTPJD!(tFHO z=qtwX>7}i;^1sww*Zr&&jprr6 zvu5JV@)dr?yaQjl@_mgP9^dxn|JsATRI__~>ii<`wI>KcmI|ClFOSPl8@cAtT+|v- z5chX#WhCNMfXI2(=GT&rL ztwc$`u||zjVWwBgtXPkpbr_DJn>L3u++sirA8z|Z701eLH6pn5+Q1OI298)1gsu%> zVC*K4X+Qe)W4sY#p+8ECqYIA%?{|puD2a`~Tbr6G^-Pi8#>*AF3dbxiMf`puMr{8+ zXzjmoR8zyc6Ob346RE!-5_3teUZ>s|L-ra%=E?N)SVIiABTxG8@_lV`UTreamv2v6 zJ=jT@>vk3obPwb$q6P9OUx6;q)?C7!=mKe2D9)5fF1f=&GD4co^jO^y;lbaewxh$f zHlsjimza~1NQbwSA~C`1iz5co@!EfM;LM*snk)joB#2FmU!H--rIf ziBq?DpmQP98yFc8h5C}2K!aAJ><}sVvyiB>9{rcGupB$OCmTB8{2aDymF}*Sm7=61 zM5>xY3eZj|Pf}`}w(oU*fP68I8(ae0KRZz;76o6wx0RAyW=6%)($T=5C}g8T@xUOd zmTqFUVn!+t6-(5=gD`)f8s|$n3y5l@E~Km|AJHQ2jpuNfDx0PIODX}G<@=M`sB^u& z9-n_0x5;2~SiYVPAo;FgGCAVh_2G+k8V37!uIxa;3y!Yu!as^&V{gq*sus(5mckwU zsjQ6$4W>rB6Tbnso(*FUzgN=@&*Xx4{odZ+v@0A;WMMxWH{+H3rg-`hf532k>mgqY zofJ(_G8&h;_MK@Bpofq>&~TYa5?IJi`(oycuE`uOqBs2E?!f`+${)qoYzXULR{>0xa5k*DObR5S86_|N>wPkNt ze?S=1);t=$95Kx;f*+=$VOSPgOf`QBci@41snGcu$VMPE!Dnz$#eFHw!zRa%)7Zzm zxhLun4mfC*)YfGG+5g#>F3=?>_)kOR6t2klCLe}}H$#U|BFSD3hP#w;6)pQweW^O7o@#8h zD7wTwXi8H>ptrFS$#Qsb4lneKAkS#G0g+Eiay|P;*{7k+U@YxBwuz+THCI%lO+!^3x|6Q2xNNfN`WM_`qp4PZ#P7#1Od zd=u4+EC?F4EegYB3ShrY^u>m;%#LGA?I2mJY$!QGvW}tkE(%n5 zsML#XBl_6G1N@nrSwIE3XWv#-VIb9p+FFn^#W3 zE~-(v#gTE5ioY8GW&Use76dmJA1RdrZoIc%WN?fx$bvw^8+D5{jC~r`zEaYPAPFFn zW%9O1LVY>U#)6;e<`8NK3Sv=V_k-zN57kAeDG?fkMc?Xpy#kr#5<~@~@`;8|=fGY} zQurMPQMuE9;Mk(QVH8dX<=o>muU<_+U=PwRr?!^2I9UfkDSm7x`cx$Oxxbf)_htx9 z>Iq!Q>Ntt!ph|yCezwwT<@$qg+t3=vs$~-yf6Hpa4l@vSMHe>g4U-cLh3aZetuojT zYF8jZL~Yb~Trj*St98JMKE^e3fGOe$Rm&19+4#*xGact~YU(by91YLr#=DEJS^wK^ zMP6PpN42N9u`KpY z?1y5ddj-SHXOrs3i1Pl$tYl)Esz0lPzmDfsQFsrPIL|!(z@oKvfa`>o3u)pM(IYAO z<~h09@w2ePO?Z{MTjzB^24-+^ttauM_Es5;Y}JqJ78lXgZBEKQ9pcas`~!EKSkKvZ zM7B;I$})g<`p0pAV`~j*3rPAQQ_@gVqDLY=ZIs;_z7F~sV_VT5yxLk6SHLHhvu}2aDGfw@TK;qOb+1X zUkz6l6@sSHupMKEGzpK!Qw zp(QfPz%jv~CN$j0VF{lkSy)bb`!aNfxg8Y^!2p5}B0>tdCQgOh(shbn6V`<$W`uo2 zoj5H*(Jd0zn^uSui%mVz$I@@il-)QuzL?X)HeSRhv5oNV7K!d3eAJ^BO-td?j^cF9 z-~?o_9sIJ;w6m`vm!p?8&j1GY49{}?eGq~&_%)>cAd9Xb8o%|HmSzvwweU@0tsj%D zdTGF$no1J|K6imx$z868dn753v4(p@8=ual57aql55K$y)(HRH{k}Rb1Rcy!sj%-( zj@(nusKJq%LDL92xOFVXi)z5mPS_w$3W6@GLq#787^IL#uNB-9aZ?BURByN^R>~{m zXW+l|a?|g3;7&Q!7QFC9Nl~>(;ma%3_p$;M&1R>Q3zvuZxM`Xl>+h^HT#YJCj!qFL z=VT}88wD{k)QnyiqWUV$1GD$CV}|AV8M52}J-ov!68!PDsg|x1wmC-8OGTa8s}zVO ze7yMhtxa+JR4YKy@NB>UanggK8gR^b_*UL+S-;aOEzN4co%LkcVN%wa5Z#RhrqnO% zg^z3$`nbH0eE*gUmWLm;rAvV*!(z=3K5GvS!tMZ68M!RKY)Z62{-GG4P1%uXStMP&?zm6@ZiQQYfH_i^S;bLU`il`dR`^ z%igo+8n#z&`r=l9@H%2jAGfr3J;{Yg&H->rO1%t>XBwjVgO zh*2o&W=)!)us)O>8k3o`!Ti(w*4_;crqF1j*^mD8DcJq|U{J`fTXr*c$BAw@e`o+2 zk%(}d+FK`==r*O0^z49qJ^4?fta9CFecJF`p$}f%yY~~&cj76sRG%i_szAJM9{~f? zt4?%>ruX*+TRP_aqc-VzmH{l;XW_R6d?*vT804qCe={N;-*~zM^3S4vPG<)z)r&gq zkaLx7Q6m=cznWa>m{0agPl}t$i}FEFr<*4L?{5dBbz{4GMzIK9-xM()Bx+tQTKR^$5Z!1AVe1){YqIlZb|Nb#B%H z!Py=GS2#I9cGW#OH9Ja4Ej<6l*~-`r6FXvU^6UULxs!>GABGur)t@MbEIVU6%24{F zoU(X9rXYZ1MsDcV%WcskZs|%miwYBX|8Pc}_oBt$`y47m986$rM7-{WxLZItx_5JW zaC7SY)rkMb3lZ~P#wyVt_T>qFxs?~h9##ySM3g9v3(LC%D%IVt>lYqd za$V3-97}A=Wnz&^wv#l%$UON*dy^ z{Cwo$SLr`lSh#i`2E215T%_BCF&+Frk?{r6E357KC#m{SZDo8c5TKGi2%q)2Y9
  • 16ZxE)n=|s#DG5x--e*_^+bv-=mWUM@6qO zVH`I!s)zjIX<7)?Mx{_%N(DlcG%(vtfJMtY*?_dd0ZvO&SR-kld*Q2&0;&Po(r$Cr zGcVoi0<-}WL@tr7QnK32A1&Kr>NqP3rWIuAPn?fNo#b5ucRc4rHjZJv$N4vzl#iKH z>-eKf0o2BgBf}t8L&U7kwckjLHO%eRuSlJxah>zyDx+zxM($S&KYQ&&6>4yQEcq4d zYZU8W#-&Tx39J8}PL##EjifyJj;oeOUr5?E`=nTTE7?GERdF|x+dh)}L=}5-Qq~g? zHp#u&7UoMDUu_pbYA59Ti^8npa+2Gl`q4M{#7YFaih<+TieVxa->{5wU9Vv4r($-znJy4R`__A8n!Z!ZZre^+j4>CF9HFsH#z8N^O-upLL(@rRp+24vkG9BaU`s6g@HBE5wDB9v9D*E%wi7GH@cCs1Q zBF17E|3-}LYpUX~D}{1l&1j0@Tq=fgVomIcFwzMaMp~DQ(H4dk1}8-*F%m@T76->i zNHKOs=oU$YMG#c#QtX6&z^Bw7@s~o!W0Wd({%jvnPBGvg0T=KAn^LBH=SMk7`BhLk z`iTELuBJ1li%{q#<{u4lQ7PoKC`iF<-q?MCFWEg-m&S2-q#Wrz*037^hMExkCFX#} zG&;K=Xvql=P6|~u6ojd#5%EDy7_Z8Fqq7>|TV7sD zf}zF_qM{xC9_OWruG<>UkASJ6$__0hz7O|fbg%eYT@+70-TC+IbRLPLeHmkKC^I7W z`kX86fCtks%X+n|LO%^dIMsTrJ5QluoLpFCptu9{dg_1=wEn>Bw(ZCDk6X0wHH~vh(Tm;N2icQYfH6TYq7o_(7jP;1#jNAQo;-a*tKD%iYkwshy9mCODaR zYrt=TMVi1#1jPbRzAYm{iSvZHuq#68mjv1O-4{e*%gncq#(NThhqlWbk1K178##%Y0TtcB~mBd2lfa|^HFhGZ~_*u0~y{ zD(R&~Oy24Y*ix3-RJkA>X2vurG)KU1kmxODO+!b$G1K5{h3?qg3$ZO8+Vav!J~1*qp!4)isq)^urNL5 zBEfK8Xd@W8Qr;YB{lb@$l##8RZLwKAhnrDyR4jG?CjcM+$y>SHTqI}inA{_6$6Rws z+-Pw-DMaKR<%pyE^n$sl4SkYB`}E2u=C6WT_M)kwjLQOw`H*t4CFyVGIclFaUkWi` zpXE@hgY<{6H>AJv#w?L+0U7}~2TlNIm?tzF{F#=%n~+%|Y!$Iiij$CEUnmVX23L>% zq*9zqz^-J;wbB&e8?*&Edm6wsF4|W`60SL0N-SDJCki};b7iqR8~kaEB*C;2T6bS; z45eY$cKl56)<-?6sO+4c6Ea%*g%%a4or6?QB1Xr!N`tO9Q!AK0x!g{DV^(NEOC|)c zz|zh7mASXjoMywkCQ<0W2Z)TB6y#H>H`RbkB44)XrAADZsx(2omH+VTtM%K;VbIK~ zF|y2!8MayCz2h7Nu)dVsS$xD@$716_llb77$&{wq29O)gqOGP${7s#({0?k{oo1(q zaBpbQy2PcOW@m_!Z)ll~d~RGLc!Ml5#W>J}3Y7Zh!j^3`jvD*iHl;qU_I_NI!e8yh zU&Sr9i74Pv3)@VsKwq=mWeu_IQ2(zOUKZas2}C5pXY|*b1_bk z!+U;g2-kZ`tbw`&b_SMEyulwUp65;j$IaL=A(%EeHWrEt9jIq5gBfK1e4HohKB5Yf z5B_Zw5$GfJcApt2D)9F*khskAN)0P-pirPNgC0Xo0-)24kqmKq7w6wGgW4s&7?j<6 z_HMcJ2mfELKM?XewxOi*zbT^L27i!-IfdgigS$ct@{|0-^|R?`HVaNfA-(mdUZ@Z# zH|SnN`YpV_zg%q5-0aSt5z?#QSwyr9QhD(|{zi~v1zag2%%Lc-VC2?6c8zqATsV$D z65TYdEs&OXQ`K0Jvse7nAbY)_o#O;vtDN#I|MjS*xg!(Lin;(l)+h*G^2hBv8;Ui; zg~Is32eY}Tsy8ckeE1(I=SSV=T3IMpf~y$>^6;hZFPxvwDHbqqBA4^ zr4^o7-)sH>M*o1Df9VZO&T?7JKcx@eoSP)UKM)z~7qs0ajx_$0%QUgP58vMwwO(1n z7L|4=e-87L)0f!3K?22C@kI#snGlp<&F`a->3_iuV>xDoe7Hm_bdWES-)Lr76J8GH zJG!xuDIS-tYfqnzhF$qnu($KiEX0RYEgazf#wK)OjhLX6)WZLbPk6x!3JAjnz&tt> zz_o}qyU@>YBz*g%z>V$={AUgSlg%JC48arW)zmcuRM8F1F~gKcW|7kcD5m^-oyZDJ z(8xU{q!nPe1^-)%2;0zqCy#(Jl)nS>qg>2En)@GeL;5$5kq?)i7ndbJOjnbB5j$p` zftwsEiHF<2xAOx$KTdiPt_8Te7NuB05FXvK5FngGsTgw*4&SE`*1w%=b+_HzUM=Z$DmsPziNSuJx;WcUs5* zjkE>8xuDpO9+^&IEE?ckRQybjEUUoyLaVk;4Uek8C$3(b4+kX*->X6ZdYm0nmiFOU7cKy_xgAiMW#X6&wQuORkC^67Hm4C@; z|ALw+I&e3Y56v~%z+>sSx=GDG&Sl|+T`!1+{O!P514in6`s{g~EE+{o(vOa!d(LuK zTsiJNIBy4v_oywU=u0YnJewj-6+e5m&Q)1EOhR~S3>C2{R9QPpl6h)O5wR&%SvyV& zdul8Zv8hy9J4qUP@>^{GduvrQZt_;QY+2tS`ssYiHf_bR!++K}uX&wL>E33I#Nx!7 zso~$?zX2$I*Ui^YId014G>>A8=l+^uq+;v!2*z12pXV4Z{SJoBe!lbtDk(0=My0p- zvx%>eO5EY`rO+{Y`W>R809>gs%rCQrQh%6Vehc43YQon^LGj>$Mynw1=*%x4xwV;W z$E6IScYXY)C@J>zF}Kh~$q2`jt{OWTdVn|p=FBH_;Ff#T*YjFYoIob*K&JoFphn`@ z$e?B8c2S9ueZ5~KcWEHjmG=@YIx8Lez*NNhB5|^hn3-L2up^P@zfVWBYzeBLC&!a7 z!z*Iws`$(mmPw@6_!0Ue`SJ#)=MbExZ#hBG@|*LUVx#8{#}C-K)*q{;fdemC98M9Q zz>sAWW;Mb81I=;rr$wiP1Kx{zzb8w+3tAiKFXVU|Le}}7sQF(KX6fs>MEjdd)M!xsW>tv`uIHe z|05a8d9(rY*geIO#sC(~75Jpba_j7esaK(Qo>R0Q&lfQ$N=rMAS(iX%OHKP_ifr_8 z0NE_(lbNt^AoXGVUsSC@a^(Jh&XR}_uAQyG=6H} z(nm-HWBBm@uJRRq7?g)&l*}e4O>y8^<+I|oqH4fiB_4Xr) zt1&mLlr*bsAZ5pd+&}Oc#>XvDm0D=-CX-#zD4? zp7Pgi=(BLG^nQc!whA7XFvOouYTw-G0EG6QMM&wi!(XF$BA+v!j7b)4j9U_aY%k(l z*yGd#PM~~)?39g^%LQMNsHshXpOaBuG+A!2pgdAgxKGZ^Q7gwnyN%JXkXOKO?ERV{ z65xn(H`3TYWgJMr&ukZ?td0*{^r6dBYyzi5jDiBjmk5mW8~IkMA1>dc{}3j%N1?ri z7CZ+Z8iXFDp0|6zdH3{2#62se6p@9~i@Hs(7W+rapx0X!F(?nrTQ96zulesMj1TkB z?MD*&?#?})YF(atd+sSsU^a~QS|k$jhw6G!68f%c@9(neEy%_KM*{7&J{DzM{QC9k%89~rp7eJx(>eOd*eZ@rXy+2PW&gS74#oJ)W;mm2Z+Talk;Gn zx;z1cDui*TKGAcq7|bqY0YOjimP`df zC0Np@nD_o7?Ej5sLLGjQ09*UB&M*X3jHCb>A+=*0g1pF?RGG;4?L=7-H#>uok8@iq zBWii`V;*yn?S-B|7s*0*<0j8H_sp<(Chx_Zk-nppC{!)3;{Oq zYxHrQT7-|ya_ia>o8f*!YI63@4~}|cM5tW`)Vx_4{Do^9`I;+pHYRfMo67b5U@57_ z#Pn{hdXu7?UhSs=nd2YKtZxV|i>a~!l4#4bFg(jQo0)L)-yLWu!fCTz-O|;H;Lr}f z(+g9lx4r;a$w^A|*{=H|F=2f$6utzB>Losj6^mqQP;YUj@-hotzACKCU}YWA5=&N( zuvL8I&{8~H4~OX)QluE}%(UUB56AIH99fB}vQ>0HS578TG$eU_HK9Cr{uOsrRUlO+ z4~n)`GObBHsYjY6;2d2rB9b%7FzG!2=_G6|QBOEpvIK9|h%70J(9tK_&3*|(FPT|R}YML>E-#DRr3(p2W-+8T*VH;G+ z)9JHgRq4ra!<^!K(WOW1yL?~~=Ti7Z>5y}Q-83-xnMT5IYXr#-HU%pbjN+slsYd*u zko>qMU3oC7n7pbS@_|+$V;*Xx)ixXH&)~0PDOU0yRWomOBkPP z315sC(;o~icFa>Er`WUOo2W(bldRKQE9?FamZ~hxsW#xnV&@U!6x;|QjUwSkebA1Q zC>o@o2wE?QzaYpRN=7#(D=nL%ylku@MX#ckYu`-$zc zz*l#|=Atmw0qYz(F{X1d=QtUTIS`H67jvvuQS2-ed0%)`X z42e;Z_8T;o0+PR#8yH$KjTE#I5Fp<=P+8$h$rZ#{@zB%+;qGT}Exl3O z5P#wRjO;J~UB?Z;u^?pt`}F(dVpw+A2;yU9`#ux-N*xK1%#bH_anOzII%WFQ|IM<< zCBD8po2|2x@VOezQqKJ|oAbR+ExK1TW)Cy5=a+bogMI_i2RLqjw_5&K&EE7BQ&%d@Uu!u^g`=rB9(LcTCC6zZaeGt8{ zUxg^xY9%!%6>Z?2`%^)iopzs@#D4SV)GN`;z|M!q9V?O?K4qj>V3%m0CL|g6U8+>p zsbW|=9^u;Nf1FhEQucI6vP&{pn_tb=7fPD$vIJ|(>JdY#k8C-lxk-5M)?(squ*RaH zRvVk^!xOYAz8t_>zoDd<%P{`5e}w)LfJ7${1W!`Gjo3I+p!sBX)?2Bb3!RWD+1LkL zf*-EHik^QOHthjR^A@J?ie_>U#^wfl_oiCc4b8ObtY^Cz5b!5Y?ay)soFbX(pbL4K z?}#WHW-bvU3WqAUxnkP4uo7Cu2dJ&@Nc+N2*QEVENmiEhDz?3UoB%0xYL7nSVIhBn zEZ8x&d}R3CC@DzPQnxu%z7u+bB&YnL4246gNw;LkDCG&9N6;SEMuIq`=6J>Ccti3@ z^Zf~j`9C^m2h>l9w4#Y>0)7KUJX{MzPxQ4@oiogf2_pzlCs%2JXThg#$b#KlMh4B5`KtCo z&5cWFz~zlFd%1+bNDjDbP%5b>emyKBn|$s4Dz*|qD@tRW18(NJgo~p9e@ZNMDyFnI zFF>w&r-dkNT>Hku+z)hI-?K-LuLt*m*b$F!y|?yUwo`l){T9YzH5J1OHlsOIag9Eq zrD?|jw_&Y6&>^{Ctq^Kc)urVFq5VAPvJC{LlcLLaJoAxo94Mg>KR$5;#0ehg3-R3W{)ssiL6>&JtV9)(Aft19|0vgvAm4sQWMcEf6i~^2 zL{#g54#>7G^@so29c-#sY&6^*tZe*4lnyxsv`VfZ8G$=AsNX!jiJocyV`IkY@?4{4QP9~ueY2RhdN z8o3H(%@i&5`TTo+O@9M^(1EoS2RtC7MBOdr6FPS36c&+?6BngXKW^>5Jo6njfHo5M z;ACE0oh<3bkNO;IR22|Ev(lOcRcl+iFZt-bsZV{b_7+}3w1>mYlOv;^5Wg9|H?XlkGr1u7MR zzvtiQ?jV(j z!iSIkvg6b|BOJeE4(akYLP^IeB=xMv;+=1%y5r(&!BeeZ48O_!@j6Ai-*ug2RS^L) zX8|E+0W2h|z3zq+wKa!DHN(_=-d|g=Y|u1soZB8v2vu4wyxAz|vJ*9w^JR<^va;c*);1cpx*PIG26q=&8YfyL9S{S~h#_$! z{FR2Ct}tj%!JW#mP#anP-0ph!hYgO@88(C%B=(*Aojk`Axbn)G)Mbg(!`)PXGxwx3 z_oo2Gr+GntQARXUL*wzZ`Evuxia7VU$-yAU&Oo{?ptkuroaJLPpGf<7TRoQspYKO0 z#gH$|@L!liv9SBt^t@lt!iM^bGsI{A)^h6#QqRnIn_e&x3x6(KC zymqNOC4QjQE@4w0LurDcBxKu9$h2eVJ9qK$(9_xyH=0sr!Ch664?ggFGQ+GpaNNkOT4; zHSz|D-oJ^jM1B~6kh`K?9nM&j_V!JZj!=voR2U-UYAUru zDDMH#@KEuRu}JB#RWOI-4W>aaWp}8oAFFD)D9XgdIGam!5g4QhD47$5^Vs<2);BjG zx7#G2JSJ!)KcngUIu#48dr$-H@y8<8wdQnA=`Losx@d(>+e?~P)1)&n zU+lcr&`JBN;-ST_D_UJg>sX9t%R-pYcy7~0OG;YBr9wZ~&hDh;6ip>(pxg8pp_N8w zI&B)YEyE|@(##3NAH+b1 zi>Fobg_I>-xVz+OTl3$UD`O7VVoayOGfeuLy9b$53(8Lz=hSsMrmQ106?4 z4xNv;X|F66^Hxm^Sh@%^pNF(Qyy46o_WM_FIP-p+(LPJrw@XjHa#07fP|HuC_xdk* zuNShBmhz7h7>N{$IZ=8iSH2ld)7N^;Uc5M8+f*8}F|S<~u*Of);*lS!_+mvHdrTXf zq>VMk4ep?=438iW|Fs8p_kVD?o%9r-;jD@eY)yOmcK zbAxiv2fPtOc?@L68_EJ0P%(xvmaOe3UWE<3*H)zd&I6ncQU$ZXU&oaqk@uVOdL=S; zQ}>iBYHH3*a9X8`&mLsg9%ZB)c*KrlKwl#nLvvxR#lq3cV{MBJ%Z0U)!zw+hlE5=Z zpXhFHrX+8!xMU<>CjhU@YdU#Lrzr^c3Q`cO@aHCU@O-L;IFbaS@(0Ip3M)L|a$-M7iT)*e@0>pl|i z2pQWR_HuXf1n&O$>$>>sy0M@kj)vm()O_>r#3({BWHa4Bo9=J2zv=!a`x9#6gO3-D1gl={-Ifi?$VMs1BpN2dBC5w8y5Zi0!epQhv53fB*1_G*89-cW*0J>8Y%q;^}AM zgrE1Ea#&W4p7i%w_&|8e+M!Q8r)B!YpU*tmU%J1enm_K34pMPQ zy>SUtN}8Fcu}*GtncS=BHc~V-%Or5h$Kb^8=bPs_UL6A?90zzDUEhC6gGSXzT|>hv zCae;s2vh$jCWO>{kbsY+GZ9*dK*~9z!JN^s#|ULh+Vd5(ND)pw#worn&S6$cN#(rh zYN_u7RlxZjIBi-RWopoFEu)oOfJN;`#`a?1~Gzo5dg$@;UJJdHbq1lqLWS0NhRPq{}#ZtHvLW9IOp1u{a3tvjr_c3=Os7q z(j7;BLQ{T1Ly^fQe@ElgDdcSfR;Pv0TwpfRsOaASW=i-Uyh2@wyN)Pvm&d3h$-FCS z+hG}XVcI&_e!IAS`$k^FejKSJNfqO0-O8bR$uQ;+f7d|Tt?|WIsRVvRs27y6yj!j8 zK=UjAZXnlaDEn>z8(v#tHjtX-xakLE#AhT1(tcYQ4;`$&D|-%p?Km9V!ds;kHf;6n!WxLIZ%5>i!o8=h;be`Iu)RU^Le>TAZx~ff z|4}*)1gD9j22jhJedY);sBBeqs_=?W(OKeX5+Iu5=Lt4x+R_9X+HK` z(gR%3IJNG-GS^(ko%1DP?^jSv6%qd_O9Vh6F=Z51p%GzfxeL4C>}CEw9P0;}Vl~gR zNKayW1;OZP)LTTb+B90N3V^ZTmt*aN1;oaB*a5z1+j0_H;MhO06=VB+!2(_H`hw?E zEVw+Cjj{J;y@Yzgv>w5NN%Gv4fsMI;dN$?RFTA?d$g>6_xoTdvDD@WG zSA+LsxS$nM=g_#I6+h4Ct-<+}&0YnX1`9R~d*(VlbIu+`EPcykUv8O3@60P$M8K3~ zteR*vyFrgiF-mT#8QXfsW)=C{XokIZDX1GgjZ9 z=ewx@&hvb;fQ(|pAL-UU6@v}hF=0EVg68y$FUP_Gw`zKyE66<<>vq8yE)()xR1kYG zmhDU^<|)j4c?^fK=~u92GWNV3TP8?szXB_ZvEmo|-HiPz*2}z#Zl*x`52|uzIQEOY zgm`%c^wi7lsvzoMEY(bdxIX9lRRI#<{zLzNGIB7@$Q96w!~ADeFa*k>Fntv?4aQF8 z$v;UZ-n4ueOBF}%0G8o4)Z?qZeX`8`V+ZO(vD5M5kc>Lcd|rHJ&EEED>;ItFqb5K0 zTp?Sp5m+JQ^#Eb`F=IF=!^Jg?rEtZwMd&Be>fPZ3;i{0R@|Y^woFIfxF~Z*$a=;@p z+3(kN*L;X?}HV-Dd%65(SH${~&LF^}*ek+67@K4g%f zu}9>iN940dgdUQ#M+3Y@pP61iQoVk%c4{Xnn|~Ni$xrrA?R3iaNWdZaxjMwh;t-!o z>y>Mx(ZkU!56bqapmfBi;?T9j{)YP->~FZg$^NGMo9xe(W{`1*#xna8>X9fOG?pjj zX^%+KBVx^nf%h~|R5FD>%@dX6iAwWCC3&LKnp=`*Dd+QuP!rnLgc$snU%Pf_v2JTd z*Tmih6zY7P;rsu&;VwBOOE&fEZ6t}t9>8Dm;g4T5VZ1*~7?`XG}-ceaD8p!+jaqv@(eb1hHg ziYVHTh(T>t(;<+R;3C!mYfZBpIP*ptq04x>%4ij$lUioCme{2Q9^Wf!Y4&chtmSf9 z!L=9zTh4CVOcmbep*mOx*;j{1Mk==P?%#hiCQ;!PdL(Ko(w#&KmovDKz@@A#nE%Sj zs(oSjpj8;zjTKag>{>0gcGgNMJp+g^zL`K*iRl9ij=bz0C+Ipu!a1Mv&L^Tdsr!}4 zrbyvc5*ZUlrzQ|Vsq4RbV)>mO9%ix}w5v)GNp>4^bp^R{j=X)vQVol%>$y33{=+9g-3f^#vHGDckh66B4flhHA=AO+f5Z z2>aWjm!)3tkGB31n4nYnFL@|Y8$WP5ANiaQCcL8@!9$Rn@B;@qN=N5A8`X*SR-J`< z`HwWps}EMV)As&HV*StW*xg|q#c>FdxP0B;pVVrUtyjx6;}Y`Ie?cuJ-YQP^!O#AP zkL-e<+50~FjqO*TEXX=t5Otw+0vgM-Z-td!#CpGi&uh@$b;0H}Xg?3z&w~Yy$16xo zdvw`eCX!>>pHmz&LtHavsF6$Kd$r2GB`zLubkrWhBj0*#II6|<^sT$cIPSMyhwR%K zVyJmI8=XDg*@IQkj4c9JO%rF0YlEqZ{B4)tV&P8^)lp0bky6D;!Lb<5l0}lN{8><% z#0`nOmB*Bc!Y9p@);)gN>`l}f;GChZO6C353^k( zibbN7yLedY=_n@QlMGcY?=~bI6g5&+Blz~si-G!#cWbUgQXwwY*+CJVrs^*6)S9J)b*E)D z?#%>?XXDdEez}gsZVR5> z6whuVv&oFSSnp=)d} z&}_tba6X1x(la^c?xvZ13cISA947fvtj85U#a@Oqe?Gs1HSNyw(w-LdC&h?%joqW1 zhXaoNBThnAZ7x&R%dIMUhJ)?=+$AL2$8gqXr!Ses+GnT0$MBeeEUsar#L3z@tZ6e$ znL0?BYEx|qYi&Rc!)YoQR8Z+AF0n@w2GeQ2awU}WnOa@gWSdF^d04GsZHeyU5`C~5 ziHn}nsZ)(7&{W+ImsVs&jd1Wf2qJkCF7hgyoiL42q-ILVMheL01b}8tcD^DYs?lK> z&>_3pG0+=YL9>(k&UqiP%Tfb-BfM5%ips=GCg2(3wwaLU1iO#qH8L#bfayLB)GV72 z`K+q)TLneIyL4?GqZp%df(prFn#p6vhLd!-JjdOkQ?A*` zmaM{inw@HNx}A7VvrkU52TM|fIQ-6JkcaquZ`6NNC5YmcBubNdyo3X?J)c*W=`bQ* z?IZq3r67DiNeOzv9t~kv5u;)E#@$;w;&ca+ZnR_2OaFwuZ12sv+Nhj?-%DIrN@Z|} zNDR^a1(9nnbR-`@cd7L5dIsc_0XZM=Z3+tKBS(dH-b@i6e-#g87fL2DAr|)dx;l7X zdxuBz{fb3dryD4AR3PcR!nQ|1@?%$F7u>QqNnHSm zWup5J0P}416E1Rff{;u@No$W2^NIDov-K+joem?mw>7x@gznh!dvT8LMm;Yo+`lJi zJps+Et&jhYk0Zo93={l49p)hG5Y(f(RB1q5)e=cb0`{Fse@oErr3|#}$?$Z%WFKY! R-1GeL{{f}k-j<8(005VEXOaK_ literal 0 HcmV?d00001 diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-365.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-365.json index 0385102c8..534320670 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-365.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-365.json @@ -301,6 +301,17 @@ "AttributeName": "ICB", "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" }, + { + "Type": "R", + "Name": "Within CP Expansion ICB not 80 plus", + "Description": "Book an appointment on NBS as within CP expansion", + "Priority": 1200, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-80", + "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" + }, { "Type": "R", "Name": "Within CP Expansion Local Authority", @@ -312,6 +323,17 @@ "AttributeName": "LOCAL_AUTHORITY", "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" }, + { + "Type": "R", + "Name": "Within CP Expansion ICB not 80 plus", + "Description": "Book an appointment on NBS as within CP expansion", + "Priority": 1300, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-80", + "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" + }, { "Type": "Y", "Name": "Already vaccinated default text", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-12.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-12.json deleted file mode 100644 index 186f1a402..000000000 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-12.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "scenario_name": "ELI-320 - Multiple Category Campaigns - Category=VACCINATIONS,screening", - "request_headers": { - "nhs-login-nhs-number": "9990032012" - }, - "query_params": { - "category": "VACCINATIONS,screening" - }, - "config_filenames": ["AUTO_RSV_ELI-320-COVID.json","AUTO_RSV_ELI-320-MMR.json", "AUTO_RSV_ELI-320-SCREENING-1.json", "AUTO_RSV_ELI-320-SCREENING-2.json"], - "data": [ - { - "NHS_NUMBER": "9990032012", - "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MEMBERSHIPS": [ - { - "COHORT_LABEL": "covid_cohort", - "DATE_JOINED": "20231020" - }, - { - "COHORT_LABEL": "rsv_cohort", - "DATE_JOINED": "20231020" - }, - { - "COHORT_LABEL": "FLU_screening_cohort", - "DATE_JOINED": "20231020" - } - ] - }, - { - "NHS_NUMBER": "9990032012", - "ATTRIBUTE_TYPE": "PERSON", - "DATE_OF_BIRTH": "19500601", - "GENDER": "0", - "POSTCODE": "SG8 6EG", - "POSTCODE_SECTOR": "SG86", - "POSTCODE_OUTCODE": "SG8", - "MSOA": "E02003792", - "LSOA": "E01018267", - "GP_PRACTICE_CODE": "D81046", - "PCN": "U75549", - "ICB": "QUE", - "COMMISSIONING_REGION": "Y61", - "13Q_FLAG": "N", - "CARE_HOME_FLAG": "N", - "DE_FLAG": "N" - }, - { - "NHS_NUMBER": "9990032012", - "ATTRIBUTE_TYPE": "RSV", - "BOOKED_APPOINTMENT_DATE": "<>", - "LAST_SUCCESSFUL_DATE": "<>" - }, - { - "NHS_NUMBER": "9990032012", - "ATTRIBUTE_TYPE": "COVID", - "BOOKED_APPOINTMENT_DATE": "<>", - "LAST_SUCCESSFUL_DATE": "<>" - } - ] -} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_023.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_023.json index 6091129e0..a8d19465a 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_023.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_023.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900036523" }, - "config_filenames": ["AUTO_RSV_ELI-365.json"], + "config_filenames": [ + "AUTO_RSV_ELI-365.json" + ], "data": [ { "NHS_NUMBER": "9900036523", @@ -12,6 +14,10 @@ { "COHORT_LABEL": "rsv_80_since_02_Sept_2024", "DATE_JOINED": "20231020" + }, + { + "COHORT_LABEL": "rsv_75to79", + "DATE_JOINED": "20231020" } ] }, @@ -38,7 +44,7 @@ "NHS_NUMBER": "9900036523", "ATTRIBUTE_TYPE": "RSV", "LAST_SUCCESSFUL_DATE": null, - "BOOKED_APPOINTMENT_DATE" : null, + "BOOKED_APPOINTMENT_DATE": null, "BOOKED_APPOINTMENT_PROVIDER": null } ] diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_024.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_024.json new file mode 100644 index 000000000..13b4135b9 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_024.json @@ -0,0 +1,47 @@ +{ + "scenario_name": "RSV - Actionable - In 3 actions - under 80 - Local Authority", + "request_headers": { + "nhs-login-nhs-number": "9900036526" + }, + "config_filenames": [ + "AUTO_RSV_ELI-365.json" + ], + "data": [ + { + "NHS_NUMBER": "9900036526", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_80_since_02_Sept_2024", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900036526", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "zz1", + "LOCAL_AUTHORITY": "E08000014", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900036526", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": null, + "BOOKED_APPOINTMENT_PROVIDER": null + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_025.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_025.json new file mode 100644 index 000000000..80a5ec4e2 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_025.json @@ -0,0 +1,47 @@ +{ + "scenario_name": "RSV - Actionable - In 3 actions - under 80 - ICB", + "request_headers": { + "nhs-login-nhs-number": "9900036527" + }, + "config_filenames": [ + "AUTO_RSV_ELI-365.json" + ], + "data": [ + { + "NHS_NUMBER": "9900036527", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_80_since_02_Sept_2024", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900036527", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QH8", + "LOCAL_AUTHORITY": "ZZ8000014", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900036527", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": null, + "BOOKED_APPOINTMENT_PROVIDER": null + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_026.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_026.json new file mode 100644 index 000000000..892196765 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_026.json @@ -0,0 +1,47 @@ +{ + "scenario_name": "RSV - Actionable - 2 actions - 80 or over - ICB", + "request_headers": { + "nhs-login-nhs-number": "9900036524" + }, + "config_filenames": [ + "AUTO_RSV_ELI-365.json" + ], + "data": [ + { + "NHS_NUMBER": "9900036524", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_80_since_02_Sept_2024", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900036524", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "QH8", + "LOCAL_AUTHORITY": "ZZ8000011", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900036524", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": null, + "BOOKED_APPOINTMENT_PROVIDER": null + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_027.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_027.json new file mode 100644 index 000000000..0c90eab68 --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_027.json @@ -0,0 +1,47 @@ +{ + "scenario_name": "RSV - Actionable - 2 actions - 80 or over - Local Authority", + "request_headers": { + "nhs-login-nhs-number": "9900036525" + }, + "config_filenames": [ + "AUTO_RSV_ELI-365.json" + ], + "data": [ + { + "NHS_NUMBER": "9900036525", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_80_since_02_Sept_2024", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900036525", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "zz1", + "LOCAL_AUTHORITY": "E08000014", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900036525", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": null, + "BOOKED_APPOINTMENT_PROVIDER": null + } + ] +} diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_028.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_028.json new file mode 100644 index 000000000..083693b2c --- /dev/null +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_028.json @@ -0,0 +1,47 @@ +{ + "scenario_name": "RSV - Actionable - 2 actions - 80 or over - No ICB or Local Authority", + "request_headers": { + "nhs-login-nhs-number": "9900036528" + }, + "config_filenames": [ + "AUTO_RSV_ELI-365.json" + ], + "data": [ + { + "NHS_NUMBER": "9900036528", + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + { + "COHORT_LABEL": "rsv_80_since_02_Sept_2024", + "DATE_JOINED": "20231020" + } + ] + }, + { + "NHS_NUMBER": "9900036528", + "ATTRIBUTE_TYPE": "PERSON", + "DATE_OF_BIRTH": "<>", + "GENDER": "0", + "POSTCODE": "SG8 6EG", + "POSTCODE_SECTOR": "SG86", + "POSTCODE_OUTCODE": "SG8", + "MSOA": "E02003792", + "LSOA": "E01018267", + "GP_PRACTICE_CODE": "D81046", + "PCN": "U75549", + "ICB": "zz1", + "LOCAL_AUTHORITY": "ZZ8000014", + "COMMISSIONING_REGION": "Y61", + "13Q_FLAG": "N", + "CARE_HOME_FLAG": "N", + "DE_FLAG": "N" + }, + { + "NHS_NUMBER": "9900036528", + "ATTRIBUTE_TYPE": "RSV", + "LAST_SUCCESSFUL_DATE": null, + "BOOKED_APPOINTMENT_DATE": null, + "BOOKED_APPOINTMENT_PROVIDER": null + } + ] +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-12.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-12.json deleted file mode 100644 index c57312555..000000000 --- a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-320-12.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "meta": { - "lastUpdated": "2025-07-15T14:52:52.785698+00:00" - }, - "processedSuggestions": [ - { - "actions": [], - "condition": "COVID", - "eligibilityCohorts": [ - { - "cohortCode": "covid_cohort_group", - "cohortStatus": "NotActionable", - "cohortText": "You are currently in a covid cohort" - } - ], - "status": "NotActionable", - "statusText": "You should have the COVID vaccine", - "suitabilityRules": [ - { - "ruleCode": "AlreadyVaccinated", - "ruleText": "##You've had your COVID vaccination\nWe believe you already had your COVID vaccination.", - "ruleType": "S" - } - ] - }, - { - "actions": [ - { - "actionCode": "AmendNBS", - "actionType": "ButtonWithAuthLink", - "description": "##You have an flu screening appointment\nYou can view, change or cancel your appointment below.", - "urlLabel": "Manage your appointment", - "urlLink": "http://www.nhs.uk/book-bs" - } - ], - "condition": "FLU", - "eligibilityCohorts": [ - { - "cohortCode": "FLU_screening_cohort_group", - "cohortStatus": "Actionable", - "cohortText": "You are currently in an flu SCREENING cohort" - } - ], - "status": "Actionable", - "statusText": "You should have the FLU vaccine", - "suitabilityRules": [] - }, - { - "actions": [], - "condition": "MMR", - "eligibilityCohorts": [ - { - "cohortCode": "mmr_cohort_group", - "cohortStatus": "NotEligible", - "cohortText": "You are not currently in an mmr cohort" - } - ], - "status": "NotEligible", - "statusText": "We do not believe you can have it", - "suitabilityRules": [] - }, - { - "actions": [], - "condition": "RSV", - "eligibilityCohorts": [ - { - "cohortCode": "rsv_screening_cohort_group", - "cohortStatus": "NotEligible", - "cohortText": "You are not currently in an RSV SCREENING cohort" - } - ], - "status": "NotEligible", - "statusText": "We do not believe you can have it", - "suitabilityRules": [] - } - ], - "responseId": "5c8d1cb3-8326-40b1-93ad-1b7fa24c2595" -} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_023.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_023.json index a4f4656b8..fe6f609a4 100644 --- a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_023.json +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_023.json @@ -22,6 +22,11 @@ ], "condition": "RSV", "eligibilityCohorts": [ + { + "cohortCode": "rsv_age", + "cohortStatus": "Actionable", + "cohortText": "are aged 75 to 79 years old" + }, { "cohortCode": "rsv_age_catchup", "cohortStatus": "Actionable", diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_024.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_024.json new file mode 100644 index 000000000..c0bb698d4 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_024.json @@ -0,0 +1,44 @@ +{ + "meta": { + "lastUpdated": "" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "BookLocal", + "actionType": "InfoText", + "description": "##Getting the vaccine\n\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "urlLabel": "", + "urlLink": "" + }, + { + "actionCode": "BookNBS", + "actionType": "ButtonWithAuthLink", + "description": "", + "urlLabel": "Continue to booking", + "urlLink": "http://www.nhs.uk/book-rsv" + }, + { + "actionCode": "HelpSupportInfo", + "actionType": "InfoText", + "description": "## CONTENT TBC\n\nBlah blah blah.", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_catchup", + "cohortStatus": "Actionable", + "cohortText": "turned 80 after 1st September 2024" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_025.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_025.json new file mode 100644 index 000000000..c0bb698d4 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_025.json @@ -0,0 +1,44 @@ +{ + "meta": { + "lastUpdated": "" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "BookLocal", + "actionType": "InfoText", + "description": "##Getting the vaccine\n\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "urlLabel": "", + "urlLink": "" + }, + { + "actionCode": "BookNBS", + "actionType": "ButtonWithAuthLink", + "description": "", + "urlLabel": "Continue to booking", + "urlLink": "http://www.nhs.uk/book-rsv" + }, + { + "actionCode": "HelpSupportInfo", + "actionType": "InfoText", + "description": "## CONTENT TBC\n\nBlah blah blah.", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_catchup", + "cohortStatus": "Actionable", + "cohortText": "turned 80 after 1st September 2024" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_026.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_026.json new file mode 100644 index 000000000..2800f4974 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_026.json @@ -0,0 +1,37 @@ +{ + "meta": { + "lastUpdated": "" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "BookLocal", + "actionType": "InfoText", + "description": "##Getting the vaccine\n\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "urlLabel": "", + "urlLink": "" + }, + { + "actionCode": "HelpSupportInfo", + "actionType": "InfoText", + "description": "## CONTENT TBC\n\nBlah blah blah.", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_catchup", + "cohortStatus": "Actionable", + "cohortText": "turned 80 after 1st September 2024" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_027.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_027.json new file mode 100644 index 000000000..2800f4974 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_027.json @@ -0,0 +1,37 @@ +{ + "meta": { + "lastUpdated": "" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "BookLocal", + "actionType": "InfoText", + "description": "##Getting the vaccine\n\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "urlLabel": "", + "urlLink": "" + }, + { + "actionCode": "HelpSupportInfo", + "actionType": "InfoText", + "description": "## CONTENT TBC\n\nBlah blah blah.", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_catchup", + "cohortStatus": "Actionable", + "cohortText": "turned 80 after 1st September 2024" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "" +} diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_028.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_028.json new file mode 100644 index 000000000..2800f4974 --- /dev/null +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_028.json @@ -0,0 +1,37 @@ +{ + "meta": { + "lastUpdated": "" + }, + "processedSuggestions": [ + { + "actions": [ + { + "actionCode": "BookLocal", + "actionType": "InfoText", + "description": "##Getting the vaccine\n\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "urlLabel": "", + "urlLink": "" + }, + { + "actionCode": "HelpSupportInfo", + "actionType": "InfoText", + "description": "## CONTENT TBC\n\nBlah blah blah.", + "urlLabel": "", + "urlLink": "" + } + ], + "condition": "RSV", + "eligibilityCohorts": [ + { + "cohortCode": "rsv_age_catchup", + "cohortStatus": "Actionable", + "cohortText": "turned 80 after 1st September 2024" + } + ], + "status": "Actionable", + "statusText": "You should have the RSV vaccine", + "suitabilityRules": [] + } + ], + "responseId": "" +} diff --git a/tests/e2e/tests/conftest.py b/tests/e2e/tests/conftest.py index 30dd5808e..7be5cad4e 100644 --- a/tests/e2e/tests/conftest.py +++ b/tests/e2e/tests/conftest.py @@ -1,9 +1,7 @@ -import json import logging import os from pathlib import Path -import boto3 import pytest from dotenv import load_dotenv @@ -25,6 +23,7 @@ logger = logging.getLogger(__name__) + @pytest.fixture(scope="session") def eligibility_client(): return EligibilityApiClient(BASE_URL, cert_dir="certs") diff --git a/tests/e2e/tests/test_in_progress.py b/tests/e2e/tests/test_in_progress.py index 4204f4519..f3a6a5917 100644 --- a/tests/e2e/tests/test_in_progress.py +++ b/tests/e2e/tests/test_in_progress.py @@ -16,7 +16,9 @@ @pytest.mark.parametrize(("filename", "scenario"), param_list, ids=id_list) def test_run_in_progress_tests(filename, scenario, eligibility_client, get_scenario_params): - nhs_number, config_filenames, request_headers, query_params, expected_response_code = get_scenario_params(scenario, config_path) + nhs_number, config_filenames, request_headers, query_params, expected_response_code = get_scenario_params( + scenario, config_path + ) actual_response = eligibility_client.make_request( nhs_number, headers=request_headers, query_params=query_params, strict_ssl=False diff --git a/tests/e2e/tests/test_regression_tests.py b/tests/e2e/tests/test_regression_tests.py index d4a17f288..e9a0ba88c 100644 --- a/tests/e2e/tests/test_regression_tests.py +++ b/tests/e2e/tests/test_regression_tests.py @@ -17,7 +17,9 @@ @pytest.mark.functionale2eregression @pytest.mark.parametrize(("filename", "scenario"), param_list, ids=id_list) def test_run_regression_tests(filename, scenario, eligibility_client, get_scenario_params): - nhs_number, config_filenames, request_headers, query_params, expected_response_code = get_scenario_params(scenario, config_path) + nhs_number, config_filenames, request_headers, query_params, expected_response_code = get_scenario_params( + scenario, config_path + ) actual_response = eligibility_client.make_request(nhs_number, headers=request_headers, strict_ssl=False) expected_response = all_expected_responses.get(filename).get("response_items", {}) diff --git a/tests/e2e/tests/test_smoke_tests.py b/tests/e2e/tests/test_smoke_tests.py index 8b2271cf3..278fd8b52 100644 --- a/tests/e2e/tests/test_smoke_tests.py +++ b/tests/e2e/tests/test_smoke_tests.py @@ -17,7 +17,9 @@ @pytest.mark.sandboxtests @pytest.mark.parametrize(("filename", "scenario"), param_list, ids=id_list) def test_run_smoke_case(filename, scenario, eligibility_client, get_scenario_params): - nhs_number, config_filenames, request_headers, query_params, expected_response_code = get_scenario_params(scenario, config_path) + nhs_number, config_filenames, request_headers, query_params, expected_response_code = get_scenario_params( + scenario, config_path + ) actual_response = eligibility_client.make_request( nhs_number, headers=request_headers, query_params=query_params, strict_ssl=False diff --git a/tests/e2e/tests/test_story_tests.py b/tests/e2e/tests/test_story_tests.py index 541fb7c77..689c51161 100644 --- a/tests/e2e/tests/test_story_tests.py +++ b/tests/e2e/tests/test_story_tests.py @@ -17,7 +17,9 @@ @pytest.mark.functionale2eregression @pytest.mark.parametrize(("filename", "scenario"), param_list, ids=id_list) def test_run_story_test_cases(filename, scenario, eligibility_client, get_scenario_params): - nhs_number, config_filenames, request_headers, query_params, expected_response_code = get_scenario_params(scenario, config_path) + nhs_number, config_filenames, request_headers, query_params, expected_response_code = get_scenario_params( + scenario, config_path + ) actual_response = eligibility_client.make_request( nhs_number=nhs_number, headers=request_headers, query_params=query_params, strict_ssl=False diff --git a/tests/e2e/tests/test_vita_integration_tests.py b/tests/e2e/tests/test_vita_integration_tests.py index 5162ee839..63175fbc9 100644 --- a/tests/e2e/tests/test_vita_integration_tests.py +++ b/tests/e2e/tests/test_vita_integration_tests.py @@ -17,7 +17,9 @@ @pytest.mark.functionale2eregression @pytest.mark.parametrize(("filename", "scenario"), param_list, ids=id_list) def test_run_story_test_cases(filename, scenario, eligibility_client, get_scenario_params): - nhs_number, config_filenames, request_headers, query_params, expected_response_code = get_scenario_params(scenario, config_path) + nhs_number, config_filenames, request_headers, query_params, expected_response_code = get_scenario_params( + scenario, config_path + ) actual_response = eligibility_client.make_request( nhs_number=nhs_number, headers=request_headers, query_params=query_params, strict_ssl=False diff --git a/tests/fixtures/builders/model/eligibility.py b/tests/fixtures/builders/model/eligibility.py index a1fd81f7d..7406bc38e 100644 --- a/tests/fixtures/builders/model/eligibility.py +++ b/tests/fixtures/builders/model/eligibility.py @@ -4,23 +4,40 @@ from polyfactory import Use from polyfactory.factories import DataclassFactory -from eligibility_signposting_api.model import eligibility -from eligibility_signposting_api.model.eligibility import UrlLink +from eligibility_signposting_api.model import eligibility_status +from eligibility_signposting_api.model.eligibility_status import ( + RuleDescription, + RuleName, + RulePriority, + RuleType, + UrlLink, +) -class SuggestedActionFactory(DataclassFactory[eligibility.SuggestedAction]): +class SuggestedActionFactory(DataclassFactory[eligibility_status.SuggestedAction]): url_link = UrlLink("https://test-example.com") -class ConditionFactory(DataclassFactory[eligibility.Condition]): - actions = Use(SuggestedActionFactory.batch, size=2) +class ReasonFactory(DataclassFactory[eligibility_status.Reason]): + rule_type = RuleType.filter + rule_name = RuleName("name") + rule_priority = RulePriority("1") + rule_description = RuleDescription("description") + matcher_matched = False -class EligibilityStatusFactory(DataclassFactory[eligibility.EligibilityStatus]): - conditions = Use(ConditionFactory.batch, size=2) +class CohortResultFactory(DataclassFactory[eligibility_status.CohortGroupResult]): + reasons = Use(ReasonFactory.batch, size=2) + + +class ConditionFactory(DataclassFactory[eligibility_status.Condition]): + actions = Use(SuggestedActionFactory.batch, size=2) + cohort_results = Use(CohortResultFactory.batch, size=2) + suitability_rules = Use(ReasonFactory.batch, size=2) -class CohortResultFactory(DataclassFactory[eligibility.CohortGroupResult]): ... +class EligibilityStatusFactory(DataclassFactory[eligibility_status.EligibilityStatus]): + conditions = Use(ConditionFactory.batch, size=2) def random_str(length: int) -> str: diff --git a/tests/fixtures/builders/model/rule.py b/tests/fixtures/builders/model/rule.py index 5388b113b..4cc5a0ac1 100644 --- a/tests/fixtures/builders/model/rule.py +++ b/tests/fixtures/builders/model/rule.py @@ -5,7 +5,26 @@ from polyfactory import Use from polyfactory.factories.pydantic_factory import ModelFactory -from eligibility_signposting_api.model import rules +from eligibility_signposting_api.model.campaign_config import ( + ActionsMapper, + AvailableAction, + CampaignConfig, + CohortGroup, + CohortLabel, + CommsRouting, + Description, + Iteration, + IterationCohort, + IterationRule, + RuleAttributeLevel, + RuleAttributeName, + RuleComparator, + RuleDescription, + RuleName, + RuleOperator, + RulePriority, + RuleType, +) def past_date(days_behind: int = 365) -> date: @@ -16,18 +35,20 @@ def future_date(days_ahead: int = 365) -> date: return datetime.now(tz=UTC).date() + timedelta(days=randint(1, days_ahead)) -class IterationCohortFactory(ModelFactory[rules.IterationCohort]): - priority = rules.RulePriority(0) +class IterationCohortFactory(ModelFactory[IterationCohort]): + priority = RulePriority(0) -class IterationRuleFactory(ModelFactory[rules.IterationRule]): +class IterationRuleFactory(ModelFactory[IterationRule]): attribute_target = None - attribute_name = None + attribute_name = "DATE_OF_BIRTH" + operator = "Y>" + comparator = "-1" cohort_label = None rule_stop = False -class AvailableActionDetailFactory(ModelFactory[rules.AvailableAction]): +class AvailableActionDetailFactory(ModelFactory[AvailableAction]): action_type = "defaultcomms" action_code = "action_code" action_description = None @@ -35,11 +56,11 @@ class AvailableActionDetailFactory(ModelFactory[rules.AvailableAction]): url_label = None -class ActionsMapperFactory(ModelFactory[rules.ActionsMapper]): +class ActionsMapperFactory(ModelFactory[ActionsMapper]): root = Use(lambda: {"defaultcomms": AvailableActionDetailFactory.build()}) -class IterationFactory(ModelFactory[rules.Iteration]): +class IterationFactory(ModelFactory[Iteration]): iteration_cohorts = Use(IterationCohortFactory.batch, size=2) iteration_rules = Use(IterationRuleFactory.batch, size=2) iteration_date = Use(past_date) @@ -47,7 +68,7 @@ class IterationFactory(ModelFactory[rules.Iteration]): actions_mapper = Use(ActionsMapperFactory.build) -class RawCampaignConfigFactory(ModelFactory[rules.CampaignConfig]): +class RawCampaignConfigFactory(ModelFactory[CampaignConfig]): iterations = Use(IterationFactory.batch, size=2) start_date = Use(past_date) @@ -56,13 +77,13 @@ class RawCampaignConfigFactory(ModelFactory[rules.CampaignConfig]): class CampaignConfigFactory(RawCampaignConfigFactory): @classmethod - def build(cls, **kwargs) -> rules.CampaignConfig: + def build(cls, **kwargs) -> CampaignConfig: """Ensure invariants are met: * no iterations with duplicate iteration dates * must have iteration active from campaign start date""" processed_kwargs = cls.process_kwargs(**kwargs) start_date: date = processed_kwargs["start_date"] - iterations: list[rules.Iteration] = processed_kwargs["iterations"] + iterations: list[Iteration] = processed_kwargs["iterations"] CampaignConfigFactory.fix_iteration_date_invariants(iterations, start_date) @@ -70,7 +91,7 @@ def build(cls, **kwargs) -> rules.CampaignConfig: return cls.__model__(**data) @staticmethod - def fix_iteration_date_invariants(iterations: list[rules.Iteration], start_date: date) -> None: + def fix_iteration_date_invariants(iterations: list[Iteration], start_date: date) -> None: iterations.sort(key=attrgetter("iteration_date")) iterations[0].iteration_date = start_date @@ -87,89 +108,113 @@ def fix_iteration_date_invariants(iterations: list[rules.Iteration], start_date: # Iteration cohort factories class MagicCohortFactory(IterationCohortFactory): - cohort_label = rules.CohortLabel("elid_all_people") - cohort_group = rules.CohortGroup("magic cohort group") - positive_description = rules.Description("magic positive description") - negative_description = rules.Description("magic negative description") + cohort_label = CohortLabel("elid_all_people") + cohort_group = CohortGroup("magic cohort group") + positive_description = Description("magic positive description") + negative_description = Description("magic negative description") priority = 1 class Rsv75RollingCohortFactory(IterationCohortFactory): - cohort_label = rules.CohortLabel("rsv_75_rolling") - cohort_group = rules.CohortGroup("rsv_age_range") - positive_description = rules.Description("rsv_age_range positive description") - negative_description = rules.Description("rsv_age_range negative description") + cohort_label = CohortLabel("rsv_75_rolling") + cohort_group = CohortGroup("rsv_age_range") + positive_description = Description("rsv_age_range positive description") + negative_description = Description("rsv_age_range negative description") priority = 2 class Rsv75to79CohortFactory(IterationCohortFactory): - cohort_label = rules.CohortLabel("rsv_75to79_2024") - cohort_group = rules.CohortGroup("rsv_age_range") - positive_description = rules.Description("rsv_age_range positive description") - negative_description = rules.Description("rsv_age_range negative description") + cohort_label = CohortLabel("rsv_75to79_2024") + cohort_group = CohortGroup("rsv_age_range") + positive_description = Description("rsv_age_range positive description") + negative_description = Description("rsv_age_range negative description") priority = 3 class RsvPretendClinicalCohortFactory(IterationCohortFactory): - cohort_label = rules.CohortLabel("rsv_pretend_clinical_cohort") - cohort_group = rules.CohortGroup("rsv_clinical_cohort") - positive_description = rules.Description("rsv_clinical_cohort positive description") - negative_description = rules.Description("rsv_clinical_cohort negative description") + cohort_label = CohortLabel("rsv_pretend_clinical_cohort") + cohort_group = CohortGroup("rsv_clinical_cohort") + positive_description = Description("rsv_clinical_cohort positive description") + negative_description = Description("rsv_clinical_cohort negative description") priority = 4 # Iteration rule factories class PersonAgeSuppressionRuleFactory(IterationRuleFactory): - type = rules.RuleType.suppression - name = rules.RuleName("Exclude too young less than 75") - description = rules.RuleDescription("Exclude too young less than 75") - priority = rules.RulePriority(10) - operator = rules.RuleOperator.year_gt - attribute_level = rules.RuleAttributeLevel.PERSON - attribute_name = rules.RuleAttributeName("DATE_OF_BIRTH") - comparator = rules.RuleComparator("-75") + type = RuleType.suppression + name = RuleName("Exclude too young less than 75") + description = RuleDescription("Exclude too young less than 75") + priority = RulePriority(10) + operator = RuleOperator.year_gt + attribute_level = RuleAttributeLevel.PERSON + attribute_name = RuleAttributeName("DATE_OF_BIRTH") + comparator = RuleComparator("-75") class PostcodeSuppressionRuleFactory(IterationRuleFactory): - type = rules.RuleType.suppression - name = rules.RuleName("Excluded postcode In SW19") - description = rules.RuleDescription("In SW19") - priority = rules.RulePriority(10) - operator = rules.RuleOperator.starts_with - attribute_level = rules.RuleAttributeLevel.PERSON - attribute_name = rules.RuleAttributeName("POSTCODE") - comparator = rules.RuleComparator("SW19") + type = RuleType.suppression + name = RuleName("Excluded postcode In SW19") + description = RuleDescription("In SW19") + priority = RulePriority(10) + operator = RuleOperator.starts_with + attribute_level = RuleAttributeLevel.PERSON + attribute_name = RuleAttributeName("POSTCODE") + comparator = RuleComparator("SW19") class DetainedEstateSuppressionRuleFactory(IterationRuleFactory): - type = rules.RuleType.suppression - name = rules.RuleName("Detained - Suppress Individuals In Detained Estates") - description = rules.RuleDescription("Suppress where individual is identified as being in a Detained Estate") - priority = rules.RulePriority(160) - attribute_level = rules.RuleAttributeLevel.PERSON - attribute_name = rules.RuleAttributeName("DE_FLAG") - operator = rules.RuleOperator.equals - comparator = rules.RuleComparator("Y") + type = RuleType.suppression + name = RuleName("Detained - Suppress Individuals In Detained Estates") + description = RuleDescription("Suppress where individual is identified as being in a Detained Estate") + priority = RulePriority(160) + attribute_level = RuleAttributeLevel.PERSON + attribute_name = RuleAttributeName("DE_FLAG") + operator = RuleOperator.equals + comparator = RuleComparator("Y") class ICBFilterRuleFactory(IterationRuleFactory): - type = rules.RuleType.filter - name = rules.RuleName("Not in QE1") - description = rules.RuleDescription("Not in QE1") - priority = rules.RulePriority(10) - operator = rules.RuleOperator.ne - attribute_level = rules.RuleAttributeLevel.PERSON - attribute_name = rules.RuleAttributeName("ICB") - comparator = rules.RuleComparator("QE1") + type = RuleType.filter + name = RuleName("Not in QE1") + description = RuleDescription("Not in QE1") + priority = RulePriority(10) + operator = RuleOperator.ne + attribute_level = RuleAttributeLevel.PERSON + attribute_name = RuleAttributeName("ICB") + comparator = RuleComparator("QE1") class ICBRedirectRuleFactory(IterationRuleFactory): - type = rules.RuleType.redirect - name = rules.RuleName("In QE1") - description = rules.RuleDescription("In QE1") - priority = rules.RulePriority(20) - operator = rules.RuleOperator.equals - attribute_level = rules.RuleAttributeLevel.PERSON - attribute_name = rules.RuleAttributeName("ICB") - comparator = rules.RuleComparator("QE1") - comms_routing = rules.CommsRouting("ActionCode1") + type = RuleType.redirect + name = RuleName("In QE1") + description = RuleDescription("In QE1") + priority = RulePriority(20) + operator = RuleOperator.equals + attribute_level = RuleAttributeLevel.PERSON + attribute_name = RuleAttributeName("ICB") + comparator = RuleComparator("QE1") + comms_routing = CommsRouting("ActionCode1") + + +class ICBNonEligibleActionRuleFactory(IterationRuleFactory): + type = RuleType.not_eligible_actions + name = RuleName("In QE1") + description = RuleDescription("In QE1") + priority = RulePriority(20) + operator = RuleOperator.equals + attribute_level = RuleAttributeLevel.PERSON + attribute_name = RuleAttributeName("ICB") + comparator = RuleComparator("QE1") + comms_routing = CommsRouting("ActionCode1") + + +class ICBNonActionableActionRuleFactory(IterationRuleFactory): + type = RuleType.not_actionable_actions + name = RuleName("In QE1") + description = RuleDescription("In QE1") + priority = RulePriority(20) + operator = RuleOperator.equals + attribute_level = RuleAttributeLevel.PERSON + attribute_name = RuleAttributeName("ICB") + comparator = RuleComparator("QE1") + comms_routing = CommsRouting("ActionCode1") diff --git a/tests/fixtures/builders/repos/person.py b/tests/fixtures/builders/repos/person.py index 6cc418e19..eb2b96d64 100644 --- a/tests/fixtures/builders/repos/person.py +++ b/tests/fixtures/builders/repos/person.py @@ -5,6 +5,7 @@ from faker import Faker +from eligibility_signposting_api.model.person import Person from tests.conftest import PersonDetailProvider Gender = Literal["0", "1", "2", "9"] # 0 - Not known, 1- Male, 2 - Female, 9 - Not specified. I know, right? @@ -27,7 +28,7 @@ def person_rows_builder( # noqa:PLR0913 de: bool | None = ..., msoa: str | None = ..., lsoa: str | None = ..., -) -> list[dict[str, Any]]: +) -> Person: faker = Faker("en_UK") faker.add_provider(PersonDetailProvider) @@ -66,13 +67,9 @@ def person_rows_builder( # noqa:PLR0913 { "NHS_NUMBER": key, "ATTRIBUTE_TYPE": "COHORTS", - "COHORT_MAP": { - "cohorts": { - "M": { - cohort: {"M": {"dateJoined": {"S": faker.past_date().strftime("%Y%m%d")}}} for cohort in cohorts - } - } - }, + "COHORT_MEMBERSHIPS": [ + {"COHORT_LABEL": cohort, "DATE_JOINED": faker.past_date().strftime("%Y%m%d")} for cohort in cohorts + ], }, ] rows.extend( @@ -89,4 +86,5 @@ def person_rows_builder( # noqa:PLR0913 ) shuffle(rows) - return rows + + return Person(data=rows) diff --git a/tests/fixtures/builders/views/response_model/eligibility.py b/tests/fixtures/builders/views/response_model/eligibility.py index 061036908..3b8bff75b 100644 --- a/tests/fixtures/builders/views/response_model/eligibility.py +++ b/tests/fixtures/builders/views/response_model/eligibility.py @@ -1,23 +1,23 @@ from polyfactory import Use from polyfactory.factories.pydantic_factory import ModelFactory -from eligibility_signposting_api.views.response_model import eligibility +from eligibility_signposting_api.views.response_model import eligibility_response -class EligibilityCohortFactory(ModelFactory[eligibility.EligibilityCohort]): ... +class EligibilityCohortFactory(ModelFactory[eligibility_response.EligibilityCohort]): ... -class SuitabilityRuleFactory(ModelFactory[eligibility.SuitabilityRule]): ... +class SuitabilityRuleFactory(ModelFactory[eligibility_response.SuitabilityRule]): ... -class ActionFactory(ModelFactory[eligibility.Action]): ... +class ActionFactory(ModelFactory[eligibility_response.Action]): ... -class ProcessedSuggestionFactory(ModelFactory[eligibility.ProcessedSuggestion]): +class ProcessedSuggestionFactory(ModelFactory[eligibility_response.ProcessedSuggestion]): eligibility_cohorts = Use(EligibilityCohortFactory.batch, size=2) suitability_rules = Use(SuitabilityRuleFactory.batch, size=2) actions = Use(ActionFactory.batch, size=2) -class EligibilityResponseFactory(ModelFactory[eligibility.EligibilityResponse]): +class EligibilityResponseFactory(ModelFactory[eligibility_response.EligibilityResponse]): processed_suggestions = Use(ProcessedSuggestionFactory.batch, size=2) diff --git a/tests/fixtures/matchers/eligibility.py b/tests/fixtures/matchers/eligibility.py index 98bfb4138..731a3de76 100644 --- a/tests/fixtures/matchers/eligibility.py +++ b/tests/fixtures/matchers/eligibility.py @@ -1,7 +1,11 @@ from hamcrest.core.matcher import Matcher -from eligibility_signposting_api.model.eligibility import CohortGroupResult, Condition, EligibilityStatus, Reason -from eligibility_signposting_api.views.response_model.eligibility import Action, EligibilityCohort, SuitabilityRule +from eligibility_signposting_api.model.eligibility_status import CohortGroupResult, Condition, EligibilityStatus, Reason +from eligibility_signposting_api.views.response_model.eligibility_response import ( + Action, + EligibilityCohort, + SuitabilityRule, +) from .meta import BaseAutoMatcher diff --git a/tests/fixtures/matchers/rules.py b/tests/fixtures/matchers/rules.py index 4aaadf311..d289f0b7e 100644 --- a/tests/fixtures/matchers/rules.py +++ b/tests/fixtures/matchers/rules.py @@ -1,6 +1,6 @@ from hamcrest.core.matcher import Matcher -from eligibility_signposting_api.model.rules import CampaignConfig, Iteration, IterationRule +from eligibility_signposting_api.model.campaign_config import CampaignConfig, Iteration, IterationRule from .meta import BaseAutoMatcher diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 75cf97878..59d696121 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -1,3 +1,4 @@ +import datetime import json import logging import os @@ -16,7 +17,13 @@ from httpx import RequestError from yarl import URL -from eligibility_signposting_api.model import eligibility, rules +from eligibility_signposting_api.model import eligibility_status +from eligibility_signposting_api.model.campaign_config import ( + CampaignConfig, + EndDate, + RuleType, + StartDate, +) from eligibility_signposting_api.repos.campaign_repo import BucketName from eligibility_signposting_api.repos.person_repo import TableName from tests.fixtures.builders.model import rule @@ -330,12 +337,12 @@ def person_table(dynamodb_resource: ServiceResource) -> Generator[Any]: @pytest.fixture -def persisted_person(person_table: Any, faker: Faker) -> Generator[eligibility.NHSNumber]: - nhs_number = eligibility.NHSNumber(faker.nhs_number()) - date_of_birth = eligibility.DateOfBirth(faker.date_of_birth(minimum_age=18, maximum_age=65)) +def persisted_person(person_table: Any, faker: Faker) -> Generator[eligibility_status.NHSNumber]: + nhs_number = eligibility_status.NHSNumber(faker.nhs_number()) + date_of_birth = eligibility_status.DateOfBirth(faker.date_of_birth(minimum_age=18, maximum_age=65)) for row in ( - rows := person_rows_builder(nhs_number, date_of_birth=date_of_birth, postcode="hp1", cohorts=["cohort1"]) + rows := person_rows_builder(nhs_number, date_of_birth=date_of_birth, postcode="hp1", cohorts=["cohort1"]).data ): person_table.put_item(Item=row) @@ -346,9 +353,9 @@ def persisted_person(person_table: Any, faker: Faker) -> Generator[eligibility.N @pytest.fixture -def persisted_77yo_person(person_table: Any, faker: Faker) -> Generator[eligibility.NHSNumber]: - nhs_number = eligibility.NHSNumber(faker.nhs_number()) - date_of_birth = eligibility.DateOfBirth(faker.date_of_birth(minimum_age=77, maximum_age=77)) +def persisted_77yo_person(person_table: Any, faker: Faker) -> Generator[eligibility_status.NHSNumber]: + nhs_number = eligibility_status.NHSNumber(faker.nhs_number()) + date_of_birth = eligibility_status.DateOfBirth(faker.date_of_birth(minimum_age=77, maximum_age=77)) for row in ( rows := person_rows_builder( @@ -356,7 +363,7 @@ def persisted_77yo_person(person_table: Any, faker: Faker) -> Generator[eligibil date_of_birth=date_of_birth, postcode="hp1", cohorts=["cohort1", "cohort2"], - ) + ).data ): person_table.put_item(Item=row) @@ -367,18 +374,18 @@ def persisted_77yo_person(person_table: Any, faker: Faker) -> Generator[eligibil @pytest.fixture -def persisted_person_all_cohorts(person_table: Any, faker: Faker) -> Generator[eligibility.NHSNumber]: - nhs_number = eligibility.NHSNumber(faker.nhs_number()) - date_of_birth = eligibility.DateOfBirth(faker.date_of_birth(minimum_age=74, maximum_age=74)) +def persisted_person_all_cohorts(person_table: Any, faker: Faker) -> Generator[eligibility_status.NHSNumber]: + nhs_number = eligibility_status.NHSNumber(faker.nhs_number()) + date_of_birth = eligibility_status.DateOfBirth(faker.date_of_birth(minimum_age=74, maximum_age=74)) for row in ( rows := person_rows_builder( nhs_number, date_of_birth=date_of_birth, - postcode="hp1", - cohorts=["cohort_label1", "cohort_label2", "cohort_label3"], + postcode="SW19", + cohorts=["cohort_label1", "cohort_label2", "cohort_label3", "cohort_label4", "cohort_label5"], icb="QE1", - ) + ).data ): person_table.put_item(Item=row) @@ -389,10 +396,10 @@ def persisted_person_all_cohorts(person_table: Any, faker: Faker) -> Generator[e @pytest.fixture -def persisted_person_no_cohorts(person_table: Any, faker: Faker) -> Generator[eligibility.NHSNumber]: - nhs_number = eligibility.NHSNumber(faker.nhs_number()) +def persisted_person_no_cohorts(person_table: Any, faker: Faker) -> Generator[eligibility_status.NHSNumber]: + nhs_number = eligibility_status.NHSNumber(faker.nhs_number()) - for row in (rows := person_rows_builder(nhs_number)): + for row in (rows := person_rows_builder(nhs_number).data): person_table.put_item(Item=row) yield nhs_number @@ -402,11 +409,11 @@ def persisted_person_no_cohorts(person_table: Any, faker: Faker) -> Generator[el @pytest.fixture -def persisted_person_pc_sw19(person_table: Any, faker: Faker) -> Generator[eligibility.NHSNumber]: - nhs_number = eligibility.NHSNumber( +def persisted_person_pc_sw19(person_table: Any, faker: Faker) -> Generator[eligibility_status.NHSNumber]: + nhs_number = eligibility_status.NHSNumber( faker.nhs_number(), ) - for row in (rows := person_rows_builder(nhs_number, postcode="SW19", cohorts=["cohort1"])): + for row in (rows := person_rows_builder(nhs_number, postcode="SW19", cohorts=["cohort1"]).data): person_table.put_item(Item=row) yield nhs_number @@ -452,13 +459,13 @@ def firehose_delivery_stream(firehose_client: BaseClient, audit_bucket: BucketNa @pytest.fixture(scope="class") -def campaign_config(s3_client: BaseClient, rules_bucket: BucketName) -> Generator[rules.CampaignConfig]: - campaign: rules.CampaignConfig = rule.CampaignConfigFactory.build( +def campaign_config(s3_client: BaseClient, rules_bucket: BucketName) -> Generator[CampaignConfig]: + campaign: CampaignConfig = rule.CampaignConfigFactory.build( target="RSV", iterations=[ rule.IterationFactory.build( iteration_rules=[ - rule.PostcodeSuppressionRuleFactory.build(type=rules.RuleType.filter), + rule.PostcodeSuppressionRuleFactory.build(type=RuleType.filter), rule.PersonAgeSuppressionRuleFactory.build(), ], iteration_cohorts=[ @@ -481,14 +488,99 @@ def campaign_config(s3_client: BaseClient, rules_bucket: BucketName) -> Generato @pytest.fixture(scope="class") -def multiple_campaign_configs(s3_client: BaseClient, rules_bucket: BucketName) -> Generator[list[rules.CampaignConfig]]: +def inactive_iteration_config(s3_client: BaseClient, rules_bucket: BucketName) -> Generator[list[CampaignConfig]]: + campaigns, campaign_data_keys = [], [] + + target_iteration_dates = { + "start_date": ("RSV", datetime.date(2025, 1, 1)), # Active Iteration Date + "start_date_plus_one_day": ("COVID", datetime.date(2025, 1, 2)), # Active Iteration Date + "today": ("FLU", datetime.date(2025, 8, 8)), # Active Iteration Date + "tomorrow": ("MMR", datetime.date(2025, 8, 9)), # Inactive Iteration Date + } + + for target, data in target_iteration_dates.items(): + campaign = rule.CampaignConfigFactory.build( + id=f"campaign_{target}", + target=data[0], + type="V", + iterations=[ + rule.IterationFactory.build( + iteration_rules=[rule.PersonAgeSuppressionRuleFactory.build()], + iteration_cohorts=[rule.IterationCohortFactory.build(cohort_label="cohort_label1")], + ) + ], + ) + + campaign.start_date = StartDate(datetime.date(2025, 1, 1)) + campaign.end_date = EndDate(datetime.date(2026, 1, 1)) + campaign.iterations[0].iteration_date = data[1] + + campaign_data = {"CampaignConfig": campaign.model_dump(by_alias=True)} + key = f"{campaign.name}.json" + s3_client.put_object( + Bucket=rules_bucket, Key=key, Body=json.dumps(campaign_data), ContentType="application/json" + ) + campaigns.append(campaign) + campaign_data_keys.append(key) + + yield campaigns + + for key in campaign_data_keys: + s3_client.delete_object(Bucket=rules_bucket, Key=key) + + +@pytest.fixture(scope="class") +def campaign_config_with_and_rule(s3_client: BaseClient, rules_bucket: BucketName) -> Generator[CampaignConfig]: + campaign: CampaignConfig = rule.CampaignConfigFactory.build( + target="RSV", + iterations=[ + rule.IterationFactory.build( + iteration_rules=[ + rule.PostcodeSuppressionRuleFactory.build( + cohort_label="cohort2", + ), + rule.PersonAgeSuppressionRuleFactory.build(), + ], + iteration_cohorts=[ + rule.IterationCohortFactory.build( + cohort_label="cohort1", + cohort_group="cohort_group1", + positive_description="positive_description", + negative_description="negative_description", + ), + rule.IterationCohortFactory.build( + cohort_label="cohort2", + cohort_group="cohort_group2", + positive_description="positive_description", + negative_description="negative_description", + ), + ], + ) + ], + ) + campaign_data = {"CampaignConfig": campaign.model_dump(by_alias=True)} + s3_client.put_object( + Bucket=rules_bucket, Key=f"{campaign.name}.json", Body=json.dumps(campaign_data), ContentType="application/json" + ) + yield campaign + s3_client.delete_object(Bucket=rules_bucket, Key=f"{campaign.name}.json") + + +@pytest.fixture(scope="class") +def multiple_campaign_configs(s3_client: BaseClient, rules_bucket: BucketName) -> Generator[list[CampaignConfig]]: """Create and upload multiple campaign configs to S3, then clean up after tests.""" campaigns, campaign_data_keys = [], [] targets = ["RSV", "COVID", "FLU"] target_rules_map = { - targets[0]: [rule.PersonAgeSuppressionRuleFactory.build(type=rules.RuleType.filter)], - targets[1]: [rule.PersonAgeSuppressionRuleFactory.build()], + targets[0]: [ + rule.PersonAgeSuppressionRuleFactory.build(type=RuleType.filter, description="TOO YOUNG"), + rule.PostcodeSuppressionRuleFactory.build(type=RuleType.filter, priority=8, cohort_label="cohort_label4"), + ], + targets[1]: [ + rule.PersonAgeSuppressionRuleFactory.build(description="TOO YOUNG"), + rule.PostcodeSuppressionRuleFactory.build(priority=12, cohort_label="cohort_label2"), + ], targets[2]: [rule.ICBRedirectRuleFactory.build()], } @@ -506,7 +598,13 @@ def multiple_campaign_configs(s3_client: BaseClient, rules_bucket: BucketName) - cohort_group=f"cohort_group{i + 1}", positive_description=f"positive_desc_{i + 1}", negative_description=f"negative_desc_{i + 1}", - ) + ), + rule.IterationCohortFactory.build( + cohort_label="cohort_label4", + cohort_group="cohort_group4", + positive_description="positive_desc_4", + negative_description="negative_desc_4", + ), ], ) ], @@ -526,15 +624,13 @@ def multiple_campaign_configs(s3_client: BaseClient, rules_bucket: BucketName) - @pytest.fixture(scope="class") -def campaign_config_with_magic_cohort( - s3_client: BaseClient, rules_bucket: BucketName -) -> Generator[rules.CampaignConfig]: - campaign: rules.CampaignConfig = rule.CampaignConfigFactory.build( +def campaign_config_with_magic_cohort(s3_client: BaseClient, rules_bucket: BucketName) -> Generator[CampaignConfig]: + campaign: CampaignConfig = rule.CampaignConfigFactory.build( target="COVID", iterations=[ rule.IterationFactory.build( iteration_rules=[ - rule.PostcodeSuppressionRuleFactory.build(type=rules.RuleType.filter), + rule.PostcodeSuppressionRuleFactory.build(type=RuleType.filter), rule.PersonAgeSuppressionRuleFactory.build(), ], iteration_cohorts=[rule.MagicCohortFactory.build(cohort_label="elid_all_people")], @@ -552,13 +648,13 @@ def campaign_config_with_magic_cohort( @pytest.fixture(scope="class") def campaign_config_with_missing_descriptions_missing_rule_text( s3_client: BaseClient, rules_bucket: BucketName -) -> Generator[rules.CampaignConfig]: - campaign: rules.CampaignConfig = rule.CampaignConfigFactory.build( +) -> Generator[CampaignConfig]: + campaign: CampaignConfig = rule.CampaignConfigFactory.build( target="FLU", iterations=[ rule.IterationFactory.build( iteration_rules=[ - rule.PostcodeSuppressionRuleFactory.build(type=rules.RuleType.filter), + rule.PostcodeSuppressionRuleFactory.build(type=RuleType.filter), rule.PersonAgeSuppressionRuleFactory.build(), rule.PersonAgeSuppressionRuleFactory.build(name="Exclude 76 rolling", description=""), ], diff --git a/tests/integration/in_process/test_eligibility_endpoint.py b/tests/integration/in_process/test_eligibility_endpoint.py index 5356eed59..5290563f1 100644 --- a/tests/integration/in_process/test_eligibility_endpoint.py +++ b/tests/integration/in_process/test_eligibility_endpoint.py @@ -11,10 +11,10 @@ has_key, ) -from eligibility_signposting_api.model.eligibility import ( +from eligibility_signposting_api.model.campaign_config import CampaignConfig +from eligibility_signposting_api.model.eligibility_status import ( NHSNumber, ) -from eligibility_signposting_api.model.rules import CampaignConfig class TestBaseLine: @@ -85,7 +85,7 @@ def test_not_base_eligible( ], "actions": [], "suitabilityRules": [], - "statusText": "Status.not_eligible", + "statusText": "We do not believe you can have it", } ] ), @@ -128,7 +128,7 @@ def test_not_eligible_by_rule( ], "actions": [], "suitabilityRules": [], - "statusText": "Status.not_eligible", + "statusText": "We do not believe you can have it", } ] ), @@ -177,7 +177,7 @@ def test_not_actionable( "ruleType": "S", } ], - "statusText": "Status.not_actionable", + "statusText": "You should have the RSV vaccine", } ] ), @@ -228,7 +228,58 @@ def test_actionable( } ], "suitabilityRules": [], - "statusText": "Status.actionable", + "statusText": "You should have the RSV vaccine", + } + ] + ), + ) + ) + ), + ) + + def test_actionable_with_and_rule( + self, + client: FlaskClient, + persisted_person: NHSNumber, + campaign_config_with_and_rule: CampaignConfig, # noqa: ARG002 + ): + # Given + + # When + response = client.get(f"/patient-check/{persisted_person}?includeActions=Y") + + # Then + assert_that( + response, + is_response() + .with_status_code(HTTPStatus.OK) + .and_text( + is_json_that( + has_entry( + "processedSuggestions", + equal_to( + [ + { + "condition": "RSV", + "status": "Actionable", + "eligibilityCohorts": [ + { + "cohortCode": "cohort_group1", + "cohortStatus": "Actionable", + "cohortText": "positive_description", + } + ], + "actions": [ + { + "actionCode": "action_code", + "actionType": "defaultcomms", + "description": "", + "urlLabel": "", + "urlLink": "", + } + ], + "suitabilityRules": [], + "statusText": "You should have the RSV vaccine", } ] ), @@ -273,7 +324,7 @@ def test_not_eligible_by_rule_when_only_magic_cohort_is_present( ], "actions": [], "suitabilityRules": [], - "statusText": "Status.not_eligible", + "statusText": "We do not believe you can have it", } ] ), @@ -322,7 +373,7 @@ def test_not_actionable_when_only_magic_cohort_is_present( "ruleType": "S", } ], - "statusText": "Status.not_actionable", + "statusText": "You should have the COVID vaccine", } ] ), @@ -373,7 +424,7 @@ def test_actionable_when_only_magic_cohort_is_present( } ], "suitabilityRules": [], - "statusText": "Status.actionable", + "statusText": "You should have the COVID vaccine", } ] ), @@ -412,7 +463,7 @@ def test_not_base_eligible( "eligibilityCohorts": [], "actions": [], "suitabilityRules": [], - "statusText": "Status.not_eligible", + "statusText": "We do not believe you can have it", } ] ), @@ -449,7 +500,7 @@ def test_not_eligible_by_rule( "eligibilityCohorts": [], "actions": [], "suitabilityRules": [], - "statusText": "Status.not_eligible", + "statusText": "We do not believe you can have it", } ] ), @@ -492,7 +543,7 @@ def test_not_actionable( "ruleType": "S", } ], - "statusText": "Status.not_actionable", + "statusText": "You should have the FLU vaccine", } ] ), @@ -537,7 +588,7 @@ def test_actionable( } ], "suitabilityRules": [], - "statusText": "Status.actionable", + "statusText": "You should have the FLU vaccine", } ] ), @@ -573,7 +624,7 @@ def test_actionable_no_actions( "status": "Actionable", "eligibilityCohorts": [], "suitabilityRules": [], - "statusText": "Status.actionable", + "statusText": "You should have the FLU vaccine", } ] ), diff --git a/tests/integration/lambda/test_app_running_as_lambda.py b/tests/integration/lambda/test_app_running_as_lambda.py index 54e0370f8..c54ea08c2 100644 --- a/tests/integration/lambda/test_app_running_as_lambda.py +++ b/tests/integration/lambda/test_app_running_as_lambda.py @@ -10,6 +10,7 @@ from brunns.matchers.data import json_matching as is_json_that from brunns.matchers.response import is_response from faker import Faker +from freezegun import freeze_time from hamcrest import ( assert_that, contains_exactly, @@ -23,8 +24,8 @@ ) from yarl import URL -from eligibility_signposting_api.model.eligibility import NHSNumber -from eligibility_signposting_api.model.rules import CampaignConfig +from eligibility_signposting_api.model.campaign_config import CampaignConfig +from eligibility_signposting_api.model.eligibility_status import NHSNumber from eligibility_signposting_api.repos.campaign_repo import BucketName logger = logging.getLogger(__name__) @@ -124,11 +125,11 @@ def test_install_and_call_flask_lambda_with_unknown_nhs_number( timeout=10, ) - # Then assert_that( response, is_response() .with_status_code(HTTPStatus.NOT_FOUND) + .with_headers(has_entries({"Content-Type": "application/fhir+json"})) .and_body( is_json_that( has_entries( @@ -153,7 +154,7 @@ def test_install_and_call_flask_lambda_with_unknown_nhs_number( ) ), ) - ) + ), ), ) @@ -232,7 +233,7 @@ def test_given_nhs_number_in_path_matches_with_nhs_number_in_headers_and_check_i "iterationVersion": campaign_config.iterations[0].version, "conditionName": campaign_config.target, "status": "not_actionable", - "statusText": "not_actionable", + "statusText": f"You should have the {campaign_config.target} vaccine", "eligibilityCohorts": [{"cohortCode": "cohort1", "cohortStatus": "not_actionable"}], "eligibilityCohortGroups": [ { @@ -242,11 +243,13 @@ def test_given_nhs_number_in_path_matches_with_nhs_number_in_headers_and_check_i } ], "filterRules": None, - "suitabilityRules": { - "rulePriority": "10", - "ruleName": "Exclude too young less than 75", - "ruleMessage": "Exclude too young less than 75", - }, + "suitabilityRules": [ + { + "rulePriority": "10", + "ruleName": "Exclude too young less than 75", + "ruleMessage": "Exclude too young less than 75", + } + ], "actionRule": None, "actions": [], } @@ -292,6 +295,7 @@ def test_given_nhs_number_in_path_does_not_match_with_nhs_number_in_headers_resu response, is_response() .with_status_code(HTTPStatus.FORBIDDEN) + .with_headers(has_entries({"Content-Type": "application/fhir+json"})) .and_body( is_json_that( has_entries( @@ -300,14 +304,13 @@ def test_given_nhs_number_in_path_does_not_match_with_nhs_number_in_headers_resu has_entries( severity="error", code="forbidden", - diagnostics=f"NHS Number {persisted_person} does " - f"not match the header NHS Number 123{persisted_person!s}", + diagnostics="You are not authorised to request information for the supplied NHS Number", details={ "coding": [ { "system": "https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1", - "code": "INVALID_NHS_NUMBER", - "display": "The provided NHS number does not match the record.", + "code": "ACCESS_DENIED", + "display": "Access has been denied to process this request.", } ] }, @@ -338,6 +341,7 @@ def test_given_nhs_number_not_present_in_headers_results_in_error_response( response, is_response() .with_status_code(HTTPStatus.FORBIDDEN) + .with_headers(has_entries({"Content-Type": "application/fhir+json"})) .and_body( is_json_that( has_entries( @@ -346,13 +350,13 @@ def test_given_nhs_number_not_present_in_headers_results_in_error_response( has_entries( severity="error", code="forbidden", - diagnostics=f"NHS Number {persisted_person} does not match the header NHS Number ", + diagnostics="You are not authorised to request information for the supplied NHS Number", details={ "coding": [ { "system": "https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1", - "code": "INVALID_NHS_NUMBER", - "display": "The provided NHS number does not match the record.", + "code": "ACCESS_DENIED", + "display": "Access has been denied to process this request.", } ] }, @@ -456,16 +460,19 @@ def test_given_person_has_unique_status_for_different_conditions_with_audit( # "iterationVersion": rsv_campaign.iterations[0].version, "conditionName": rsv_campaign.target, "status": "not_eligible", - "statusText": "not_eligible", - "eligibilityCohorts": [{"cohortCode": "cohort_label1", "cohortStatus": "not_eligible"}], + "statusText": "We do not believe you can have it", + "eligibilityCohorts": [ + {"cohortCode": "cohort_label1", "cohortStatus": "not_eligible"}, + {"cohortCode": "cohort_label4", "cohortStatus": "not_eligible"}, + ], "eligibilityCohortGroups": [ - { - "cohortCode": "cohort_group1", - "cohortText": "negative_desc_1", - "cohortStatus": "not_eligible", - } + {"cohortCode": "cohort_group1", "cohortText": "negative_desc_1", "cohortStatus": "not_eligible"}, + {"cohortCode": "cohort_group4", "cohortText": "negative_desc_4", "cohortStatus": "not_eligible"}, + ], + "filterRules": [ + {"rulePriority": "10", "ruleName": "Exclude too young less than 75"}, + {"rulePriority": "8", "ruleName": "Excluded postcode In SW19"}, ], - "filterRules": {"rulePriority": "10", "ruleName": "Exclude too young less than 75"}, "suitabilityRules": None, "actionRule": None, "actions": [], @@ -477,21 +484,20 @@ def test_given_person_has_unique_status_for_different_conditions_with_audit( # "iterationVersion": covid_campaign.iterations[0].version, "conditionName": covid_campaign.target, "status": "not_actionable", - "statusText": "not_actionable", - "eligibilityCohorts": [{"cohortCode": "cohort_label2", "cohortStatus": "not_actionable"}], + "statusText": f"You should have the {covid_campaign.target} vaccine", + "eligibilityCohorts": [ + {"cohortCode": "cohort_label2", "cohortStatus": "not_actionable"}, + {"cohortCode": "cohort_label4", "cohortStatus": "not_actionable"}, + ], "eligibilityCohortGroups": [ - { - "cohortCode": "cohort_group2", - "cohortText": "positive_desc_2", - "cohortStatus": "not_actionable", - } + {"cohortCode": "cohort_group2", "cohortText": "positive_desc_2", "cohortStatus": "not_actionable"}, + {"cohortCode": "cohort_group4", "cohortText": "positive_desc_4", "cohortStatus": "not_actionable"}, ], "filterRules": None, - "suitabilityRules": { - "rulePriority": "10", - "ruleName": "Exclude too young less than 75", - "ruleMessage": "Exclude too young less than 75", - }, + "suitabilityRules": [ + {"rulePriority": "10", "ruleName": "Exclude too young less than 75", "ruleMessage": "TOO YOUNG"}, + {"rulePriority": "12", "ruleName": "Excluded postcode In SW19", "ruleMessage": "In SW19"}, + ], "actionRule": None, "actions": [], }, @@ -502,14 +508,14 @@ def test_given_person_has_unique_status_for_different_conditions_with_audit( # "iterationVersion": flu_campaign.iterations[0].version, "conditionName": flu_campaign.target, "status": "actionable", - "statusText": "actionable", - "eligibilityCohorts": [{"cohortCode": "cohort_label3", "cohortStatus": "actionable"}], + "statusText": f"You should have the {flu_campaign.target} vaccine", + "eligibilityCohorts": [ + {"cohortCode": "cohort_label3", "cohortStatus": "actionable"}, + {"cohortCode": "cohort_label4", "cohortStatus": "actionable"}, + ], "eligibilityCohortGroups": [ - { - "cohortCode": "cohort_group3", - "cohortText": "positive_desc_3", - "cohortStatus": "actionable", - } + {"cohortCode": "cohort_group3", "cohortText": "positive_desc_3", "cohortStatus": "actionable"}, + {"cohortCode": "cohort_group4", "cohortText": "positive_desc_4", "cohortStatus": "actionable"}, ], "filterRules": None, "suitabilityRules": None, @@ -534,3 +540,40 @@ def test_given_person_has_unique_status_for_different_conditions_with_audit( # assert_that(audit_data["response"]["responseId"], is_not(equal_to(""))) assert_that(audit_data["response"]["lastUpdated"], is_not(equal_to(""))) assert_that(audit_data["response"]["condition"], contains_inanyorder(*expected_conditions)) + + +@freeze_time("2025-08-08") +def test_no_active_iteration_returns_empty_processed_suggestions( + lambda_client: BaseClient, # noqa:ARG001 + persisted_person_all_cohorts: NHSNumber, + inactive_iteration_config: list[CampaignConfig], # noqa:ARG001 + api_gateway_endpoint: URL, +): + invoke_url = f"{api_gateway_endpoint}/patient-check/{persisted_person_all_cohorts}" + response = httpx.get( + invoke_url, + headers={ + "nhs-login-nhs-number": str(persisted_person_all_cohorts), + "x_request_id": "x_request_id", + "x_correlation_id": "x_correlation_id", + "nhsd_end_user_organisation_ods": "nhsd_end_user_organisation_ods", + "nhsd_application_id": "nhsd_application_id", + }, + params={"includeActions": "Y", "category": "VACCINATIONS", "conditions": "COVID,FLU,RSV"}, + timeout=10, + ) + + assert_that( + response, + is_response().with_status_code(HTTPStatus.OK).and_body(is_json_that(has_key("processedSuggestions"))), + ) + + body = response.json() + assert_that( + body["processedSuggestions"], + contains_inanyorder( + has_entries("condition", "COVID"), + has_entries("condition", "RSV"), + has_entries("condition", "FLU"), + ), + ) diff --git a/tests/integration/repo/test_campaign_repo.py b/tests/integration/repo/test_campaign_repo.py index 5870d1b32..96742d38a 100644 --- a/tests/integration/repo/test_campaign_repo.py +++ b/tests/integration/repo/test_campaign_repo.py @@ -5,7 +5,7 @@ from botocore.client import BaseClient from hamcrest import assert_that, has_item -from eligibility_signposting_api.model.rules import CampaignConfig +from eligibility_signposting_api.model.campaign_config import CampaignConfig from eligibility_signposting_api.repos.campaign_repo import BucketName, CampaignRepo from tests.fixtures.builders.model.rule import CampaignConfigFactory from tests.fixtures.matchers.rules import is_campaign_config, is_iteration, is_iteration_rule diff --git a/tests/integration/repo/test_person_repo.py b/tests/integration/repo/test_person_repo.py index 7a444d20e..25905e9cf 100644 --- a/tests/integration/repo/test_person_repo.py +++ b/tests/integration/repo/test_person_repo.py @@ -4,7 +4,7 @@ from faker import Faker from hamcrest import assert_that, contains_inanyorder, has_entries -from eligibility_signposting_api.model.eligibility import NHSNumber +from eligibility_signposting_api.model.eligibility_status import NHSNumber from eligibility_signposting_api.repos import NotFoundError from eligibility_signposting_api.repos.person_repo import PersonRepo @@ -18,7 +18,7 @@ def test_person_found(person_table: Any, persisted_person: NHSNumber): # Then assert_that( - actual, + actual.data, contains_inanyorder( has_entries({"NHS_NUMBER": persisted_person, "ATTRIBUTE_TYPE": "PERSON"}), has_entries({"NHS_NUMBER": persisted_person, "ATTRIBUTE_TYPE": "COHORTS"}), diff --git a/tests/test_data/test_config/test_config.json b/tests/test_data/test_config/test_config.json index ab2672df5..8f9cb1445 100644 --- a/tests/test_data/test_config/test_config.json +++ b/tests/test_data/test_config/test_config.json @@ -5,22 +5,38 @@ "Name": "Test Config", "Type": "V", "Target": "RSV", - "Manager": "person@test.com", - "Approver": "person@test.com", - "Reviewer": "person@test.com", + "Manager": ["person@test.com"], + "Approver": ["person@test.com"], + "Reviewer": ["person@test.com"], + "StartDate": "20250101", + "EndDate": "20260101", + "ApprovalMinimum": 1, + "ApprovalMaximum": 5000000, "IterationFrequency": "X", "IterationType": "M", "IterationTime": "07:00:00", - "DefaultCommsRouting": "Default_Comms_1", "Iterations": [ { "ID": "id_100", + "Version": "1", + "Name": "Test Config", + "Type": "M", + "IterationDate": "20250101", + "IterationNumber": 1, + "CommsType": "R", + "ApprovalMinimum": 1, + "ApprovalMaximum": 5000000, "DefaultCommsRouting": "INTERNALCONTACTGP1", + "DefaultNotActionableRouting": "INTERNALCONTACTGP1", + "DefaultNotEligibleRouting": "INTERNALCONTACTGP1", "ActionsMapper": { "INTERNALCONTACTGP1": {"ExternalRoutingCode": "CONTACTGP","ActionDescription":"Contact GP Text1 description", "ActionType":"text1"}, - "INTERNALCONTACTGP2": {"ExternalRoutingCode": "CONTACTGP","ActionDescription":"Contact GP Link description", "ActionType":"link", "UrlLink": "link123", "UrlLabel": "link label"}, - "INTERNALTESCO": {"ExternalRoutingCode": "TESCO","ActionDescription":"Tesco description", "ActionType":"link", "UrlLink": "tesco link", "UrlLabel": "link label"}, - "INTERNALFINDWALKIN": {"ExternalRoutingCode": "FINDWALKIN","ActionDescription":"Find walkin description", "ActionType":"button"} + "INTERNALCONTACTGP2": {"ExternalRoutingCode": "CONTACTGP","ActionDescription":"Contact GP Link description", "ActionType":"link", "UrlLink": "https://www.link123.example", "UrlLabel": "link label"}, + "INTERNALTESCO": {"ExternalRoutingCode": "TESCO","ActionDescription":"Tesco description", "ActionType":"link", "UrlLink": "https://www.tesco_link.example", "UrlLabel": "link label"}, + "INTERNALFINDWALKIN": {"ExternalRoutingCode": "FINDWALKIN","ActionDescription":"Find walkin description", "ActionType":"button"}, + + "XRULEID1": {"ExternalRoutingCode": "FINDWALKIN","ActionDescription":"Find walkin description", "ActionType":"button"}, + "YRULEID1": {"ExternalRoutingCode": "FINDWALKIN","ActionDescription":"Find walkin description", "ActionType":"button"} }, "IterationCohorts": [ { @@ -91,21 +107,31 @@ "Operator": ">", "Comparator": "19000101", "CommsRouting": "INTERNALCONTACTGP1|INTERNALTESCO" + }, + { + "Type": "X", + "Name": "Test X Rule for not eligible", + "Description": "Test X Rule Desc", + "Priority": 20, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": ">", + "Comparator": "19000101", + "CommsRouting": "XRULEID1|INTERNALTESCO" + }, + { + "Type": "Y", + "Name": "Test Y Rule for not actionable", + "Description": "Test Y Rule Desc", + "Priority": 20, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": ">", + "Comparator": "19000101", + "CommsRouting": "YRULEID1|INTERNALTESCO" } - ], - "Version": "1", - "Name": "Test Config", - "Type": "M", - "IterationDate": "20250101", - "IterationNumber": 1, - "CommsType": "R", - "ApprovalMinimum": 1, - "ApprovalMaximum": 5000000 + ] } - ], - "StartDate": "20250101", - "EndDate": "20260101", - "ApprovalMinimum": 1, - "ApprovalMaximum": 5000000 + ] } } diff --git a/tests/unit/audit/test_audit_context.py b/tests/unit/audit/test_audit_context.py index 2ee9aafdc..05d78bd8e 100644 --- a/tests/unit/audit/test_audit_context.py +++ b/tests/unit/audit/test_audit_context.py @@ -9,14 +9,17 @@ from eligibility_signposting_api.audit.audit_context import AuditContext from eligibility_signposting_api.audit.audit_models import AuditAction, AuditEvent from eligibility_signposting_api.audit.audit_service import AuditService -from eligibility_signposting_api.model.eligibility import ( +from eligibility_signposting_api.model.campaign_config import CampaignID, CampaignVersion, RuleType +from eligibility_signposting_api.model.eligibility_status import ( ActionCode, ActionDescription, ActionType, + BestIterationResult, CohortGroupResult, ConditionName, InternalActionCode, IterationResult, + MatchedActionDetail, Reason, RuleDescription, RuleName, @@ -26,7 +29,6 @@ UrlLabel, UrlLink, ) -from eligibility_signposting_api.model.rules import CampaignID, CampaignVersion, Iteration, RuleType from tests.fixtures.builders.model.rule import IterationFactory @@ -83,9 +85,7 @@ def test_add_request_details_when_headers_are_empty_sets_audit_log_on_g(app): def test_append_audit_condition_adds_condition_to_audit_log_on_g(app): suggested_actions: list[SuggestedAction] | None condition_name: ConditionName - best_results: tuple[Iteration, IterationResult, dict[str, CohortGroupResult]] campaign_details: tuple[CampaignID | None, CampaignVersion | None] - redirect_rule_details: tuple[RulePriority | None, RuleName | None] suggested_actions = [ SuggestedAction( @@ -99,7 +99,7 @@ def test_append_audit_condition_adds_condition_to_audit_log_on_g(app): ] condition_name = ConditionName("Condition1") - iteration = IterationFactory.build() + iteration = IterationFactory.build(version=12345) audit_rules = [ Reason( rule_type=RuleType.filter, @@ -119,15 +119,18 @@ def test_append_audit_condition_adds_condition_to_audit_log_on_g(app): iteration_result = IterationResult( status=Status.actionable, cohort_results=[cohort_group_result], actions=suggested_actions ) - best_results = (iteration, iteration_result, {"CohortCode1": cohort_group_result}) - campaign_details = (CampaignID("CampaignID1"), CampaignVersion("CampaignVersion1")) - redirect_rule_details = (RulePriority("1"), RuleName("RedirectRuleName1")) + campaign_details = (CampaignID("CampaignID1"), CampaignVersion(123)) + matched_action_detail = MatchedActionDetail(RuleName("RedirectRuleName1"), RulePriority("1"), suggested_actions) + + best_iteration_results = BestIterationResult( + iteration_result, iteration, campaign_details[0], campaign_details[1], {"CohortCode1": cohort_group_result} + ) with app.app_context(): g.audit_log = AuditEvent() AuditContext.append_audit_condition( - suggested_actions, condition_name, best_results, campaign_details, redirect_rule_details + condition_name, best_iteration_results, matched_action_detail, [cohort_group_result] ) expected_audit_action = [ @@ -148,8 +151,8 @@ def test_append_audit_condition_adds_condition_to_audit_log_on_g(app): assert cond.campaign_version == campaign_details[1] assert cond.iteration_id == iteration.id assert cond.iteration_version == iteration.version - assert cond.status == best_results[1].status.name - assert cond.status_text == best_results[1].status.name + assert cond.status == "actionable" + assert cond.status_text == "You should have the Condition1 vaccine" assert cond.actions == expected_audit_action assert cond.action_rule.rule_priority == "1" assert cond.action_rule.rule_name == "RedirectRuleName1" @@ -190,3 +193,44 @@ def test_write_to_firehose_calls_audit_service_with_correct_data_from_g(app): assert g.audit_log.response.last_updated == last_updated mock_audit_service.audit.assert_called_once_with(g.audit_log.model_dump(by_alias=True)) + + +def test_no_duplicates_returns_same_list(): + reasons = [ + Reason(RuleType("F"), RuleName("code1"), RulePriority("1"), RuleDescription("desc1"), matcher_matched=True), + Reason(RuleType("S"), RuleName("code2"), RulePriority("2"), RuleDescription("desc2"), matcher_matched=False), + ] + expected = reasons + assert AuditContext.deduplicate_reasons(reasons) == expected + + +def test_duplicates_are_removed(): + reasons = [ + Reason(RuleType("F"), RuleName("code1"), RulePriority("1"), RuleDescription("desc1"), matcher_matched=True), + Reason(RuleType("S"), RuleName("code1"), RulePriority("2"), RuleDescription("desc2"), matcher_matched=False), + Reason(RuleType("R"), RuleName("code3"), RulePriority("3"), RuleDescription("desc3"), matcher_matched=True), + ] + expected = [ + Reason(RuleType("F"), RuleName("code1"), RulePriority("1"), RuleDescription("desc1"), matcher_matched=True), + Reason(RuleType("R"), RuleName("code3"), RulePriority("3"), RuleDescription("desc3"), matcher_matched=True), + ] + assert AuditContext.deduplicate_reasons(reasons) == expected + + +def test_empty_list_returns_empty_list(): + reasons = [] + expected = [] + assert AuditContext.deduplicate_reasons(reasons) == expected + + +def test_reasons_with_no_description_are_filtered_out(): + reasons = [ + Reason(RuleType("F"), RuleName("code1"), RulePriority("1"), RuleDescription("desc1"), matcher_matched=True), + Reason(RuleType("S"), RuleName("code2"), RulePriority("2"), None, matcher_matched=False), + Reason(RuleType("R"), RuleName("code3"), RulePriority("3"), RuleDescription("desc3"), matcher_matched=True), + ] + expected = [ + Reason(RuleType("F"), RuleName("code1"), RulePriority("1"), RuleDescription("desc1"), matcher_matched=True), + Reason(RuleType("R"), RuleName("code3"), RulePriority("3"), RuleDescription("desc3"), matcher_matched=True), + ] + assert AuditContext.deduplicate_reasons(reasons) == expected diff --git a/tests/unit/common/__init__.py b/tests/unit/common/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit/common/test_request_validator.py b/tests/unit/common/test_request_validator.py new file mode 100644 index 000000000..0ec6726d5 --- /dev/null +++ b/tests/unit/common/test_request_validator.py @@ -0,0 +1,387 @@ +import json +import logging +from http import HTTPStatus +from unittest.mock import MagicMock + +import pytest + +from eligibility_signposting_api.common import request_validator +from eligibility_signposting_api.common.request_validator import logger + + +@pytest.fixture(autouse=True) +def setup_logging_for_tests(): + logger.handlers = [] + logger.setLevel(logging.INFO) + logger.addHandler(logging.NullHandler()) + + +class TestValidateNHSNumber: + @pytest.mark.parametrize( + ("path_nhs", "header_nhs", "expected_result", "expected_log_msg"), + [ + (None, None, False, "NHS number is not present"), + ("1234567890", None, False, "NHS number is not present"), + (None, "1234567890", False, "NHS number is not present"), + ("1234567890", "0987654321", False, "NHS number mismatch"), + ("1234567890", "1234567890", True, None), + ], + ) + def test_validate_nhs_number(self, path_nhs, header_nhs, expected_result, expected_log_msg, caplog): + with caplog.at_level(logging.ERROR): + result = request_validator.validate_nhs_number(path_nhs, header_nhs) + + assert result == expected_result + + if expected_log_msg: + assert any(expected_log_msg in record.message for record in caplog.records) + else: + assert not caplog.records + + +class TestValidateRequestParams: + def test_validate_request_params_success(self, caplog): + mock_handler = MagicMock() + mock_handler.__name__ = "mock_handler" + + mock_event_valid = { + "pathParameters": {"id": "1234567890"}, + "headers": {"nhs-login-nhs-number": "1234567890"}, + } + mock_context = {} + + decorator = request_validator.validate_request_params() + wrapped_handler = decorator(mock_handler) + with caplog.at_level(logging.INFO): + wrapped_handler(mock_event_valid, mock_context) + + assert any("NHS numbers from the request" in record.message for record in caplog.records) + assert not any(record.levelname == "ERROR" for record in caplog.records) + + def test_validate_request_params_nhs_mismatch(self, caplog): + mock_handler = MagicMock() + mock_context = {} + event = { + "pathParameters": {"id": "0987654321"}, + "headers": {"nhs-login-nhs-number": "1234567890"}, + } + + decorator = request_validator.validate_request_params() + wrapped_handler = decorator(mock_handler) + + with caplog.at_level(logging.ERROR): + response = wrapped_handler(event, mock_context) + + mock_handler.assert_not_called() + + assert response is not None + assert response["statusCode"] == HTTPStatus.FORBIDDEN + response_body = json.loads(response["body"]) + issue = response_body["issue"][0] + assert issue["code"] == "forbidden" + assert issue["details"]["coding"][0]["code"] == "ACCESS_DENIED" + assert issue["details"]["coding"][0]["display"] == "Access has been denied to process this request." + assert issue["diagnostics"] == "You are not authorised to request information for the supplied NHS Number" + + def test_validate_request_params_nhs_missing_in_path(self, caplog): + mock_handler = MagicMock() + mock_context = {} + event = { + "headers": {"nhs-login-nhs-number": "1234567890"}, + } + + decorator = request_validator.validate_request_params() + wrapped_handler = decorator(mock_handler) + + with caplog.at_level(logging.ERROR): + response = wrapped_handler(event, mock_context) + + mock_handler.assert_not_called() + + assert response is not None + assert response["statusCode"] == HTTPStatus.BAD_REQUEST + response_body = json.loads(response["body"]) + issue = response_body["issue"][0] + assert issue["code"] == "invalid" + assert issue["severity"] == "error" + assert issue["details"]["coding"][0]["code"] == "BAD_REQUEST" + assert issue["details"]["coding"][0]["display"] == "Bad Request" + assert issue["diagnostics"] == "Missing required NHS Number from path parameters" + assert issue["location"][0] == "parameters/id" + assert any( + (record.levelname == "ERROR" and "Missing required NHS Number from path parameters" in record.message) + for record in caplog.records + ) + + +class TestValidateQueryParameters: + @pytest.mark.parametrize( + ("conditions_input", "is_valid_expected", "expected_log_msg"), + [ + ("ALL", True, None), + ("COVID", True, None), + ("covid19", True, None), + ("FLU,MMR", True, None), + (" RSV , COVID19", True, None), + (" condition_with_spaces ", False, "Invalid condition query param: ' condition_with_spaces '"), + ("CONDITION_A,ANOTHER_ONE,123ABC", False, "Invalid condition query param: 'CONDITION_A'"), + ("condition1,", False, "Invalid condition query param: ''"), + (",condition2", False, "Invalid condition query param: ''"), + ("condition-invalid", False, "Invalid condition query param: 'condition-invalid'"), + ("condition with spaces", False, "Invalid condition query param: 'condition with spaces'"), + ("condition!", False, "Invalid condition query param: 'condition!'"), + ("condition@#$", False, "Invalid condition query param: 'condition@#$'"), + ], + ) + def test_validate_query_params_conditions(self, conditions_input, is_valid_expected, expected_log_msg, caplog): + params = {"conditions": conditions_input} + + with caplog.at_level(logging.ERROR): + is_valid, problem = request_validator.validate_query_params(params) + + assert is_valid == is_valid_expected + if is_valid_expected: + assert problem is None + assert not caplog.records + else: + assert problem is not None + assert any( + (record.levelname == "ERROR" and expected_log_msg in record.message) for record in caplog.records + ) + + def test_validate_query_params_conditions_default(self, caplog): + params = {"category": "ALL", "includeActions": "Y"} + with caplog.at_level(logging.ERROR): + is_valid, problem = request_validator.validate_query_params(params) + assert is_valid is True + assert problem is None + assert not caplog.records + + @pytest.mark.parametrize( + ("category_input", "is_valid_expected", "expected_log_msg"), + [ + ("VACCINATIONS", True, None), + ("SCREENING", True, None), + ("ALL", True, None), + ("vaccinations", True, None), + ("screening", True, None), + ("all", True, None), + (" VACCINATIONS ", True, None), + ("OTHER_CATEGORY ", False, "Invalid category query param: 'OTHER_CATEGORY '"), + ("invalid!", False, "Invalid category query param: 'invalid!'"), + ("VACCINATION", False, "Invalid category query param: 'VACCINATION'"), + ], + ) + def test_validate_query_params_category(self, category_input, is_valid_expected, expected_log_msg, caplog): + params = {"category": category_input} + with caplog.at_level(logging.ERROR): + is_valid, problem = request_validator.validate_query_params(params) + assert is_valid == is_valid_expected + + if is_valid_expected: + assert problem is None + assert not caplog.records + else: + assert problem is not None + assert any( + (record.levelname == "ERROR" and expected_log_msg in record.message) for record in caplog.records + ) + + def test_validate_query_params_category_default(self, caplog): + params = {"conditions": "ALL", "includeActions": "Y"} + with caplog.at_level(logging.ERROR): + is_valid, problem = request_validator.validate_query_params(params) + assert is_valid is True + assert problem is None + assert not caplog.records + + @pytest.mark.parametrize( + ("include_actions_input", "is_valid_expected", "expected_log_msg"), + [ + ("Y", True, None), + ("N", True, None), + ("y", True, None), + ("n", True, None), + ("n ", True, None), + ("TRUE", False, "Invalid include actions query param: 'TRUE'"), + ("YES", False, "Invalid include actions query param: 'YES'"), + ("0", False, "Invalid include actions query param: '0'"), + ("1", False, "Invalid include actions query param: '1'"), + ("", False, "Invalid include actions query param: ''"), + (" ", False, "Invalid include actions query param: ' '"), + ], + ) + def test_validate_query_params_include_actions( + self, include_actions_input, is_valid_expected, expected_log_msg, caplog + ): + params = {"includeActions": include_actions_input} + with caplog.at_level(logging.ERROR): + is_valid, problem = request_validator.validate_query_params(params) + assert is_valid == is_valid_expected + + if is_valid_expected: + assert problem is None + assert not caplog.records + else: + assert problem is not None + assert any( + (record.levelname == "ERROR" and expected_log_msg in record.message) for record in caplog.records + ) + + def test_validate_query_params_include_actions_default(self, caplog): + params = {"conditions": "ALL", "category": "ALL"} + with caplog.at_level(logging.ERROR): + is_valid, problem = request_validator.validate_query_params(params) + assert is_valid is True + assert problem is None + assert not caplog.records + + def test_validate_query_params_all_valid_params(self, caplog): + params = {"conditions": "COND1,COND2", "category": "SCREENING", "includeActions": "N"} + with caplog.at_level(logging.ERROR): + is_valid, problem = request_validator.validate_query_params(params) + assert is_valid is True + assert problem is None + assert not caplog.records + + def test_validate_query_params_mixed_valid_invalid_conditions_fail_first(self, caplog): + params = {"conditions": "VALID_COND,INVALID!,ANOTHER_VALID", "category": "SCREENING", "includeActions": "N"} + with caplog.at_level(logging.ERROR): + is_valid, problem = request_validator.validate_query_params(params) + assert is_valid is False + assert problem is not None + assert any( + (record.levelname == "ERROR" and "Invalid condition query param: " in record.message) + for record in caplog.records + ) + + def test_validate_query_params_valid_conditions_invalid_category_fail_second(self, caplog): + params = {"conditions": "CONDITION", "category": "BAD_CAT", "includeActions": "N"} + with caplog.at_level(logging.ERROR): + is_valid, problem = request_validator.validate_query_params(params) + assert is_valid is False + assert problem is not None + assert any( + (record.levelname == "ERROR" and "Invalid category query param: " in record.message) + for record in caplog.records + ) + error_logs = [r for r in caplog.records if r.levelname == "ERROR"] + assert len(error_logs) == 1 + + def test_validate_query_params_valid_conditions_category_invalid_actions_fail_third(self, caplog): + params = {"conditions": "CONDITION", "category": "VACCINATIONS", "includeActions": "Nope"} + with caplog.at_level(logging.ERROR): + is_valid, problem = request_validator.validate_query_params(params) + assert is_valid is False + assert problem is not None + assert any( + (record.levelname == "ERROR" and "Invalid include actions query param: " in record.message) + for record in caplog.records + ) + error_logs = [r for r in caplog.records if r.levelname == "ERROR"] + assert len(error_logs) == 1 + + def test_validate_query_params_returns_correct_problem_details_for_conditions_error(self): + invalid_condition = "FLU&COVID" + params = {"conditions": invalid_condition} + + is_valid, problem = request_validator.validate_query_params(params) + + assert is_valid is False + assert problem is not None + assert problem["statusCode"] == HTTPStatus.BAD_REQUEST + assert problem["headers"]["Content-Type"] == "application/fhir+json" + + response_body = json.loads(problem["body"]) + + assert response_body["resourceType"] == "OperationOutcome" + assert "id" in response_body + assert "meta" in response_body + assert "lastUpdated" in response_body["meta"] + + assert len(response_body["issue"]) == 1 + issue = response_body["issue"][0] + + assert issue["severity"] == "error" + assert issue["code"] == "value" + assert issue["diagnostics"] == ( + f"{invalid_condition} should be a single or comma separated list of condition " + f"strings with no other punctuation or special characters" + ) + assert issue["location"] == ["parameters/conditions"] + assert "details" in issue + assert "coding" in issue["details"] + assert len(issue["details"]["coding"]) == 1 + coding = issue["details"]["coding"][0] + + assert coding["system"] == "https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1" + assert coding["code"] == "INVALID_PARAMETER" + assert coding["display"] == "The given conditions were not in the expected format." + + def test_validate_query_params_returns_correct_problem_details_for_category_error(self): + invalid_category = "HEALTHCHECKS" + params = {"category": invalid_category} + + is_valid, problem = request_validator.validate_query_params(params) + + assert is_valid is False + assert problem is not None + assert problem["statusCode"] == HTTPStatus.UNPROCESSABLE_ENTITY + assert problem["headers"]["Content-Type"] == "application/fhir+json" + + response_body = json.loads(problem["body"]) + + assert response_body["resourceType"] == "OperationOutcome" + assert "id" in response_body + assert "meta" in response_body + assert "lastUpdated" in response_body["meta"] + + assert len(response_body["issue"]) == 1 + issue = response_body["issue"][0] + + assert issue["severity"] == "error" + assert issue["code"] == "value" + assert issue["diagnostics"] == f"{invalid_category} is not a category that is supported by the API" + assert issue["location"] == ["parameters/category"] + assert "details" in issue + assert "coding" in issue["details"] + assert len(issue["details"]["coding"]) == 1 + coding = issue["details"]["coding"][0] + + assert coding["system"] == "https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1" + assert coding["code"] == "INVALID_PARAMETER" + assert coding["display"] == "The supplied category was not recognised by the API." + + def test_validate_query_params_returns_correct_problem_details_for_include_actions_error(self): + invalid_include_actions = "NAH" + params = {"includeActions": invalid_include_actions} + + is_valid, problem = request_validator.validate_query_params(params) + + assert is_valid is False + assert problem is not None + assert problem["statusCode"] == HTTPStatus.UNPROCESSABLE_ENTITY + assert problem["headers"]["Content-Type"] == "application/fhir+json" + + response_body = json.loads(problem["body"]) + + assert response_body["resourceType"] == "OperationOutcome" + assert "id" in response_body + assert "meta" in response_body + assert "lastUpdated" in response_body["meta"] + + assert len(response_body["issue"]) == 1 + issue = response_body["issue"][0] + + assert issue["severity"] == "error" + assert issue["code"] == "value" + assert issue["diagnostics"] == f"{invalid_include_actions} is not a value that is supported by the API" + assert issue["location"] == ["parameters/includeActions"] + assert "details" in issue + assert "coding" in issue["details"] + assert len(issue["details"]["coding"]) == 1 + coding = issue["details"]["coding"][0] + + assert coding["system"] == "https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1" + assert coding["code"] == "INVALID_PARAMETER" + assert coding["display"] == "The supplied value was not recognised by the API." diff --git a/tests/unit/config/__init__.py b/tests/unit/config/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit/test_config.py b/tests/unit/config/test_config.py similarity index 100% rename from tests/unit/test_config.py rename to tests/unit/config/test_config.py diff --git a/tests/unit/logging/__init__.py b/tests/unit/logging/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit/logging/test_logs_helper.py b/tests/unit/logging/test_logs_helper.py new file mode 100644 index 000000000..d11885bc9 --- /dev/null +++ b/tests/unit/logging/test_logs_helper.py @@ -0,0 +1,70 @@ +import logging +from http import HTTPStatus +from unittest.mock import Mock + +import pytest +from mangum.types import LambdaContext + + +@pytest.fixture +def lambda_context(): + context = Mock(spec=LambdaContext) + context.aws_request_id = "test-request-id" + return context + + +@pytest.mark.parametrize( + ("headers", "gateway_request_id", "expected_extra"), + [ + ( + {"X-Request-ID": "req-123", "X-Correlation-ID": "corr-abc"}, + "gw-id-999", + { + "x_request_id": "req-123", + "x_correlation_id": "corr-abc", + "gateway_request_id": "gw-id-999", + }, + ), + ( + {}, # No headers + "gw-id-000", + { + "x_request_id": None, + "x_correlation_id": None, + "gateway_request_id": "gw-id-000", + }, + ), + ( + {"X-Request-ID": "req-local"}, + None, # No requestContext (non-Gateway trigger) + { + "x_request_id": "req-local", + "x_correlation_id": None, + "gateway_request_id": None, + }, + ), + ], +) +def test_log_request_ids_decorator_logs_metadata(headers, gateway_request_id, expected_extra, lambda_context, caplog): + from eligibility_signposting_api.app import log_request_ids_from_headers + + event = {"headers": headers} + if gateway_request_id is not None: + event["requestContext"] = {"requestId": gateway_request_id} + + @log_request_ids_from_headers() + def test_handler(event, context): # noqa : ARG001 + logger = logging.getLogger("test_logger") + logger.info("Inside test handler") + return HTTPStatus.OK + + with caplog.at_level(logging.INFO): + test_handler(event, lambda_context) + + for record in caplog.records: + if record.message == "request trace metadata": + for key, val in expected_extra.items(): + assert getattr(record, key) == val + break + else: + pytest.fail("'request trace metadata' log not found") diff --git a/tests/unit/logging/test_logs_manager.py b/tests/unit/logging/test_logs_manager.py new file mode 100644 index 000000000..f78c13451 --- /dev/null +++ b/tests/unit/logging/test_logs_manager.py @@ -0,0 +1,120 @@ +import io +import json +import logging +import threading +from http import HTTPStatus +from unittest.mock import MagicMock, Mock + +import pytest +from mangum.types import LambdaContext + +from eligibility_signposting_api.logging.logs_manager import ( + LOG_FORMAT, + EnrichedJsonFormatter, + add_lambda_request_id_to_logger, + request_id_context_var, +) + + +def test_decorator_sets_request_id_in_context(): + test_request_id = "test-id-12345" + mock_context = MagicMock() + mock_context.aws_request_id = test_request_id + + @add_lambda_request_id_to_logger() + def decorated_handler(event, context): # noqa : ARG001 + return request_id_context_var.get() + + result = decorated_handler({}, mock_context) + + assert result == test_request_id + + +def test_decorator_preserves_function_return_value(): + expected_result = {"statusCode": 200, "body": "Success"} + mock_context = MagicMock() + mock_context.aws_request_id = "any-id" + + @add_lambda_request_id_to_logger() + def decorated_handler(event, context): # noqa : ARG001 + return expected_result + + result = decorated_handler({}, mock_context) + + assert result == expected_result + + +def test_request_id_context_is_properly_isolated(): + results = {} + + @add_lambda_request_id_to_logger() + def decorated_handler(event, context): # noqa : ARG001 + rid = request_id_context_var.get() + results[threading.current_thread().name] = rid + return rid + + def thread_func(name, rid): # noqa : ARG001 + mock_context = MagicMock(aws_request_id=rid) + decorated_handler({}, mock_context) + + threads = [ + threading.Thread(target=thread_func, name="Thread-A", args=("Thread-A", "id-A")), + threading.Thread(target=thread_func, name="Thread-B", args=("Thread-B", "id-B")), + threading.Thread(target=thread_func, name="Thread-C", args=("Thread-C", "id-C")), + ] + + for t in threads: + t.start() + for t in threads: + t.join() + + assert results["Thread-A"] == "id-A" + assert request_id_context_var.get() is None + + assert results["Thread-B"] == "id-B" + assert request_id_context_var.get() is None + + assert results["Thread-C"] == "id-C" + assert request_id_context_var.get() is None + + +@pytest.fixture +def lambda_context(): + context = Mock(spec=LambdaContext) + context.aws_request_id = "test-request-id" + return context + + +def test_enriched_json_formatter_adds_all_fields(lambda_context): + @add_lambda_request_id_to_logger() + def test_handler(event, context): # noqa : ARG001 + logger = logging.getLogger("test_logger") + logger.info("Test log inside handler") + return HTTPStatus.OK + + log_stream = io.StringIO() + handler = logging.StreamHandler(log_stream) + handler.setFormatter(EnrichedJsonFormatter(LOG_FORMAT)) + + test_logger = logging.getLogger("test_logger") + test_logger.handlers = [] + test_logger.addHandler(handler) + test_logger.setLevel(logging.INFO) + + result = test_handler({}, lambda_context) + log_output = log_stream.getvalue() + + test_logger.removeHandler(handler) + + assert result == HTTPStatus.OK + logged_json = json.loads(log_output) + + assert logged_json["request_id"] == lambda_context.aws_request_id + assert "asctime" in logged_json + assert logged_json["levelname"] == "INFO" + assert logged_json["name"] == "test_logger" + assert logged_json["module"] == "test_logs_manager" + assert logged_json["funcName"] == "test_handler" + assert "lineno" in logged_json + assert logged_json["message"] == "Test log inside handler" + assert request_id_context_var.get() is None diff --git a/tests/unit/model/test_rules.py b/tests/unit/model/test_campaign_config.py similarity index 81% rename from tests/unit/model/test_rules.py rename to tests/unit/model/test_campaign_config.py index 6419455d1..dbeebc73f 100644 --- a/tests/unit/model/test_rules.py +++ b/tests/unit/model/test_campaign_config.py @@ -5,7 +5,7 @@ from faker import Faker from hamcrest import assert_that -from eligibility_signposting_api.model.rules import IterationRule +from eligibility_signposting_api.model.campaign_config import IterationRule from tests.fixtures.builders.model.rule import IterationFactory, RawCampaignConfigFactory from tests.fixtures.matchers.rules import is_iteration_rule @@ -52,20 +52,6 @@ def test_iteration_with_overlapping_start_dates_not_allowed(faker: Faker): RawCampaignConfigFactory.build(start_date=start_date, iterations=[iteration1, iteration2]) -def test_iteration_must_have_active_iteration_from_its_start(faker: Faker): - # Given - start_date = faker.date_object() - iteration = IterationFactory.build(iteration_date=start_date + relativedelta(days=1)) - - # When, Then - with pytest.raises( - ValueError, - match=r"1 validation error for CampaignConfig\n" - r".*1st iteration starts later", - ): - RawCampaignConfigFactory.build(start_date=start_date, iterations=[iteration]) - - @pytest.mark.parametrize( ("rule_stop", "expected"), [ @@ -92,3 +78,23 @@ def test_iteration_rule_deserialisation(rule_stop: str, expected): # Then assert_that(actual, is_iteration_rule().with_rule_stop(expected)) + + +@pytest.mark.parametrize( + ("field_name", "value"), + [ + ("manager", "manager@test.com"), + ("approver", "approver@test.com"), + ("reviewer", "reviewer@test.com"), + ], +) +def test_campaign_should_accept_list_of_strings_for_different_role_emails(field_name: str, value: str): + # Given + kwargs = {field_name: value} + + # When, Then + with pytest.raises( + ValueError, + match=rf"1 validation error for CampaignConfig\n{field_name}\n\s+Input should be a valid list.*", + ): + RawCampaignConfigFactory.build(**kwargs) diff --git a/tests/unit/model/test_status.py b/tests/unit/model/test_status.py new file mode 100644 index 000000000..cc3c018d9 --- /dev/null +++ b/tests/unit/model/test_status.py @@ -0,0 +1,47 @@ +from eligibility_signposting_api.model.eligibility_status import ConditionName, RuleType, Status, StatusText + + +class TestStatus: + def test_ordering(self): + assert Status.not_eligible < Status.not_actionable + assert Status.not_actionable < Status.actionable + assert Status.actionable > Status.not_actionable + assert Status.not_actionable > Status.not_eligible + assert Status.not_eligible == Status.not_eligible + + def test_is_exclusion(self): + assert Status.not_eligible.is_exclusion + assert Status.not_actionable.is_exclusion + assert not Status.actionable.is_exclusion + + def test_worst_status(self): + assert Status.worst(Status.not_eligible, Status.actionable) == Status.not_eligible + assert Status.worst(Status.actionable, Status.not_actionable) == Status.not_actionable + assert Status.worst(Status.not_eligible, Status.not_actionable, Status.actionable) == Status.not_eligible + assert Status.worst(Status.actionable) == Status.actionable + + def test_best_status(self): + assert Status.best(Status.not_eligible, Status.actionable) == Status.actionable + assert Status.best(Status.actionable, Status.not_actionable) == Status.actionable + assert Status.best(Status.not_eligible, Status.not_actionable, Status.actionable) == Status.actionable + assert Status.best(Status.not_eligible) == Status.not_eligible + + def test_get_status_text(self): + assert Status.not_eligible.get_status_text(ConditionName("COVID")) == StatusText( + "We do not believe you can have it" + ) + + assert Status.not_actionable.get_status_text(ConditionName("FLU")) == StatusText( + "You should have the FLU vaccine" + ) + + assert Status.actionable.get_status_text(ConditionName("COVID")) == StatusText( + "You should have the COVID vaccine" + ) + + def test_get_action_rule_type(self): + assert Status.not_eligible.get_action_rule_type() == RuleType(RuleType.not_eligible_actions) + + assert Status.not_actionable.get_action_rule_type() == RuleType(RuleType.not_actionable_actions) + + assert Status.actionable.get_action_rule_type() == RuleType(RuleType.redirect) diff --git a/tests/unit/services/calculators/test_eligibility_calculator.py b/tests/unit/services/calculators/test_eligibility_calculator.py index c93edb77c..37a13e8ff 100644 --- a/tests/unit/services/calculators/test_eligibility_calculator.py +++ b/tests/unit/services/calculators/test_eligibility_calculator.py @@ -1,110 +1,58 @@ import datetime +import logging from typing import Any import pytest from faker import Faker +from flask import Flask from freezegun import freeze_time -from hamcrest import assert_that, contains_exactly, contains_inanyorder, equal_to, has_item, has_items, is_, is_in -from pydantic import HttpUrl, ValidationError - -from eligibility_signposting_api.model import rules -from eligibility_signposting_api.model import rules as rules_model -from eligibility_signposting_api.model.eligibility import ( +from hamcrest import assert_that, contains_exactly, contains_inanyorder, has_item, has_items, is_, is_in + +from eligibility_signposting_api.model import campaign_config as rules_model +from eligibility_signposting_api.model import eligibility_status +from eligibility_signposting_api.model.campaign_config import ( + CohortLabel, + Description, + RuleAttributeLevel, + RuleAttributeName, + RuleAttributeTarget, + RuleComparator, + RuleName, + RuleOperator, + RuleType, +) +from eligibility_signposting_api.model.eligibility_status import ( ActionCode, ActionDescription, ActionType, + CohortGroupResult, + Condition, ConditionName, DateOfBirth, InternalActionCode, + IterationResult, NHSNumber, Postcode, + Reason, RuleDescription, + RulePriority, Status, SuggestedAction, - UrlLabel, - UrlLink, ) -from eligibility_signposting_api.model.rules import ActionsMapper, AvailableAction from eligibility_signposting_api.services.calculators.eligibility_calculator import EligibilityCalculator from tests.fixtures.builders.model import rule as rule_builder +from tests.fixtures.builders.model.eligibility import ReasonFactory from tests.fixtures.builders.repos.person import person_rows_builder from tests.fixtures.matchers.eligibility import ( is_cohort_result, is_condition, is_eligibility_status, - is_reason, ) -from tests.fixtures.matchers.rules import is_iteration_rule - - -class TestEligibilityCalculator: - @staticmethod - def test_get_redirect_rules(): - # Given - - iteration = rule_builder.IterationFactory.build( - iteration_cohorts=[rule_builder.IterationCohortFactory.build(cohort_label="cohort2")], - default_comms_routing="defaultcomms", - actions_mapper=rule_builder.ActionsMapperFactory.build( - root={ - "ActionCode1": AvailableAction( - ActionType="ActionType1", - ExternalRoutingCode="ActionCode1", - ActionDescription="ActionDescription1", - UrlLink=HttpUrl("https://www.ActionUrl1.com"), - UrlLabel="ActionLabel1", - ), - "defaultcomms": AvailableAction( - ActionType="ActionType2", - ExternalRoutingCode="defaultcomms", - ActionDescription="ActionDescription2", - UrlLink=HttpUrl("https://www.ActionUrl2.com"), - UrlLabel="ActionLabel2", - ), - } - ), - iteration_rules=[rule_builder.ICBRedirectRuleFactory.build()], - ) - - # when - actual_rules, actual_action_mapper, actual_default_comms = EligibilityCalculator.get_redirect_rules(iteration) - - # then - assert_that(actual_rules, has_item(is_iteration_rule().with_name(iteration.iteration_rules[0].name))) - assert actual_action_mapper == iteration.actions_mapper - assert actual_default_comms == iteration.default_comms_routing - - -def test_not_base_eligible(faker: Faker): - # Given - nhs_number = NHSNumber(faker.nhs_number()) - - person_rows = person_rows_builder(nhs_number, cohorts=["cohort1"]) - campaign_configs = [ - ( - rule_builder.CampaignConfigFactory.build( - target="RSV", - iterations=[ - rule_builder.IterationFactory.build( - iteration_cohorts=[rule_builder.IterationCohortFactory.build(cohort_label="cohort2")] - ) - ], - ) - ) - ] - - calculator = EligibilityCalculator(person_rows, campaign_configs) - # When - actual = calculator.evaluate_eligibility("Y", ["ALL"], "ALL") - # Then - assert_that( - actual, - is_eligibility_status().with_conditions( - has_item(is_condition().with_condition_name(ConditionName("RSV")).and_status(Status.not_eligible)) - ), - ) +@pytest.fixture +def app(): + return Flask(__name__) @pytest.mark.parametrize( @@ -141,7 +89,7 @@ def test_base_eligible_with_when_magic_cohort_is_present( calculator = EligibilityCalculator(person_rows, campaign_configs) # When - actual = calculator.evaluate_eligibility("Y", ["ALL"], "ALL") + actual = calculator.get_eligibility_status("Y", ["ALL"], "ALL") # Then assert_that( @@ -153,54 +101,6 @@ def test_base_eligible_with_when_magic_cohort_is_present( ) -@freeze_time("2025-04-25") -def test_only_live_campaigns_considered(faker: Faker): - # Given - nhs_number = NHSNumber(faker.nhs_number()) - - person_rows = person_rows_builder(nhs_number, cohorts=["cohort1"]) - campaign_configs = [ - rule_builder.CampaignConfigFactory.build( - name="Live", - target="RSV", - iterations=[ - rule_builder.IterationFactory.build( - iteration_cohorts=[rule_builder.IterationCohortFactory.build(cohort_label="cohort2")], - ) - ], - start_date=datetime.date(2025, 4, 20), - end_date=datetime.date(2025, 4, 30), - ), - rule_builder.CampaignConfigFactory.build( - name="No longer live", - target="RSV", - iterations=[ - rule_builder.IterationFactory.build( - iteration_cohorts=[ - rule_builder.IterationCohortFactory.build(cohort_label="cohort1"), - rule_builder.IterationCohortFactory.build(cohort_label="cohort2"), - ], - ) - ], - start_date=datetime.date(2025, 4, 1), - end_date=datetime.date(2025, 4, 24), - ), - ] - - calculator = EligibilityCalculator(person_rows, campaign_configs) - - # When - actual = calculator.evaluate_eligibility("Y", ["ALL"], "ALL") - - # Then - assert_that( - actual, - is_eligibility_status().with_conditions( - has_item(is_condition().with_condition_name(ConditionName("RSV")).and_status(Status.not_eligible)) - ), - ) - - @pytest.mark.parametrize( "iteration_type", ["A", "M", "S", "O"], @@ -209,46 +109,13 @@ def test_campaigns_with_applicable_iteration_types_in_campaign_level_considered( # Given nhs_number = NHSNumber(faker.nhs_number()) - person_rows = person_rows_builder(nhs_number) + person_rows = person_rows_builder(nhs_number, cohorts=[]) campaign_configs = [rule_builder.CampaignConfigFactory.build(target="RSV", iteration_type=iteration_type)] calculator = EligibilityCalculator(person_rows, campaign_configs) # When - actual = calculator.evaluate_eligibility("Y", ["ALL"], "ALL") - - # Then - assert_that( - actual, - is_eligibility_status().with_conditions( - has_item( - is_condition() - .with_condition_name(ConditionName("RSV")) - .and_status(is_in([Status.actionable, Status.not_actionable, Status.not_eligible])) - ), - ), - ) - - -@pytest.mark.parametrize( - "iteration_type", - ["A", "M", "S", "O"], -) -def test_campaigns_with_applicable_iteration_types_in_iteration_level_considered(iteration_type: str, faker: Faker): - # Given - nhs_number = NHSNumber(faker.nhs_number()) - - person_rows = person_rows_builder(nhs_number) - campaign_configs = [ - rule_builder.CampaignConfigFactory.build( - target="RSV", iterations=[rule_builder.IterationFactory.build(type=iteration_type)] - ) - ] - - calculator = EligibilityCalculator(person_rows, campaign_configs) - - # When - actual = calculator.evaluate_eligibility("Y", ["ALL"], "ALL") + actual = calculator.get_eligibility_status("Y", ["ALL"], "ALL") # Then assert_that( @@ -263,90 +130,6 @@ def test_campaigns_with_applicable_iteration_types_in_iteration_level_considered ) -@pytest.mark.parametrize( - "iteration_type", - ["NA", "N", "FAKE", "F"], -) -def test_invalid_iteration_types_in_campaign_level_raises_validation_error(iteration_type: str): - with pytest.raises(ValidationError): - rule_builder.CampaignConfigFactory.build(target="RSV", iteration_type=iteration_type) - - -@pytest.mark.parametrize( - "iteration_type", - ["NA", "N", "FAKE", "F"], -) -def test_invalid_iteration_types_in_iteration_level_raises_validation_error(iteration_type: str): - with pytest.raises(ValidationError): - rule_builder.CampaignConfigFactory.build( - target="RSV", iterations=[rule_builder.IterationFactory.build(type=iteration_type)] - ) - - -def test_base_eligible_and_simple_rule_includes(faker: Faker): - # Given - nhs_number = NHSNumber(faker.nhs_number()) - date_of_birth = DateOfBirth(faker.date_of_birth(minimum_age=76, maximum_age=79)) - - person_rows = person_rows_builder(nhs_number, date_of_birth=date_of_birth, cohorts=["cohort1"]) - campaign_configs = [ - rule_builder.CampaignConfigFactory.build( - target="RSV", - iterations=[ - rule_builder.IterationFactory.build( - iteration_rules=[rule_builder.PersonAgeSuppressionRuleFactory.build()], - iteration_cohorts=[rule_builder.IterationCohortFactory.build(cohort_label="cohort1")], - ) - ], - ) - ] - - calculator = EligibilityCalculator(person_rows, campaign_configs) - - # When - actual = calculator.evaluate_eligibility("Y", ["ALL"], "ALL") - - # Then - assert_that( - actual, - is_eligibility_status().with_conditions( - has_item(is_condition().with_condition_name(ConditionName("RSV")).and_status(Status.actionable)) - ), - ) - - -def test_base_eligible_but_simple_rule_excludes(faker: Faker): - # Given - nhs_number = NHSNumber(faker.nhs_number()) - date_of_birth = DateOfBirth(faker.date_of_birth(minimum_age=18, maximum_age=74)) - - person_rows = person_rows_builder(nhs_number, date_of_birth=date_of_birth, cohorts=["cohort1"]) - campaign_configs = [ - rule_builder.CampaignConfigFactory.build( - target="RSV", - iterations=[ - rule_builder.IterationFactory.build( - iteration_rules=[rule_builder.PersonAgeSuppressionRuleFactory.build()], - iteration_cohorts=[rule_builder.IterationCohortFactory.build(cohort_label="cohort1")], - ) - ], - ) - ] - - calculator = EligibilityCalculator(person_rows, campaign_configs) - - # When - actual = calculator.evaluate_eligibility("Y", ["ALL"], "ALL") - - # Then - assert_that( - actual, - is_eligibility_status().with_conditions( - has_item(is_condition().with_condition_name(ConditionName("RSV")).and_status(Status.not_actionable)) - ), - ) - - @freeze_time("2025-04-25") def test_simple_rule_only_excludes_from_live_iteration(faker: Faker): # Given @@ -383,7 +166,7 @@ def test_simple_rule_only_excludes_from_live_iteration(faker: Faker): calculator = EligibilityCalculator(person_rows, campaign_configs) # When - actual = calculator.evaluate_eligibility("Y", ["ALL"], "ALL") + actual = calculator.get_eligibility_status("Y", ["ALL"], "ALL") # Then assert_that( @@ -394,86 +177,6 @@ def test_simple_rule_only_excludes_from_live_iteration(faker: Faker): ) -@pytest.mark.parametrize( - ("rule_type", "expected_status"), - [(rules_model.RuleType.suppression, Status.not_actionable), (rules_model.RuleType.filter, Status.not_eligible)], -) -def test_rule_types_cause_correct_statuses(rule_type: rules_model.RuleType, expected_status: Status, faker: Faker): - # Given - nhs_number = NHSNumber(faker.nhs_number()) - date_of_birth = DateOfBirth(faker.date_of_birth(minimum_age=18, maximum_age=74)) - - person_rows = person_rows_builder(nhs_number, date_of_birth=date_of_birth, cohorts=["cohort1"]) - campaign_configs = [ - rule_builder.CampaignConfigFactory.build( - target="RSV", - iterations=[ - rule_builder.IterationFactory.build( - iteration_rules=[rule_builder.PersonAgeSuppressionRuleFactory.build(type=rule_type)], - iteration_cohorts=[rule_builder.IterationCohortFactory.build(cohort_label="cohort1")], - ) - ], - ) - ] - - calculator = EligibilityCalculator(person_rows, campaign_configs) - - # When - actual = calculator.evaluate_eligibility("Y", ["ALL"], "ALL") - - # Then - assert_that( - actual, - is_eligibility_status().with_conditions( - has_item( - is_condition().with_condition_name(ConditionName("RSV")).and_status(expected_status).and_actions([]) - ) - ), - ) - - -def test_multiple_rule_types_cause_correct_status(faker: Faker): - # Given - nhs_number = NHSNumber(faker.nhs_number()) - date_of_birth = DateOfBirth(faker.date_of_birth(minimum_age=18, maximum_age=74)) - - person_rows = person_rows_builder(nhs_number, date_of_birth=date_of_birth, cohorts=["cohort1"]) - campaign_configs = [ - rule_builder.CampaignConfigFactory.build( - target="RSV", - iterations=[ - rule_builder.IterationFactory.build( - iteration_rules=[ - rule_builder.PersonAgeSuppressionRuleFactory.build( - priority=rules_model.RulePriority(5), type=rules_model.RuleType.suppression - ), - rule_builder.PersonAgeSuppressionRuleFactory.build( - priority=rules_model.RulePriority(10), type=rules_model.RuleType.filter - ), - rule_builder.PersonAgeSuppressionRuleFactory.build( - priority=rules_model.RulePriority(15), type=rules_model.RuleType.suppression - ), - ], - iteration_cohorts=[rule_builder.IterationCohortFactory.build(cohort_label="cohort1")], - ) - ], - ) - ] - - calculator = EligibilityCalculator(person_rows, campaign_configs) - - # When - actual = calculator.evaluate_eligibility("Y", ["ALL"], "ALL") - - # Then - assert_that( - actual, - is_eligibility_status().with_conditions( - has_item(is_condition().with_condition_name(ConditionName("RSV")).and_status(Status.not_eligible)) - ), - ) - - @pytest.mark.parametrize( ("test_comment", "rule1", "rule2", "expected_status"), [ @@ -552,7 +255,7 @@ def test_rules_with_same_priority_must_all_match_to_exclude( calculator = EligibilityCalculator(person_rows, campaign_configs) # When - actual = calculator.evaluate_eligibility("Y", ["ALL"], "ALL") + actual = calculator.get_eligibility_status("Y", ["ALL"], "ALL") # Then assert_that( @@ -564,251 +267,216 @@ def test_rules_with_same_priority_must_all_match_to_exclude( ) -def test_multiple_conditions_where_both_are_actionable(faker: Faker): +@pytest.mark.parametrize( + ("vaccine", "last_successful_date", "expected_status", "test_comment"), + [ + ("RSV", "20240601", Status.not_actionable, "last_successful_date is a past date"), + ("RSV", "20250101", Status.not_actionable, "last_successful_date is today"), + # Below is a non-ideal situation (might be due to a data entry error), so considered as actionable. + ("RSV", "20260101", Status.actionable, "last_successful_date is a future date"), + ("RSV", "20230601", Status.actionable, "last_successful_date is a long past"), + ("RSV", "", Status.actionable, "last_successful_date is empty"), + ("RSV", None, Status.actionable, "last_successful_date is none"), + ("COVID", "20240601", Status.actionable, "No RSV row"), + ], +) +@freeze_time("2025-01-01") +def test_status_on_target_based_on_last_successful_date( + vaccine: str, last_successful_date: str, expected_status: Status, test_comment: str, faker: Faker +): # Given nhs_number = NHSNumber(faker.nhs_number()) - date_of_birth = DateOfBirth(faker.date_of_birth(minimum_age=76, maximum_age=78)) - person_rows = person_rows_builder(nhs_number, date_of_birth=date_of_birth, cohorts=["cohort1"], icb="QE1") + target_rows = person_rows_builder( + nhs_number, + cohorts=["cohort1"], + vaccines=[ + ( + vaccine, + datetime.datetime.strptime(last_successful_date, "%Y%m%d").replace(tzinfo=datetime.UTC) + if last_successful_date + else None, + ) + ], + ) + campaign_configs = [ rule_builder.CampaignConfigFactory.build( target="RSV", iterations=[ rule_builder.IterationFactory.build( - iteration_cohorts=[rule_builder.IterationCohortFactory.build(cohort_label="cohort1")], - iteration_rules=[rule_builder.PersonAgeSuppressionRuleFactory.build()], - default_comms_routing="defaultcomms", - actions_mapper=rule_builder.ActionsMapperFactory.build( - root={"rule_1_comms_routing": book_nbs_comms, "defaultcomms": default_comms_detail} - ), - ) - ], - ), - rule_builder.CampaignConfigFactory.build( - target="COVID", - iterations=[ - rule_builder.IterationFactory.build( - iteration_cohorts=[rule_builder.IterationCohortFactory.build(cohort_label="cohort1")], iteration_rules=[ - rule_builder.PersonAgeSuppressionRuleFactory.build(), - rule_builder.ICBRedirectRuleFactory.build(), + rule_builder.IterationRuleFactory.build( + type=RuleType.suppression, + name=RuleName("You have already been vaccinated against RSV in the last year"), + description=RuleDescription("Exclude anyone Completed RSV Vaccination in the last year"), + priority=10, + operator=RuleOperator.day_gte, + attribute_level=RuleAttributeLevel.TARGET, + attribute_name=RuleAttributeName("LAST_SUCCESSFUL_DATE"), + comparator=RuleComparator("-365"), + attribute_target=RuleAttributeTarget("RSV"), + ), + rule_builder.IterationRuleFactory.build( + type=RuleType.suppression, + name=RuleName("You have a vaccination date in the future for RSV"), + description=RuleDescription("Exclude anyone with future Completed RSV Vaccination"), + priority=10, + operator=RuleOperator.day_lte, + attribute_level=RuleAttributeLevel.TARGET, + attribute_name=RuleAttributeName("LAST_SUCCESSFUL_DATE"), + comparator=RuleComparator("0"), + attribute_target=RuleAttributeTarget("RSV"), + ), ], - default_comms_routing="defaultcomms", - actions_mapper=rule_builder.ActionsMapperFactory.build( - root={"ActionCode1": book_nbs_comms, "defaultcomms": default_comms_detail} - ), + iteration_cohorts=[rule_builder.IterationCohortFactory.build(cohort_label="cohort1")], ) ], - ), + ) ] - calculator = EligibilityCalculator(person_rows, campaign_configs) + calculator = EligibilityCalculator(target_rows, campaign_configs) # When - actual = calculator.evaluate_eligibility("Y", ["ALL"], "ALL") + actual = calculator.get_eligibility_status("Y", ["ALL"], "ALL") # Then assert_that( actual, is_eligibility_status().with_conditions( - has_items( - is_condition() - .with_condition_name(ConditionName("RSV")) - .and_status(Status.actionable) - .and_actions( - [ - SuggestedAction( - internal_action_code=InternalActionCode("defaultcomms"), - action_type=ActionType("CareCardWithText"), - action_code=ActionCode("BookLocal"), - action_description=ActionDescription("You can get an RSV vaccination at your GP surgery"), - url_link=None, - url_label=None, - ) - ] - ), - is_condition() - .with_condition_name(ConditionName("COVID")) - .and_status(Status.actionable) - .and_actions( - [ - SuggestedAction( - internal_action_code=InternalActionCode("ActionCode1"), - action_type=ActionType("ButtonAuthLink"), - action_code=ActionCode("BookNBS"), - action_description=ActionDescription("Action description"), - url_link=UrlLink(HttpUrl("https://www.nhs.uk/book-rsv")), - url_label=UrlLabel("Continue to booking"), - ) - ] - ), - ) + has_item(is_condition().with_condition_name(ConditionName("RSV")).and_status(expected_status)) ), + test_comment, ) -def test_multiple_conditions_where_all_give_unique_statuses(faker: Faker): +@pytest.mark.parametrize( + ("person_cohorts", "expected_status", "test_comment"), + [ + (["cohort1", "cohort2"], Status.actionable, "cohort1 is not actionable, cohort 2 is actionable"), + (["cohort3", "cohort2"], Status.actionable, "cohort3 is not eligible, cohort 2 is actionable"), + (["cohort1"], Status.not_actionable, "cohort1 is not actionable"), + ], +) +def test_status_if_iteration_rules_contains_cohort_label_field( + person_cohorts, expected_status: Status, test_comment: str, faker: Faker +): # Given nhs_number = NHSNumber(faker.nhs_number()) - date_of_birth = DateOfBirth(faker.date_of_birth(minimum_age=76, maximum_age=78)) + date_of_birth = DateOfBirth(faker.date_of_birth(minimum_age=66, maximum_age=74)) - person_rows = person_rows_builder(nhs_number, date_of_birth=date_of_birth, cohorts=["cohort1"]) + person_rows = person_rows_builder(nhs_number, date_of_birth=date_of_birth, cohorts=person_cohorts) campaign_configs = [ rule_builder.CampaignConfigFactory.build( target="RSV", iterations=[ rule_builder.IterationFactory.build( - iteration_cohorts=[rule_builder.IterationCohortFactory.build(cohort_label="cohort1")], - iteration_rules=[rule_builder.PersonAgeSuppressionRuleFactory.build()], - ) - ], - ), - rule_builder.CampaignConfigFactory.build( - target="COVID", - iterations=[ - rule_builder.IterationFactory.build( - iteration_cohorts=[rule_builder.IterationCohortFactory.build(cohort_label="cohort1")], - iteration_rules=[rule_builder.PersonAgeSuppressionRuleFactory.build(comparator="-85")], - ) - ], - ), - rule_builder.CampaignConfigFactory.build( - target="FLU", - iterations=[ - rule_builder.IterationFactory.build( - iteration_cohorts=[rule_builder.IterationCohortFactory.build(cohort_label="cohort2")], - iteration_rules=[rule_builder.PersonAgeSuppressionRuleFactory.build(comparator="-85")], + iteration_cohorts=[ + rule_builder.IterationCohortFactory.build(cohort_label="cohort1"), + rule_builder.IterationCohortFactory.build(cohort_label="cohort2"), + ], + iteration_rules=[rule_builder.PersonAgeSuppressionRuleFactory.build(cohort_label="cohort1")], ) ], - ), + ) ] calculator = EligibilityCalculator(person_rows, campaign_configs) # When - actual = calculator.evaluate_eligibility("N", ["ALL"], "ALL") + actual = calculator.get_eligibility_status("Y", ["ALL"], "ALL") # Then assert_that( actual, is_eligibility_status().with_conditions( - has_items( - is_condition() - .with_condition_name(ConditionName("RSV")) - .and_status(Status.actionable) - .and_actions(None), - is_condition() - .with_condition_name(ConditionName("COVID")) - .and_status(Status.not_actionable) - .and_actions(None), - is_condition() - .with_condition_name(ConditionName("FLU")) - .and_status(Status.not_eligible) - .and_actions(None), - ) + has_items(is_condition().with_condition_name(ConditionName("RSV")).and_status(expected_status)) ), + test_comment, ) @pytest.mark.parametrize( - ("test_comment", "campaign1", "campaign2"), + ("person_rows", "expected_status", "expected_cohort_group_and_description", "test_comment"), [ ( - "1st campaign allows, 2nd excludes", - rule_builder.CampaignConfigFactory.build( - target="RSV", - iterations=[ - rule_builder.IterationFactory.build( - iteration_cohorts=[rule_builder.IterationCohortFactory.build(cohort_label="cohort1")], - iteration_rules=[rule_builder.PersonAgeSuppressionRuleFactory.build()], - ) - ], - ), - rule_builder.CampaignConfigFactory.build( - target="RSV", - iterations=[ - rule_builder.IterationFactory.build( - iteration_cohorts=[rule_builder.IterationCohortFactory.build(cohort_label="cohort1")], - iteration_rules=[rule_builder.PersonAgeSuppressionRuleFactory.build(comparator="-85")], - ) - ], - ), + person_rows_builder(nhs_number="123", cohorts=[], postcode="AC01", de=True, icb="QE1"), + Status.not_eligible, + [ + ("magic cohort group", "magic negative description"), + ("rsv_age_range", "rsv_age_range negative description"), + ], + "rsv_75_rolling is not base-eligible & magic cohort group not eligible by F rules ", ), ( - "1st campaign excludes, 2nd allows", - rule_builder.CampaignConfigFactory.build( - target="RSV", - iterations=[ - rule_builder.IterationFactory.build( - iteration_cohorts=[rule_builder.IterationCohortFactory.build(cohort_label="cohort1")], - iteration_rules=[rule_builder.PersonAgeSuppressionRuleFactory.build(comparator="-85")], - ) - ], - ), - rule_builder.CampaignConfigFactory.build( - target="RSV", - iterations=[ - rule_builder.IterationFactory.build( - iteration_cohorts=[rule_builder.IterationCohortFactory.build(cohort_label="cohort1")], - iteration_rules=[rule_builder.PersonAgeSuppressionRuleFactory.build()], - ) - ], - ), + person_rows_builder(nhs_number="123", cohorts=["rsv_75_rolling"], postcode="AC01", de=True, icb="QE1"), + Status.not_eligible, + [ + ("magic cohort group", "magic negative description"), + ("rsv_age_range", "rsv_age_range negative description"), + ], + "all the cohorts are not-eligible by F rules", ), - ], -) -def test_multiple_campaigns_for_single_condition( - test_comment: str, campaign1: rules_model.CampaignConfig, campaign2: rules_model.CampaignConfig, faker: Faker -): - # Given - nhs_number = NHSNumber(faker.nhs_number()) - date_of_birth = DateOfBirth(faker.date_of_birth(minimum_age=76, maximum_age=78)) - - person_rows = person_rows_builder(nhs_number, date_of_birth=date_of_birth, cohorts=["cohort1"]) - campaign_configs = [campaign1, campaign2] - - calculator = EligibilityCalculator(person_rows, campaign_configs) - - # When - actual = calculator.evaluate_eligibility("Y", ["ALL"], "ALL") - - # Then - assert_that( - actual, - is_eligibility_status().with_conditions( - contains_exactly(is_condition().with_condition_name(ConditionName("RSV")).and_status(Status.actionable)) + ( + person_rows_builder(nhs_number="123", cohorts=["rsv_75_rolling"], postcode="SW19", de=False, icb="QE1"), + Status.not_actionable, + [ + ("magic cohort group", "magic positive description"), + ("rsv_age_range", "rsv_age_range positive description"), + ], + "all the cohorts are not-actionable", + ), + ( + person_rows_builder(nhs_number="123", cohorts=["rsv_75_rolling"], postcode="AC01", de=False, icb="QE1"), + Status.actionable, + [ + ("magic cohort group", "magic positive description"), + ("rsv_age_range", "rsv_age_range positive description"), + ], + "all the cohorts are actionable", + ), + ( + person_rows_builder(nhs_number="123", cohorts=["rsv_75_rolling"], postcode="AC01", de=False, icb="NOT_QE1"), + Status.actionable, + [("magic cohort group", "magic positive description")], + "magic_cohort is actionable, but not others", + ), + ( + person_rows_builder(nhs_number="123", cohorts=["rsv_75_rolling"], postcode="SW19", de=False, icb="NOT_QE1"), + Status.not_actionable, + [("magic cohort group", "magic positive description")], + "magic_cohort is not-actionable, but others are not eligible", ), - test_comment, - ) - - -@pytest.mark.parametrize( - ("icb", "rule_type", "expected_status"), - [ - ("QE1", rules_model.RuleType.suppression, Status.actionable), - ("QWU", rules_model.RuleType.suppression, Status.not_actionable), - ("", rules_model.RuleType.suppression, Status.not_actionable), - (None, rules_model.RuleType.suppression, Status.not_actionable), - ("QE1", rules_model.RuleType.filter, Status.actionable), - ("QWU", rules_model.RuleType.filter, Status.not_eligible), - ("", rules_model.RuleType.filter, Status.not_eligible), - (None, rules_model.RuleType.filter, Status.not_eligible), ], ) -def test_base_eligible_and_icb_example( - icb: str | None, rule_type: rules_model.RuleType, expected_status: Status, faker: Faker +def test_cohort_groups_and_their_descriptions_when_magic_cohort_is_present( + person_rows: list[dict[str, Any]], + expected_status: str, + expected_cohort_group_and_description: list[tuple[str, str]], + test_comment: str, ): # Given - nhs_number = NHSNumber(faker.nhs_number()) - - person_rows = person_rows_builder(nhs_number, cohorts=["cohort1"], icb=icb) campaign_configs = [ rule_builder.CampaignConfigFactory.build( target="RSV", iterations=[ rule_builder.IterationFactory.build( - iteration_rules=[rule_builder.ICBFilterRuleFactory.build(type=rule_type)], - iteration_cohorts=[rule_builder.IterationCohortFactory.build(cohort_label="cohort1")], + iteration_cohorts=[ + rule_builder.Rsv75RollingCohortFactory.build(), + rule_builder.MagicCohortFactory.build(), + ], + iteration_rules=[ + # F common rule + rule_builder.DetainedEstateSuppressionRuleFactory.build(type=RuleType.filter), + # F rules for rsv_75_rolling + rule_builder.ICBFilterRuleFactory.build( + type=RuleType.filter, cohort_label=CohortLabel("rsv_75_rolling") + ), + # S common rule + rule_builder.PostcodeSuppressionRuleFactory.build( + comparator=RuleComparator("SW19"), + ), + ], ) ], ) @@ -817,1590 +485,549 @@ def test_base_eligible_and_icb_example( calculator = EligibilityCalculator(person_rows, campaign_configs) # When - actual = calculator.evaluate_eligibility("Y", ["ALL"], "ALL") + actual = calculator.get_eligibility_status("Y", ["ALL"], "ALL") # Then assert_that( actual, is_eligibility_status().with_conditions( - has_item(is_condition().with_condition_name(ConditionName("RSV")).and_status(expected_status)) + has_items( + is_condition() + .with_condition_name(ConditionName("RSV")) + .and_cohort_results( + contains_exactly( + *[ + is_cohort_result() + .with_cohort_code(item[0]) + .with_description(item[1]) + .with_status(expected_status) + for item in expected_cohort_group_and_description + ] + ) + ) + ) ), + test_comment, ) @pytest.mark.parametrize( - ("vaccine", "last_successful_date", "expected_status", "test_comment"), + ("person_rows", "expected_description", "test_comment"), [ - ("RSV", "20240601", Status.not_actionable, "last_successful_date is a past date"), - ("RSV", "20250101", Status.not_actionable, "last_successful_date is today"), - # Below is a non-ideal situation (might be due to a data entry error), so considered as actionable. - ("RSV", "20260101", Status.actionable, "last_successful_date is a future date"), - ("RSV", "20230601", Status.actionable, "last_successful_date is a long past"), - ("RSV", "", Status.actionable, "last_successful_date is empty"), - ("RSV", None, Status.actionable, "last_successful_date is none"), - ("COVID", "20240601", Status.actionable, "No RSV row"), + ( + person_rows_builder(nhs_number="123", cohorts=[]), + "rsv_age_range negative description 1", + "status - not eligible", + ), + ( + person_rows_builder(nhs_number="123", cohorts=["rsv_75_rolling", "rsv_75to79_2024"], postcode="SW19"), + "rsv_age_range positive description 1", + "status - not actionable", + ), + ( + person_rows_builder(nhs_number="123", cohorts=["rsv_75_rolling", "rsv_75to79_2024"], postcode="hp"), + "rsv_age_range positive description 1", + "status - actionable", + ), + ( + person_rows_builder(nhs_number="123", cohorts=["rsv_75to79_2024"], postcode="hp"), + "rsv_age_range positive description 2", + "rsv_75to79_2024 - actionable and rsv_75_rolling is not eligible", + ), ], ) -@freeze_time("2025-01-01") -def test_status_on_target_based_on_last_successful_date( - vaccine: str, last_successful_date: str, expected_status: Status, test_comment: str, faker: Faker +def test_cohort_group_descriptions_are_selected_based_on_priority_when_cohorts_have_different_non_empty_descriptions( + person_rows: list[dict[str, Any]], expected_description: str, test_comment: str ): # Given - nhs_number = NHSNumber(faker.nhs_number()) - - target_rows = person_rows_builder( - nhs_number, - cohorts=["cohort1"], - vaccines=[ - ( - vaccine, - datetime.datetime.strptime(last_successful_date, "%Y%m%d").replace(tzinfo=datetime.UTC) - if last_successful_date - else None, - ) - ], - ) - campaign_configs = [ rule_builder.CampaignConfigFactory.build( target="RSV", iterations=[ rule_builder.IterationFactory.build( - iteration_rules=[ - rule_builder.IterationRuleFactory.build( - type=rules.RuleType.suppression, - name=rules.RuleName("You have already been vaccinated against RSV in the last year"), - description=rules.RuleDescription( - "Exclude anyone Completed RSV Vaccination in the last year" - ), - priority=10, - operator=rules.RuleOperator.day_gte, - attribute_level=rules.RuleAttributeLevel.TARGET, - attribute_name=rules.RuleAttributeName("LAST_SUCCESSFUL_DATE"), - comparator=rules.RuleComparator("-365"), - attribute_target=rules.RuleAttributeTarget("RSV"), + iteration_cohorts=[ + rule_builder.Rsv75to79CohortFactory.build( + positive_description=Description("rsv_age_range positive description 2"), + negative_description=Description("rsv_age_range negative description 2"), + priority=2, ), - rule_builder.IterationRuleFactory.build( - type=rules.RuleType.suppression, - name=rules.RuleName("You have a vaccination date in the future for RSV"), - description=rules.RuleDescription("Exclude anyone with future Completed RSV Vaccination"), - priority=10, - operator=rules.RuleOperator.day_lte, - attribute_level=rules.RuleAttributeLevel.TARGET, - attribute_name=rules.RuleAttributeName("LAST_SUCCESSFUL_DATE"), - comparator=rules.RuleComparator("0"), - attribute_target=rules.RuleAttributeTarget("RSV"), + rule_builder.Rsv75RollingCohortFactory.build( + positive_description=Description("rsv_age_range positive description 1"), + negative_description=Description("rsv_age_range negative description 1"), + priority=1, ), ], - iteration_cohorts=[rule_builder.IterationCohortFactory.build(cohort_label="cohort1")], + iteration_rules=[rule_builder.PostcodeSuppressionRuleFactory.build()], ) ], ) ] - calculator = EligibilityCalculator(target_rows, campaign_configs) + calculator = EligibilityCalculator(person_rows, campaign_configs) # When - actual = calculator.evaluate_eligibility("Y", ["ALL"], "ALL") + actual = calculator.get_eligibility_status("Y", ["ALL"], "ALL") # Then assert_that( actual, is_eligibility_status().with_conditions( - has_item(is_condition().with_condition_name(ConditionName("RSV")).and_status(expected_status)) - ), - test_comment, - ) - - -@pytest.mark.parametrize( - ("attribute_name", "expected_status", "test_comment"), - [ - ( - rules.RuleAttributeName("COHORT_LABEL"), - Status.not_eligible, - "cohort label provided", - ), - ( - None, - Status.not_eligible, - "cohort label is the default attribute name for the cohort attribute level", - ), - ( - rules.RuleAttributeName("LOCATION"), - Status.actionable, - "attribute name that is not cohort label", + has_items( + is_condition() + .with_condition_name(ConditionName("RSV")) + .and_cohort_results( + contains_exactly( + is_cohort_result().with_cohort_code("rsv_age_range").with_description(expected_description) + ) + ) + ) ), - ], -) -def test_status_on_cohort_attribute_level( - attribute_name: rules.RuleAttributeName, expected_status: Status, test_comment: str, faker: Faker -): - # Given - nhs_number = NHSNumber(faker.nhs_number()) - - person_row: list[dict[str, Any]] = person_rows_builder( - nhs_number, cohorts=["cohort1", "covid_eligibility_complaint_list"] + test_comment, ) - person_row_with_extra_items_in_cohort_row = [ - {**r, "LOCATION": "HP1"} for r in person_row if r.get("ATTRIBUTE_TYPE", "") == "COHORTS" - ] + +@freeze_time("2025-04-25") +def test_no_active_iteration_returns_empty_conditions_with_single_active_campaign(faker: Faker): + # Given + person_rows = person_rows_builder(NHSNumber(faker.nhs_number())) campaign_configs = [ rule_builder.CampaignConfigFactory.build( target="RSV", iterations=[ rule_builder.IterationFactory.build( + name="inactive iteration", + iteration_rules=[], iteration_cohorts=[rule_builder.IterationCohortFactory.build(cohort_label="cohort1")], - iteration_rules=[ - rule_builder.IterationRuleFactory.build( - type=rules.RuleType.filter, - name=rules.RuleName("Exclude those in a complaint cohort"), - description=rules.RuleDescription( - "Ensure anyone who has registered a complaint is not shown as eligible" - ), - priority=15, - operator=rules.RuleOperator.member_of, - attribute_level=rules.RuleAttributeLevel.COHORT, - attribute_name=attribute_name, - comparator=rules.RuleComparator("covid_eligibility_complaint_list"), - ) - ], ) ], ) ] + # Need to set the iteration date to override CampaignConfigFactory.fix_iteration_date_invariants behavior + campaign_configs[0].iterations[0].iteration_date = datetime.date(2025, 5, 10) - calculator = EligibilityCalculator(person_row_with_extra_items_in_cohort_row, campaign_configs) + calculator = EligibilityCalculator(person_rows, campaign_configs) # When - actual = calculator.evaluate_eligibility("Y", ["ALL"], "ALL") + actual = calculator.get_eligibility_status("Y", ["ALL"], "ALL") # Then - assert_that( - actual, - is_eligibility_status().with_conditions( - has_item(is_condition().with_condition_name(ConditionName("RSV")).and_status(expected_status)) - ), - test_comment, - ) + assert_that(actual, is_eligibility_status().with_conditions([])) -@pytest.mark.parametrize( - ("person_cohorts", "expected_status", "test_comment"), - [ - (["cohort1", "cohort2"], Status.actionable, "cohort1 is not actionable, cohort 2 is actionable"), - (["cohort3", "cohort2"], Status.actionable, "cohort3 is not eligible, cohort 2 is actionable"), - (["cohort1"], Status.not_actionable, "cohort1 is not actionable"), - ], -) -def test_status_if_iteration_rules_contains_cohort_label_field( - person_cohorts, expected_status: Status, test_comment: str, faker: Faker -): +@pytest.mark.usefixtures("caplog") +@freeze_time("2025-04-25") +def test_returns_no_condition_data_for_campaign_without_active_iteration(faker: Faker, caplog): # Given - nhs_number = NHSNumber(faker.nhs_number()) - date_of_birth = DateOfBirth(faker.date_of_birth(minimum_age=66, maximum_age=74)) - - person_rows = person_rows_builder(nhs_number, date_of_birth=date_of_birth, cohorts=person_cohorts) + person_rows = person_rows_builder(NHSNumber(faker.nhs_number())) campaign_configs = [ rule_builder.CampaignConfigFactory.build( target="RSV", iterations=[ rule_builder.IterationFactory.build( - iteration_cohorts=[ - rule_builder.IterationCohortFactory.build(cohort_label="cohort1"), - rule_builder.IterationCohortFactory.build(cohort_label="cohort2"), - ], - iteration_rules=[rule_builder.PersonAgeSuppressionRuleFactory.build(cohort_label="cohort1")], + name="inactive iteration", + iteration_rules=[], + iteration_cohorts=[rule_builder.IterationCohortFactory.build(cohort_label="cohort1")], ) ], - ) + ), + rule_builder.CampaignConfigFactory.build( + target="COVID", + iterations=[ + rule_builder.IterationFactory.build( + name="active iteration", + iteration_rules=[], + iteration_cohorts=[rule_builder.IterationCohortFactory.build(cohort_label="cohort1")], + ) + ], + ), ] + # Need to set the iteration date to override CampaignConfigFactory.fix_iteration_date_invariants behavior + rsv_campaign = campaign_configs[0] + rsv_campaign.iterations[0].iteration_date = datetime.date(2025, 5, 10) calculator = EligibilityCalculator(person_rows, campaign_configs) # When - actual = calculator.evaluate_eligibility("Y", ["ALL"], "ALL") + with caplog.at_level(logging.INFO): + actual = calculator.get_eligibility_status("Y", ["ALL"], "ALL") # Then - assert_that( - actual, - is_eligibility_status().with_conditions( - has_items(is_condition().with_condition_name(ConditionName("RSV")).and_status(expected_status)) - ), - test_comment, - ) + condition_names = [condition.condition_name for condition in actual.conditions] + assert ConditionName("RSV") not in condition_names + assert ConditionName("COVID") in condition_names + assert f"Skipping campaign ID {rsv_campaign.id} as no active iteration was found." in caplog.text -@pytest.mark.parametrize( - ("rule_stop", "expected_reason_results", "test_comment"), # Changed expected_reasons to expected_reason_results - [ - ( - rules.RuleStop(True), # noqa: FBT003 - [ - RuleDescription("reason 1"), - RuleDescription("reason 2"), - ], - "rule_stop is True, last rule should not run", - ), - ( - rules.RuleStop(False), # noqa: FBT003 - [ - RuleDescription("reason 1"), - RuleDescription("reason 2"), - RuleDescription("reason 3"), - ], - "rule_stop is False, last rule should run", - ), - ], -) -def test_rules_stop_behavior( - rule_stop: rules.RuleStop, expected_reason_results: list[RuleDescription], test_comment: str, faker: Faker -) -> None: - # Given - nhs_number = NHSNumber(faker.nhs_number()) - date_of_birth = DateOfBirth(faker.date_of_birth(minimum_age=66, maximum_age=74)) - person_rows = person_rows_builder(nhs_number, date_of_birth=date_of_birth, cohorts=["cohort1"]) - # Build campaign configuration - campaign_config = rule_builder.CampaignConfigFactory.build( - target="RSV", - iterations=[ - rule_builder.IterationFactory.build( - iteration_rules=[ - rule_builder.PersonAgeSuppressionRuleFactory.build( - priority=10, description="reason 1", rule_stop=rule_stop - ), - rule_builder.PersonAgeSuppressionRuleFactory.build(priority=10, description="reason 2"), - rule_builder.PersonAgeSuppressionRuleFactory.build(priority=15, description="reason 3"), - ], - iteration_cohorts=[ - rule_builder.IterationCohortFactory.build(cohort_group="cohort_group1", cohort_label="cohort1") - ], - ) - ], - ) +@freeze_time("2025-04-25") +def test_no_active_campaign(faker: Faker): + # Given + person_rows = person_rows_builder(NHSNumber(faker.nhs_number())) + campaign_configs = [rule_builder.CampaignConfigFactory.build()] + # Need to set the campaign dates to override CampaignConfigFactory.fix_iteration_date_invariants behavior + campaign_configs[0].start_date = datetime.date(2025, 5, 10) - calculator = EligibilityCalculator(person_rows, [campaign_config]) + calculator = EligibilityCalculator(person_rows, campaign_configs) # When - actual = calculator.evaluate_eligibility("Y", ["ALL"], "ALL") + actual = calculator.get_eligibility_status("Y", ["ALL"], "ALL") # Then - assert_that( - actual, - is_eligibility_status().with_conditions( - has_items( - is_condition() - .with_condition_name(ConditionName("RSV")) - .and_status(equal_to(Status.not_actionable)) - .and_cohort_results( - has_items( - is_cohort_result().with_reasons( - contains_inanyorder( - *[ - is_reason().with_rule_description(equal_to(result)) - for result in expected_reason_results - ] - ) - ) - ) - ) + assert_that(actual, is_eligibility_status().with_conditions([])) + + +class TestEligibilityResultBuilder: + def test_build_condition_results_single_condition_single_cohort_actionable(self): + cohort_group_results = [CohortGroupResult("COHORT_A", Status.actionable, [], "Cohort A Description", [])] + suggested_actions = [ + SuggestedAction( + internal_action_code=InternalActionCode("default_action_code"), + action_type=ActionType("CareCardWithText"), + action_code=ActionCode("BookLocal"), + action_description=ActionDescription("You can get an RSV vaccination at your GP surgery"), + url_link=None, + url_label=None, ) - ), - test_comment, - ) + ] + iteration_result = IterationResult(Status.actionable, cohort_group_results, suggested_actions) + + result = EligibilityCalculator.build_condition(iteration_result, ConditionName("RSV")) + + assert_that(result.condition_name, is_(ConditionName("RSV"))) + assert_that(result.status, is_(Status.actionable)) + assert_that(result.actions, is_(suggested_actions)) + assert_that(result.status_text, is_(Status.actionable.get_status_text(ConditionName("RSV")))) + + assert_that(len(result.cohort_results), is_(1)) + deduplicated_cohort = result.cohort_results[0] + assert_that(deduplicated_cohort.cohort_code, is_("COHORT_A")) + assert_that(deduplicated_cohort.status, is_(Status.actionable)) + assert_that(deduplicated_cohort.reasons, is_([])) + assert_that(deduplicated_cohort.description, is_("Cohort A Description")) + assert_that(deduplicated_cohort.audit_rules, is_([])) + assert_that(result.suitability_rules, is_([])) + + def test_build_condition_results_single_condition_single_cohort_not_eligible_with_reasons(self): + cohort_group_results = [CohortGroupResult("COHORT_A", Status.not_eligible, [], "Cohort A Description", [])] + suggested_actions = [ + SuggestedAction( + internal_action_code=InternalActionCode("default_action_code"), + action_type=ActionType("CareCardWithText"), + action_code=ActionCode("BookLocal"), + action_description=ActionDescription("You can get an RSV vaccination at your GP surgery"), + url_link=None, + url_label=None, + ) + ] + iteration_result = IterationResult(Status.not_eligible, cohort_group_results, suggested_actions) + + result = EligibilityCalculator.build_condition(iteration_result, ConditionName("RSV")) + + assert_that(result.condition_name, is_(ConditionName("RSV"))) + assert_that(result.status, is_(Status.not_eligible)) + assert_that(result.actions, is_(suggested_actions)) + assert_that(result.status_text, is_(Status.not_eligible.get_status_text(ConditionName("RSV")))) + + assert_that(len(result.cohort_results), is_(1)) + deduplicated_cohort = result.cohort_results[0] + assert_that(deduplicated_cohort.cohort_code, is_("COHORT_A")) + assert_that(deduplicated_cohort.status, is_(Status.not_eligible)) + assert_that(deduplicated_cohort.reasons, is_([])) + assert_that(deduplicated_cohort.description, is_("Cohort A Description")) + assert_that(deduplicated_cohort.audit_rules, is_([])) + assert_that(result.suitability_rules, is_([])) + + def test_build_condition_results_single_condition_multiple_cohorts_same_cohort_code_same_status(self): + reason_1 = Reason( + RuleType.suppression, + eligibility_status.RuleName("Filter Rule 1"), + RulePriority("1"), + RuleDescription("Filter Rule Description 2"), + matcher_matched=True, + ) + reason_2 = Reason( + RuleType.suppression, + eligibility_status.RuleName("Filter Rule 2"), + RulePriority("2"), + RuleDescription("Filter Rule Description 2"), + matcher_matched=True, + ) + cohort_group_results = [ + CohortGroupResult("COHORT_A", Status.not_eligible, [reason_1], "", []), + # The below description will be picked up as the first one is empty + CohortGroupResult("COHORT_A", Status.not_eligible, [reason_2], "Cohort A Description 2", []), + CohortGroupResult("COHORT_A", Status.not_eligible, [], "Cohort A Description 3", []), + ] + suggested_actions = [ + SuggestedAction( + internal_action_code=InternalActionCode("default_action_code"), + action_type=ActionType("CareCardWithText"), + action_code=ActionCode("BookLocal"), + action_description=ActionDescription("You can get an RSV vaccination at your GP surgery"), + url_link=None, + url_label=None, + ) + ] + iteration_result = IterationResult(Status.not_eligible, cohort_group_results, suggested_actions) + + result: Condition = EligibilityCalculator.build_condition(iteration_result, ConditionName("RSV")) + + assert_that(len(result.cohort_results), is_(1)) + + deduplicated_cohort = result.cohort_results[0] + assert_that(deduplicated_cohort.cohort_code, is_("COHORT_A")) + assert_that(deduplicated_cohort.status, is_(Status.not_eligible)) + assert_that(deduplicated_cohort.reasons, contains_inanyorder(reason_1, reason_2)) + assert_that(deduplicated_cohort.description, is_("Cohort A Description 2")) + assert_that(deduplicated_cohort.audit_rules, is_([])) + assert_that(result.suitability_rules, contains_inanyorder(reason_1, reason_2)) + + def test_build_condition_results_multiple_cohorts_different_cohort_code_same_status(self): + reason_1 = Reason( + RuleType.suppression, + eligibility_status.RuleName("Filter Rule 1"), + RulePriority("1"), + RuleDescription("Filter Rule Description 2"), + matcher_matched=True, + ) + reason_2 = Reason( + RuleType.suppression, + eligibility_status.RuleName("Filter Rule 2"), + RulePriority("2"), + RuleDescription("Filter Rule Description 2"), + matcher_matched=True, + ) + cohort_group_results = [ + CohortGroupResult("COHORT_X", Status.not_eligible, [reason_1], "Cohort X Description", []), + CohortGroupResult("COHORT_Y", Status.not_eligible, [reason_2], "Cohort Y Description", []), + ] + suggested_actions = [ + SuggestedAction( + internal_action_code=InternalActionCode("default_action_code"), + action_type=ActionType("CareCardWithText"), + action_code=ActionCode("BookLocal"), + action_description=ActionDescription("You can get an RSV vaccination at your GP surgery"), + url_link=None, + url_label=None, + ) + ] + iteration_result = IterationResult(Status.not_eligible, cohort_group_results, suggested_actions) + + result = EligibilityCalculator.build_condition(iteration_result, ConditionName("RSV")) + + assert_that(len(result.cohort_results), is_(2)) + + expected_deduplicated_cohorts = [ + CohortGroupResult("COHORT_X", Status.not_eligible, [reason_1], "Cohort X Description", []), + CohortGroupResult("COHORT_Y", Status.not_eligible, [reason_2], "Cohort Y Description", []), + ] + assert_that(result.cohort_results, contains_inanyorder(*expected_deduplicated_cohorts)) + + def test_build_condition_results_cohorts_status_not_matching_iteration_status(self): + reason_1 = Reason( + RuleType.suppression, + eligibility_status.RuleName("Filter Rule 1"), + RulePriority("1"), + RuleDescription("Matching"), + matcher_matched=True, + ) + reason_2 = Reason( + RuleType.suppression, + eligibility_status.RuleName("Filter Rule 2"), + RulePriority("2"), + RuleDescription("Not matching"), + matcher_matched=True, + ) + cohort_group_results = [ + CohortGroupResult("COHORT_X", Status.not_eligible, [reason_1], "Cohort X Description", []), + CohortGroupResult("COHORT_Y", Status.not_actionable, [reason_2], "Cohort Y Description", []), + ] + + iteration_result = IterationResult(Status.not_eligible, cohort_group_results, []) + + result = EligibilityCalculator.build_condition(iteration_result, ConditionName("RSV")) + + assert_that(len(result.cohort_results), is_(1)) + assert_that(result.cohort_results[0].cohort_code, is_("COHORT_X")) + assert_that(result.cohort_results[0].status, is_(Status.not_eligible)) @pytest.mark.parametrize( - ("person_cohorts", "iteration_cohorts", "expected_status", "expected_cohorts"), + ("reason_1", "reason_2", "reason_3", "expected_reasons"), [ + # Same rule name, type, and priority, different description ( - ["covid_cohort", "flu_cohort"], - ["rsv_clinical_cohort", "rsv_75_rolling"], - Status.not_eligible, - ["rsv_clinical_cohort_group", "rsv_75_rolling_group"], - ), - ( - ["rsv_clinical_cohort", "rsv_75_rolling"], - ["rsv_clinical_cohort", "rsv_75_rolling"], - Status.actionable, - ["rsv_clinical_cohort_group"], + ReasonFactory.build(rule_description="description1", matcher_matched=True), + ReasonFactory.build(rule_description="description2", matcher_matched=True), + ReasonFactory.build(rule_description="description3", matcher_matched=True), + [ReasonFactory.build(rule_description="description1", matcher_matched=True)], ), + # Different rule name, same type, same priority ( - ["covid_cohort", "rsv_75_rolling"], - ["rsv_clinical_cohort", "rsv_75_rolling"], - Status.not_actionable, - ["rsv_75_rolling_group"], + ReasonFactory.build(rule_name="Supress Rule 1", rule_description="description1", matcher_matched=True), + ReasonFactory.build(rule_name="Supress Rule 2", rule_description="description2", matcher_matched=True), + ReasonFactory.build(rule_name="Supress Rule 1", rule_description="description3", matcher_matched=True), + [ReasonFactory.build(rule_name="Supress Rule 1", rule_description="description1", matcher_matched=True)], ), + # Same rule name, same type, different priority ( - ["covid_cohort", "rsv_clinical_cohort"], - ["rsv_clinical_cohort", "rsv_75_rolling"], - Status.actionable, - ["rsv_clinical_cohort_group"], + ReasonFactory.build(rule_priority="1", rule_description="description1", matcher_matched=True), + ReasonFactory.build(rule_priority="2", rule_description="description2", matcher_matched=True), + ReasonFactory.build(rule_priority="1", rule_description="description3", matcher_matched=True), + [ + ReasonFactory.build(rule_priority="1", rule_description="description1", matcher_matched=True), + ReasonFactory.build(rule_priority="2", rule_description="description2", matcher_matched=True), + ], ), + # Same rule name, same priority, different type ( - ["rsv_75to79_2024", "rsv_75_rolling"], - ["rsv_75to79_2024", "rsv_75_rolling"], - Status.not_actionable, - ["rsv_75_rolling_group", "rsv_75to79_2024_group"], + ReasonFactory.build(rule_type=RuleType.suppression, rule_description="description1", matcher_matched=True), + ReasonFactory.build(rule_type=RuleType.filter, rule_description="description2", matcher_matched=True), + ReasonFactory.build(rule_type=RuleType.suppression, rule_description="description3", matcher_matched=True), + [ + ReasonFactory.build( + rule_type=RuleType.suppression, rule_description="description1", matcher_matched=True + ), + ReasonFactory.build(rule_type=RuleType.filter, rule_description="description2", matcher_matched=True), + ], ), ], ) -def test_eligibility_results_when_multiple_cohorts( - person_cohorts: list[str], - iteration_cohorts: list[str], - expected_status: Status, - expected_cohorts: list[str], - faker: Faker, -): - # Given - nhs_number = NHSNumber(faker.nhs_number()) - dob_person_less_than_75 = DateOfBirth(faker.date_of_birth(minimum_age=66, maximum_age=74)) - - person_rows = person_rows_builder(nhs_number, date_of_birth=dob_person_less_than_75, cohorts=person_cohorts) - campaign_configs = [ - rule_builder.CampaignConfigFactory.build( - target="RSV", - iterations=[ - rule_builder.IterationFactory.build( - iteration_cohorts=[ - rule_builder.IterationCohortFactory.build( - cohort_group=f"{cohorts}_group", - cohort_label=cohorts, - positive_description="positive description", - negative_description="negative description", - ) - for cohorts in iteration_cohorts - ], - iteration_rules=[ - rule_builder.PersonAgeSuppressionRuleFactory.build(cohort_label="rsv_75_rolling"), - rule_builder.PersonAgeSuppressionRuleFactory.build(cohort_label="rsv_75to79_2024"), - ], - ) - ], - ) +def test_build_condition_results_grouping_reasons(reason_1, reason_2, reason_3, expected_reasons): + cohort_group_results = [ + CohortGroupResult( + "COHORT_X", + Status.not_actionable, + [reason_1, reason_3], + "Cohort X Description", + [], + ), + CohortGroupResult( + "COHORT_Y", + Status.not_actionable, + [reason_2, reason_3], + "Cohort Y Description", + [], + ), ] - calculator = EligibilityCalculator(person_rows, campaign_configs) + iteration_result = IterationResult(Status.not_actionable, cohort_group_results, []) - # When - actual = calculator.evaluate_eligibility("Y", ["ALL"], "ALL") + result: Condition = EligibilityCalculator.build_condition(iteration_result, ConditionName("RSV")) - # Then - assert_that( - actual, - is_eligibility_status().with_conditions( - has_items( - is_condition() - .with_condition_name(ConditionName("RSV")) - .and_status(equal_to(expected_status)) - .and_cohort_results( - contains_inanyorder( - *[ - is_cohort_result().with_cohort_code(equal_to(cohort_label)) - for cohort_label in expected_cohorts - ] - ) - ) - ) - ), - ) + assert_that(result.suitability_rules, contains_inanyorder(*expected_reasons)) @pytest.mark.parametrize( - ("person_rows", "expected_status", "expected_cohort_group_and_description", "test_comment"), + ("reason_2", "expected_reasons"), [ + # Same rule name, type, and priority, different description ( - person_rows_builder(nhs_number="123", cohorts=[], postcode="AC01", de=True, icb="QE1"), - Status.not_eligible, + ReasonFactory.build( + rule_type=RuleType.suppression, + rule_description="Matching", + rule_name="Supress Rule 1", + rule_priority="1", + matcher_matched=True, + ), [ - ("magic cohort group", "magic negative description"), - ("rsv_age_range", "rsv_age_range negative description"), + ReasonFactory.build( + rule_type=RuleType.suppression, + rule_description="Not matching", + rule_name="Supress Rule 1", + rule_priority="1", + matcher_matched=True, + ) ], - "rsv_75_rolling is not base-eligible & magic cohort group not eligible by F rules ", ), + # Different rule name ( - person_rows_builder(nhs_number="123", cohorts=["rsv_75_rolling"], postcode="AC01", de=True, icb="QE1"), - Status.not_eligible, + ReasonFactory.build( + rule_type=RuleType.suppression, + rule_description="Matching", + rule_name="Supress Rule 2", + rule_priority="1", + matcher_matched=True, + ), [ - ("magic cohort group", "magic negative description"), - ("rsv_age_range", "rsv_age_range negative description"), + ReasonFactory.build( + rule_type=RuleType.suppression, + rule_description="Not matching", + rule_name="Supress Rule 1", + rule_priority="1", + matcher_matched=True, + ) ], - "all the cohorts are not-eligible by F rules", ), + # Different priority ( - person_rows_builder(nhs_number="123", cohorts=["rsv_75_rolling"], postcode="SW19", de=False, icb="QE1"), - Status.not_actionable, + ReasonFactory.build( + rule_type=RuleType.suppression, + rule_description="Matching", + rule_name="Supress Rule 1", + rule_priority="2", + matcher_matched=True, + ), [ - ("magic cohort group", "magic positive description"), - ("rsv_age_range", "rsv_age_range positive description"), + ReasonFactory.build( + rule_type=RuleType.suppression, + rule_description="Not matching", + rule_name="Supress Rule 1", + rule_priority="1", + matcher_matched=True, + ), + ReasonFactory.build( + rule_type=RuleType.suppression, + rule_description="Matching", + rule_name="Supress Rule 1", + rule_priority="2", + matcher_matched=True, + ), ], - "all the cohorts are not-actionable", ), + # Different type ( - person_rows_builder(nhs_number="123", cohorts=["rsv_75_rolling"], postcode="AC01", de=False, icb="QE1"), - Status.actionable, + ReasonFactory.build( + rule_type=RuleType.filter, + rule_description="Matching", + rule_name="Supress Rule 1", + rule_priority="2", + matcher_matched=True, + ), [ - ("magic cohort group", "magic positive description"), - ("rsv_age_range", "rsv_age_range positive description"), + ReasonFactory.build( + rule_type=RuleType.suppression, + rule_description="Not matching", + rule_name="Supress Rule 1", + rule_priority="1", + matcher_matched=True, + ), + ReasonFactory.build( + rule_type=RuleType.filter, + rule_description="Matching", + rule_name="Supress Rule 1", + rule_priority="2", + matcher_matched=True, + ), ], - "all the cohorts are actionable", - ), - ( - person_rows_builder(nhs_number="123", cohorts=["rsv_75_rolling"], postcode="AC01", de=False, icb="NOT_QE1"), - Status.actionable, - [("magic cohort group", "magic positive description")], - "magic_cohort is actionable, but not others", - ), - ( - person_rows_builder(nhs_number="123", cohorts=["rsv_75_rolling"], postcode="SW19", de=False, icb="NOT_QE1"), - Status.not_actionable, - [("magic cohort group", "magic positive description")], - "magic_cohort is not-actionable, but others are not eligible", ), ], ) -def test_cohort_groups_and_their_descriptions_when_magic_cohort_is_present( - person_rows: list[dict[str, Any]], - expected_status: str, - expected_cohort_group_and_description: list[tuple[str, str]], - test_comment: str, -): - # Given - campaign_configs = [ - rule_builder.CampaignConfigFactory.build( - target="RSV", - iterations=[ - rule_builder.IterationFactory.build( - iteration_cohorts=[ - rule_builder.Rsv75RollingCohortFactory.build(), - rule_builder.MagicCohortFactory.build(), - ], - iteration_rules=[ - # F common rule - rule_builder.DetainedEstateSuppressionRuleFactory.build(type=rules.RuleType.filter), - # F rules for rsv_75_rolling - rule_builder.ICBFilterRuleFactory.build( - type=rules.RuleType.filter, cohort_label=rules.CohortLabel("rsv_75_rolling") - ), - # S common rule - rule_builder.PostcodeSuppressionRuleFactory.build( - comparator=rules.RuleComparator("SW19"), - ), - ], - ) - ], - ) - ] - - calculator = EligibilityCalculator(person_rows, campaign_configs) - - # When - actual = calculator.evaluate_eligibility("Y", ["ALL"], "ALL") +def test_build_condition_results_single_cohort(reason_2, expected_reasons): + reason_1 = ReasonFactory.build( + rule_type=RuleType.suppression, + rule_description="Not matching", + rule_name="Supress Rule 1", + rule_priority="1", + matcher_matched=True, + ) - # Then - assert_that( - actual, - is_eligibility_status().with_conditions( - has_items( - is_condition() - .with_condition_name(ConditionName("RSV")) - .and_cohort_results( - contains_exactly( - *[ - is_cohort_result() - .with_cohort_code(item[0]) - .with_description(item[1]) - .with_status(expected_status) - for item in expected_cohort_group_and_description - ] - ) - ) - ) - ), - test_comment, - ) - - -def test_cohort_groups_and_their_descriptions_when_best_status_is_not_eligible( - faker: Faker, -): - # Given - nhs_number = NHSNumber(faker.nhs_number()) - date_of_birth = DateOfBirth(faker.date_of_birth(minimum_age=66, maximum_age=74)) - - person_rows = person_rows_builder(nhs_number, date_of_birth=date_of_birth, cohorts=[]) - campaign_configs = [ - rule_builder.CampaignConfigFactory.build( - target="RSV", - iterations=[ - rule_builder.IterationFactory.build( - iteration_cohorts=[ - rule_builder.Rsv75RollingCohortFactory.build(), - rule_builder.Rsv75to79CohortFactory.build(), - rule_builder.RsvPretendClinicalCohortFactory.build(), - ], - iteration_rules=[rule_builder.PostcodeSuppressionRuleFactory.build()], - ) - ], - ) - ] - - calculator = EligibilityCalculator(person_rows, campaign_configs) - - # When - actual = calculator.evaluate_eligibility("Y", ["ALL"], "ALL") - - # Then - assert_that( - actual, - is_eligibility_status().with_conditions( - has_items( - is_condition() - .with_condition_name(ConditionName("RSV")) - .and_status(Status.not_eligible) - .and_cohort_results( - contains_exactly( - is_cohort_result() - .with_cohort_code("rsv_age_range") - .with_description("rsv_age_range negative description"), - is_cohort_result() - .with_cohort_code("rsv_clinical_cohort") - .with_description("rsv_clinical_cohort negative description"), - ) - ) - ) - ), - ) - - -@pytest.mark.parametrize( - ("person_cohorts", "expected_cohort_group_and_description_and_s_rule_names", "test_comment"), - [ - ( - ["rsv_75_rolling"], - [("rsv_age_range", "rsv_age_range positive description", ["Excluded postcode In SW19"])], - "rsv_75_rolling is not-actionable, others are not-eligible", - ), - ( - ["rsv_75_rolling", "rsv_75to79_2024"], - [ - ( - "rsv_age_range", - "rsv_age_range positive description", - ["Excluded postcode In SW19", "Excluded postcode In SW19"], - ) - ], - "rsv_75_rolling, rsv_75to79_2024 is not-actionable, rsv_pretend_clinical_cohort are not-eligible", - ), - ( - ["rsv_75_rolling", "rsv_75to79_2024", "rsv_pretend_clinical_cohort"], - [ - ( - "rsv_age_range", - "rsv_age_range positive description", - ["Excluded postcode In SW19", "Excluded postcode In SW19"], - ), - ("rsv_clinical_cohort", "rsv_clinical_cohort positive description", ["Excluded postcode In SW19"]), - ], - "all are not-actionable", - ), - ], -) -def test_cohort_groups_and_their_descriptions_and_the_collection_of_s_rules_when_best_status_is_not_actionable( - person_cohorts: list[str], - expected_cohort_group_and_description_and_s_rule_names: list[tuple[str, str, list[str]]], - test_comment: str, - faker: Faker, -): - # Given - nhs_number = NHSNumber(faker.nhs_number()) - date_of_birth = DateOfBirth(faker.date_of_birth(minimum_age=66, maximum_age=74)) - - person_rows = person_rows_builder(nhs_number, date_of_birth=date_of_birth, cohorts=person_cohorts, postcode="SW19") - campaign_configs = [ - rule_builder.CampaignConfigFactory.build( - target="RSV", - iterations=[ - rule_builder.IterationFactory.build( - iteration_cohorts=[ - rule_builder.Rsv75RollingCohortFactory.build(), - rule_builder.Rsv75to79CohortFactory.build(), - rule_builder.RsvPretendClinicalCohortFactory.build(), - ], - iteration_rules=[rule_builder.PostcodeSuppressionRuleFactory.build()], - ) - ], - ) - ] - - calculator = EligibilityCalculator(person_rows, campaign_configs) - - # When - actual = calculator.evaluate_eligibility("Y", ["ALL"], "ALL") - - # Then - assert_that( - actual, - is_eligibility_status().with_conditions( - has_items( - is_condition() - .with_condition_name(ConditionName("RSV")) - .and_status(Status.not_actionable) - .and_cohort_results( - contains_exactly( - *[ - is_cohort_result() - .with_cohort_code(item[0]) - .and_description(item[1]) - .and_reasons( - contains_exactly(*[is_reason().with_rule_name(rule_name) for rule_name in item[2]]) - ) - for item in expected_cohort_group_and_description_and_s_rule_names - ] - ) - ), - ) - ), - test_comment, - ) - - -@pytest.mark.parametrize( - ("person_cohorts", "expected_cohort_group_and_description", "test_comment"), - [ - ( - ["rsv_75_rolling"], - [("rsv_age_range", "rsv_age_range positive description")], - "rsv_75_rolling is actionable, others are not-eligible", - ), - ( - ["rsv_75_rolling", "rsv_75to79_2024"], - [("rsv_age_range", "rsv_age_range positive description")], - "rsv_75_rolling, rsv_75to79_2024 is actionable, rsv_pretend_clinical_cohort are not-eligible", - ), - ( - ["rsv_75_rolling", "rsv_75to79_2024", "rsv_pretend_clinical_cohort"], - [ - ("rsv_age_range", "rsv_age_range positive description"), - ("rsv_clinical_cohort", "rsv_clinical_cohort positive description"), - ], - "all are actionable", - ), - ], -) -def test_cohort_group_and_descriptions_when_best_status_is_actionable( - person_cohorts: list[str], - expected_cohort_group_and_description: list[tuple[str, str]], - test_comment: str, - faker: Faker, -): - # Given - nhs_number = NHSNumber(faker.nhs_number()) - date_of_birth = DateOfBirth(faker.date_of_birth(minimum_age=66, maximum_age=74)) - - person_rows = person_rows_builder(nhs_number, date_of_birth=date_of_birth, cohorts=person_cohorts, postcode="hp") - campaign_configs = [ - rule_builder.CampaignConfigFactory.build( - target="RSV", - iterations=[ - rule_builder.IterationFactory.build( - iteration_cohorts=[ - rule_builder.Rsv75RollingCohortFactory.build(), - rule_builder.Rsv75to79CohortFactory.build(), - rule_builder.RsvPretendClinicalCohortFactory.build(), - ], - iteration_rules=[rule_builder.PostcodeSuppressionRuleFactory.build()], - ) - ], - ) - ] - - calculator = EligibilityCalculator(person_rows, campaign_configs) - - # When - actual = calculator.evaluate_eligibility("Y", ["ALL"], "ALL") - - # Then - assert_that( - actual, - is_eligibility_status().with_conditions( - has_items( - is_condition() - .with_condition_name(ConditionName("RSV")) - .and_status(Status.actionable) - .and_cohort_results( - contains_exactly( - *[ - is_cohort_result().with_cohort_code(item[0]).with_description(item[1]) - for item in expected_cohort_group_and_description - ] - ) - ) - ) - ), - test_comment, - ) - - -@pytest.mark.parametrize( - ("person_rows", "expected_description", "test_comment"), - [ - ( - person_rows_builder(nhs_number="123", cohorts=[]), - "rsv_age_range negative description 1", - "status - not eligible", - ), - ( - person_rows_builder(nhs_number="123", cohorts=["rsv_75_rolling", "rsv_75to79_2024"], postcode="SW19"), - "rsv_age_range positive description 1", - "status - not actionable", - ), - ( - person_rows_builder(nhs_number="123", cohorts=["rsv_75_rolling", "rsv_75to79_2024"], postcode="hp"), - "rsv_age_range positive description 1", - "status - actionable", - ), - ( - person_rows_builder(nhs_number="123", cohorts=["rsv_75to79_2024"], postcode="hp"), - "rsv_age_range positive description 2", - "rsv_75to79_2024 - actionable and rsv_75_rolling is not eligible", - ), - ], -) -def test_cohort_group_descriptions_are_selected_based_on_priority_when_cohorts_have_different_non_empty_descriptions( - person_rows: list[dict[str, Any]], expected_description: str, test_comment: str -): - # Given - campaign_configs = [ - rule_builder.CampaignConfigFactory.build( - target="RSV", - iterations=[ - rule_builder.IterationFactory.build( - iteration_cohorts=[ - rule_builder.Rsv75to79CohortFactory.build( - positive_description=rules.Description("rsv_age_range positive description 2"), - negative_description=rules.Description("rsv_age_range negative description 2"), - priority=2, - ), - rule_builder.Rsv75RollingCohortFactory.build( - positive_description=rules.Description("rsv_age_range positive description 1"), - negative_description=rules.Description("rsv_age_range negative description 1"), - priority=1, - ), - ], - iteration_rules=[rule_builder.PostcodeSuppressionRuleFactory.build()], - ) - ], - ) - ] - - calculator = EligibilityCalculator(person_rows, campaign_configs) - - # When - actual = calculator.evaluate_eligibility("Y", ["ALL"], "ALL") - - # Then - assert_that( - actual, - is_eligibility_status().with_conditions( - has_items( - is_condition() - .with_condition_name(ConditionName("RSV")) - .and_cohort_results( - contains_exactly( - is_cohort_result().with_cohort_code("rsv_age_range").with_description(expected_description) - ) - ) - ) - ), - test_comment, - ) - - -@pytest.mark.parametrize( - ("person_rows", "iteration_cohorts", "expected_cohort_group_and_description", "expected_status", "test_comment"), - [ - ( - person_rows_builder("123", postcode="SW19", cohorts=[], de=False), - [rule_builder.Rsv75to79CohortFactory.build(negative_description=None, priority=2)], - [("rsv_age_range", "")], - Status.not_eligible, - "if group has one cohort, with no description, expect no description", - ), - ( - person_rows_builder("123", postcode="SW19", cohorts=["rsv_75to79_2024", "rsv_75_rolling"], de=False), - [rule_builder.Rsv75to79CohortFactory.build(negative_description=None, priority=2)], - [("rsv_age_range", "")], - Status.not_eligible, - "if group has one cohort, with no description, expect no description", - ), - ( - person_rows_builder("123", postcode="HP1", cohorts=["rsv_75to79_2024", "rsv_75_rolling"], de=True), - [rule_builder.Rsv75to79CohortFactory.build(positive_description=None, priority=2)], - [("rsv_age_range", "")], - Status.not_actionable, - "if group has one cohort, with no description, expect no description", - ), - ( - person_rows_builder("123", postcode="HP1", cohorts=["rsv_75to79_2024", "rsv_75_rolling"], de=False), - [rule_builder.Rsv75to79CohortFactory.build(positive_description=None, priority=2)], - [("rsv_age_range", "")], - Status.actionable, - "if group has one cohort, with no description, expect no description", - ), - ( - person_rows_builder("123", postcode="SW19", cohorts=[], de=False), - [ - rule_builder.Rsv75to79CohortFactory.build(priority=2, negative_description=None), - rule_builder.Rsv75RollingCohortFactory.build(priority=3, negative_description="rsv age range -ve 1"), - rule_builder.Rsv75RollingCohortFactory.build( - cohort_label="rsv_75_rolling_2", priority=4, negative_description="rsv age range -ve 2" - ), - ], - [("rsv_age_range", "rsv age range -ve 1")], - Status.not_eligible, - "if group has more than one cohort, at least one has description, expect first non empty description", - ), - ( - person_rows_builder("123", postcode="SW19", cohorts=["rsv_75to79_2024", "rsv_75_rolling"], de=False), - [ - rule_builder.Rsv75to79CohortFactory.build(priority=2, negative_description=None), - rule_builder.Rsv75RollingCohortFactory.build(priority=3, negative_description="rsv age range -ve 1"), - rule_builder.Rsv75RollingCohortFactory.build( - cohort_label="rsv_75_rolling_2", priority=4, negative_description="rsv age range -ve 2" - ), - ], - [("rsv_age_range", "rsv age range -ve 1")], - Status.not_eligible, - "if group has more than one cohort, at least one has description, expect first non empty description", - ), - ( - person_rows_builder("123", postcode="HP1", cohorts=["rsv_75to79_2024", "rsv_75_rolling"], de=True), - [ - rule_builder.Rsv75to79CohortFactory.build(priority=2, positive_description=None), - rule_builder.Rsv75RollingCohortFactory.build(priority=3, positive_description="rsv age range +ve 1"), - rule_builder.Rsv75RollingCohortFactory.build( - cohort_label="rsv_75_rolling_2", priority=4, positive_description="rsv age range +ve 2" - ), - ], - [("rsv_age_range", "rsv age range +ve 1")], - Status.not_actionable, - "if group has more than one cohort, at least one has description, expect first non empty description", - ), - ( - person_rows_builder("123", postcode="HP1", cohorts=["rsv_75to79_2024", "rsv_75_rolling"], de=False), - [ - rule_builder.Rsv75to79CohortFactory.build(priority=2, positive_description=None), - rule_builder.Rsv75RollingCohortFactory.build(priority=3, positive_description="rsv age range +ve 1"), - rule_builder.Rsv75RollingCohortFactory.build( - cohort_label="rsv_75_rolling_2", priority=4, positive_description="rsv age range +ve 2" - ), - ], - [("rsv_age_range", "rsv age range +ve 1")], - Status.actionable, - "if group has more than one cohort, at least one has description, expect first non empty description", - ), - ], -) -def test_cohort_group_descriptions_pick_first_non_empty_if_available( - person_rows: list[dict[str, Any]], - iteration_cohorts: list[rules.IterationCohort], - expected_cohort_group_and_description: list[tuple[str, str]], - expected_status: Status, - test_comment: str, -): - # Given - campaign_configs = [ - rule_builder.CampaignConfigFactory.build( - target="RSV", - iterations=[ - rule_builder.IterationFactory.build( - iteration_cohorts=iteration_cohorts, - iteration_rules=[ - rule_builder.PostcodeSuppressionRuleFactory.build(type=rules.RuleType.filter), - rule_builder.DetainedEstateSuppressionRuleFactory.build(), - ], - ) - ], - ) - ] - - calculator = EligibilityCalculator(person_rows, campaign_configs) - - # When - actual = calculator.evaluate_eligibility("Y", ["ALL"], "ALL") - - # Then - assert_that( - actual, - is_eligibility_status().with_conditions( - has_items( - is_condition() - .with_condition_name(ConditionName("RSV")) - .and_status(expected_status) - .and_cohort_results( - contains_exactly( - *[ - is_cohort_result() - .with_cohort_code(item[0]) - .with_description(item[1]) - .with_status(expected_status) - for item in expected_cohort_group_and_description - ] - ) - ) - ) - ), - test_comment, - ) - - -book_nbs_comms = AvailableAction( - ActionType="ButtonAuthLink", - ExternalRoutingCode="BookNBS", - ActionDescription="Action description", - UrlLink=HttpUrl("https://www.nhs.uk/book-rsv"), - UrlLabel="Continue to booking", -) - -default_comms_detail = AvailableAction( - ActionType="CareCardWithText", - ExternalRoutingCode="BookLocal", - ActionDescription="You can get an RSV vaccination at your GP surgery", -) - - -@pytest.mark.parametrize( - ("test_comment", "default_comms_routing", "comms_routing", "actions_mapper", "expected_actions"), - [ - ( - """Rule match: default_comms_routing present, action_mapper present, - return actions from matching comms from rule""", - "defaultcomms", - "InternalBookNBS", - {"InternalBookNBS": book_nbs_comms, "defaultcomms": default_comms_detail}, - [ - SuggestedAction( - internal_action_code=InternalActionCode("InternalBookNBS"), - action_type=ActionType("ButtonAuthLink"), - action_code=ActionCode("BookNBS"), - action_description=ActionDescription("Action description"), - url_link=UrlLink(HttpUrl("https://www.nhs.uk/book-rsv")), - url_label=UrlLabel("Continue to booking"), - ) - ], - ), - ( - """Rule match: default_comms_routing has multiple values, - comms missing in rule, all default comms should be returned in actions""", - "defaultcomms1|defaultcomms2", - None, - {"defaultcomms1": default_comms_detail, "defaultcomms2": default_comms_detail}, - [ - SuggestedAction( - internal_action_code=InternalActionCode("defaultcomms1"), - action_type=ActionType("CareCardWithText"), - action_code=ActionCode("BookLocal"), - action_description=ActionDescription("You can get an RSV vaccination at your GP surgery"), - url_link=None, - url_label=None, - ), - SuggestedAction( - internal_action_code=InternalActionCode("defaultcomms2"), - action_type=ActionType("CareCardWithText"), - action_code=ActionCode("BookLocal"), - action_description=ActionDescription("You can get an RSV vaccination at your GP surgery"), - url_link=None, - url_label=None, - ), - ], - ), - ( - """Rule match: default_comms_routing has multiple values, - comms is empty string, all default comms should be returned in actions""", - "defaultcomms1", - "", - {"defaultcomms1": default_comms_detail}, - [ - SuggestedAction( - internal_action_code=InternalActionCode("defaultcomms1"), - action_type=ActionType("CareCardWithText"), - action_code=ActionCode("BookLocal"), - action_description=ActionDescription("You can get an RSV vaccination at your GP surgery"), - url_link=None, - url_label=None, - ) - ], - ), - ( - """Rule match: default_comms_routing present, - action_mapper missing for matching comms, return default_comms in actions""", - "defaultcomms", - "InternalBookNBS", - {"defaultcomms": default_comms_detail}, - [ - SuggestedAction( - internal_action_code=InternalActionCode("defaultcomms"), - action_type=ActionType("CareCardWithText"), - action_code=ActionCode("BookLocal"), - action_description=ActionDescription("You can get an RSV vaccination at your GP surgery"), - url_link=None, - url_label=None, - ) - ], - ), - ( - """Rule match: default_comms_routing present, - rule has an incorrect comms key, return default_comms in actions""", - "defaultcomms", - "InvalidCode", - {"defaultcomms": default_comms_detail}, - [ - SuggestedAction( - internal_action_code=InternalActionCode("defaultcomms"), - action_type=ActionType("CareCardWithText"), - action_code=ActionCode("BookLocal"), - action_description=ActionDescription("You can get an RSV vaccination at your GP surgery"), - url_link=None, - url_label=None, - ) - ], - ), - ( - """Rule match: action_mapper present without url, - return actions from matching comms from rule""", - "defaultcomms", - "InternalBookNBS", - { - "InternalBookNBS": AvailableAction( - ActionType=book_nbs_comms.action_type, - ExternalRoutingCode=book_nbs_comms.action_code, - ActionDescription=book_nbs_comms.action_description, - ) - }, - [ - SuggestedAction( - internal_action_code=InternalActionCode("InternalBookNBS"), - action_type=ActionType(book_nbs_comms.action_type), - action_code=ActionCode(book_nbs_comms.action_code), - action_description=ActionDescription(book_nbs_comms.action_description), - url_link=None, - url_label=None, - ) - ], - ), - ( - """Rule match: default_comms_routing missing, - comms present in rule, action_mapper missing, return no actions""", - "", - "InternalBookNBS", - {}, - [], - ), - ( - """Rule match: default_comms_routing missing, but action_mapper present, - return actions from matching comms from rule""", - "", - "InternalBookNBS", - {"InternalBookNBS": book_nbs_comms}, - [ - SuggestedAction( - internal_action_code=InternalActionCode("InternalBookNBS"), - action_type=ActionType("ButtonAuthLink"), - action_code=ActionCode("BookNBS"), - action_description=ActionDescription("Action description"), - url_link=UrlLink(HttpUrl("https://www.nhs.uk/book-rsv")), - url_label=UrlLabel("Continue to booking"), - ) - ], - ), - ( - """Rule match: default_comms_routing present, - comms present in rule, but action_mapper missing, return no actions""", - "defaultcommskeywithoutactionmapper", - "InternalBookNBS", - {}, - [], - ), - ( - """Rule match: default_comms_routing has multiple values, - one of the value is invalid, valid values should be returned in actions""", - "defaultcomms1|invaliddefault", - None, - {"defaultcomms1": default_comms_detail}, - [ - SuggestedAction( - internal_action_code=InternalActionCode("defaultcomms1"), - action_type=ActionType("CareCardWithText"), - action_code=ActionCode("BookLocal"), - action_description=ActionDescription("You can get an RSV vaccination at your GP surgery"), - url_link=None, - url_label=None, - ) - ], - ), - ], -) -def test_correct_actions_determined_from_redirect_r_rules( # noqa: PLR0913 - test_comment: str, - default_comms_routing: str, - comms_routing: str, - actions_mapper: ActionsMapper, - expected_actions: list[SuggestedAction], - faker: Faker, -): - # Given - nhs_number = NHSNumber(faker.nhs_number()) - - person_rows = person_rows_builder(nhs_number, cohorts=["cohort1"], icb="QE1") - - campaign_configs = [ - ( - rule_builder.CampaignConfigFactory.build( - target="RSV", - iterations=[ - rule_builder.IterationFactory.build( - iteration_cohorts=[rule_builder.IterationCohortFactory.build(cohort_label="cohort1")], - default_comms_routing=default_comms_routing, - actions_mapper=rule_builder.ActionsMapperFactory.build(root=actions_mapper), - iteration_rules=[rule_builder.ICBRedirectRuleFactory.build(comms_routing=comms_routing)], - ) - ], - ) - ) - ] - - calculator = EligibilityCalculator(person_rows, campaign_configs) - - # When - actual = calculator.evaluate_eligibility("Y", ["ALL"], "ALL") - - # Then - assert_that( - actual, - is_eligibility_status().with_conditions( - has_items( - is_condition() - .with_condition_name(ConditionName("RSV")) - .and_status(equal_to(Status.actionable)) - .and_actions(equal_to(expected_actions)) - ) - ), - test_comment, - ) - - -@pytest.mark.parametrize( - ("test_comment", "redirect_r_rule_cohort_label"), - [ - ("cohort_label matches person cohort, result action ActionCode1", "cohort1"), - ("cohort_label NOT matches person cohort, result action ActionCode1", "cohort2"), - ], -) -def test_cohort_label_not_supported_used_in_r_rules(test_comment: str, redirect_r_rule_cohort_label: str, faker: Faker): - # Given - nhs_number = NHSNumber(faker.nhs_number()) - - person_rows = person_rows_builder(nhs_number, cohorts=["cohort1"], icb="QE1") - campaign_configs = [ - ( - rule_builder.CampaignConfigFactory.build( - target="RSV", - iterations=[ - rule_builder.IterationFactory.build( - iteration_cohorts=[rule_builder.IterationCohortFactory.build(cohort_label="cohort1")], - default_comms_routing="defaultcomms", - actions_mapper=rule_builder.ActionsMapperFactory.build( - root={ - "ActionCode1": book_nbs_comms, - "defaultcomms": default_comms_detail, - } - ), - iteration_rules=[ - rule_builder.ICBRedirectRuleFactory.build( - cohort_label=rules.CohortLabel(redirect_r_rule_cohort_label) - ) - ], - ) - ], - ) - ) - ] - - calculator = EligibilityCalculator(person_rows, campaign_configs) - - # When - actual = calculator.evaluate_eligibility("Y", ["ALL"], "ALL") - - # Then - assert_that( - actual, - is_eligibility_status().with_conditions( - has_items( - is_condition() - .with_condition_name(ConditionName("RSV")) - .and_status(equal_to(Status.actionable)) - .and_actions( - equal_to( - [ - SuggestedAction( - internal_action_code=InternalActionCode("ActionCode1"), - action_type=ActionType("ButtonAuthLink"), - action_code=ActionCode("BookNBS"), - action_description=ActionDescription("Action description"), - url_link=UrlLink(HttpUrl("https://www.nhs.uk/book-rsv")), - url_label=UrlLabel("Continue to booking"), - ) - ] - ) - ) - ) - ), - test_comment, - ) - - -def test_multiple_r_rules_match_with_same_priority(faker: Faker): - # Given - nhs_number = NHSNumber(faker.nhs_number()) - - person_rows = person_rows_builder(nhs_number, cohorts=["cohort1"], icb="QE1") - campaign_configs = [ - ( - rule_builder.CampaignConfigFactory.build( - target="RSV", - iterations=[ - rule_builder.IterationFactory.build( - iteration_cohorts=[rule_builder.IterationCohortFactory.build(cohort_label="cohort1")], - default_comms_routing="defaultcomms", - actions_mapper=rule_builder.ActionsMapperFactory.build( - root={ - "rule_1_comms_routing": book_nbs_comms, - "rule_2_comms_routing": book_nbs_comms, - "rule_3_comms_routing": book_nbs_comms, - "defaultcomms": default_comms_detail, - } - ), - iteration_rules=[ - rule_builder.ICBRedirectRuleFactory.build(comms_routing="rule_1_comms_routing"), - rule_builder.ICBRedirectRuleFactory.build(comms_routing="rule_2_comms_routing"), - rule_builder.ICBRedirectRuleFactory.build( - priority=2, - attribute_name=rules.RuleAttributeName("ICBMismatch"), - comms_routing="rule_3_comms_routing", - ), - ], - ) - ], - ) - ) - ] - - calculator = EligibilityCalculator(person_rows, campaign_configs) - - # When - actual = calculator.evaluate_eligibility("Y", ["ALL"], "ALL") - - # Then - assert_that( - actual, - is_eligibility_status().with_conditions( - has_items( - is_condition() - .with_condition_name(ConditionName("RSV")) - .and_status(equal_to(Status.actionable)) - .and_actions( - equal_to( - [ - SuggestedAction( - internal_action_code=InternalActionCode("rule_1_comms_routing"), - action_type=ActionType("ButtonAuthLink"), - action_code=ActionCode("BookNBS"), - action_description=ActionDescription("Action description"), - url_link=UrlLink(HttpUrl("https://www.nhs.uk/book-rsv")), - url_label=UrlLabel("Continue to booking"), - ) - ] - ) - ) - ) - ), - ) - - -def test_multiple_r_rules_with_same_priority_one_rule_mismatch_should_return_default_comms(faker: Faker): - # Given - nhs_number = NHSNumber(faker.nhs_number()) - - person_rows = person_rows_builder(nhs_number, cohorts=["cohort1"], icb="QE1") - campaign_configs = [ - ( - rule_builder.CampaignConfigFactory.build( - target="RSV", - iterations=[ - rule_builder.IterationFactory.build( - iteration_cohorts=[rule_builder.IterationCohortFactory.build(cohort_label="cohort1")], - default_comms_routing="defaultcomms", - actions_mapper=rule_builder.ActionsMapperFactory.build( - root={ - "rule_1_comms_routing": book_nbs_comms, - "rule_2_comms_routing": book_nbs_comms, - "rule_3_comms_routing": book_nbs_comms, - "defaultcomms": default_comms_detail, - } - ), - iteration_rules=[ - rule_builder.ICBRedirectRuleFactory.build(comms_routing="rule_1_comms_routing"), - rule_builder.ICBRedirectRuleFactory.build(comms_routing="rule_2_comms_routing"), - rule_builder.ICBRedirectRuleFactory.build( - attribute_name=rules.RuleAttributeName("ICBMismatch"), - comms_routing="rule_3_comms_routing", - ), - ], - ) - ], - ) - ) - ] - - calculator = EligibilityCalculator(person_rows, campaign_configs) - - # When - actual = calculator.evaluate_eligibility("Y", ["ALL"], "ALL") - - # Then - assert_that( - actual, - is_eligibility_status().with_conditions( - has_items( - is_condition() - .with_condition_name(ConditionName("RSV")) - .and_status(equal_to(Status.actionable)) - .and_actions( - equal_to( - [ - SuggestedAction( - internal_action_code=InternalActionCode("defaultcomms"), - action_type=ActionType("CareCardWithText"), - action_code=ActionCode("BookLocal"), - action_description=ActionDescription( - "You can get an RSV vaccination at your GP surgery" - ), - url_link=None, - url_label=None, - ) - ] - ) - ) - ) - ), - ) - - -def test_only_highest_priority_rule_is_applied_and_return_actions_only_for_that_rule(faker: Faker): - # Given - nhs_number = NHSNumber(faker.nhs_number()) - - person_rows = person_rows_builder(nhs_number, cohorts=["cohort1"], icb="QE1") - campaign_configs = [ - ( - rule_builder.CampaignConfigFactory.build( - target="RSV", - iterations=[ - rule_builder.IterationFactory.build( - iteration_cohorts=[rule_builder.IterationCohortFactory.build(cohort_label="cohort1")], - default_comms_routing="defaultcomms", - actions_mapper=rule_builder.ActionsMapperFactory.build( - root={ - "rule_1_comms_routing": AvailableAction( - ActionType="ButtonAuthLink", - ExternalRoutingCode="BookNBS", - ActionDescription="Action description", - ), - "rule_2_comms_routing": AvailableAction( - ActionType="AuthLink", - ExternalRoutingCode="BookNBS", - ActionDescription="Action description", - UrlLink=HttpUrl("https://www.nhs.uk/book-rsv"), - UrlLabel="Continue to booking", - ), - "defaultcomms": default_comms_detail, - } - ), - iteration_rules=[ - rule_builder.ICBRedirectRuleFactory.build(priority=2, comms_routing="rule_2_comms_routing"), - rule_builder.ICBRedirectRuleFactory.build(priority=1, comms_routing="rule_1_comms_routing"), - ], - ) - ], - ) - ) - ] - - calculator = EligibilityCalculator(person_rows, campaign_configs) - - # When - actual = calculator.evaluate_eligibility("Y", ["ALL"], "ALL") - - expected_actions = SuggestedAction( - internal_action_code=InternalActionCode("rule_1_comms_routing"), - action_type=ActionType("ButtonAuthLink"), - action_code=ActionCode("BookNBS"), - action_description=ActionDescription("Action description"), - url_link=None, - url_label=None, - ) - - # Then - assert_that( - actual, - is_eligibility_status().with_conditions( - has_items( - is_condition() - .with_condition_name(ConditionName("RSV")) - .and_status(equal_to(Status.actionable)) - .and_actions(equal_to([expected_actions])) - ) - ), - ) - - -def test_should_include_actions_when_include_actions_flag_is_true_when_status_is_actionable(faker: Faker): - # Given - nhs_number = NHSNumber(faker.nhs_number()) - - person_rows = person_rows_builder(nhs_number, cohorts=["cohort1"], icb="QE1") - campaign_configs = [ - ( - rule_builder.CampaignConfigFactory.build( - target="RSV", - iterations=[ - rule_builder.IterationFactory.build( - iteration_cohorts=[rule_builder.IterationCohortFactory.build(cohort_label="cohort1")], - default_comms_routing="defaultcomms", - actions_mapper=rule_builder.ActionsMapperFactory.build( - root={ - "book_nbs": book_nbs_comms, - "defaultcomms": default_comms_detail, - } - ), - iteration_rules=[ - rule_builder.ICBRedirectRuleFactory.build(priority=2, comms_routing="book_nbs"), - ], - ) - ], - ) - ) - ] - - calculator = EligibilityCalculator(person_rows, campaign_configs) - - # When - actual = calculator.evaluate_eligibility("Y", ["ALL"], "ALL") - - # Then - assert_that( - actual, - is_eligibility_status().with_conditions( - has_items( - is_condition() - .with_condition_name(ConditionName("RSV")) - .and_status(equal_to(Status.actionable)) - .and_actions( - equal_to( - [ - SuggestedAction( - internal_action_code=InternalActionCode("book_nbs"), - action_type=ActionType("ButtonAuthLink"), - action_code=ActionCode("BookNBS"), - action_description=ActionDescription("Action description"), - url_link=UrlLink(HttpUrl("https://www.nhs.uk/book-rsv")), - url_label=UrlLabel("Continue to booking"), - ) - ] - ) - ) - ) - ), - ) - - -def test_should_not_include_actions_when_include_actions_flag_is_false_when_status_is_actionable(faker: Faker): - # Given - nhs_number = NHSNumber(faker.nhs_number()) - - person_rows = person_rows_builder(nhs_number, cohorts=["cohort1"], icb="QE1") - campaign_configs = [ - ( - rule_builder.CampaignConfigFactory.build( - target="RSV", - iterations=[ - rule_builder.IterationFactory.build( - iteration_cohorts=[rule_builder.IterationCohortFactory.build(cohort_label="cohort1")], - default_comms_routing="defaultcomms", - actions_mapper=rule_builder.ActionsMapperFactory.build( - root={ - "book_nbs": book_nbs_comms, - "defaultcomms": default_comms_detail, - } - ), - iteration_rules=[ - rule_builder.ICBRedirectRuleFactory.build(priority=2, comms_routing="book_nbs"), - ], - ) - ], - ) - ) + cohort_group_results = [ + CohortGroupResult("COHORT_Y", Status.not_actionable, [reason_1, reason_2], "Cohort Y Description", []) ] - calculator = EligibilityCalculator(person_rows, campaign_configs) - - # When - actual = calculator.evaluate_eligibility("N", ["ALL"], "ALL") - - # Then - assert_that( - actual, - is_eligibility_status().with_conditions( - has_items( - is_condition() - .with_condition_name(ConditionName("RSV")) - .and_status(equal_to(Status.actionable)) - .and_actions(equal_to(None)) - ) - ), - ) - - -@pytest.mark.parametrize( - ("campaign_target", "campaign_type", "conditions_filter", "category_filter", "expected_result"), - [ - # Multiple matching campaigns under the same condition - ("RSV", "V", ["RSV"], "VACCINATIONS", [("RSV", "V")]), - ("RSV", "V", ["COVID"], "VACCINATIONS", []), - ("RSV", "S", ["RSV"], "ALL", [("RSV", "S")]), - ("RSV", "S", ["ALL"], "ALL", [("RSV", "S")]), - ("RSV", "S", ["RSV"], "VACCINATIONS", []), - # Multiple campaigns with different types under the same condition name - ("RSV", "V", ["RSV"], "ALL", [("RSV", "V")]), - # Campaign is live but condition not in filter (no yield) - ("FLU", "V", ["COVID", "RSV"], "ALL", []), - # Category is ALL and condition filter includes ALL (everything matches) - ("FLU", "S", ["ALL"], "ALL", [("FLU", "S")]), - # Condition filter is unknown (should not match anything) - ("COVID", "V", ["UNKNOWN"], "VACCINATIONS", []), - # Campaign with the target matching one of several condition filters - ("FLU", "V", ["COVID", "FLU"], "VACCINATIONS", [("FLU", "V")]), - ], -) -def test_campaigns_grouped_by_condition_name_filters_correctly( - campaign_target, campaign_type, conditions_filter, category_filter, expected_result -): - campaign = rule_builder.CampaignConfigFactory.build(target=campaign_target, type=campaign_type, campaign_live=True) - - calculator = EligibilityCalculator(person_data=[], campaign_configs=[campaign]) - result = list(calculator.campaigns_grouped_by_condition_name(conditions_filter, category_filter)) + iteration_result = IterationResult(Status.not_actionable, cohort_group_results, []) + result = EligibilityCalculator.build_condition(iteration_result, ConditionName("RSV")) - assert_that([(str(name), group[0].type) for name, group in result], is_(expected_result)) + assert_that(len(result.cohort_results), is_(1)) + assert_that(result.cohort_results[0].reasons, contains_inanyorder(*expected_reasons)) diff --git a/tests/unit/services/calculators/test_rule_calculator.py b/tests/unit/services/calculators/test_rule_calculator.py index b8069a16a..8e42013d9 100644 --- a/tests/unit/services/calculators/test_rule_calculator.py +++ b/tests/unit/services/calculators/test_rule_calculator.py @@ -1,31 +1,27 @@ -from collections.abc import Collection, Mapping -from typing import Any - import pytest -from eligibility_signposting_api.model import rules +from eligibility_signposting_api.model.campaign_config import IterationRule, RuleAttributeLevel +from eligibility_signposting_api.model.person import Person from eligibility_signposting_api.services.calculators.rule_calculator import RuleCalculator from tests.fixtures.builders.model import rule as rule_builder -Row = Collection[Mapping[str, Any]] - @pytest.mark.parametrize( ("person_data", "rule", "expected"), [ # PERSON attribute level ( - [{"ATTRIBUTE_TYPE": "PERSON", "POSTCODE": "SW19"}], + Person([{"ATTRIBUTE_TYPE": "PERSON", "POSTCODE": "SW19"}]), rule_builder.IterationRuleFactory.build( - attribute_level=rules.RuleAttributeLevel.PERSON, attribute_name="POSTCODE" + attribute_level=RuleAttributeLevel.PERSON, attribute_name="POSTCODE" ), "SW19", ), # TARGET attribute level ( - [{"ATTRIBUTE_TYPE": "RSV", "LAST_SUCCESSFUL_DATE": "20240101"}], + Person([{"ATTRIBUTE_TYPE": "RSV", "LAST_SUCCESSFUL_DATE": "20240101"}]), rule_builder.IterationRuleFactory.build( - attribute_level=rules.RuleAttributeLevel.TARGET, + attribute_level=RuleAttributeLevel.TARGET, attribute_name="LAST_SUCCESSFUL_DATE", attribute_target="RSV", ), @@ -33,17 +29,17 @@ ), # COHORT attribute level ( - [{"ATTRIBUTE_TYPE": "COHORTS", "COHORT_LABEL": ""}], + Person([{"ATTRIBUTE_TYPE": "COHORTS", "COHORT_LABEL": ""}]), rule_builder.IterationRuleFactory.build( - attribute_level=rules.RuleAttributeLevel.COHORT, attribute_name="COHORT_LABEL" + attribute_level=RuleAttributeLevel.COHORT, attribute_name="COHORT_LABEL" ), "", ), ], ) -def test_get_attribute_value_for_all_attribute_levels(person_data: Row, rule: rules.IterationRule, expected: str): +def test_get_attribute_value_for_all_attribute_levels(person_data: Person, rule: IterationRule, expected: str): # Given - calc = RuleCalculator(person_data=person_data, rule=rule) + calc = RuleCalculator(person=person_data, rule=rule) # When actual = calc.get_attribute_value() # Then diff --git a/tests/unit/services/operators/test_operators.py b/tests/unit/services/operators/test_operators.py index 1c2b2ba70..e1c74164e 100644 --- a/tests/unit/services/operators/test_operators.py +++ b/tests/unit/services/operators/test_operators.py @@ -2,8 +2,8 @@ from freezegun import freeze_time from hamcrest import assert_that, equal_to -from eligibility_signposting_api.model.rules import RuleOperator -from eligibility_signposting_api.services.rules.operators import Operator, OperatorRegistry +from eligibility_signposting_api.model.campaign_config import RuleOperator +from eligibility_signposting_api.services.operators.operators import Operator, OperatorRegistry # Test cases: person_data, rule_operator, rule_value, expected, test_comment cases: list[tuple[str | None, RuleOperator, str | None, bool, str]] = [] diff --git a/tests/unit/services/processors/__init__.py b/tests/unit/services/processors/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit/services/processors/test_action_rule_handler.py b/tests/unit/services/processors/test_action_rule_handler.py new file mode 100644 index 000000000..13e9d4592 --- /dev/null +++ b/tests/unit/services/processors/test_action_rule_handler.py @@ -0,0 +1,805 @@ +from unittest.mock import Mock, call, patch + +import pytest +from hamcrest import assert_that, is_ +from pydantic import HttpUrl + +from eligibility_signposting_api.model.campaign_config import AvailableAction, RuleName, RulePriority, RuleType +from eligibility_signposting_api.model.eligibility_status import ( + ActionCode, + ActionDescription, + ActionType, + InternalActionCode, + IterationResult, + MatchedActionDetail, + Status, + SuggestedAction, + UrlLabel, + UrlLink, +) +from eligibility_signposting_api.model.person import Person +from eligibility_signposting_api.services.processors.action_rule_handler import ActionRuleHandler +from tests.fixtures.builders.model import rule as rule_builder +from tests.fixtures.builders.model.rule import ActionsMapperFactory, IterationFactory + +# flake8: noqa: SLF001 + + +@pytest.fixture +def handler(): + return ActionRuleHandler() + + +MOCK_PERSON = Person([{"ATTRIBUTE_TYPE": "PERSON", "AGE": "30"}]) + +BOOK_NBS_COMMS = AvailableAction( + ActionType="ButtonAuthLink", + ExternalRoutingCode="BookNBS", + ActionDescription="Action description", + UrlLink=HttpUrl("https://www.nhs.uk/book-rsv"), + UrlLabel="Continue to booking", +) + +DEFAULT_COMMS_DETAIL = AvailableAction( + ActionType="CareCardWithText", + ExternalRoutingCode="BookLocal", + ActionDescription="You can get an RSV vaccination at your GP surgery", +) + + +def test_get_action_rules_components_redirect_type(): + iteration = rule_builder.IterationFactory.build( + default_comms_routing="default_redirect", + default_not_eligible_routing="default_not_eligible", + default_not_actionable_routing="default_not_actionable", + actions_mapper=ActionsMapperFactory.build(), + iteration_rules=[rule_builder.ICBRedirectRuleFactory.build(name="RedirectRule")], + ) + rules_found, mapper, default_comms = ActionRuleHandler._get_action_rules_components(iteration, RuleType.redirect) + assert_that(len(rules_found), is_(1)) + assert_that(rules_found[0].name, is_(RuleName("RedirectRule"))) + assert_that(mapper, is_(iteration.actions_mapper)) + assert_that(default_comms, is_("default_redirect")) + + +def test_get_action_rules_components_not_eligible_actions_type(): + iteration = rule_builder.IterationFactory.build( + default_comms_routing="default_redirect", + default_not_eligible_routing="default_not_eligible", + default_not_actionable_routing="default_not_actionable", + actions_mapper=ActionsMapperFactory.build(), + iteration_rules=[rule_builder.ICBNonEligibleActionRuleFactory.build(name="NonEligibleRule")], + ) + rules_found, mapper, default_comms = ActionRuleHandler._get_action_rules_components( + iteration, RuleType.not_eligible_actions + ) + assert_that(len(rules_found), is_(1)) + assert_that(rules_found[0].name, is_(RuleName("NonEligibleRule"))) + assert_that(mapper, is_(iteration.actions_mapper)) + assert_that(default_comms, is_("default_not_eligible")) + + +def test_get_action_rules_components_not_actionable_actions_type(): + iteration = rule_builder.IterationFactory.build( + default_comms_routing="default_redirect", + default_not_eligible_routing="default_not_eligible", + default_not_actionable_routing="default_not_actionable", + actions_mapper=ActionsMapperFactory.build(), + iteration_rules=[rule_builder.ICBNonActionableActionRuleFactory.build(name="NonActionableRule")], + ) + rules_found, mapper, default_comms = ActionRuleHandler._get_action_rules_components( + iteration, RuleType.not_actionable_actions + ) + assert_that(len(rules_found), is_(1)) + assert_that(rules_found[0].name, is_(RuleName("NonActionableRule"))) + assert_that(mapper, is_(iteration.actions_mapper)) + assert_that(default_comms, is_("default_not_actionable")) + + +def test_get_action_rules_components_no_matching_rules(): + iteration = rule_builder.IterationFactory.build( + iteration_rules=[rule_builder.PersonAgeSuppressionRuleFactory.build()] + ) + rules_found, _, _ = ActionRuleHandler._get_action_rules_components(iteration, RuleType.redirect) + assert_that(len(rules_found), is_(0)) + + +def test_get_actions_from_comms_single_comm(): + action_mapper = ActionsMapperFactory.build(root={"book_nbs": BOOK_NBS_COMMS}) + actions = ActionRuleHandler._get_actions_from_comms(action_mapper, "book_nbs") + assert_that(len(actions), is_(1)) + assert_that(actions[0].internal_action_code, is_(InternalActionCode("book_nbs"))) + assert_that(actions[0].action_code, is_(ActionCode("BookNBS"))) + + +def test_get_actions_from_comms_multiple_comms(): + action_mapper = ActionsMapperFactory.build(root={"book_nbs": BOOK_NBS_COMMS, "default_comms": DEFAULT_COMMS_DETAIL}) + actions = ActionRuleHandler._get_actions_from_comms(action_mapper, "book_nbs|default_comms") + assert_that(len(actions), is_(2)) + assert_that(actions[0].internal_action_code, is_(InternalActionCode("book_nbs"))) + assert_that(actions[1].internal_action_code, is_(InternalActionCode("default_comms"))) + + +def test_get_actions_from_comms_unknown_comm_code(): + action_mapper = ActionsMapperFactory.build(root={"book_nbs": BOOK_NBS_COMMS}) + actions = ActionRuleHandler._get_actions_from_comms(action_mapper, "book_nbs|unknown_code") + assert_that(len(actions), is_(1)) + assert_that(actions[0].internal_action_code, is_(InternalActionCode("book_nbs"))) + + +def test_get_actions_from_comms_empty_string(): + action_mapper = ActionsMapperFactory.build(root={"book_nbs": BOOK_NBS_COMMS}) + actions = ActionRuleHandler._get_actions_from_comms(action_mapper, "") + assert_that(len(actions), is_(0)) + + +def test_get_actions_from_comms_no_actions_found(): + action_mapper = ActionsMapperFactory.build(root={}) + actions = ActionRuleHandler._get_actions_from_comms(action_mapper, "unknown_code") + assert_that(len(actions), is_(0)) + + +@patch("eligibility_signposting_api.services.calculators.rule_calculator.RuleCalculator") +@patch.object(ActionRuleHandler, "_get_actions_from_comms") +@patch.object(ActionRuleHandler, "_get_action_rules_components") +def test_handle_actions_no_matching_rules_returns_default( + mock_get_action_rules_components, + mock_get_actions_from_comms, + mock_rule_calculator_class, + handler: ActionRuleHandler, +): + active_iteration = rule_builder.IterationFactory.build( + default_comms_routing="default_action_code", + actions_mapper=ActionsMapperFactory.build(root={"default_action_code": DEFAULT_COMMS_DETAIL}), + iteration_rules=[], + ) + + mock_get_action_rules_components.return_value = ( + [], + active_iteration.actions_mapper, + active_iteration.default_comms_routing, + ) + + mock_get_actions_from_comms.side_effect = [ + [ + SuggestedAction( + internal_action_code=InternalActionCode("default_action_code"), + action_type=ActionType(DEFAULT_COMMS_DETAIL.action_type), + action_code=ActionCode(DEFAULT_COMMS_DETAIL.action_code), + action_description=ActionDescription(DEFAULT_COMMS_DETAIL.action_description), + url_link=DEFAULT_COMMS_DETAIL.url_link, + url_label=DEFAULT_COMMS_DETAIL.url_label, + ) + ], + [], + ] + + matched_action_detail = handler._handle(MOCK_PERSON, active_iteration, RuleType.redirect) + + assert_that(len(matched_action_detail.actions), is_(1)) + assert_that(matched_action_detail.actions[0].internal_action_code, is_(InternalActionCode("default_action_code"))) + assert_that(matched_action_detail.rule_priority, is_(None)) + assert_that(matched_action_detail.rule_name, is_(None)) + mock_get_action_rules_components.assert_called_once_with(active_iteration, RuleType.redirect) + mock_get_actions_from_comms.assert_called_once_with(active_iteration.actions_mapper, "default_action_code") + mock_rule_calculator_class.assert_not_called() + + +@patch("eligibility_signposting_api.services.processors.action_rule_handler.RuleCalculator") +@patch.object(ActionRuleHandler, "_get_actions_from_comms") +@patch.object(ActionRuleHandler, "_get_action_rules_components") +def test_handle_actions_matching_redirect_rule_overrides_default( + mock_get_action_rules_components, + mock_get_actions_from_comms, + mock_rule_calculator_class, + handler: ActionRuleHandler, +): + matching_rule = rule_builder.ICBRedirectRuleFactory.build( + priority=10, comms_routing="rule_specific_action", name="RuleSpecificAction" + ) + active_iteration = rule_builder.IterationFactory.build( + default_comms_routing="default_action_code", + actions_mapper=ActionsMapperFactory.build( + root={"default_action_code": DEFAULT_COMMS_DETAIL, "rule_specific_action": BOOK_NBS_COMMS} + ), + iteration_rules=[matching_rule], + ) + mock_get_action_rules_components.return_value = ( + (matching_rule,), + active_iteration.actions_mapper, + active_iteration.default_comms_routing, + ) + + mock_get_actions_from_comms.side_effect = [ + [ + SuggestedAction( + internal_action_code=InternalActionCode("default_action_code"), + action_type=ActionType(DEFAULT_COMMS_DETAIL.action_type), + action_code=ActionCode(DEFAULT_COMMS_DETAIL.action_code), + action_description=ActionDescription(DEFAULT_COMMS_DETAIL.action_description), + url_link=DEFAULT_COMMS_DETAIL.url_link, + url_label=DEFAULT_COMMS_DETAIL.url_label, + ) + ], + [ + SuggestedAction( + internal_action_code=InternalActionCode("rule_specific_action"), + action_type=ActionType(BOOK_NBS_COMMS.action_type), + action_code=ActionCode(BOOK_NBS_COMMS.action_code), + action_description=ActionDescription(BOOK_NBS_COMMS.action_description), + url_link=BOOK_NBS_COMMS.url_link, + url_label=BOOK_NBS_COMMS.url_label, + ) + ], + ] + + mock_rule_instance = Mock() + mock_rule_instance.evaluate_exclusion.return_value = (Status.actionable, Mock(matcher_matched=True)) + mock_rule_calculator_class.return_value = mock_rule_instance + + matched_action_detail = handler._handle(MOCK_PERSON, active_iteration, RuleType.redirect) + + assert_that(len(matched_action_detail.actions), is_(1)) + assert_that(matched_action_detail.actions[0].internal_action_code, is_(InternalActionCode("rule_specific_action"))) + assert_that(matched_action_detail.rule_priority, is_(RulePriority(10))) + assert_that(matched_action_detail.rule_name, is_(RuleName("RuleSpecificAction"))) + + mock_get_action_rules_components.assert_called_once_with(active_iteration, RuleType.redirect) + assert_that(mock_get_actions_from_comms.call_count, is_(2)) + mock_get_actions_from_comms.assert_any_call(active_iteration.actions_mapper, "default_action_code") + mock_get_actions_from_comms.assert_any_call(active_iteration.actions_mapper, "rule_specific_action") + mock_rule_calculator_class.assert_called_once_with(person=MOCK_PERSON, rule=matching_rule) + + +@patch("eligibility_signposting_api.services.processors.action_rule_handler.RuleCalculator") +@patch.object(ActionRuleHandler, "_get_actions_from_comms") +@patch.object(ActionRuleHandler, "_get_action_rules_components") +def test_handle_actions_matching_not_eligible_rule_overrides_default( + mock_get_action_rules_components, + mock_get_actions_from_comms, + mock_rule_calculator_class, + handler: ActionRuleHandler, +): + matching_rule = rule_builder.ICBNonEligibleActionRuleFactory.build( + priority=10, comms_routing="rule_specific_action", name="RuleSpecificAction" + ) + active_iteration = rule_builder.IterationFactory.build( + default_not_eligible_routing="default_not_eligible", + actions_mapper=ActionsMapperFactory.build( + root={"default_not_eligible": DEFAULT_COMMS_DETAIL, "rule_specific_action": BOOK_NBS_COMMS} + ), + iteration_rules=[matching_rule], + ) + mock_get_action_rules_components.return_value = ( + (matching_rule,), + active_iteration.actions_mapper, + active_iteration.default_not_eligible_routing, + ) + + mock_get_actions_from_comms.side_effect = [ + [ + SuggestedAction( + internal_action_code=InternalActionCode("default_not_eligible"), + action_type=ActionType(DEFAULT_COMMS_DETAIL.action_type), + action_code=ActionCode(DEFAULT_COMMS_DETAIL.action_code), + action_description=ActionDescription(DEFAULT_COMMS_DETAIL.action_description), + url_link=DEFAULT_COMMS_DETAIL.url_link, + url_label=DEFAULT_COMMS_DETAIL.url_label, + ) + ], + [ + SuggestedAction( + internal_action_code=InternalActionCode("rule_specific_action"), + action_type=ActionType(BOOK_NBS_COMMS.action_type), + action_code=ActionCode(BOOK_NBS_COMMS.action_code), + action_description=ActionDescription(BOOK_NBS_COMMS.action_description), + url_link=BOOK_NBS_COMMS.url_link, + url_label=BOOK_NBS_COMMS.url_label, + ) + ], + ] + + mock_rule_instance = Mock() + mock_rule_instance.evaluate_exclusion.return_value = (Status.actionable, Mock(matcher_matched=True)) + mock_rule_calculator_class.return_value = mock_rule_instance + + matched_action_detail = handler._handle(MOCK_PERSON, active_iteration, RuleType.not_eligible_actions) + + assert_that(len(matched_action_detail.actions), is_(1)) + assert_that(matched_action_detail.actions[0].internal_action_code, is_(InternalActionCode("rule_specific_action"))) + assert_that(matched_action_detail.rule_priority, is_(RulePriority(10))) + assert_that(matched_action_detail.rule_name, is_(RuleName("RuleSpecificAction"))) + + mock_get_action_rules_components.assert_called_once_with(active_iteration, RuleType.not_eligible_actions) + assert_that(mock_get_actions_from_comms.call_count, is_(2)) + mock_get_actions_from_comms.assert_any_call(active_iteration.actions_mapper, "default_not_eligible") + mock_get_actions_from_comms.assert_any_call(active_iteration.actions_mapper, "rule_specific_action") + mock_rule_calculator_class.assert_called_once_with(person=MOCK_PERSON, rule=matching_rule) + + +@patch("eligibility_signposting_api.services.processors.action_rule_handler.RuleCalculator") +@patch.object(ActionRuleHandler, "_get_actions_from_comms") +@patch.object(ActionRuleHandler, "_get_action_rules_components") +def test_handle_actions_matching_not_actionable_rule_overrides_default( + mock_get_action_rules_components, + mock_get_actions_from_comms, + mock_rule_calculator_class, + handler: ActionRuleHandler, +): + matching_rule = rule_builder.ICBNonActionableActionRuleFactory.build( + priority=10, comms_routing="rule_specific_action", name="RuleSpecificAction" + ) + active_iteration = rule_builder.IterationFactory.build( + default_not_actionable_routing="default_not_actionable", + actions_mapper=ActionsMapperFactory.build( + root={"default_not_actionable": DEFAULT_COMMS_DETAIL, "rule_specific_action": BOOK_NBS_COMMS} + ), + iteration_rules=[matching_rule], + ) + mock_get_action_rules_components.return_value = ( + (matching_rule,), + active_iteration.actions_mapper, + active_iteration.default_not_actionable_routing, + ) + + mock_get_actions_from_comms.side_effect = [ + [ + SuggestedAction( + internal_action_code=InternalActionCode("default_not_actionable"), + action_type=ActionType(DEFAULT_COMMS_DETAIL.action_type), + action_code=ActionCode(DEFAULT_COMMS_DETAIL.action_code), + action_description=ActionDescription(DEFAULT_COMMS_DETAIL.action_description), + url_link=DEFAULT_COMMS_DETAIL.url_link, + url_label=DEFAULT_COMMS_DETAIL.url_label, + ) + ], + [ + SuggestedAction( + internal_action_code=InternalActionCode("rule_specific_action"), + action_type=ActionType(BOOK_NBS_COMMS.action_type), + action_code=ActionCode(BOOK_NBS_COMMS.action_code), + action_description=ActionDescription(BOOK_NBS_COMMS.action_description), + url_link=BOOK_NBS_COMMS.url_link, + url_label=BOOK_NBS_COMMS.url_label, + ) + ], + ] + + mock_rule_instance = Mock() + mock_rule_instance.evaluate_exclusion.return_value = (Status.actionable, Mock(matcher_matched=True)) + mock_rule_calculator_class.return_value = mock_rule_instance + + matched_action_detail = handler._handle(MOCK_PERSON, active_iteration, RuleType.not_actionable_actions) + + assert_that(len(matched_action_detail.actions), is_(1)) + assert_that(matched_action_detail.actions[0].internal_action_code, is_(InternalActionCode("rule_specific_action"))) + assert_that(matched_action_detail.rule_priority, is_(RulePriority(10))) + assert_that(matched_action_detail.rule_name, is_(RuleName("RuleSpecificAction"))) + + mock_get_action_rules_components.assert_called_once_with(active_iteration, RuleType.not_actionable_actions) + assert_that(mock_get_actions_from_comms.call_count, is_(2)) + mock_get_actions_from_comms.assert_any_call(active_iteration.actions_mapper, "default_not_actionable") + mock_get_actions_from_comms.assert_any_call(active_iteration.actions_mapper, "rule_specific_action") + mock_rule_calculator_class.assert_called_once_with(person=MOCK_PERSON, rule=matching_rule) + + +@patch("eligibility_signposting_api.services.processors.action_rule_handler.RuleCalculator") +@patch.object(ActionRuleHandler, "_get_actions_from_comms") +@patch.object(ActionRuleHandler, "_get_action_rules_components") +def test_handle_non_matching_rule_returns_default( + mock_get_action_rules_components, + mock_get_actions_from_comms, + mock_rule_calculator_class, + handler: ActionRuleHandler, +): + non_matching_rule = rule_builder.ICBRedirectRuleFactory.build( + priority=10, comms_routing="rule_specific_action", name="RuleSpecificAction" + ) + active_iteration = rule_builder.IterationFactory.build( + default_comms_routing="default_action_code", + actions_mapper=ActionsMapperFactory.build( + root={"default_action_code": DEFAULT_COMMS_DETAIL, "rule_specific_action": BOOK_NBS_COMMS} + ), + iteration_rules=[non_matching_rule], + ) + rule_type = RuleType.redirect + + mock_get_action_rules_components.return_value = ( + (non_matching_rule,), + active_iteration.actions_mapper, + active_iteration.default_comms_routing, + ) + + mock_get_actions_from_comms.side_effect = [ + [ + SuggestedAction( + internal_action_code=InternalActionCode("default_action_code"), + action_type=ActionType(DEFAULT_COMMS_DETAIL.action_type), + action_code=ActionCode(DEFAULT_COMMS_DETAIL.action_code), + action_description=ActionDescription(DEFAULT_COMMS_DETAIL.action_description), + url_link=DEFAULT_COMMS_DETAIL.url_link, + url_label=DEFAULT_COMMS_DETAIL.url_label, + ) + ], + [ + SuggestedAction( + internal_action_code=InternalActionCode("rule_specific_action"), + action_type=ActionType(BOOK_NBS_COMMS.action_type), + action_code=ActionCode(BOOK_NBS_COMMS.action_code), + action_description=ActionDescription(BOOK_NBS_COMMS.action_description), + url_link=BOOK_NBS_COMMS.url_link, + url_label=BOOK_NBS_COMMS.url_label, + ) + ], + ] + + mock_rule_calculator_class.return_value.evaluate_exclusion.return_value = ( + Status.actionable, + Mock(matcher_matched=False), + ) + + matched_action_detail = handler._handle(MOCK_PERSON, active_iteration, rule_type) + + assert_that(len(matched_action_detail.actions), is_(1)) + assert_that(matched_action_detail.actions[0].internal_action_code, is_(InternalActionCode("default_action_code"))) + assert_that(matched_action_detail.rule_priority, is_(None)) + assert_that(matched_action_detail.rule_name, is_(None)) + + mock_get_action_rules_components.assert_called_once_with(active_iteration, rule_type) + assert_that(mock_get_actions_from_comms.call_count, is_(1)) + mock_get_actions_from_comms.assert_called_once_with(active_iteration.actions_mapper, "default_action_code") + mock_rule_calculator_class.assert_called_once_with(person=MOCK_PERSON, rule=non_matching_rule) + + +@patch("eligibility_signposting_api.services.processors.action_rule_handler.RuleCalculator") +@patch.object(ActionRuleHandler, "_get_actions_from_comms") +@patch.object(ActionRuleHandler, "_get_action_rules_components") +def test_handle_multiple_rules_same_priority_all_match( + mock_get_action_rules_components, + mock_get_actions_from_comms, + mock_rule_calculator_class, + handler: ActionRuleHandler, +): + rule1 = rule_builder.ICBRedirectRuleFactory.build(priority=10, comms_routing="action_a", name="RuleA") + rule2 = rule_builder.ICBRedirectRuleFactory.build(priority=10, comms_routing="action_b", name="RuleB") + active_iteration = rule_builder.IterationFactory.build( + default_comms_routing="default_action_code", + actions_mapper=ActionsMapperFactory.build( + root={ + "default_action_code": DEFAULT_COMMS_DETAIL, + "action_a": BOOK_NBS_COMMS, + "action_b": DEFAULT_COMMS_DETAIL, + } + ), + iteration_rules=[rule1, rule2], + ) + + mock_get_action_rules_components.return_value = ( + (rule1, rule2), + active_iteration.actions_mapper, + active_iteration.default_comms_routing, + ) + + mock_get_actions_from_comms.side_effect = [ + [ + SuggestedAction( + internal_action_code=InternalActionCode("default_action_code"), + action_type=ActionType(DEFAULT_COMMS_DETAIL.action_type), + action_code=ActionCode(DEFAULT_COMMS_DETAIL.action_code), + action_description=ActionDescription(DEFAULT_COMMS_DETAIL.action_description), + url_link=DEFAULT_COMMS_DETAIL.url_link, + url_label=DEFAULT_COMMS_DETAIL.url_label, + ) + ], + [ + SuggestedAction( + internal_action_code=InternalActionCode("action_a"), + action_type=ActionType(BOOK_NBS_COMMS.action_type), + action_code=ActionCode(BOOK_NBS_COMMS.action_code), + action_description=ActionDescription(BOOK_NBS_COMMS.action_description), + url_link=BOOK_NBS_COMMS.url_link, + url_label=BOOK_NBS_COMMS.url_label, + ), + SuggestedAction( + internal_action_code=InternalActionCode("action_b"), + action_type=ActionType(DEFAULT_COMMS_DETAIL.action_type), + action_code=ActionCode(DEFAULT_COMMS_DETAIL.action_code), + action_description=ActionDescription(DEFAULT_COMMS_DETAIL.action_description), + url_link=DEFAULT_COMMS_DETAIL.url_link, + url_label=DEFAULT_COMMS_DETAIL.url_label, + ), + ], + ] + + mock_rule_calculator_class.side_effect = [ + Mock(evaluate_exclusion=Mock(return_value=(Status.actionable, Mock(matcher_matched=True)))), + Mock(evaluate_exclusion=Mock(return_value=(Status.actionable, Mock(matcher_matched=True)))), + ] + + matched_action_detail = handler._handle(MOCK_PERSON, active_iteration, RuleType.redirect) + + assert_that(len(matched_action_detail.actions), is_(2)) + assert_that(matched_action_detail.actions[0].internal_action_code, is_(InternalActionCode("action_a"))) + assert_that(matched_action_detail.actions[1].internal_action_code, is_(InternalActionCode("action_b"))) + assert_that(matched_action_detail.rule_priority, is_(RulePriority(10))) + assert_that(matched_action_detail.rule_name, is_(RuleName("RuleA"))) + + assert_that(mock_rule_calculator_class.call_count, is_(2)) + assert_that(mock_get_actions_from_comms.call_count, is_(2)) + mock_get_actions_from_comms.assert_any_call(active_iteration.actions_mapper, "default_action_code") + mock_get_actions_from_comms.assert_any_call(active_iteration.actions_mapper, "action_a") + + +@patch("eligibility_signposting_api.services.processors.action_rule_handler.RuleCalculator") +@patch.object(ActionRuleHandler, "_get_actions_from_comms") +@patch.object(ActionRuleHandler, "_get_action_rules_components") +def test_handle_multiple_rules_same_priority_one_mismatch( + mock_get_action_rules_components, + mock_get_actions_from_comms, + mock_rule_calculator_class, + handler: ActionRuleHandler, +): + rule1 = rule_builder.ICBRedirectRuleFactory.build(priority=10, comms_routing="action_a", name="RuleA") + rule2 = rule_builder.ICBRedirectRuleFactory.build(priority=10, comms_routing="action_b", name="RuleB") + active_iteration = rule_builder.IterationFactory.build( + default_comms_routing="default_action_code", + actions_mapper=ActionsMapperFactory.build( + root={ + "default_action_code": DEFAULT_COMMS_DETAIL, + "action_a": BOOK_NBS_COMMS, + "action_b": DEFAULT_COMMS_DETAIL, + } + ), + iteration_rules=[rule1, rule2], + ) + rule_type = RuleType.redirect + + mock_get_action_rules_components.return_value = ( + (rule1, rule2), + active_iteration.actions_mapper, + active_iteration.default_comms_routing, + ) + + mock_get_actions_from_comms.side_effect = [ + [ + SuggestedAction( + internal_action_code=InternalActionCode("default_action_code"), + action_type=ActionType("DefaultInfoText"), + action_code=ActionCode("DefaultHealthcareProInfo"), + action_description=ActionDescription("Default Speak to your healthcare professional."), + url_link=None, + url_label=None, + ) + ] + ] + + mock_rule_calculator_class.side_effect = [ + Mock(evaluate_exclusion=Mock(return_value=(Status.actionable, Mock(matcher_matched=True)))), + Mock(evaluate_exclusion=Mock(return_value=(Status.actionable, Mock(matcher_matched=False)))), + ] + + matched_action_detail = handler._handle(MOCK_PERSON, active_iteration, rule_type) + + assert_that(len(matched_action_detail.actions), is_(1)) + assert_that(matched_action_detail.actions[0].internal_action_code, is_(InternalActionCode("default_action_code"))) + assert_that(matched_action_detail.rule_priority, is_(None)) + assert_that(matched_action_detail.rule_name, is_(None)) + + mock_get_action_rules_components.assert_called_once_with(active_iteration, rule_type) + assert_that(mock_get_actions_from_comms.call_count, is_(1)) + mock_get_actions_from_comms.assert_called_once_with(active_iteration.actions_mapper, "default_action_code") + assert_that( + mock_rule_calculator_class.call_args_list, + is_([call(person=MOCK_PERSON, rule=rule1), call(person=MOCK_PERSON, rule=rule2)]), + ) + + +@patch("eligibility_signposting_api.services.processors.action_rule_handler.RuleCalculator") +@patch.object(ActionRuleHandler, "_get_actions_from_comms") +@patch.object(ActionRuleHandler, "_get_action_rules_components") +def test_handle_different_priority_rules_highest_priority_wins( + mock_get_action_rules_components, + mock_get_actions_from_comms, + mock_rule_calculator_class, + handler: ActionRuleHandler, +): + lower_priority_rule = rule_builder.ICBRedirectRuleFactory.build( + priority=20, comms_routing="action_low", name="LowP" + ) + higher_priority_rule = rule_builder.ICBRedirectRuleFactory.build( + priority=10, comms_routing="action_high", name="HighP" + ) + active_iteration = rule_builder.IterationFactory.build( + default_comms_routing="default_action_code", + actions_mapper=ActionsMapperFactory.build( + root={ + "default_action_code": DEFAULT_COMMS_DETAIL, + "action_low": DEFAULT_COMMS_DETAIL, + "action_high": BOOK_NBS_COMMS, + } + ), + iteration_rules=[lower_priority_rule, higher_priority_rule], + ) + rule_type = RuleType.redirect + + mock_get_action_rules_components.return_value = ( + (lower_priority_rule, higher_priority_rule), + active_iteration.actions_mapper, + active_iteration.default_comms_routing, + ) + + mock_get_actions_from_comms.side_effect = [ + [ + SuggestedAction( + internal_action_code=InternalActionCode("default_action_code"), + action_type=ActionType("DefaultInfoText"), + action_code=ActionCode("DefaultHealthcareProInfo"), + action_description=ActionDescription("Default Speak to your healthcare professional."), + url_link=None, + url_label=None, + ) + ], + [ + SuggestedAction( + internal_action_code=InternalActionCode("action_high"), + action_type=ActionType("ButtonAuthLink"), + action_code=ActionCode("BookNBS"), + action_description=ActionDescription("Action description"), + url_link=UrlLink(HttpUrl("https://www.nhs.uk/book-rsv")), + url_label=UrlLabel("Continue to booking"), + ) + ], + [ + SuggestedAction( + internal_action_code=InternalActionCode("action_low"), + action_type=ActionType("CareCardWithText"), + action_code=ActionCode("BookLocal"), + action_description=ActionDescription("You can get an RSV vaccination at your GP surgery"), + url_link=None, + url_label=None, + ) + ], + ] + + mock_rule_calculator_class.side_effect = [ + Mock(evaluate_exclusion=Mock(return_value=(Status.actionable, Mock(matcher_matched=True)))), + Mock(evaluate_exclusion=Mock(return_value=(Status.actionable, Mock(matcher_matched=True)))), + ] + + matched_action_detail = handler._handle(MOCK_PERSON, active_iteration, rule_type) + + assert_that(len(matched_action_detail.actions), is_(1)) + assert_that(matched_action_detail.actions[0].internal_action_code, is_(InternalActionCode("action_high"))) + assert_that(matched_action_detail.rule_priority, is_(RulePriority(10))) + assert_that(matched_action_detail.rule_name, is_(RuleName("HighP"))) + + assert_that(mock_rule_calculator_class.call_count, is_(1)) + mock_rule_calculator_class.assert_called_once_with(person=MOCK_PERSON, rule=higher_priority_rule) + assert_that(mock_get_actions_from_comms.call_count, is_(2)) + mock_get_actions_from_comms.assert_any_call(active_iteration.actions_mapper, "default_action_code") + mock_get_actions_from_comms.assert_any_call(active_iteration.actions_mapper, "action_high") + + +def test_handle_no_actions_mapper_entry_for_rule_comms_returns_default(handler: ActionRuleHandler): + matching_rule = rule_builder.ICBRedirectRuleFactory.build( + priority=10, comms_routing="non_existent_action", name="RuleSpecificAction" + ) + active_iteration = rule_builder.IterationFactory.build( + default_comms_routing="default_action_code", + actions_mapper=ActionsMapperFactory.build(root={"default_action_code": DEFAULT_COMMS_DETAIL}), + iteration_rules=[matching_rule], + ) + rule_type = RuleType.redirect + + with ( + patch.object(ActionRuleHandler, "_get_action_rules_components") as mock_get_action_rules_components, + patch.object(ActionRuleHandler, "_get_actions_from_comms") as mock_get_actions_from_comms, + patch( + "eligibility_signposting_api.services.processors.action_rule_handler.RuleCalculator" + ) as mock_rule_calculator_class, + ): + mock_get_action_rules_components.return_value = ( + (matching_rule,), + active_iteration.actions_mapper, + active_iteration.default_comms_routing, + ) + mock_get_actions_from_comms.side_effect = [ + [ + SuggestedAction( + internal_action_code=InternalActionCode("default_action_code"), + action_type=ActionType("DefaultInfoText"), + action_code=ActionCode("DefaultHealthcareProInfo"), + action_description=ActionDescription("Default Speak to your healthcare professional."), + url_link=None, + url_label=None, + ) + ], + None, + ] + mock_rule_calculator_class.return_value.evaluate_exclusion.return_value = ( + Status.actionable, + Mock(matcher_matched=True), + ) + + matched_action_detail = handler._handle(MOCK_PERSON, active_iteration, rule_type) + + assert_that(len(matched_action_detail.actions), is_(1)) + assert_that( + matched_action_detail.actions[0].internal_action_code, is_(InternalActionCode("default_action_code")) + ) + assert_that(matched_action_detail.rule_priority, is_(RulePriority(10))) + assert_that(matched_action_detail.rule_name, is_(RuleName("RuleSpecificAction"))) + + assert_that(mock_get_actions_from_comms.call_count, is_(2)) + mock_get_actions_from_comms.assert_any_call(active_iteration.actions_mapper, "default_action_code") + mock_get_actions_from_comms.assert_any_call(active_iteration.actions_mapper, "non_existent_action") + mock_rule_calculator_class.assert_called_once() + + +def test_handle_no_default_comms_and_no_matching_rule(handler: ActionRuleHandler): + active_iteration = rule_builder.IterationFactory.build( + default_comms_routing="", + actions_mapper=ActionsMapperFactory.build(root={}), + iteration_rules=[rule_builder.ICBRedirectRuleFactory.build(comms_routing="some_action")], + ) + rule_type = RuleType.redirect + + with ( + patch.object(ActionRuleHandler, "_get_action_rules_components") as mock_get_action_rules_components, + patch.object(ActionRuleHandler, "_get_actions_from_comms") as mock_get_actions_from_comms, + patch( + "eligibility_signposting_api.services.processors.action_rule_handler.RuleCalculator" + ) as mock_rule_calculator_class, + ): + mock_get_action_rules_components.return_value = ( + (rule_builder.ICBRedirectRuleFactory.build(comms_routing="some_action"),), + active_iteration.actions_mapper, + None, + ) + mock_get_actions_from_comms.side_effect = [None, None] + mock_rule_calculator_class.return_value.evaluate_exclusion.return_value = ( + Status.actionable, + Mock(matcher_matched=True), + ) + + matched_action_detail = handler._handle(MOCK_PERSON, active_iteration, rule_type) + + assert_that(matched_action_detail.actions, is_(None)) + assert_that(matched_action_detail.rule_priority, is_(RulePriority(20))) + assert_that(matched_action_detail.rule_name, is_(RuleName("In QE1"))) + + assert_that(mock_get_actions_from_comms.call_count, is_(2)) + mock_get_actions_from_comms.assert_any_call(active_iteration.actions_mapper, None) + mock_get_actions_from_comms.assert_any_call(active_iteration.actions_mapper, "some_action") + mock_rule_calculator_class.assert_called_once() + + +@patch.object(ActionRuleHandler, "_handle") +def test_handle_when_active_iteration_present_and_include_actions_is_true(mock_handle, handler: ActionRuleHandler): + mock_handle.side_effect = [MatchedActionDetail()] + + handler.get_actions( + MOCK_PERSON, IterationFactory.build(), IterationResult(Status.actionable, [], []), include_actions_flag=True + ) + + assert_that(mock_handle.call_count, is_(1)) + + +@patch.object(ActionRuleHandler, "_handle") +def test_handle_when_active_iteration_absent_and_include_actions_is_true(mock_handle, handler: ActionRuleHandler): + mock_handle.side_effect = [MatchedActionDetail()] + + handler.get_actions(MOCK_PERSON, None, IterationResult(Status.actionable, [], []), include_actions_flag=True) + + assert_that(mock_handle.call_count, is_(0)) + + +@patch.object(ActionRuleHandler, "_handle") +def test_handle_is_not_called_when_include_actions_is_false(mock_handle, handler: ActionRuleHandler): + mock_handle.side_effect = [MatchedActionDetail()] + + handler.get_actions( + MOCK_PERSON, IterationFactory.build(), IterationResult(Status.actionable, [], []), include_actions_flag=False + ) + + assert_that(mock_handle.call_count, is_(0)) diff --git a/tests/unit/services/processors/test_campaign_evaluator.py b/tests/unit/services/processors/test_campaign_evaluator.py new file mode 100644 index 000000000..a0b59a53a --- /dev/null +++ b/tests/unit/services/processors/test_campaign_evaluator.py @@ -0,0 +1,117 @@ +import datetime + +import pytest +from hamcrest import assert_that, is_ + +from eligibility_signposting_api.model.campaign_config import CampaignID +from eligibility_signposting_api.services.processors.campaign_evaluator import CampaignEvaluator +from tests.fixtures.builders.model import rule + + +@pytest.fixture +def campaign_evaluator(): + return CampaignEvaluator() + + +@pytest.mark.parametrize( + ("campaign_target", "campaign_type", "conditions_filter", "category_filter", "expected_result"), + [ + ("RSV", "V", ["RSV"], "VACCINATIONS", [("RSV", "V")]), + ("RSV", "V", ["COVID"], "VACCINATIONS", []), + ("RSV", "S", ["RSV"], "ALL", [("RSV", "S")]), + ("RSV", "S", ["ALL"], "ALL", [("RSV", "S")]), + ("RSV", "S", ["RSV"], "VACCINATIONS", []), + ("RSV", "V", ["RSV"], "ALL", [("RSV", "V")]), + ("FLU", "V", ["COVID", "RSV"], "ALL", []), + ("FLU", "S", ["ALL"], "ALL", [("FLU", "S")]), + ("COVID", "V", ["UNKNOWN"], "VACCINATIONS", []), + ("FLU", "V", ["COVID", "FLU"], "VACCINATIONS", [("FLU", "V")]), + ], +) +def test_campaigns_grouped_by_condition_name_filters_correctly( # noqa: PLR0913 + campaign_evaluator, campaign_target, campaign_type, conditions_filter, category_filter, expected_result +): + campaign = rule.CampaignConfigFactory.build(target=campaign_target, type=campaign_type) + + result = campaign_evaluator.get_requested_grouped_campaigns([campaign], conditions_filter, category_filter) + assert_that([(str(name), group[0].type) for name, group in result], is_(expected_result)) + + +def test_campaigns_grouped_by_condition_name_with_no_campaigns(campaign_evaluator): + result = campaign_evaluator.get_requested_grouped_campaigns([], ["RSV"], "VACCINATIONS") + assert_that(list(result), is_([])) + + +def test_campaigns_grouped_by_condition_name_with_no_active_campaigns(campaign_evaluator): + campaign = rule.CampaignConfigFactory.build( + target="RSV", type="V", start_date=datetime.date(2025, 4, 20), end_date=datetime.date(2025, 4, 21) + ) + + result = campaign_evaluator.get_requested_grouped_campaigns([campaign], ["RSV"], "VACCINATIONS") + assert_that(list(result), is_([])) + + +@pytest.mark.parametrize( + ("category_filter", "campaign_type", "expected_count"), + [ + ("SCREENING", "S", 1), + ("SCREENING", "V", 0), + ("INVALID_CATEGORY", "S", 0), + ], +) +def test_campaigns_grouped_by_condition_name_with_various_categories( + campaign_evaluator, category_filter, campaign_type, expected_count +): + campaign = rule.CampaignConfigFactory.build(target="COVID", type=campaign_type) + result = list(campaign_evaluator.get_requested_grouped_campaigns([campaign], ["COVID"], category_filter)) + assert_that(len(result), is_(expected_count)) + if expected_count > 0: + assert_that(str(result[0][0]), is_("COVID")) + + +def test_campaigns_grouped_by_condition_name_with_empty_conditions_filter(campaign_evaluator): + campaign = rule.CampaignConfigFactory.build(target="RSV", type="V") + result = campaign_evaluator.get_requested_grouped_campaigns([campaign], [], "VACCINATIONS") + assert_that(list(result), is_([])) + + +def test_campaigns_grouped_by_condition_name_groups_multiple_campaigns_for_same_target(campaign_evaluator): + campaign1 = rule.CampaignConfigFactory.build(target="COVID", type="V", id="C1") + campaign2 = rule.CampaignConfigFactory.build(target="COVID", type="V", id="C2") + campaign3 = rule.CampaignConfigFactory.build(target="FLU", type="V", id="F1") + inactive_campaign = rule.CampaignConfigFactory.build( + target="COVID", type="V", id="C3", start_date=datetime.date(2025, 4, 20), end_date=datetime.date(2025, 4, 21) + ) + + all_campaigns = [campaign1, campaign2, campaign3, inactive_campaign] + result = list(campaign_evaluator.get_requested_grouped_campaigns(all_campaigns, ["COVID", "FLU"], "VACCINATIONS")) + + assert_that(len(result), is_(2)) + + result_dict = {str(name): campaigns for name, campaigns in result} + assert_that("COVID" in result_dict) + assert_that("FLU" in result_dict) + + assert_that(len(result_dict["COVID"]), is_(2)) + assert_that({c.id for c in result_dict["COVID"]}, is_({CampaignID("C1"), CampaignID("C2")})) + + assert_that(len(result_dict["FLU"]), is_(1)) + assert_that(result_dict["FLU"][0].id, is_(CampaignID("F1"))) + + +def test_campaign_grouping_is_affected_by_order_for_mixed_types(campaign_evaluator): + campaign_v = rule.CampaignConfigFactory.build(target="RSV", type="V") + campaign_s = rule.CampaignConfigFactory.build(target="RSV", type="S") + + evaluator_s_first = campaign_evaluator + result_s_first = list( + evaluator_s_first.get_requested_grouped_campaigns([campaign_s, campaign_v], ["RSV"], "VACCINATIONS") + ) + assert_that(result_s_first, is_([])) + + evaluator_v_first = campaign_evaluator + result_v_first = list( + evaluator_v_first.get_requested_grouped_campaigns([campaign_v, campaign_s], ["RSV"], "VACCINATIONS") + ) + assert_that(len(result_v_first), is_(1)) + assert_that(len(result_v_first[0][1]), is_(2)) diff --git a/tests/unit/services/processors/test_cohort_handler.py b/tests/unit/services/processors/test_cohort_handler.py new file mode 100644 index 000000000..8eb25ca32 --- /dev/null +++ b/tests/unit/services/processors/test_cohort_handler.py @@ -0,0 +1,122 @@ +from unittest.mock import Mock + +import pytest +from hamcrest import assert_that, has_length, is_ + +from eligibility_signposting_api.model.eligibility_status import CohortGroupResult, Status +from eligibility_signposting_api.model.person import Person +from eligibility_signposting_api.services.processors.cohort_handler import ( + BaseEligibilityHandler, + CohortEligibilityHandler, + FilterRuleHandler, + SuppressionRuleHandler, +) +from eligibility_signposting_api.services.processors.rule_processor import RuleProcessor +from tests.fixtures.builders.model import rule as rule_builder + +MOCK_PERSON = Person([{"ATTRIBUTE_TYPE": "PERSON", "AGE": "30"}]) + + +@pytest.fixture +def mock_rule_processor_for_handlers(): + return Mock(spec=RuleProcessor) + + +@pytest.fixture +def mock_next_handler(): + return Mock(spec=CohortEligibilityHandler) + + +def test_base_eligibility_handler_is_base_eligible(mock_rule_processor_for_handlers, mock_next_handler): + handler = BaseEligibilityHandler(next_handler=mock_next_handler) + cohort = rule_builder.IterationCohortFactory.build(cohort_label="cohort1") + cohort_results = {} + + mock_rule_processor_for_handlers.is_base_eligible.return_value = True + + handler.handle(MOCK_PERSON, cohort, cohort_results, mock_rule_processor_for_handlers) + + mock_rule_processor_for_handlers.is_base_eligible.assert_called_once_with(MOCK_PERSON, cohort) + assert_that(cohort_results, is_({})) + + mock_next_handler.handle.assert_called_once_with( + MOCK_PERSON, cohort, cohort_results, mock_rule_processor_for_handlers + ) + + +def test_base_eligibility_handler_is_not_base_eligible(mock_rule_processor_for_handlers, mock_next_handler): + handler = BaseEligibilityHandler(next_handler=mock_next_handler) + cohort = rule_builder.IterationCohortFactory.build(cohort_label="cohort1", negative_description="Not Base eligible") + cohort_results = {} + + mock_rule_processor_for_handlers.is_base_eligible.return_value = False + + handler.handle(MOCK_PERSON, cohort, cohort_results, mock_rule_processor_for_handlers) + + mock_rule_processor_for_handlers.is_base_eligible.assert_called_once_with(MOCK_PERSON, cohort) + assert_that(cohort_results, has_length(1)) + assert_that(cohort_results["cohort1"].status, is_(Status.not_eligible)) + assert_that(cohort_results["cohort1"].description, is_("Not Base eligible")) + mock_next_handler.handle.assert_not_called() + + +def test_filter_rule_handler_is_eligible(mock_rule_processor_for_handlers, mock_next_handler): + cohort = rule_builder.IterationCohortFactory.build(cohort_label="cohort1") + cohort_results = {} + filter_rules = [Mock()] + handler = FilterRuleHandler(next_handler=mock_next_handler, filter_rules=filter_rules) + + mock_rule_processor_for_handlers.is_eligible.return_value = True + + handler.handle(MOCK_PERSON, cohort, cohort_results, mock_rule_processor_for_handlers) + + mock_rule_processor_for_handlers.is_eligible.assert_called_once_with( + MOCK_PERSON, cohort, cohort_results, filter_rules + ) + assert_that(cohort_results, is_({})) + + mock_next_handler.handle.assert_called_once_with( + MOCK_PERSON, cohort, cohort_results, mock_rule_processor_for_handlers + ) + + +def test_filter_rule_handler_is_not_eligible(mock_rule_processor_for_handlers, mock_next_handler): + filter_rules = [Mock()] + handler = FilterRuleHandler(next_handler=mock_next_handler, filter_rules=filter_rules) + cohort = rule_builder.IterationCohortFactory.build(cohort_label="cohort1", negative_description="Not Eligible") + cohort_results = {} + + mock_rule_processor_for_handlers.is_eligible.side_effect = ( + lambda p, c, cr, fr: cr.update( # noqa: ARG005 + {c.cohort_label: CohortGroupResult(c.cohort_group, Status.not_eligible, [], c.negative_description, [])} + ) + or False + ) + + handler.handle(MOCK_PERSON, cohort, cohort_results, mock_rule_processor_for_handlers) + + mock_rule_processor_for_handlers.is_eligible.assert_called_once_with( + MOCK_PERSON, cohort, cohort_results, filter_rules + ) + assert_that(cohort_results, has_length(1)) + assert_that(cohort_results["cohort1"].status, is_(Status.not_eligible)) + mock_next_handler.handle.assert_not_called() + + +def test_suppression_rule_handler_is_actionable(mock_rule_processor_for_handlers): + suppression_rules = [Mock()] + handler = SuppressionRuleHandler(suppression_rules=suppression_rules) + cohort = rule_builder.IterationCohortFactory.build(cohort_label="cohort1", positive_description="Actionable") + cohort_results = {} + + mock_rule_processor_for_handlers.is_actionable.side_effect = lambda p, c, cr, sr: cr.update( # noqa: ARG005 + {c.cohort_label: CohortGroupResult(c.cohort_group, Status.actionable, [], c.positive_description, [])} + ) + + handler.handle(MOCK_PERSON, cohort, cohort_results, mock_rule_processor_for_handlers) + + mock_rule_processor_for_handlers.is_actionable.assert_called_once_with( + MOCK_PERSON, cohort, cohort_results, suppression_rules + ) + assert_that(cohort_results, has_length(1)) + assert_that(cohort_results["cohort1"].status, is_(Status.actionable)) diff --git a/tests/unit/services/processors/test_person_data_reader.py b/tests/unit/services/processors/test_person_data_reader.py new file mode 100644 index 000000000..6219cd8a3 --- /dev/null +++ b/tests/unit/services/processors/test_person_data_reader.py @@ -0,0 +1,102 @@ +import pytest +from hamcrest import assert_that, is_ + +from eligibility_signposting_api.model.person import Person +from eligibility_signposting_api.services.processors.person_data_reader import PersonDataReader + + +@pytest.fixture +def person_data_reader(): + return PersonDataReader() + + +def test_get_person_cohorts_empty_data(person_data_reader): + result = person_data_reader.get_person_cohorts(Person([])) + assert_that(result, is_(set())) + + +def test_get_person_cohorts_no_cohorts_attribute_type(person_data_reader): + no_cohorts_type = Person( + [ + {"ATTRIBUTE_TYPE": "NAME", "VALUE": "John Doe"}, + {"ATTRIBUTE_TYPE": "AGE", "VALUE": 30}, + ] + ) + result = person_data_reader.get_person_cohorts(no_cohorts_type) + assert_that(result, is_(set())) + + +def test_get_person_cohorts_no_cohort_map_key(person_data_reader): + no_cohorts_map = Person( + [ + {"ATTRIBUTE_TYPE": "COHORTS", "OTHER_FIELD": "value"}, + ] + ) + result = person_data_reader.get_person_cohorts(no_cohorts_map) + assert_that(result, is_(set())) + + +def test_get_person_cohorts_single_cohort(person_data_reader): + single_cohorts = Person( + [ + { + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [{"COHORT_LABEL": "flu_65+_autumnwinter2023", "DATE_JOINED": "20231020"}], + }, + {"ATTRIBUTE_TYPE": "NAME", "VALUE": "Jane Smith"}, + ] + ) + result = person_data_reader.get_person_cohorts(single_cohorts) + assert_that(result, is_({"flu_65+_autumnwinter2023"})) + + +def test_get_person_cohorts_multiple_cohorts(person_data_reader): + multiple_cohorts = Person( + [ + { + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + {"COHORT_LABEL": "COHORT_B", "DATE_JOINED": "20231020"}, + {"COHORT_LABEL": "COHORT_C", "DATE_JOINED": "20241020"}, + ], + }, + {"ATTRIBUTE_TYPE": "AGE", "VALUE": 45}, + ] + ) + result = person_data_reader.get_person_cohorts(multiple_cohorts) + assert_that(result, is_({"COHORT_B", "COHORT_C"})) + + +def test_get_person_cohorts_mixed_data(person_data_reader): + mixed_data = Person( + [ + { + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + {"COHORT_LABEL": "COHORT_D", "DATE_JOINED": "20231020"}, + {"COHORT_LABEL": "COHORT_E", "DATE_JOINED": "20241020"}, + ], + }, + {"ATTRIBUTE_TYPE": "NAME", "VALUE": "Alice"}, + {"ATTRIBUTE_TYPE": "ADDRESS", "VALUE": "123 Main St"}, + ] + ) + + result = person_data_reader.get_person_cohorts(mixed_data) + assert_that(result, is_({"COHORT_D", "COHORT_E"})) + + +def test_get_person_cohorts_with_other_attribute_types_present(person_data_reader): + data = Person( + [ + { + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [{"COHORT_LABEL": "COHORT_F", "DATE_JOINED": "20231020"}], + }, + {"ATTRIBUTE_TYPE": "NAME", "VALUE": "Charlie"}, + {"ATTRIBUTE_TYPE": "AGE", "VALUE": 25}, + ] + ) + + result = person_data_reader.get_person_cohorts(data) + assert_that(result, is_({"COHORT_F"})) diff --git a/tests/unit/services/processors/test_rule_processor.py b/tests/unit/services/processors/test_rule_processor.py new file mode 100644 index 000000000..592322770 --- /dev/null +++ b/tests/unit/services/processors/test_rule_processor.py @@ -0,0 +1,657 @@ +from unittest.mock import Mock, patch + +import pytest +from hamcrest import assert_that, empty, is_ + +from eligibility_signposting_api.model.campaign_config import CohortLabel, RuleType +from eligibility_signposting_api.model.eligibility_status import CohortGroupResult, Reason, RuleName, Status +from eligibility_signposting_api.model.person import Person +from eligibility_signposting_api.services.processors.person_data_reader import PersonDataReader +from eligibility_signposting_api.services.processors.rule_processor import RuleProcessor +from tests.fixtures.builders.model import rule as rule_builder +from tests.fixtures.builders.model.eligibility import ReasonFactory + + +@pytest.fixture +def mock_person_data_reader(): + return Mock(spec=PersonDataReader) + + +@pytest.fixture +def rule_processor(mock_person_data_reader): + return RuleProcessor(mock_person_data_reader) + + +MOCK_PERSON_DATA = Person([{"ATTRIBUTE_TYPE": "PERSON", "AGE": "30"}]) + + +def test_get_exclusion_rules_no_rules(): + cohort = rule_builder.IterationCohortFactory.build(cohort_label="COHORT_A") + rules_to_filter = [] + result = list(RuleProcessor.get_exclusion_rules(cohort, rules_to_filter)) + assert_that(result, is_([])) + + +def test_get_exclusion_rules_general_rule(): + cohort = rule_builder.IterationCohortFactory.build(cohort_label="COHORT_A") + no_cohort_label_rule = rule_builder.IterationRuleFactory.build(cohort_label=None) + rules_to_filter = [no_cohort_label_rule] + result = list(RuleProcessor.get_exclusion_rules(cohort, rules_to_filter)) + assert_that(result, is_([no_cohort_label_rule])) + + +def test_get_exclusion_rules_matching_cohort_label(): + cohort = rule_builder.IterationCohortFactory.build(cohort_label="COHORT_A") + matching_rule = rule_builder.IterationRuleFactory.build(cohort_label="COHORT_A") + rules_to_filter = [matching_rule] + result = list(RuleProcessor.get_exclusion_rules(cohort, rules_to_filter)) + assert_that(result, is_([matching_rule])) + + +def test_get_exclusion_rules_non_matching_cohort_label(): + cohort = rule_builder.IterationCohortFactory.build(cohort_label="COHORT_A") + non_matching_rule = rule_builder.IterationRuleFactory.build(cohort_label="COHORT_B") + rules_to_filter = [non_matching_rule] + result = list(RuleProcessor.get_exclusion_rules(cohort, rules_to_filter)) + assert_that(result, is_([])) + + +def test_get_exclusion_rules_matching_from_list_cohort_label(): + cohort = rule_builder.IterationCohortFactory.build(cohort_label="COHORT_A") + rule1 = rule_builder.IterationRuleFactory.build(cohort_label="COHORT_A") + rule2 = rule_builder.IterationRuleFactory.build(cohort_label="COHORT_B") + rules_to_filter = [rule1, rule2] + result = list(RuleProcessor.get_exclusion_rules(cohort, rules_to_filter)) + assert_that(result, is_([rule1])) + + +def test_get_exclusion_rules_mixed_rules(): + cohort = rule_builder.IterationCohortFactory.build(cohort_label="COHORT_A") + no_cohort_label_rule = rule_builder.IterationRuleFactory.build(cohort_label=None, name="General") + matching_rule = rule_builder.IterationRuleFactory.build(cohort_label="COHORT_A", name="Matching") + non_matching_rule = rule_builder.IterationRuleFactory.build(cohort_label="COHORT_B", name="NonMatching") + + rules_to_filter = [no_cohort_label_rule, matching_rule, non_matching_rule] + result = list(RuleProcessor.get_exclusion_rules(cohort, rules_to_filter)) + assert_that({r.name for r in result}, is_({"General", "Matching"})) + + +@patch("eligibility_signposting_api.services.processors.rule_processor.RuleCalculator") +def test_evaluate_rules_priority_group_all_actionable(mock_rule_calculator_class, rule_processor): + mock_rule_calculator_class.return_value.evaluate_exclusion.return_value = ( + Status.actionable, + Mock(spec=Reason, matcher_matched=False), + ) + + rule1 = rule_builder.IterationRuleFactory.build(priority=1, type=RuleType.filter) + rule2 = rule_builder.IterationRuleFactory.build(priority=1, type=RuleType.filter) + rules_group = iter([rule1, rule2]) + + status, reasons, is_rule_stop = rule_processor.evaluate_rules_priority_group(MOCK_PERSON_DATA, rules_group) + + assert_that(status, is_(Status.actionable)) + assert_that(reasons, is_([])) + assert_that(is_rule_stop, is_(False)) + assert_that(mock_rule_calculator_class.call_count, is_(2)) + + +@patch("eligibility_signposting_api.services.processors.rule_processor.RuleCalculator") +def test_evaluate_rules_priority_group_one_not_eligible(mock_rule_calculator_class, rule_processor): + mock_rule_calculator_class.side_effect = [ + Mock(evaluate_exclusion=Mock(return_value=(Status.actionable, Mock(spec=Reason, matcher_matched=False)))), + Mock( + evaluate_exclusion=Mock( + return_value=( + Status.not_eligible, + ReasonFactory.build(rule_name="ExclusionReason", matcher_matched=True), + ) + ) + ), + ] + + rule1 = rule_builder.IterationRuleFactory.build(priority=1, type=RuleType.filter, name="Rule1") + rule2 = rule_builder.IterationRuleFactory.build(priority=1, type=RuleType.filter, name="Rule2") + rules_group = iter([rule1, rule2]) + + status, reasons, is_rule_stop = rule_processor.evaluate_rules_priority_group(MOCK_PERSON_DATA, rules_group) + + assert_that(status, is_(Status.actionable)) + assert_that(len(reasons), is_(1)) + assert_that(reasons[0].rule_name, is_(RuleName("ExclusionReason"))) + assert_that(is_rule_stop, is_(False)) + assert_that(mock_rule_calculator_class.call_count, is_(2)) + + +@patch("eligibility_signposting_api.services.processors.rule_processor.RuleCalculator") +def test_evaluate_rules_priority_group_with_rule_stop(mock_rule_calculator_class, rule_processor): + mock_rule_calculator_class.side_effect = [ + Mock(evaluate_exclusion=Mock(return_value=(Status.actionable, Mock(spec=Reason, matcher_matched=False)))), + Mock( + evaluate_exclusion=Mock( + return_value=(Status.not_eligible, ReasonFactory.build(rule_name="StopReason", matcher_matched=True)) + ) + ), + ] + + rule1 = rule_builder.IterationRuleFactory.build(priority=1, type=RuleType.suppression, rule_stop=False) + rule2 = rule_builder.IterationRuleFactory.build(priority=1, type=RuleType.suppression, rule_stop=True) + rules_group = iter([rule1, rule2]) + + status, reasons, is_rule_stop = rule_processor.evaluate_rules_priority_group(MOCK_PERSON_DATA, rules_group) + + assert_that(status, is_(Status.actionable)) + assert_that(len(reasons), is_(1)) + assert_that(is_rule_stop, is_(True)) + + +@patch.object(RuleProcessor, "evaluate_rules_priority_group") +def test_general_suppression_rule_should_not_evaluate_in_isolation_without_matching_specific_rule( + mock_evaluate_rules_priority_group, + rule_processor, +): + # Person is in COHORT_B + cohort = rule_builder.IterationCohortFactory.build(cohort_label="COHORT_B", positive_description="Eligible") + cohort_results = {} + + # Rule 1: Non-matching rule cohort-specific to COHORT_A โ€” should not be evaluated + rule_specific = rule_builder.IterationRuleFactory.build( + priority=510, type=RuleType.suppression, cohort_label="COHORT_A", name="SPECIFIC_RULE" + ) + + # Rule 2: Matching general rule of the same priority as cohort-specific rule + # - should also not be evaluated + rule_general = rule_builder.IterationRuleFactory.build( + priority=510, type=RuleType.suppression, cohort_label=None, name="GENERAL_RULE" + ) + + suppression_rules = [rule_specific, rule_general] + + # Act + rule_processor.is_actionable(MOCK_PERSON_DATA, cohort, cohort_results, suppression_rules) + + # None of the rules should be evaluated + mock_evaluate_rules_priority_group.assert_not_called() + # Cohort remains actionable + assert_that(cohort_results["COHORT_B"].status, is_(Status.actionable)) + + +@patch.object(RuleProcessor, "evaluate_rules_priority_group") +def test_general_filter_rule_should_not_evaluate_in_isolation_without_matching_specific_rule( + mock_evaluate_rules_priority_group, + rule_processor, +): + # Person is in COHORT_B + cohort = rule_builder.IterationCohortFactory.build(cohort_label="COHORT_B", positive_description="Eligible") + cohort_results = {} + + # Rule 1: Non-matching rule cohort-specific to COHORT_A โ€” should not be evaluated + rule_specific = rule_builder.IterationRuleFactory.build( + priority=510, type=RuleType.filter, cohort_label="COHORT_A", name="SPECIFIC_RULE" + ) + + # Rule 2: Matching general rule of the same priority as cohort-specific rule + # - should also not be evaluated + rule_general = rule_builder.IterationRuleFactory.build( + priority=510, type=RuleType.filter, cohort_label=None, name="GENERAL_RULE" + ) + + filter_rules = [rule_specific, rule_general] + + # Act + rule_processor.is_eligible(MOCK_PERSON_DATA, cohort, cohort_results, filter_rules) + + # None of the rules should be evaluated + mock_evaluate_rules_priority_group.assert_not_called() + + +@patch.object(RuleProcessor, "evaluate_rules_priority_group") +@patch.object(RuleProcessor, "_should_skip_rule_group", return_value=False) +def test_is_eligible_by_filter_rules_eligible( + mock_should_skip_rule_group, mock_evaluate_rules_priority_group, rule_processor +): + cohort = rule_builder.IterationCohortFactory.build(cohort_label="COHORT_A") + cohort_results = {} + filter_rule = rule_builder.IterationRuleFactory.build(priority=1, type=RuleType.filter) + filter_rules = [filter_rule] + + mock_evaluate_rules_priority_group.return_value = (Status.actionable, [], False) + + is_eligible = rule_processor.is_eligible(MOCK_PERSON_DATA, cohort, cohort_results, filter_rules) + + assert_that(is_eligible, is_(True)) + assert_that(cohort_results, is_({})) + mock_should_skip_rule_group.assert_called_once_with(cohort, filter_rules) + mock_evaluate_rules_priority_group.assert_called_once() + + +@patch.object(RuleProcessor, "evaluate_rules_priority_group") +@patch.object(RuleProcessor, "_should_skip_rule_group", return_value=False) +def test_is_eligible_by_filter_rules_not_eligible( + mock_should_skip_rule_group, mock_evaluate_rules_priority_group, rule_processor +): + cohort = rule_builder.IterationCohortFactory.build(cohort_label="COHORT_A", negative_description="Not Eligible") + cohort_results = {} + filter_rule = rule_builder.IterationRuleFactory.build(priority=1, type=RuleType.filter, name="F1") + filter_rules = [filter_rule] + mock_reason = ReasonFactory.build(rule_name="F1_Reason") + + mock_evaluate_rules_priority_group.return_value = (Status.not_eligible, [mock_reason], False) + + is_eligible = rule_processor.is_eligible(MOCK_PERSON_DATA, cohort, cohort_results, filter_rules) + + assert_that(is_eligible, is_(False)) + assert_that(len(cohort_results), is_(1)) + assert_that(cohort_results["COHORT_A"].status, is_(Status.not_eligible)) + assert_that(cohort_results["COHORT_A"].description, is_("Not Eligible")) + assert_that(cohort_results["COHORT_A"].audit_rules, is_([mock_reason])) + mock_should_skip_rule_group.assert_called_once_with(cohort, filter_rules) + mock_evaluate_rules_priority_group.assert_called_once() + + +@patch.object(RuleProcessor, "evaluate_rules_priority_group") +@patch.object(RuleProcessor, "_should_skip_rule_group", return_value=False) +def test_evaluate_suppression_rules_actionable( + mock_should_skip_rule_group, mock_evaluate_rules_priority_group, rule_processor +): + cohort = rule_builder.IterationCohortFactory.build(cohort_label="COHORT_A", positive_description="Actionable") + cohort_results = {} + suppression_rule = rule_builder.IterationRuleFactory.build(priority=1, type=RuleType.suppression) + suppression_rules = [suppression_rule] + + mock_evaluate_rules_priority_group.return_value = (Status.actionable, [], False) + + rule_processor.is_actionable(MOCK_PERSON_DATA, cohort, cohort_results, suppression_rules) + + assert_that(len(cohort_results), is_(1)) + assert_that(cohort_results["COHORT_A"].status, is_(Status.actionable)) + assert_that(cohort_results["COHORT_A"].description, is_("Actionable")) + assert_that(cohort_results["COHORT_A"].reasons, is_([])) + assert_that(cohort_results["COHORT_A"].audit_rules, is_([])) + mock_should_skip_rule_group.assert_called_once_with(cohort, suppression_rules) + mock_evaluate_rules_priority_group.assert_called_once() + + +@patch.object(RuleProcessor, "evaluate_rules_priority_group") +@patch.object(RuleProcessor, "_should_skip_rule_group", return_value=False) +def test_evaluate_suppression_rules_not_actionable( + mock_should_skip_rule_group, mock_evaluate_rules_priority_group, rule_processor +): + cohort = rule_builder.IterationCohortFactory.build( + cohort_label="COHORT_A", positive_description="Positive Description" + ) + cohort_results = {} + suppression_rule = rule_builder.IterationRuleFactory.build(priority=1, type=RuleType.suppression, name="S1") + suppression_rules = [suppression_rule] + mock_reason = ReasonFactory.build(rule_name="S1_Reason") + + mock_evaluate_rules_priority_group.return_value = (Status.not_actionable, [mock_reason], False) + + rule_processor.is_actionable(MOCK_PERSON_DATA, cohort, cohort_results, suppression_rules) + + assert_that(len(cohort_results), is_(1)) + assert_that(cohort_results["COHORT_A"].status, is_(Status.not_actionable)) + assert_that(cohort_results["COHORT_A"].description, is_("Positive Description")) + assert_that(cohort_results["COHORT_A"].reasons, is_([mock_reason])) + assert_that(cohort_results["COHORT_A"].audit_rules, is_([mock_reason])) + mock_should_skip_rule_group.assert_called_once_with(cohort, suppression_rules) + mock_evaluate_rules_priority_group.assert_called_once() + + +@patch.object(RuleProcessor, "evaluate_rules_priority_group") +@patch.object(RuleProcessor, "_should_skip_rule_group", return_value=False) +def test_evaluate_suppression_rules_stops_on_rule_stop( + mock_should_skip_rule_group, mock_evaluate_rules_priority_group, rule_processor +): + cohort = rule_builder.IterationCohortFactory.build(cohort_label="COHORT_A") + cohort_results = {} + suppression_rule_p1 = rule_builder.IterationRuleFactory.build( + priority=1, type=RuleType.suppression, rule_stop=True, name="S1" + ) + suppression_rule_p2 = rule_builder.IterationRuleFactory.build(priority=2, type=RuleType.suppression, name="S2") + suppression_rules = [suppression_rule_p1, suppression_rule_p2] + + mock_reason_p1 = ReasonFactory.build(rule_name="S1_Reason") + mock_reason_p2 = ReasonFactory.build(rule_name="S2_Reason") + + mock_evaluate_rules_priority_group.side_effect = [ + (Status.not_actionable, [mock_reason_p1], True), + (Status.not_actionable, [mock_reason_p2], False), + ] + + rule_processor.is_actionable(MOCK_PERSON_DATA, cohort, cohort_results, suppression_rules) + + assert_that(len(cohort_results), is_(1)) + assert_that(cohort_results["COHORT_A"].status, is_(Status.not_actionable)) + assert_that(cohort_results["COHORT_A"].reasons, is_([mock_reason_p1])) + assert_that(cohort_results["COHORT_A"].audit_rules, is_([mock_reason_p1])) + assert_that(mock_evaluate_rules_priority_group.call_count, is_(1)) + mock_should_skip_rule_group.assert_called_once_with(cohort, [suppression_rule_p1]) + + +@patch.object(RuleProcessor, "evaluate_rules_priority_group") +@patch.object(RuleProcessor, "_should_skip_rule_group", return_value=False) +def test_evaluate_suppression_rules_does_not_stop_on_rule_stop_when_status_is_actionable( + mock_should_skip_rule_group, mock_evaluate_rules_priority_group, rule_processor +): + cohort = rule_builder.IterationCohortFactory.build(cohort_label="COHORT_A") + cohort_results = {} + suppression_rule_p1 = rule_builder.IterationRuleFactory.build( + priority=1, type=RuleType.suppression, rule_stop=True, name="S1" + ) + suppression_rule_p2 = rule_builder.IterationRuleFactory.build(priority=2, type=RuleType.suppression, name="S2") + suppression_rules = [suppression_rule_p1, suppression_rule_p2] + + mock_reason_p1 = ReasonFactory.build(rule_name="S1_Reason") + mock_reason_p2 = ReasonFactory.build(rule_name="S2_Reason") + + mock_evaluate_rules_priority_group.side_effect = [ + (Status.actionable, [mock_reason_p1], True), + (Status.not_actionable, [mock_reason_p2], False), + ] + + rule_processor.is_actionable(MOCK_PERSON_DATA, cohort, cohort_results, suppression_rules) + + assert_that(len(cohort_results), is_(1)) + assert_that(cohort_results["COHORT_A"].status, is_(Status.not_actionable)) + assert_that(cohort_results["COHORT_A"].reasons, is_([mock_reason_p2])) + assert_that(cohort_results["COHORT_A"].audit_rules, is_([mock_reason_p2])) + + assert_that(mock_evaluate_rules_priority_group.call_count, is_(2)) + assert_that(mock_should_skip_rule_group.call_count, is_(2)) + + +def test_is_base_eligible(mock_person_data_reader): + person = Person( + [ + {"ATTRIBUTE_TYPE": "PERSON", "AGE": "30"}, + { + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + {"COHORT_LABEL": "COHORT_A"}, + {"COHORT_LABEL": "COHORT_C"}, + ], + }, + ] + ) + + rule_processor = RuleProcessor(mock_person_data_reader) + mock_person_data_reader.get_person_cohorts.return_value = {"COHORT_A", "COHORT_C"} + + cohort = rule_builder.IterationCohortFactory.build(cohort_label="COHORT_A") + + assert_that(rule_processor.is_base_eligible(person, cohort), is_(True)) + mock_person_data_reader.get_person_cohorts.assert_called_once_with(person) + + +def test_is_not_base_eligible(mock_person_data_reader): + person = Person( + [ + {"ATTRIBUTE_TYPE": "PERSON", "AGE": "30"}, + { + "ATTRIBUTE_TYPE": "COHORTS", + "COHORT_MEMBERSHIPS": [ + {"COHORT_LABEL": "COHORT_C"}, + ], + }, + ] + ) + + rule_processor = RuleProcessor(mock_person_data_reader) + mock_person_data_reader.get_person_cohorts.return_value = {"COHORT_C"} + + cohort = rule_builder.IterationCohortFactory.build(cohort_label="COHORT_A") + + assert_that(rule_processor.is_base_eligible(person, cohort), is_(False)) + mock_person_data_reader.get_person_cohorts.assert_called_once_with(person) + + +def test_rules_get_group_by_types_of_rules(rule_processor): + active_iteration = rule_builder.IterationFactory.build() + iteration_rules = active_iteration.iteration_rules + iteration_rules.append(rule_builder.IterationRuleFactory.build()) + + iteration_rules[0].type = RuleType.filter + iteration_rules[1].type = RuleType.suppression + iteration_rules[2].type = RuleType.filter + + rules_by_type = rule_processor.get_rules_by_type(active_iteration) + + assert_that(len(rules_by_type), is_(2)) + + assert_that(rules_by_type[0][0].type, is_(RuleType.filter)) + assert_that(rules_by_type[0][1].type, is_(RuleType.filter)) + assert_that(rules_by_type[1][0].type, is_(RuleType.suppression)) + + +@patch.object(RuleProcessor, "evaluate_rules_priority_group") +@patch.object(RuleProcessor, "_should_skip_rule_group", return_value=False) +def test_is_eligible_by_filter_rules(mock_should_skip_rule_group, mock_evaluate_rules_priority_group, rule_processor): + cohort = rule_builder.IterationCohortFactory.build(cohort_label="COHORT_A") + cohort_results = {} + filter_rule = rule_builder.IterationRuleFactory.build(priority=1, type=RuleType.filter) + filter_rules = [filter_rule] + + mock_evaluate_rules_priority_group.return_value = (Status.actionable, [], False) + + is_eligible = rule_processor.is_eligible(MOCK_PERSON_DATA, cohort, cohort_results, filter_rules) + + assert_that(is_eligible, is_(True)) + assert_that(cohort_results, is_({})) + mock_should_skip_rule_group.assert_called_once_with(cohort, filter_rules) + mock_evaluate_rules_priority_group.assert_called_once() + + +@patch.object(RuleProcessor, "evaluate_rules_priority_group") +@patch.object(RuleProcessor, "_should_skip_rule_group", return_value=False) +def test_is_not_eligible_by_filter_rules( + mock_should_skip_rule_group, mock_evaluate_rules_priority_group, rule_processor +): + cohort = rule_builder.IterationCohortFactory.build(cohort_label="COHORT_A", negative_description="Not Eligible") + cohort_results = {} + filter_rule = rule_builder.IterationRuleFactory.build(priority=1, type=RuleType.filter, name="F1") + filter_rules = [filter_rule] + mock_reason = ReasonFactory.build(rule_name="F1_Reason") + + def mock_evaluate_side_effect(person, rules_group): # noqa: ARG001 + cohort_results[cohort.cohort_label] = CohortGroupResult( + cohort.cohort_group, + Status.not_eligible, + [], + cohort.negative_description, + [mock_reason], + ) + return Status.not_eligible, [mock_reason], False + + mock_evaluate_rules_priority_group.side_effect = mock_evaluate_side_effect + + is_eligible = rule_processor.is_eligible(MOCK_PERSON_DATA, cohort, cohort_results, filter_rules) + + assert_that(is_eligible, is_(False)) + assert_that(len(cohort_results), is_(1)) + assert_that(cohort_results["COHORT_A"].status, is_(Status.not_eligible)) + assert_that(cohort_results["COHORT_A"].description, is_("Not Eligible")) + assert_that(cohort_results["COHORT_A"].audit_rules, is_([mock_reason])) + mock_should_skip_rule_group.assert_called_once_with(cohort, filter_rules) + mock_evaluate_rules_priority_group.assert_called_once() + + +@patch.object(RuleProcessor, "evaluate_rules_priority_group") +@patch.object(RuleProcessor, "_should_skip_rule_group", return_value=False) +def test_is_actionable_by_suppression_rules( + mock_should_skip_rule_group, mock_evaluate_rules_priority_group, rule_processor +): + cohort = rule_builder.IterationCohortFactory.build(cohort_label="COHORT_A", positive_description="Actionable") + cohort_results = {} + suppression_rule = rule_builder.IterationRuleFactory.build(priority=1, type=RuleType.suppression) + suppression_rules = [suppression_rule] + + mock_evaluate_rules_priority_group.return_value = (Status.actionable, [], False) + + rule_processor.is_actionable(MOCK_PERSON_DATA, cohort, cohort_results, suppression_rules) + + assert_that(len(cohort_results), is_(1)) + assert_that(cohort_results["COHORT_A"].status, is_(Status.actionable)) + assert_that(cohort_results["COHORT_A"].description, is_("Actionable")) + assert_that(cohort_results["COHORT_A"].reasons, is_(empty())) + assert_that(cohort_results["COHORT_A"].audit_rules, is_(empty())) + mock_should_skip_rule_group.assert_called_once_with(cohort, suppression_rules) + mock_evaluate_rules_priority_group.assert_called_once() + + +@patch.object(RuleProcessor, "evaluate_rules_priority_group") +@patch.object(RuleProcessor, "_should_skip_rule_group", return_value=False) +def test_is_not_actionable_by_suppression_rules( + mock_should_skip_rule_group, mock_evaluate_rules_priority_group, rule_processor +): + cohort = rule_builder.IterationCohortFactory.build( + cohort_label="COHORT_A", positive_description="Positive Description" + ) + cohort_results = {} + suppression_rule = rule_builder.IterationRuleFactory.build(priority=1, type=RuleType.suppression, name="S1") + suppression_rules = [suppression_rule] + mock_reason = ReasonFactory.build(rule_name="S1_Reason") + + def mock_evaluate_side_effect(person, rules_group): # noqa: ARG001 + cohort_results[cohort.cohort_label] = CohortGroupResult( + cohort.cohort_group, + Status.not_actionable, + [mock_reason], + cohort.positive_description, + [mock_reason], + ) + return Status.not_actionable, [mock_reason], False + + mock_evaluate_rules_priority_group.side_effect = mock_evaluate_side_effect + + rule_processor.is_actionable(MOCK_PERSON_DATA, cohort, cohort_results, suppression_rules) + + assert_that(len(cohort_results), is_(1)) + assert_that(cohort_results["COHORT_A"].status, is_(Status.not_actionable)) + assert_that(cohort_results["COHORT_A"].description, is_("Positive Description")) + assert_that(cohort_results["COHORT_A"].reasons, is_([mock_reason])) + assert_that(cohort_results["COHORT_A"].audit_rules, is_([mock_reason])) + mock_should_skip_rule_group.assert_called_once_with(cohort, suppression_rules) + mock_evaluate_rules_priority_group.assert_called_once() + + +@patch.object(RuleProcessor, "get_rules_by_type") +@patch("eligibility_signposting_api.services.processors.rule_processor.BaseEligibilityHandler") +@patch("eligibility_signposting_api.services.processors.rule_processor.FilterRuleHandler") +@patch("eligibility_signposting_api.services.processors.rule_processor.SuppressionRuleHandler") +def test_get_cohort_group_results( + mock_suppression_handler_class, + mock_filter_handler_class, + mock_base_handler_class, + mock_get_rules_by_type, + rule_processor, +): + mock_base_handler_instance = mock_base_handler_class.return_value + mock_filter_handler_instance = mock_filter_handler_class.return_value + mock_suppression_handler_instance = mock_suppression_handler_class.return_value + + mock_base_handler_instance.next.return_value = mock_filter_handler_instance + mock_filter_handler_instance.next.return_value = mock_suppression_handler_instance + + cohort_a = rule_builder.IterationCohortFactory.build( + cohort_label="COHORT_A", priority=1, cohort_group="common_cohort" + ) + cohort_b = rule_builder.IterationCohortFactory.build( + cohort_label="COHORT_B", priority=2, cohort_group="common_cohort" + ) + active_iteration = rule_builder.IterationFactory.build( + iteration_cohorts=[cohort_a, cohort_b], + iteration_rules=[ + rule_builder.IterationRuleFactory.build(type=RuleType.filter, priority=1), + rule_builder.IterationRuleFactory.build(type=RuleType.suppression, priority=1), + ], + ) + + filter_rules = (rule_builder.IterationRuleFactory.build(type=RuleType.filter),) + suppression_rules = (rule_builder.IterationRuleFactory.build(type=RuleType.suppression),) + mock_get_rules_by_type.return_value = (filter_rules, suppression_rules) + + def mock_handle_side_effect(person, cohort, cohort_results_dict, rule_processor_instance): # noqa: ARG001 + if cohort.cohort_label == CohortLabel("COHORT_A"): + cohort_results_dict[CohortLabel("COHORT_A")] = CohortGroupResult( + cohort_code=cohort.cohort_group, + status=Status.actionable, + reasons=[], + description="Cohort A Description", + audit_rules=[], + ) + elif cohort.cohort_label == CohortLabel("COHORT_B"): + cohort_results_dict[CohortLabel("COHORT_B")] = CohortGroupResult( + cohort_code=cohort.cohort_group, + status=Status.not_eligible, + reasons=[], + description="Cohort B Description", + audit_rules=[], + ) + + mock_base_handler_instance.handle.side_effect = mock_handle_side_effect + + result = rule_processor.get_cohort_group_results(MOCK_PERSON_DATA, active_iteration) + + mock_get_rules_by_type.assert_called_once_with(active_iteration) + + mock_base_handler_class.assert_called_once_with() + mock_filter_handler_class.assert_called_once_with(filter_rules=filter_rules) + mock_suppression_handler_class.assert_called_once_with(suppression_rules=suppression_rules) + + mock_base_handler_instance.next.assert_called_once_with(mock_filter_handler_instance) + mock_filter_handler_instance.next.assert_called_once_with(mock_suppression_handler_instance) + + assert_that(mock_base_handler_instance.handle.call_count, is_(2)) + calls = mock_base_handler_instance.handle.call_args_list + assert_that(calls[0].args[1], is_(cohort_a)) + assert_that(calls[1].args[1], is_(cohort_b)) + + assert_that(len(result), is_(2)) + expected_result = { + CohortLabel("COHORT_A"): CohortGroupResult( + cohort_code=cohort_a.cohort_group, + status=Status.actionable, + reasons=[], + description="Cohort A Description", + audit_rules=[], + ), + CohortLabel("COHORT_B"): CohortGroupResult( + cohort_code=cohort_b.cohort_group, + status=Status.not_eligible, + reasons=[], + description="Cohort B Description", + audit_rules=[], + ), + } + assert_that(result, is_(expected_result)) + + assert_that(result[CohortLabel("COHORT_A")].status, is_(Status.actionable)) + assert_that(result[CohortLabel("COHORT_B")].status, is_(Status.not_eligible)) + + assert_that(result[CohortLabel("COHORT_A")].status, is_(Status.actionable)) + assert_that(result[CohortLabel("COHORT_B")].status, is_(Status.not_eligible)) + + +@patch.object(RuleProcessor, "get_rules_by_type", return_value=((), ())) +@patch("eligibility_signposting_api.services.processors.rule_processor.BaseEligibilityHandler") +@patch("eligibility_signposting_api.services.processors.rule_processor.FilterRuleHandler") +@patch("eligibility_signposting_api.services.processors.rule_processor.SuppressionRuleHandler") +def test_get_cohort_group_results_no_rules_no_cohorts( + mock_suppression_handler_class, + mock_filter_handler_class, + mock_base_handler_class, + mock_get_rules_by_type, + rule_processor, +): + mock_base_handler_instance = mock_base_handler_class.return_value + active_iteration = rule_builder.IterationFactory.build(iteration_cohorts=[], iteration_rules=[]) + + result = rule_processor.get_cohort_group_results(MOCK_PERSON_DATA, active_iteration) + + mock_get_rules_by_type.assert_called_once_with(active_iteration) + mock_base_handler_class.assert_called_once_with() + mock_filter_handler_class.assert_called_once_with(filter_rules=()) + mock_suppression_handler_class.assert_called_once_with(suppression_rules=()) + + mock_base_handler_instance.handle.assert_not_called() + assert_that(result, is_({})) diff --git a/tests/unit/services/test_eligibility_services.py b/tests/unit/services/test_eligibility_services.py index 872347a00..504888f12 100644 --- a/tests/unit/services/test_eligibility_services.py +++ b/tests/unit/services/test_eligibility_services.py @@ -3,7 +3,7 @@ import pytest from hamcrest import assert_that, empty -from eligibility_signposting_api.model.eligibility import NHSNumber +from eligibility_signposting_api.model.eligibility_status import NHSNumber from eligibility_signposting_api.repos import CampaignRepo, NotFoundError, PersonRepo from eligibility_signposting_api.services import EligibilityService, UnknownPersonError from eligibility_signposting_api.services.calculators.eligibility_calculator import EligibilityCalculatorFactory diff --git a/tests/unit/test_wrapper.py b/tests/unit/test_wrapper.py deleted file mode 100644 index 18ef5d477..000000000 --- a/tests/unit/test_wrapper.py +++ /dev/null @@ -1,313 +0,0 @@ -import json -import logging -from http import HTTPStatus - -import pytest - -from eligibility_signposting_api import wrapper -from eligibility_signposting_api.wrapper import logger - - -@pytest.fixture(autouse=True) -def setup_logging_for_tests(): - logger.handlers = [] - logger.setLevel(logging.INFO) - logger.addHandler(logging.NullHandler()) - - -@pytest.mark.parametrize( - ("path_nhs", "header_nhs", "expected_result", "expected_log_msg"), - [ - (None, None, False, "NHS number is not present"), - ("1234567890", None, False, "NHS number is not present"), - (None, "1234567890", False, "NHS number is not present"), - ("1234567890", "0987654321", False, "NHS number mismatch"), - ("1234567890", "1234567890", True, None), - ], -) -def test_validate_nhs_number(path_nhs, header_nhs, expected_result, expected_log_msg, caplog): - with caplog.at_level(logging.ERROR): - result = wrapper.validate_nhs_number(path_nhs, header_nhs) - - assert result == expected_result - - if expected_log_msg: - assert any(expected_log_msg in record.message for record in caplog.records) - else: - assert not caplog.records - - -@pytest.mark.parametrize( - ("conditions_input", "is_valid_expected", "expected_log_msg"), - [ - ("ALL", True, None), - ("COVID", True, None), - ("covid19", True, None), - ("FLU,MMR", True, None), - (" RSV , COVID19", True, None), - (" condition_with_spaces ", False, "Invalid condition query param: ' condition_with_spaces '"), - ("CONDITION_A,ANOTHER_ONE,123ABC", False, "Invalid condition query param: 'CONDITION_A'"), - ("condition1,", False, "Invalid condition query param: ''"), - (",condition2", False, "Invalid condition query param: ''"), - ("condition-invalid", False, "Invalid condition query param: 'condition-invalid'"), - ("condition with spaces", False, "Invalid condition query param: 'condition with spaces'"), - ("condition!", False, "Invalid condition query param: 'condition!'"), - ("condition@#$", False, "Invalid condition query param: 'condition@#$'"), - ], -) -def test_validate_query_params_conditions(conditions_input, is_valid_expected, expected_log_msg, caplog): - params = {"conditions": conditions_input} - - with caplog.at_level(logging.ERROR): - is_valid, problem = wrapper.validate_query_params(params) - - assert is_valid == is_valid_expected - if is_valid_expected: - assert problem is None - assert not caplog.records - else: - assert problem is not None - assert any((record.levelname == "ERROR" and expected_log_msg in record.message) for record in caplog.records) - - -def test_validate_query_params_conditions_default(caplog): - params = {"category": "ALL", "includeActions": "Y"} - with caplog.at_level(logging.ERROR): - is_valid, problem = wrapper.validate_query_params(params) - assert is_valid is True - assert problem is None - assert not caplog.records - - -@pytest.mark.parametrize( - ("category_input", "is_valid_expected", "expected_log_msg"), - [ - ("VACCINATIONS", True, None), - ("SCREENING", True, None), - ("ALL", True, None), - ("vaccinations", True, None), - ("screening", True, None), - ("all", True, None), - (" VACCINATIONS ", True, None), - ("OTHER_CATEGORY ", False, "Invalid category query param: 'OTHER_CATEGORY '"), - ("invalid!", False, "Invalid category query param: 'invalid!'"), - ("VACCINATION", False, "Invalid category query param: 'VACCINATION'"), - ], -) -def test_validate_query_params_category(category_input, is_valid_expected, expected_log_msg, caplog): - params = {"category": category_input} - with caplog.at_level(logging.ERROR): - is_valid, problem = wrapper.validate_query_params(params) - assert is_valid == is_valid_expected - - if is_valid_expected: - assert problem is None - assert not caplog.records - else: - assert problem is not None - assert any((record.levelname == "ERROR" and expected_log_msg in record.message) for record in caplog.records) - - -def test_validate_query_params_category_default(caplog): - params = {"conditions": "ALL", "includeActions": "Y"} - with caplog.at_level(logging.ERROR): - is_valid, problem = wrapper.validate_query_params(params) - assert is_valid is True - assert problem is None - assert not caplog.records - - -@pytest.mark.parametrize( - ("include_actions_input", "is_valid_expected", "expected_log_msg"), - [ - ("Y", True, None), - ("N", True, None), - ("y", True, None), - ("n", True, None), - ("n ", True, None), - ("TRUE", False, "Invalid include actions query param: 'TRUE'"), - ("YES", False, "Invalid include actions query param: 'YES'"), - ("0", False, "Invalid include actions query param: '0'"), - ("1", False, "Invalid include actions query param: '1'"), - ("", False, "Invalid include actions query param: ''"), - (" ", False, "Invalid include actions query param: ' '"), - ], -) -def test_validate_query_params_include_actions(include_actions_input, is_valid_expected, expected_log_msg, caplog): - params = {"includeActions": include_actions_input} - with caplog.at_level(logging.ERROR): - is_valid, problem = wrapper.validate_query_params(params) - assert is_valid == is_valid_expected - - if is_valid_expected: - assert problem is None - assert not caplog.records - else: - assert problem is not None - assert any((record.levelname == "ERROR" and expected_log_msg in record.message) for record in caplog.records) - - -def test_validate_query_params_include_actions_default(caplog): - params = {"conditions": "ALL", "category": "ALL"} - with caplog.at_level(logging.ERROR): - is_valid, problem = wrapper.validate_query_params(params) - assert is_valid is True - assert problem is None - assert not caplog.records - - -def test_validate_query_params_all_valid_params(caplog): - params = {"conditions": "COND1,COND2", "category": "SCREENING", "includeActions": "N"} - with caplog.at_level(logging.ERROR): - is_valid, problem = wrapper.validate_query_params(params) - assert is_valid is True - assert problem is None - assert not caplog.records - - -def test_validate_query_params_mixed_valid_invalid_conditions_fail_first(caplog): - params = {"conditions": "VALID_COND,INVALID!,ANOTHER_VALID", "category": "SCREENING", "includeActions": "N"} - with caplog.at_level(logging.ERROR): - is_valid, problem = wrapper.validate_query_params(params) - assert is_valid is False - assert problem is not None - assert any( - (record.levelname == "ERROR" and "Invalid condition query param: " in record.message) - for record in caplog.records - ) - - -def test_validate_query_params_valid_conditions_invalid_category_fail_second(caplog): - params = {"conditions": "CONDITION", "category": "BAD_CAT", "includeActions": "N"} - with caplog.at_level(logging.ERROR): - is_valid, problem = wrapper.validate_query_params(params) - assert is_valid is False - assert problem is not None - assert any( - (record.levelname == "ERROR" and "Invalid category query param: " in record.message) - for record in caplog.records - ) - error_logs = [r for r in caplog.records if r.levelname == "ERROR"] - assert len(error_logs) == 1 - - -def test_validate_query_params_valid_conditions_category_invalid_actions_fail_third(caplog): - params = {"conditions": "CONDITION", "category": "VACCINATIONS", "includeActions": "Nope"} - with caplog.at_level(logging.ERROR): - is_valid, problem = wrapper.validate_query_params(params) - assert is_valid is False - assert problem is not None - assert any( - (record.levelname == "ERROR" and "Invalid include actions query param: " in record.message) - for record in caplog.records - ) - error_logs = [r for r in caplog.records if r.levelname == "ERROR"] - assert len(error_logs) == 1 - - -def test_validate_query_params_returns_correct_problem_details_for_conditions_error(): - invalid_condition = "FLU&COVID" - params = {"conditions": invalid_condition} - - is_valid, problem = wrapper.validate_query_params(params) - - assert is_valid is False - assert problem is not None - assert problem["statusCode"] == HTTPStatus.BAD_REQUEST - assert problem["headers"]["Content-Type"] == "application/fhir+json" - - response_body = json.loads(problem["body"]) - - assert response_body["resourceType"] == "OperationOutcome" - assert "id" in response_body - assert "meta" in response_body - assert "lastUpdated" in response_body["meta"] - - assert len(response_body["issue"]) == 1 - issue = response_body["issue"][0] - - assert issue["severity"] == "error" - assert issue["code"] == "value" - assert issue["diagnostics"] == ( - f"{invalid_condition} should be a single or comma separated list of condition " - f"strings with no other punctuation or special characters" - ) - assert issue["location"] == ["parameters/conditions"] - assert "details" in issue - assert "coding" in issue["details"] - assert len(issue["details"]["coding"]) == 1 - coding = issue["details"]["coding"][0] - - assert coding["system"] == "https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1" - assert coding["code"] == "INVALID_PARAMETER" - assert coding["display"] == "The given conditions were not in the expected format." - - -def test_validate_query_params_returns_correct_problem_details_for_category_error(): - invalid_category = "HEALTHCHECKS" - params = {"category": invalid_category} - - is_valid, problem = wrapper.validate_query_params(params) - - assert is_valid is False - assert problem is not None - assert problem["statusCode"] == HTTPStatus.UNPROCESSABLE_ENTITY - assert problem["headers"]["Content-Type"] == "application/fhir+json" - - response_body = json.loads(problem["body"]) - - assert response_body["resourceType"] == "OperationOutcome" - assert "id" in response_body - assert "meta" in response_body - assert "lastUpdated" in response_body["meta"] - - assert len(response_body["issue"]) == 1 - issue = response_body["issue"][0] - - assert issue["severity"] == "error" - assert issue["code"] == "value" - assert issue["diagnostics"] == f"{invalid_category} is not a category that is supported by the API" - assert issue["location"] == ["parameters/category"] - assert "details" in issue - assert "coding" in issue["details"] - assert len(issue["details"]["coding"]) == 1 - coding = issue["details"]["coding"][0] - - assert coding["system"] == "https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1" - assert coding["code"] == "INVALID_PARAMETER" - assert coding["display"] == "The supplied category was not recognised by the API." - - -def test_validate_query_params_returns_correct_problem_details_for_include_actions_error(): - invalid_include_actions = "NAH" - params = {"includeActions": invalid_include_actions} - - is_valid, problem = wrapper.validate_query_params(params) - - assert is_valid is False - assert problem is not None - assert problem["statusCode"] == HTTPStatus.UNPROCESSABLE_ENTITY - assert problem["headers"]["Content-Type"] == "application/fhir+json" - - response_body = json.loads(problem["body"]) - - assert response_body["resourceType"] == "OperationOutcome" - assert "id" in response_body - assert "meta" in response_body - assert "lastUpdated" in response_body["meta"] - - assert len(response_body["issue"]) == 1 - issue = response_body["issue"][0] - - assert issue["severity"] == "error" - assert issue["code"] == "value" - assert issue["diagnostics"] == f"{invalid_include_actions} is not a value that is supported by the API" - assert issue["location"] == ["parameters/includeActions"] - assert "details" in issue - assert "coding" in issue["details"] - assert len(issue["details"]["coding"]) == 1 - coding = issue["details"]["coding"][0] - - assert coding["system"] == "https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1" - assert coding["code"] == "INVALID_PARAMETER" - assert coding["display"] == "The supplied value was not recognised by the API." diff --git a/tests/unit/validation/__init__.py b/tests/unit/validation/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit/validation/conftest.py b/tests/unit/validation/conftest.py new file mode 100644 index 000000000..cb711c20d --- /dev/null +++ b/tests/unit/validation/conftest.py @@ -0,0 +1,61 @@ +import pytest + + +@pytest.fixture +def valid_campaign_config_with_only_mandatory_fields(): + return { + "ID": "CAMP001", + "Version": 1, + "Name": "Spring Campaign", + "Type": "V", + "Target": "COVID", + "IterationFrequency": "M", + "IterationType": "A", + "StartDate": "20250101", + "EndDate": "20250331", + "Iterations": [ + { + "ID": "ITER001", + "Version": 1, + "Name": "Mid-January Push", + "IterationDate": "20250101", + "IterationNumber": 1, + "ApprovalMinimum": 10, + "ApprovalMaximum": 100, + "Type": "A", + "DefaultCommsRouting": "", + "DefaultNotEligibleRouting": "", + "DefaultNotActionableRouting": "", + "IterationCohorts": [], + "IterationRules": [], + "ActionsMapper": {}, + } + ], + } + + +@pytest.fixture +def valid_iteration_rule_with_only_mandatory_fields(): + return { + "Type": "F", + "Name": "Assure only already vaccinated taken from magic cohort", + "Description": "Exclude anyone who has NOT been given a dose of RSV Vaccination from the magic cohort", + "Operator": "is_empty", + "Comparator": "", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "elid_all_people", + "Priority": 100, + } + + +@pytest.fixture +def valid_available_action(): + return { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking", + } diff --git a/tests/unit/validation/test_actions_mapper_validator.py b/tests/unit/validation/test_actions_mapper_validator.py new file mode 100644 index 000000000..89af4958f --- /dev/null +++ b/tests/unit/validation/test_actions_mapper_validator.py @@ -0,0 +1,64 @@ +import pytest +from pydantic import ValidationError + +from eligibility_signposting_api.model.campaign_config import AvailableAction +from rules_validation_api.validators.actions_mapper_validator import ActionsMapperValidation + + +@pytest.fixture +def valid_available_action(): + return { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking", + } + + +class TestBUCValidations: + def make_action(self, data: dict) -> AvailableAction: + return AvailableAction(**data) + + def test_valid_actions_mapper(self, valid_available_action): + data = { + "action1": self.make_action(valid_available_action), + "action2": self.make_action({**valid_available_action, "ExternalRoutingCode": "AltCode"}), + } + mapper = ActionsMapperValidation(root=data) + + expected_action_count = 2 + assert isinstance(mapper, ActionsMapperValidation) + assert len(mapper.root) == expected_action_count + + @pytest.mark.parametrize( + "invalid_action", + [ + {"action1": ""}, + {"action1": "invalid_action"}, + {"action3": None}, + {"action1": "", "action3": None}, + {"action1": "invalid_action", "action2": ""}, + ], + ) + def test_if_exception_raised_when_adding_invalid_actions_to_action_mapper(self, invalid_action): + data = {"": invalid_action} + with pytest.raises(ValidationError): + ActionsMapperValidation(root=data) + + def test_invalid_actions_mapper_empty_key(self, valid_available_action): + data = {"": self.make_action(valid_available_action), "action2": self.make_action(valid_available_action)} + with pytest.raises(ValidationError) as exc_info: + ActionsMapperValidation(root=data) + assert "Invalid keys found in ActionsMapper" in str(exc_info.value) + assert "['']" in str(exc_info.value) + + @pytest.mark.parametrize("bad_key", [""]) + def test_invalid_keys_parametrized(self, bad_key, valid_available_action): + data = { + bad_key: self.make_action(valid_available_action), + "valid_key": self.make_action(valid_available_action), + } + with pytest.raises(ValidationError) as exc_info: + ActionsMapperValidation(root=data) + assert "Invalid keys found in ActionsMapper" in str(exc_info.value) diff --git a/tests/unit/validation/test_available_action_validator.py b/tests/unit/validation/test_available_action_validator.py new file mode 100644 index 000000000..468ead7b9 --- /dev/null +++ b/tests/unit/validation/test_available_action_validator.py @@ -0,0 +1,55 @@ +import copy + +import pytest +from pydantic import ValidationError + +from rules_validation_api.validators.available_action_validator import AvailableActionValidation + + +# ๐Ÿ” Mandatory Fields +class TestMandatoryFieldsSchemaValidations: + def test_valid_minimal_input(self, valid_available_action): + data = copy.deepcopy(valid_available_action) + data.pop("ActionDescription") + data.pop("UrlLink") + data.pop("UrlLabel") + action = AvailableActionValidation(**data) + assert action.action_type == "ButtonWithAuthLink" + assert action.action_code == "BookNBS" + assert action.action_description is None + assert action.url_link is None + assert action.url_label is None + + def test_missing_required_fields(self, valid_available_action): + data = copy.deepcopy(valid_available_action) + data.pop("ActionType") + data.pop("ExternalRoutingCode") + with pytest.raises(ValidationError) as exc_info: + AvailableActionValidation(**data) + error_msg = str(exc_info.value) + assert "ActionType" in error_msg + assert "ExternalRoutingCode" in error_msg + + +# ๐Ÿ” Optional Fields +class TestOptionalFieldsSchemaValidations: + def test_valid_full_input(self, valid_available_action): + action = AvailableActionValidation(**valid_available_action) + assert action.action_type == "ButtonWithAuthLink" + assert action.action_code == "BookNBS" + assert action.action_description == "" + assert str(action.url_link) == "http://www.nhs.uk/book-rsv" + assert action.url_label == "Continue to booking" + + def test_empty_string_is_valid_for_optional_fields(self, valid_available_action): + action = AvailableActionValidation(**valid_available_action) + assert action.action_description == "" + assert action.url_label == "Continue to booking" + + @pytest.mark.parametrize("bad_url", ["not-a-url", "ftp://bad", "123"]) + def test_invalid_url_raises_validation_error(self, valid_available_action, bad_url): + data = copy.deepcopy(valid_available_action) + data["UrlLink"] = bad_url + with pytest.raises(ValidationError) as exc_info: + AvailableActionValidation(**data) + assert "UrlLink" in str(exc_info.value) diff --git a/tests/unit/validation/test_campaign_config_validator.py b/tests/unit/validation/test_campaign_config_validator.py new file mode 100644 index 000000000..61bb75ca7 --- /dev/null +++ b/tests/unit/validation/test_campaign_config_validator.py @@ -0,0 +1,235 @@ +import pytest +from pydantic import ValidationError + +from rules_validation_api.validators.campaign_config_validator import CampaignConfigValidation + + +class TestMandatoryFieldsSchemaValidations: + def test_campaign_config_with_only_mandatory_fields_configuration( + self, valid_campaign_config_with_only_mandatory_fields + ): + try: + CampaignConfigValidation(**valid_campaign_config_with_only_mandatory_fields) + except ValidationError as e: + pytest.fail(f"Unexpected error during model instantiation: {e}") + + @pytest.mark.parametrize( + "mandatory_field", + [ + "ID", + "Version", + "Name", + "Type", + "Target", + "IterationFrequency", + "IterationType", + "StartDate", + "EndDate", + "Iterations", + ], + ) + def test_missing_mandatory_fields(self, mandatory_field, valid_campaign_config_with_only_mandatory_fields): + data = valid_campaign_config_with_only_mandatory_fields.copy() + data.pop(mandatory_field, None) # Simulate missing field + with pytest.raises(ValidationError): + CampaignConfigValidation(**data) + + # ID + @pytest.mark.parametrize("id_value", ["CAMP001", "12345", "X001"]) + def test_valid_id(self, id_value, valid_campaign_config_with_only_mandatory_fields): + data = {**valid_campaign_config_with_only_mandatory_fields, "ID": id_value} + model = CampaignConfigValidation(**data) + assert model.id == id_value + + # Version + @pytest.mark.parametrize("version_value", [1, 2, 100]) + def test_valid_version(self, version_value, valid_campaign_config_with_only_mandatory_fields): + data = {**valid_campaign_config_with_only_mandatory_fields, "Version": version_value} + model = CampaignConfigValidation(**data) + assert model.version == version_value + + # Name + @pytest.mark.parametrize("name_value", ["Spring Campaign", "COVID-Alert", "Mass Outreach"]) + def test_valid_name(self, name_value, valid_campaign_config_with_only_mandatory_fields): + data = {**valid_campaign_config_with_only_mandatory_fields, "Name": name_value} + model = CampaignConfigValidation(**data) + assert model.name == name_value + + # Type + @pytest.mark.parametrize("type_value", ["V", "S"]) + def test_valid_type(self, type_value, valid_campaign_config_with_only_mandatory_fields): + data = {**valid_campaign_config_with_only_mandatory_fields, "Type": type_value} + model = CampaignConfigValidation(**data) + assert model.type == type_value + + @pytest.mark.parametrize("type_value", ["X", "", None]) + def test_invalid_type(self, type_value, valid_campaign_config_with_only_mandatory_fields): + data = {**valid_campaign_config_with_only_mandatory_fields, "Type": type_value} + with pytest.raises(ValidationError): + CampaignConfigValidation(**data) + + # Target + @pytest.mark.parametrize("target_value", ["COVID", "FLU", "MMR", "RSV"]) + def test_valid_target(self, target_value, valid_campaign_config_with_only_mandatory_fields): + data = {**valid_campaign_config_with_only_mandatory_fields, "Target": target_value} + model = CampaignConfigValidation(**data) + assert model.target == target_value + + @pytest.mark.parametrize("target_value", ["XYZ", "ABC", "", None]) + def test_invalid_target(self, target_value, valid_campaign_config_with_only_mandatory_fields): + data = {**valid_campaign_config_with_only_mandatory_fields, "Target": target_value} + with pytest.raises(ValidationError): + CampaignConfigValidation(**data) + + # IterationFrequency + @pytest.mark.parametrize("freq_value", ["X", "D", "W", "M", "Q", "A"]) + def test_valid_iteration_frequency(self, freq_value, valid_campaign_config_with_only_mandatory_fields): + data = {**valid_campaign_config_with_only_mandatory_fields, "IterationFrequency": freq_value} + model = CampaignConfigValidation(**data) + assert model.iteration_frequency == freq_value + + @pytest.mark.parametrize("freq_value", ["Z", "", None]) + def test_invalid_iteration_frequency(self, freq_value, valid_campaign_config_with_only_mandatory_fields): + data = {**valid_campaign_config_with_only_mandatory_fields, "IterationFrequency": freq_value} + with pytest.raises(ValidationError): + CampaignConfigValidation(**data) + + # IterationType + @pytest.mark.parametrize("iter_type", ["A", "M", "S", "O"]) + def test_valid_iteration_type(self, iter_type, valid_campaign_config_with_only_mandatory_fields): + data = {**valid_campaign_config_with_only_mandatory_fields, "IterationType": iter_type} + model = CampaignConfigValidation(**data) + assert model.iteration_type == iter_type + + @pytest.mark.parametrize("iter_type", ["B", "", None]) + def test_invalid_iteration_type(self, iter_type, valid_campaign_config_with_only_mandatory_fields): + data = {**valid_campaign_config_with_only_mandatory_fields, "IterationType": iter_type} + with pytest.raises(ValidationError): + CampaignConfigValidation(**data) + + # StartDate + @pytest.mark.parametrize( + "start_date", + [ + "", # empty string + "invalid-date", # malformed value + ], + ) + def test_invalid_start_date(self, start_date, valid_campaign_config_with_only_mandatory_fields): + data = valid_campaign_config_with_only_mandatory_fields.copy() + data["StartDate"] = start_date + + with pytest.raises(ValidationError) as exc_info: + CampaignConfigValidation(**data) + + errors = exc_info.value.errors() + for error in errors: + assert error["loc"][0] == "StartDate" + + # EndDates + @pytest.mark.parametrize( + "end_date", + [ + "", # empty string + "31032025", # malformed value + ], + ) + def test_invalid_end_date(self, end_date, valid_campaign_config_with_only_mandatory_fields): + data = valid_campaign_config_with_only_mandatory_fields.copy() + data["EndDate"] = end_date + + with pytest.raises(ValidationError) as exc_info: + CampaignConfigValidation(**data) + + errors = exc_info.value.errors() + for error in errors: + assert error["loc"][0] == "EndDate" + + +class TestOptionalFieldsSchemaValidations: + @pytest.mark.parametrize("manager", [["alice"], ["bob"], ["carol"]]) + def test_manager_field(self, manager, valid_campaign_config_with_only_mandatory_fields): + data = {**valid_campaign_config_with_only_mandatory_fields, "Manager": manager} + model = CampaignConfigValidation(**data) + assert model.manager == manager + + @pytest.mark.parametrize("approver", [["alice"], ["bob"], ["carol"]]) + def test_approver_field(self, approver, valid_campaign_config_with_only_mandatory_fields): + data = {**valid_campaign_config_with_only_mandatory_fields, "Approver": approver} + model = CampaignConfigValidation(**data) + assert model.approver == approver + + @pytest.mark.parametrize("reviewer", [["alice"], ["bob"], ["carol"]]) + def test_reviewer_field(self, reviewer, valid_campaign_config_with_only_mandatory_fields): + data = {**valid_campaign_config_with_only_mandatory_fields, "Reviewer": reviewer} + model = CampaignConfigValidation(**data) + assert model.reviewer == reviewer + + @pytest.mark.parametrize("iteration_time", ["14:00", "09:30", "18:45"]) + def test_iteration_time_field(self, iteration_time, valid_campaign_config_with_only_mandatory_fields): + data = {**valid_campaign_config_with_only_mandatory_fields, "IterationTime": iteration_time} + model = CampaignConfigValidation(**data) + assert model.iteration_time == iteration_time + + @pytest.mark.parametrize("routing", ["email", "sms", "push"]) + def test_default_comms_routing_field(self, routing, valid_campaign_config_with_only_mandatory_fields): + data = {**valid_campaign_config_with_only_mandatory_fields, "DefaultCommsRouting": routing} + model = CampaignConfigValidation(**data) + assert model.default_comms_routing == routing + + @pytest.mark.parametrize("min_approval", [0, 1, 2]) + def test_approval_minimum_field(self, min_approval, valid_campaign_config_with_only_mandatory_fields): + data = {**valid_campaign_config_with_only_mandatory_fields, "ApprovalMinimum": min_approval} + model = CampaignConfigValidation(**data) + assert model.approval_minimum == min_approval + + @pytest.mark.parametrize("max_approval", [5, 10, 15]) + def test_approval_maximum_field(self, max_approval, valid_campaign_config_with_only_mandatory_fields): + data = {**valid_campaign_config_with_only_mandatory_fields, "ApprovalMaximum": max_approval} + model = CampaignConfigValidation(**data) + assert model.approval_maximum == max_approval + + +class TestBUCValidations: + # StartDate and EndDates + @pytest.mark.parametrize( + ("start_date", "end_date"), + [ + ("20250101", "20250331"), # valid range + ("20250601", "20250630"), # valid short range + ("20250101", "20250101"), # same day + ], + ) + def test_valid_start_and_end_dates_and_iteration_dates_relation( + self, start_date, end_date, valid_campaign_config_with_only_mandatory_fields + ): + data = valid_campaign_config_with_only_mandatory_fields.copy() + data["StartDate"] = start_date + data["EndDate"] = end_date + data["Iterations"][0]["IterationDate"] = "20241231" + CampaignConfigValidation(**data) + + @pytest.mark.parametrize( + ("start_date", "end_date"), + [ + ("20241230", "20250101"), # campaign start date is after the iteration date + ("20250331", "20250101"), # end before start + ], + ) + def test_invalid_start_and_end_dates_and_iteration_dates_relation( + self, start_date, end_date, valid_campaign_config_with_only_mandatory_fields + ): + data = valid_campaign_config_with_only_mandatory_fields.copy() + data["StartDate"] = start_date + data["EndDate"] = end_date + data["Iterations"][0]["IterationDate"] = "20241231" + with pytest.raises(ValidationError): + CampaignConfigValidation(**data) + + # Iteration + def test_validate_iterations_non_empty(self, valid_campaign_config_with_only_mandatory_fields): + data = {**valid_campaign_config_with_only_mandatory_fields, "Iterations": []} + with pytest.raises(ValidationError) as error: + CampaignConfigValidation(**data) + errors = error.value.errors() + assert any(e["loc"][-1] == "Iterations" for e in errors), "Expected validation error on 'Iterations'" diff --git a/tests/unit/validation/test_iteration_cohorts_validator.py b/tests/unit/validation/test_iteration_cohorts_validator.py new file mode 100644 index 000000000..2b8c2ac4c --- /dev/null +++ b/tests/unit/validation/test_iteration_cohorts_validator.py @@ -0,0 +1,65 @@ +import pytest +from pydantic import ValidationError + +from rules_validation_api.validators.iteration_cohort_validator import IterationCohortValidation + + +class TestMandatoryFieldsSchemaValidations: + def test_missing_cohort_label_raises_error(self): + data = {"CohortGroup": "rsv_age_rolling"} + with pytest.raises(ValidationError) as exc_info: + IterationCohortValidation(**data) + assert "CohortLabel" in str(exc_info.value) + + def test_missing_cohort_group_raises_error(self): + data = {"CohortLabel": "rsv_75_rolling"} + with pytest.raises(ValidationError) as exc_info: + IterationCohortValidation(**data) + assert "CohortGroup" in str(exc_info.value) + + def test_valid_with_only_mandatory_fields(self): + data = {"CohortLabel": "rsv_75_rolling", "CohortGroup": "rsv_age_rolling"} + cohort = IterationCohortValidation(**data) + assert cohort.cohort_label == "rsv_75_rolling" + assert cohort.cohort_group == "rsv_age_rolling" + + +class TestOptionalFieldsSchemaValidations: + def test_positive_description_can_be_none(self): + data = {"CohortLabel": "rsv_75_rolling", "CohortGroup": "rsv_age_rolling", "PositiveDescription": None} + cohort = IterationCohortValidation(**data) + assert cohort.positive_description is None + + def test_negative_description_can_be_none(self): + data = {"CohortLabel": "rsv_75_rolling", "CohortGroup": "rsv_age_rolling", "NegativeDescription": None} + cohort = IterationCohortValidation(**data) + assert cohort.negative_description is None + + def test_priority_can_be_none(self): + data = {"CohortLabel": "rsv_75_rolling", "CohortGroup": "rsv_age_rolling", "Priority": None} + cohort = IterationCohortValidation(**data) + assert cohort.priority is None + + def test_positive_description_accepts_valid_value(self): + data = { + "CohortLabel": "rsv_75_rolling", + "CohortGroup": "rsv_age_rolling", + "PositiveDescription": "Eligible for benefits", + } + cohort = IterationCohortValidation(**data) + assert cohort.positive_description == "Eligible for benefits" + + def test_negative_description_accepts_valid_value(self): + data = { + "CohortLabel": "rsv_75_rolling", + "CohortGroup": "rsv_age_rolling", + "NegativeDescription": "Not eligible", + } + cohort = IterationCohortValidation(**data) + assert cohort.negative_description == "Not eligible" + + def test_priority_accepts_valid_value(self): + cohort_priority = 10 + data = {"CohortLabel": "rsv_75_rolling", "CohortGroup": "rsv_age_rolling", "Priority": cohort_priority} + cohort = IterationCohortValidation(**data) + assert cohort.priority == cohort_priority diff --git a/tests/unit/validation/test_iteration_rules_validator.py b/tests/unit/validation/test_iteration_rules_validator.py new file mode 100644 index 000000000..fd544528e --- /dev/null +++ b/tests/unit/validation/test_iteration_rules_validator.py @@ -0,0 +1,247 @@ +import pytest +from pydantic import ValidationError + +from rules_validation_api.validators.iteration_validator import IterationRuleValidation + + +class TestMandatoryFieldsSchemaValidations: + def test_campaign_config_with_only_mandatory_fields_configuration( + self, valid_iteration_rule_with_only_mandatory_fields + ): + try: + IterationRuleValidation(**valid_iteration_rule_with_only_mandatory_fields) + except ValidationError as e: + pytest.fail(f"Unexpected error during model instantiation: {e}") + + @pytest.mark.parametrize( + "mandatory_field", + ["Type", "Name", "Description", "Priority", "AttributeLevel", "Operator", "Comparator"], + ) + def test_missing_mandatory_fields(self, mandatory_field, valid_iteration_rule_with_only_mandatory_fields): + data = valid_iteration_rule_with_only_mandatory_fields.copy() + data.pop(mandatory_field, None) # Simulate missing field + with pytest.raises(ValidationError): + IterationRuleValidation(**data) + assert mandatory_field.lower() + + @pytest.mark.parametrize("type_value", ["F", "S", "R", "X", "Y"]) + def test_valid_type(self, type_value, valid_iteration_rule_with_only_mandatory_fields): + data = valid_iteration_rule_with_only_mandatory_fields.copy() + data["Type"] = type_value + result = IterationRuleValidation(**data) + assert result.type.value == type_value + + @pytest.mark.parametrize("type_value", ["Z", 123, None]) + def test_invalid_type(self, type_value, valid_iteration_rule_with_only_mandatory_fields): + data = valid_iteration_rule_with_only_mandatory_fields.copy() + data["Type"] = type_value + with pytest.raises(ValidationError): + IterationRuleValidation(**data) + + @pytest.mark.parametrize("name_value", ["", "ValidName", "Test_Rule_01"]) + def test_valid_name(self, name_value, valid_iteration_rule_with_only_mandatory_fields): + data = valid_iteration_rule_with_only_mandatory_fields.copy() + data["Name"] = name_value + result = IterationRuleValidation(**data) + assert result.name == name_value + + @pytest.mark.parametrize("name_value", [None, 42]) + def test_invalid_name(self, name_value, valid_iteration_rule_with_only_mandatory_fields): + data = valid_iteration_rule_with_only_mandatory_fields.copy() + data["Name"] = name_value + with pytest.raises(ValidationError): + IterationRuleValidation(**data) + + @pytest.mark.parametrize("description_value", ["", "A rule description", "Sample text"]) + def test_valid_description(self, description_value, valid_iteration_rule_with_only_mandatory_fields): + data = valid_iteration_rule_with_only_mandatory_fields.copy() + data["Description"] = description_value + result = IterationRuleValidation(**data) + assert result.description == description_value + + @pytest.mark.parametrize("description_value", [None]) + def test_invalid_description(self, description_value, valid_iteration_rule_with_only_mandatory_fields): + data = valid_iteration_rule_with_only_mandatory_fields.copy() + data["Description"] = description_value + with pytest.raises(ValidationError): + IterationRuleValidation(**data) + + @pytest.mark.parametrize("priority_value", [-1, -5, 1, 100, 999]) + def test_valid_priority(self, priority_value, valid_iteration_rule_with_only_mandatory_fields): + data = valid_iteration_rule_with_only_mandatory_fields.copy() + data["Priority"] = priority_value + result = IterationRuleValidation(**data) + assert result.priority == priority_value + + @pytest.mark.parametrize("priority_value", ["high", None]) + def test_invalid_priority(self, priority_value, valid_iteration_rule_with_only_mandatory_fields): + data = valid_iteration_rule_with_only_mandatory_fields.copy() + data["Priority"] = priority_value + with pytest.raises(ValidationError): + IterationRuleValidation(**data) + + @pytest.mark.parametrize("attribute_level", ["PERSON", "TARGET", "COHORT"]) + def test_valid_attribute_level(self, attribute_level, valid_iteration_rule_with_only_mandatory_fields): + data = valid_iteration_rule_with_only_mandatory_fields.copy() + data["AttributeLevel"] = attribute_level + data["AttributeName"] = None # Ignoring the validation constraint btw AttributeLevel and AttributeName + result = IterationRuleValidation(**data) + assert result.attribute_level == attribute_level + + @pytest.mark.parametrize("attribute_level", ["", None, 42, "basic", "BASIC"]) + def test_invalid_attribute_level(self, attribute_level, valid_iteration_rule_with_only_mandatory_fields): + data = valid_iteration_rule_with_only_mandatory_fields.copy() + data["AttributeLevel"] = attribute_level + data["AttributeName"] = None # Ignoring the validation constraint btw AttributeLevel and AttributeName + with pytest.raises(ValidationError): + IterationRuleValidation(**data) + + @pytest.mark.parametrize("operator_value", ["=", "!=", ">", "<=", "contains", "is_true"]) + def test_valid_operator(self, operator_value, valid_iteration_rule_with_only_mandatory_fields): + data = valid_iteration_rule_with_only_mandatory_fields.copy() + data["Operator"] = operator_value + result = IterationRuleValidation(**data) + assert result.operator.value == operator_value + + @pytest.mark.parametrize("operator_value", ["approx", "", None]) + def test_invalid_operator(self, operator_value, valid_iteration_rule_with_only_mandatory_fields): + data = valid_iteration_rule_with_only_mandatory_fields.copy() + data["Operator"] = operator_value + with pytest.raises(ValidationError): + IterationRuleValidation(**data) + + @pytest.mark.parametrize("comparator_value", ["status", "true", "0"]) + def test_valid_comparator(self, comparator_value, valid_iteration_rule_with_only_mandatory_fields): + data = valid_iteration_rule_with_only_mandatory_fields.copy() + data["Comparator"] = comparator_value + result = IterationRuleValidation(**data) + assert result.comparator == comparator_value + + @pytest.mark.parametrize("comparator_value", [None, 123]) + def test_invalid_comparator(self, comparator_value, valid_iteration_rule_with_only_mandatory_fields): + data = valid_iteration_rule_with_only_mandatory_fields.copy() + data["Comparator"] = comparator_value + with pytest.raises(ValidationError): + IterationRuleValidation(**data) + + @pytest.mark.parametrize( + ("rule_stop_input", "expected_bool"), + [ + (True, True), + (False, False), + ("Y", True), + ("N", False), + ("YES", False), + ("NO", False), + ("YEAH", False), + ("ONE", False), + ], + ) + def test_rule_stop_boolean_resolution( + self, rule_stop_input, expected_bool, valid_iteration_rule_with_only_mandatory_fields + ): + data = valid_iteration_rule_with_only_mandatory_fields.copy() + data["RuleStop"] = rule_stop_input + result = IterationRuleValidation(**data) + assert result.rule_stop is expected_bool + + +class TestOptionalFieldsSchemaValidations: + # AttributeName + @pytest.mark.parametrize("attr_name", ["status", "user_type", None]) + def test_valid_attribute_name(self, attr_name, valid_iteration_rule_with_only_mandatory_fields): + data = valid_iteration_rule_with_only_mandatory_fields.copy() + data["AttributeName"] = attr_name + result = IterationRuleValidation(**data) + assert result.attribute_name == attr_name + + @pytest.mark.parametrize("attr_name", [123, {}, []]) + def test_invalid_attribute_name(self, attr_name, valid_iteration_rule_with_only_mandatory_fields): + data = valid_iteration_rule_with_only_mandatory_fields.copy() + data["AttributeName"] = attr_name + with pytest.raises(ValidationError): + IterationRuleValidation(**data) + + # CohortLabel + @pytest.mark.parametrize("label", ["Cohort_A", "Segment_2025", None, ""]) + def test_valid_cohort_label(self, label, valid_iteration_rule_with_only_mandatory_fields): + data = valid_iteration_rule_with_only_mandatory_fields.copy() + data["CohortLabel"] = label + result = IterationRuleValidation(**data) + assert result.cohort_label == label + + @pytest.mark.parametrize("label", [123, [], {}]) + def test_invalid_cohort_label(self, label, valid_iteration_rule_with_only_mandatory_fields): + data = valid_iteration_rule_with_only_mandatory_fields.copy() + data["CohortLabel"] = label + with pytest.raises(ValidationError): + IterationRuleValidation(**data) + + # AttributeTarget + @pytest.mark.parametrize("target", ["target_value", None]) + def test_valid_attribute_target(self, target, valid_iteration_rule_with_only_mandatory_fields): + data = valid_iteration_rule_with_only_mandatory_fields.copy() + data["AttributeTarget"] = target + result = IterationRuleValidation(**data) + assert result.attribute_target == target + + @pytest.mark.parametrize("target", [123, [], {}]) + def test_invalid_attribute_target(self, target, valid_iteration_rule_with_only_mandatory_fields): + data = valid_iteration_rule_with_only_mandatory_fields.copy() + data["AttributeTarget"] = target + with pytest.raises(ValidationError): + IterationRuleValidation(**data) + + # RuleStop + @pytest.mark.parametrize("rule_stop_value", [True, False, "Y", "N", "YES", "NO", "YEAH", "ONE"]) + def test_valid_rule_stop(self, rule_stop_value, valid_iteration_rule_with_only_mandatory_fields): + data = valid_iteration_rule_with_only_mandatory_fields.copy() + data["RuleStop"] = rule_stop_value + result = IterationRuleValidation(**data) + assert isinstance(result.rule_stop, bool) + + @pytest.mark.parametrize("rule_stop_value", [{}, None]) + def test_invalid_rule_stop(self, rule_stop_value, valid_iteration_rule_with_only_mandatory_fields): + data = valid_iteration_rule_with_only_mandatory_fields.copy() + data["RuleStop"] = rule_stop_value + with pytest.raises(ValidationError): + IterationRuleValidation(**data) + + # CommsRouting + @pytest.mark.parametrize("routing_value", ["route_A", None]) + def test_valid_comms_routing(self, routing_value, valid_iteration_rule_with_only_mandatory_fields): + data = valid_iteration_rule_with_only_mandatory_fields.copy() + data["CommsRouting"] = routing_value + result = IterationRuleValidation(**data) + assert result.comms_routing == routing_value + + @pytest.mark.parametrize("routing_value", [123, [], {}]) + def test_invalid_comms_routing(self, routing_value, valid_iteration_rule_with_only_mandatory_fields): + data = valid_iteration_rule_with_only_mandatory_fields.copy() + data["CommsRouting"] = routing_value + with pytest.raises(ValidationError): + IterationRuleValidation(**data) + + +class TestBUCValidations: + @pytest.mark.parametrize("attribute_name", [None, "", "COHORT_LABEL"]) + def test_valid_when_attribute_level_is_cohort_then_attribute_name_should_be_none_or_cohort_label( + self, attribute_name, valid_iteration_rule_with_only_mandatory_fields + ): + data = valid_iteration_rule_with_only_mandatory_fields.copy() + data["AttributeLevel"] = "COHORT" + data["AttributeName"] = attribute_name + result = IterationRuleValidation(**data) + assert result.attribute_name == attribute_name + + @pytest.mark.parametrize("attribute_name", ["LAST_SUCCESSFUL_DATE", "cohort_label"]) + def test_invalid_when_attribute_level_is_cohort_but_attribute_name_is_neither_none_nor_cohort_label( + self, attribute_name, valid_iteration_rule_with_only_mandatory_fields + ): + data = valid_iteration_rule_with_only_mandatory_fields.copy() + data["AttributeLevel"] = "COHORT" + data["AttributeName"] = attribute_name + with pytest.raises(ValidationError) as error: + IterationRuleValidation(**data) + msg = "When attribute_level is COHORT, attribute_name must be COHORT_LABEL or None (default:COHORT_LABEL)" + assert msg in str(error.value) diff --git a/tests/unit/validation/test_iteration_validator.py b/tests/unit/validation/test_iteration_validator.py new file mode 100644 index 000000000..bc2d54633 --- /dev/null +++ b/tests/unit/validation/test_iteration_validator.py @@ -0,0 +1,408 @@ +from datetime import UTC, datetime +from typing import ClassVar + +import pytest +from pydantic import ValidationError + +from rules_validation_api.validators.iteration_validator import IterationValidation + + +class TestMandatoryFieldsSchemaValidations: + def test_campaign_config_with_only_mandatory_fields_configuration( + self, valid_campaign_config_with_only_mandatory_fields + ): + try: + IterationValidation(**(valid_campaign_config_with_only_mandatory_fields["Iterations"][0])) + except ValidationError as e: + pytest.fail(f"Unexpected error during model instantiation: {e}") + + @pytest.mark.parametrize( + "mandatory_field", + [ + "ID", + "Version", + "Name", + "IterationDate", + "Type", + "DefaultCommsRouting", + "DefaultNotEligibleRouting", + "DefaultNotActionableRouting", + "IterationCohorts", + "IterationRules", + "ActionsMapper", + ], + ) + def test_missing_mandatory_fields(self, mandatory_field, valid_campaign_config_with_only_mandatory_fields): + data = valid_campaign_config_with_only_mandatory_fields["Iterations"][0].copy() + data.pop(mandatory_field, None) # Simulate missing field + with pytest.raises(ValidationError): + IterationValidation(**data) + assert mandatory_field.lower() + + # ID + @pytest.mark.parametrize("id_value", ["ITER001", "X123", "IT01"]) + def test_valid_id(self, id_value, valid_campaign_config_with_only_mandatory_fields): + data = {**valid_campaign_config_with_only_mandatory_fields["Iterations"][0], "ID": id_value} + model = IterationValidation(**data) + assert model.id == id_value + + # Version + @pytest.mark.parametrize("version_value", [1, 2, 100]) + def test_valid_version(self, version_value, valid_campaign_config_with_only_mandatory_fields): + data = {**valid_campaign_config_with_only_mandatory_fields["Iterations"][0], "Version": version_value} + model = IterationValidation(**data) + assert model.version == version_value + + # Name + @pytest.mark.parametrize("name_value", ["Mid-January Push", "Spring Surge", "Early Outreach"]) + def test_valid_name(self, name_value, valid_campaign_config_with_only_mandatory_fields): + data = {**valid_campaign_config_with_only_mandatory_fields["Iterations"][0], "Name": name_value} + model = IterationValidation(**data) + assert model.name == name_value + + # IterationDate + @pytest.mark.parametrize("date_value", ["20250101", "20250215", "20250301"]) + def test_valid_iteration_date(self, date_value, valid_campaign_config_with_only_mandatory_fields): + data = {**valid_campaign_config_with_only_mandatory_fields["Iterations"][0], "IterationDate": date_value} + model = IterationValidation(**data) + expected_date = datetime.strptime(str(date_value), "%Y%m%d").replace(tzinfo=UTC).date() + assert model.iteration_date == expected_date, f"Expected {expected_date}, got {model.iteration_date}" + + # Type + @pytest.mark.parametrize("type_value", ["A", "M", "S", "O"]) + def test_valid_type(self, type_value, valid_campaign_config_with_only_mandatory_fields): + data = {**valid_campaign_config_with_only_mandatory_fields["Iterations"][0], "Type": type_value} + model = IterationValidation(**data) + assert model.type == type_value + + @pytest.mark.parametrize("type_value", ["", "Z", None]) + def test_invalid_type(self, type_value, valid_campaign_config_with_only_mandatory_fields): + data = {**valid_campaign_config_with_only_mandatory_fields["Iterations"][0], "Type": type_value} + with pytest.raises(ValidationError): + IterationValidation(**data) + + # DefaultCommsRouting + @pytest.mark.parametrize("routing_value", ["BOOK_NBS"]) + def test_valid_default_comms_routing(self, routing_value, valid_campaign_config_with_only_mandatory_fields): + data = { + **valid_campaign_config_with_only_mandatory_fields["Iterations"][0], + "DefaultCommsRouting": routing_value, + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking", + } + }, + } + model = IterationValidation(**data) + assert model.default_comms_routing == routing_value + + # DefaultNotEligibleRouting + @pytest.mark.parametrize("routing_value", ["", "BOOK_NBS"]) + def test_valid_default_not_eligible_routing(self, routing_value, valid_campaign_config_with_only_mandatory_fields): + data = { + **valid_campaign_config_with_only_mandatory_fields["Iterations"][0], + "DefaultNotEligibleRouting": routing_value, + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking", + } + }, + } + model = IterationValidation(**data) + assert model.default_not_eligible_routing == routing_value + + # DefaultNotActionableRouting + @pytest.mark.parametrize("routing_value", ["", "BOOK_NBS"]) + def test_valid_default_not_actionable_routing( + self, routing_value, valid_campaign_config_with_only_mandatory_fields + ): + data = { + **valid_campaign_config_with_only_mandatory_fields["Iterations"][0], + "DefaultNotActionableRouting": routing_value, + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking", + } + }, + } + model = IterationValidation(**data) + assert model.default_not_actionable_routing == routing_value + + def test_invalid_actions_mapper_empty_key( + self, valid_campaign_config_with_only_mandatory_fields, valid_available_action + ): + actions_mapper = {"": valid_available_action, "action2": valid_available_action} + data = { + **valid_campaign_config_with_only_mandatory_fields["Iterations"][0], + "ActionsMapper": actions_mapper, + } + with pytest.raises(ValidationError): + IterationValidation(**data) + + +class TestOptionalFieldsSchemaValidations: + @pytest.mark.parametrize("iteration_number", [1, 5, 10]) + def test_iteration_number(self, iteration_number, valid_campaign_config_with_only_mandatory_fields): + data = { + **valid_campaign_config_with_only_mandatory_fields["Iterations"][0], + "IterationNumber": iteration_number, + } + model = IterationValidation(**data) + assert model.iteration_number == iteration_number + + @pytest.mark.parametrize("approval_minimum", [0, 25, 99]) + def test_approval_minimum(self, approval_minimum, valid_campaign_config_with_only_mandatory_fields): + data = { + **valid_campaign_config_with_only_mandatory_fields["Iterations"][0], + "ApprovalMinimum": approval_minimum, + } + model = IterationValidation(**data) + assert model.approval_minimum == approval_minimum + + @pytest.mark.parametrize("approval_maximum", [100, 250, 999]) + def test_approval_maximum(self, approval_maximum, valid_campaign_config_with_only_mandatory_fields): + data = { + **valid_campaign_config_with_only_mandatory_fields["Iterations"][0], + "ApprovalMaximum": approval_maximum, + } + model = IterationValidation(**data) + assert model.approval_maximum == approval_maximum + + +class TestIterationCohortsSchemaValidations: + book_local_1_action: ClassVar[dict] = { + "ExternalRoutingCode": "BookLocal_1", + "ActionDescription": "##Getting the vaccine\n" + "You can get an RSV vaccination at your GP surgery.\n" + "Your GP surgery may contact you about getting the RSV vaccine. " + "This may be by letter, text, phone call, email or through the NHS App. " + "You do not need to wait to be contacted before booking your vaccination.", + "ActionType": "InfoText", + } + + book_local_2_action: ClassVar[dict] = { + "ExternalRoutingCode": "BookLocal_2", + "ActionDescription": "##Getting the vaccine\n" + "You can get an RSV vaccination at your GP surgery.\n" + "Your GP surgery may contact you about getting the RSV vaccine. " + "This may be by letter, text, phone call, email or through the NHS App. " + "You do not need to wait to be contacted before booking your vaccination.", + "ActionType": "InfoText", + } + + def test_valid_iteration_if_actions_mapper_has_entry_for_the_provided_default_routing_key( + self, valid_campaign_config_with_only_mandatory_fields + ): + data = { + **valid_campaign_config_with_only_mandatory_fields["Iterations"][0], + "DefaultCommsRouting": "BOOK_LOCAL_1|BOOK_LOCAL_2", + "ActionsMapper": {"BOOK_LOCAL_1": self.book_local_1_action, "BOOK_LOCAL_2": self.book_local_2_action}, + } + IterationValidation(**data) + + def test_invalid_iteration_if_actions_mapper_has_doesnt_have_entries_for_every_default_not_default_routing_keys( + self, valid_campaign_config_with_only_mandatory_fields + ): + data = { + **valid_campaign_config_with_only_mandatory_fields["Iterations"][0], + "DefaultCommsRouting": "BOOK_LOCAL_1|BOOK_LOCAL_2", + "ActionsMapper": {"BOOK_LOCAL_1": self.book_local_1_action}, + } + with pytest.raises(ValidationError) as error: + IterationValidation(**data) + + errors = error.value.errors() + assert any(e["loc"][-1] == "actions_mapper" and "BOOK_LOCAL_2" in str(e["msg"]) for e in errors), ( + "Expected validation error for missing BOOK_LOCAL_2 entry in ActionsMapper" + ) + + def test_invalid_iteration_if_actions_mapper_has_no_entry_for_the_provided_default_routing_key( + self, valid_campaign_config_with_only_mandatory_fields + ): + data = { + **valid_campaign_config_with_only_mandatory_fields["Iterations"][0], + "DefaultCommsRouting": "BOOK_LOCAL", + "ActionsMapper": {}, + } # Missing BOOK_LOCAL in ActionsMapper + + with pytest.raises(ValidationError) as error: + IterationValidation(**data) + + errors = error.value.errors() + assert any(e["loc"][-1] == "actions_mapper" and "BOOK_LOCAL" in str(e["msg"]) for e in errors), ( + "Expected validation error for missing BOOK_LOCAL entry in ActionsMapper" + ) + + def test_valid_iteration_if_actions_mapper_has_entry_for_the_provided_default_not_eligible_routing_key( + self, valid_campaign_config_with_only_mandatory_fields + ): + data = { + **valid_campaign_config_with_only_mandatory_fields["Iterations"][0], + "DefaultNotEligibleRouting": "BOOK_LOCAL_1|BOOK_LOCAL_2", + "ActionsMapper": {"BOOK_LOCAL_1": self.book_local_1_action, "BOOK_LOCAL_2": self.book_local_2_action}, + } + IterationValidation(**data) + + def test_invalid_iteration_if_actions_mapper_has_doesnt_have_entries_for_every_default_not_eligible_routing_keys( + self, valid_campaign_config_with_only_mandatory_fields + ): + data = { + **valid_campaign_config_with_only_mandatory_fields["Iterations"][0], + "DefaultNotEligibleRouting": "BOOK_LOCAL_1|BOOK_LOCAL_2", + "ActionsMapper": {"BOOK_LOCAL_1": self.book_local_1_action}, + } + with pytest.raises(ValidationError) as error: + IterationValidation(**data) + + errors = error.value.errors() + assert any(e["loc"][-1] == "actions_mapper" and "BOOK_LOCAL_2" in str(e["msg"]) for e in errors), ( + "Expected validation error for missing BOOK_LOCAL_2 entry in ActionsMapper" + ) + + def test_invalid_iteration_if_actions_mapper_has_no_entry_for_the_provided_default_not_eligible_routing( + self, valid_campaign_config_with_only_mandatory_fields + ): + data = { + **valid_campaign_config_with_only_mandatory_fields["Iterations"][0], + "DefaultNotEligibleRouting": "BOOK_LOCAL", + "ActionsMapper": {}, + } # Missing BOOK_LOCAL in ActionsMapper + + with pytest.raises(ValidationError) as error: + IterationValidation(**data) + + errors = error.value.errors() + assert any(e["loc"][-1] == "actions_mapper" and "BOOK_LOCAL" in str(e["msg"]) for e in errors), ( + "Expected validation error for missing BOOK_LOCAL entry in ActionsMapper" + ) + + def test_valid_iteration_if_actions_mapper_has_entry_for_the_provided_default_not_actionable_routing_key( + self, valid_campaign_config_with_only_mandatory_fields + ): + data = { + **valid_campaign_config_with_only_mandatory_fields["Iterations"][0], + "DefaultNotActionableRouting": "BOOK_LOCAL_1|BOOK_LOCAL_2", + "ActionsMapper": {"BOOK_LOCAL_1": self.book_local_1_action, "BOOK_LOCAL_2": self.book_local_2_action}, + } + IterationValidation(**data) + + def test_invalid_iteration_if_actions_mapper_has_doesnt_have_entries_for_every_default_not_actionable_routing_keys( + self, valid_campaign_config_with_only_mandatory_fields + ): + data = { + **valid_campaign_config_with_only_mandatory_fields["Iterations"][0], + "DefaultNotActionableRouting": "BOOK_LOCAL_1|BOOK_LOCAL_2", + "ActionsMapper": {"BOOK_LOCAL_1": self.book_local_1_action}, + } + with pytest.raises(ValidationError) as error: + IterationValidation(**data) + + errors = error.value.errors() + assert any(e["loc"][-1] == "actions_mapper" and "BOOK_LOCAL_2" in str(e["msg"]) for e in errors), ( + "Expected validation error for missing BOOK_LOCAL_2 entry in ActionsMapper" + ) + + def test_invalid_iteration_if_actions_mapper_has_no_entry_for_the_provided_default_not_actionable_routing( + self, valid_campaign_config_with_only_mandatory_fields + ): + data = { + **valid_campaign_config_with_only_mandatory_fields["Iterations"][0], + "DefaultNotActionableRouting": "BOOK_LOCAL", + "ActionsMapper": {}, + } # Missing BOOK_LOCAL in ActionsMapper + + with pytest.raises(ValidationError) as error: + IterationValidation(**data) + + errors = error.value.errors() + assert any(e["loc"][-1] == "actions_mapper" and "BOOK_LOCAL" in str(e["msg"]) for e in errors), ( + "Expected validation error for missing BOOK_LOCAL entry in ActionsMapper" + ) + + @pytest.mark.parametrize("rule_type", ["R", "X", "Y", "F"]) + @pytest.mark.parametrize( + ("default_routing", "actions_mapper"), + [ + ("BOOK_LOCAL_1|BOOK_LOCAL_2", {"BOOK_LOCAL_1": book_local_1_action, "BOOK_LOCAL_2": book_local_2_action}), + ("BOOK_LOCAL_1", {"BOOK_LOCAL_1": book_local_1_action}), + ("", {"BOOK_LOCAL_1": book_local_1_action}), + ], + ) + def test_valid_iteration_if_actions_mapper_exists_for_rule_routing( + self, valid_campaign_config_with_only_mandatory_fields, rule_type, default_routing, actions_mapper + ): + iteration_rule = { + "Type": rule_type, + "Name": "Test Rule", + "Description": "Test rule description", + "Operator": "is_empty", + "Comparator": "", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "elid_all_people", + "Priority": 100, + "CommsRouting": default_routing, + } + + iteration_data = { + **valid_campaign_config_with_only_mandatory_fields["Iterations"][0], + "IterationRules": [iteration_rule], + "ActionsMapper": actions_mapper, + } + + iteration = IterationValidation(**iteration_data) + assert iteration is not None, ( + f"Expected iteration to be valid for rule type '{rule_type}' with routing '{default_routing}'" + ) + + @pytest.mark.parametrize("rule_type", ["R", "X", "Y"]) + @pytest.mark.parametrize( + ("default_routing", "actions_mapper"), + [ + ("BOOK_LOCAL_1|BOOK_LOCAL_2", {"BOOK_LOCAL_2": book_local_2_action}), + ("BOOK_LOCAL_1", {"BOOK_LOCAL_2": book_local_2_action}), + ], + ) + def test_invalid_iteration_if_actions_mapper_exists_for_rule_routing( + self, valid_campaign_config_with_only_mandatory_fields, rule_type, default_routing, actions_mapper + ): + iteration_rule = { + "Type": rule_type, + "Name": "Test Rule", + "Description": "Test rule description", + "Operator": "is_empty", + "Comparator": "", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "elid_all_people", + "Priority": 100, + "CommsRouting": default_routing, + } + + iteration_data = { + **valid_campaign_config_with_only_mandatory_fields["Iterations"][0], + "IterationRules": [iteration_rule], + "ActionsMapper": actions_mapper, + } + + with pytest.raises(ValidationError) as error: + IterationValidation(**iteration_data) + + errors = error.value.errors() + assert any(e["loc"][-1] == "iteration_rules" and "BOOK_LOCAL_1" in str(e["msg"]) for e in errors), ( + "Expected validation error for missing BOOK_LOCAL entry in ActionsMapper" + ) diff --git a/tests/unit/validation/test_rule_validator.py b/tests/unit/validation/test_rule_validator.py new file mode 100644 index 000000000..a24419824 --- /dev/null +++ b/tests/unit/validation/test_rule_validator.py @@ -0,0 +1,17 @@ +import pytest +from pydantic import ValidationError + +from rules_validation_api.validators.rules_validator import RulesValidation + + +def test_valid_campaign_config(valid_campaign_config_with_only_mandatory_fields): + config_data = {"campaign_config": valid_campaign_config_with_only_mandatory_fields} + validated = RulesValidation(**config_data) + assert validated.campaign_config.name is not None + + +def test_invalid_campaign_config_missing_field(): + invalid_data = {} + + with pytest.raises(ValidationError): + RulesValidation(**invalid_data) diff --git a/tests/unit/views/test_eligibility.py b/tests/unit/views/test_eligibility.py index cd0dda37b..04d223236 100644 --- a/tests/unit/views/test_eligibility.py +++ b/tests/unit/views/test_eligibility.py @@ -14,7 +14,7 @@ from wireup.integration.flask import get_app_container from eligibility_signposting_api.audit.audit_service import AuditService -from eligibility_signposting_api.model.eligibility import ( +from eligibility_signposting_api.model.eligibility_status import ( ActionCode, ActionDescription, ActionType, @@ -22,11 +22,6 @@ Condition, EligibilityStatus, NHSNumber, - Reason, - RuleDescription, - RuleName, - RulePriority, - RuleType, Status, SuggestedAction, UrlLabel, @@ -39,13 +34,13 @@ build_suitability_results, get_or_default_query_params, ) -from eligibility_signposting_api.views.response_model import eligibility +from eligibility_signposting_api.views.response_model import eligibility_response from tests.fixtures.builders.model.eligibility import ( CohortResultFactory, ConditionFactory, EligibilityStatusFactory, ) -from tests.fixtures.matchers.eligibility import is_eligibility_cohort, is_suitability_rule +from tests.fixtures.matchers.eligibility import is_eligibility_cohort logger = logging.getLogger(__name__) @@ -121,6 +116,7 @@ def test_no_nhs_number_given(app: Flask, client: FlaskClient): response, is_response() .with_status_code(HTTPStatus.NOT_FOUND) + .with_headers(has_entries({"Content-Type": "application/fhir+json"})) .and_text( is_json_that( has_entries( @@ -158,6 +154,7 @@ def test_unexpected_error(app: Flask, client: FlaskClient): response, is_response() .with_status_code(HTTPStatus.INTERNAL_SERVER_ERROR) + .with_headers(has_entries({"Content-Type": "application/fhir+json"})) .and_text( is_json_that( has_entries( @@ -251,166 +248,6 @@ def test_build_eligibility_cohorts_results_consider_only_cohorts_groups_that_has ) -def test_build_suitability_results_with_deduplication(): - condition: Condition = ConditionFactory.build( - status=Status.not_actionable, - cohort_results=[ - CohortResultFactory.build( - cohort_code="cohort_group1", - status=Status.not_actionable, - reasons=[ - Reason( - rule_type=RuleType.suppression, - rule_name=RuleName("Exclude too young less than 75"), - rule_description=RuleDescription("your age is greater than 75"), - matcher_matched=False, - rule_priority=RulePriority(1), - ), - Reason( - rule_type=RuleType.suppression, - rule_name=RuleName("Exclude too young less than 75"), - rule_description=RuleDescription("your age is greater than 75"), - matcher_matched=False, - rule_priority=RulePriority(1), - ), - Reason( - rule_type=RuleType.suppression, - rule_name=RuleName("Exclude more than 100"), - rule_description=RuleDescription("your age is greater than 100"), - matcher_matched=False, - rule_priority=RulePriority(1), - ), - ], - ), - CohortResultFactory.build( - cohort_code="cohort_group2", - status=Status.not_actionable, - reasons=[ - Reason( - rule_type=RuleType.suppression, - rule_name=RuleName("Exclude too young less than 75"), - rule_description=RuleDescription("your age is greater than 75"), - matcher_matched=False, - rule_priority=RulePriority(1), - ) - ], - ), - CohortResultFactory.build( - cohort_code="cohort_group3", - status=Status.not_eligible, - reasons=[ - Reason( - rule_type=RuleType.filter, - rule_name=RuleName("Exclude is present in sw1"), - rule_description=RuleDescription("your a member of sw1"), - matcher_matched=False, - rule_priority=RulePriority(1), - ) - ], - ), - CohortResultFactory.build( - cohort_code="cohort_group4", - description="", - status=Status.not_actionable, - reasons=[ - Reason( - rule_type=RuleType.filter, - rule_name=RuleName("Already vaccinated"), - rule_description=RuleDescription("you have already vaccinated"), - matcher_matched=False, - rule_priority=RulePriority(1), - ) - ], - ), - ], - ) - - results = build_suitability_results(condition) - - assert_that( - results, - contains_exactly( - is_suitability_rule() - .with_rule_code("Exclude too young less than 75") - .and_rule_text("your age is greater than 75"), - is_suitability_rule().with_rule_code("Exclude more than 100").and_rule_text("your age is greater than 100"), - is_suitability_rule().with_rule_code("Already vaccinated").and_rule_text("you have already vaccinated"), - ), - ) - - -def test_build_suitability_results_when_rule_text_is_empty_or_null(): - condition: Condition = ConditionFactory.build( - status=Status.not_actionable, - cohort_results=[ - CohortResultFactory.build( - cohort_code="cohort_group1", - status=Status.not_actionable, - reasons=[ - Reason( - rule_type=RuleType.suppression, - rule_name=RuleName("Exclude too young less than 75"), - rule_description=RuleDescription("your age is greater than 75"), - matcher_matched=False, - rule_priority=RulePriority(1), - ), - Reason( - rule_type=RuleType.suppression, - rule_name=RuleName("Exclude more than 100"), - rule_description=RuleDescription(""), - matcher_matched=False, - rule_priority=RulePriority(1), - ), - Reason( - rule_type=RuleType.suppression, - rule_name=RuleName("Exclude more than 100"), - matcher_matched=False, - rule_description=None, - rule_priority=RulePriority(1), - ), - ], - ), - CohortResultFactory.build( - cohort_code="cohort_group2", - status=Status.not_actionable, - reasons=[ - Reason( - rule_type=RuleType.filter, - rule_name=RuleName("Exclude is present in sw1"), - rule_description=RuleDescription(""), - matcher_matched=False, - rule_priority=RulePriority(1), - ) - ], - ), - CohortResultFactory.build( - cohort_code="cohort_group3", - status=Status.not_actionable, - reasons=[ - Reason( - rule_type=RuleType.filter, - rule_name=RuleName("Exclude is present in sw1"), - rule_description=None, - matcher_matched=False, - rule_priority=RulePriority(1), - ) - ], - ), - ], - ) - - results = build_suitability_results(condition) - - assert_that( - results, - contains_exactly( - is_suitability_rule() - .with_rule_code("Exclude too young less than 75") - .and_rule_text("your age is greater than 75") - ), - ) - - def test_no_suitability_rules_for_actionable(): condition = ConditionFactory.build(status=Status.actionable, cohort_results=[]) @@ -433,12 +270,12 @@ def test_no_suitability_rules_for_actionable(): ) ], [ - eligibility.Action( - actionType=eligibility.ActionType("TYPE_A"), - actionCode=eligibility.ActionCode("CODE123"), - description=eligibility.Description("Some description"), - urlLink=eligibility.UrlLink("https://example.com"), - urlLabel=eligibility.UrlLabel("Learn more"), + eligibility_response.Action( + actionType=eligibility_response.ActionType("TYPE_A"), + actionCode=eligibility_response.ActionCode("CODE123"), + description=eligibility_response.Description("Some description"), + urlLink=eligibility_response.UrlLink("https://example.com"), + urlLabel=eligibility_response.UrlLabel("Learn more"), ) ], ), @@ -453,9 +290,9 @@ def test_no_suitability_rules_for_actionable(): ) ], [ - eligibility.Action( - actionType=eligibility.ActionType("TYPE_B"), - actionCode=eligibility.ActionCode("CODE123"), + eligibility_response.Action( + actionType=eligibility_response.ActionType("TYPE_B"), + actionCode=eligibility_response.ActionCode("CODE123"), description="", urlLink="", urlLabel="", @@ -481,23 +318,23 @@ def test_build_actions(suggested_actions, expected): def test_excludes_nulls_via_build_response(client: FlaskClient): - mocked_response = eligibility.EligibilityResponse( + mocked_response = eligibility_response.EligibilityResponse( responseId=uuid4(), - meta=eligibility.Meta(lastUpdated=eligibility.LastUpdated(datetime(2023, 1, 1, tzinfo=UTC))), + meta=eligibility_response.Meta(lastUpdated=eligibility_response.LastUpdated(datetime(2023, 1, 1, tzinfo=UTC))), processedSuggestions=[ - eligibility.ProcessedSuggestion( - condition=eligibility.ConditionName("ConditionA"), - status=eligibility.Status.actionable, - statusText=eligibility.StatusText("Go ahead"), + eligibility_response.ProcessedSuggestion( + condition=eligibility_response.ConditionName("ConditionA"), + status=eligibility_response.Status.actionable, + statusText=eligibility_response.StatusText("Go ahead"), eligibilityCohorts=[], suitabilityRules=[], actions=[ - eligibility.Action( - actionType=eligibility.ActionType("TYPE_A"), - actionCode=eligibility.ActionCode("CODE123"), - description=eligibility.Description(""), # Should be an empty string - urlLink=eligibility.UrlLink(""), # Should be an empty string - urlLabel=eligibility.UrlLabel(""), # Should be an empty string + eligibility_response.Action( + actionType=eligibility_response.ActionType("TYPE_A"), + actionCode=eligibility_response.ActionCode("CODE123"), + description=eligibility_response.Description(""), # Should be an empty string + urlLink=eligibility_response.UrlLink(""), # Should be an empty string + urlLabel=eligibility_response.UrlLabel(""), # Should be an empty string ) ], ) @@ -533,23 +370,23 @@ def test_excludes_nulls_via_build_response(client: FlaskClient): def test_build_response_include_values_that_are_not_null(client: FlaskClient): - mocked_response = eligibility.EligibilityResponse( + mocked_response = eligibility_response.EligibilityResponse( responseId=uuid4(), - meta=eligibility.Meta(lastUpdated=eligibility.LastUpdated(datetime(2023, 1, 1, tzinfo=UTC))), + meta=eligibility_response.Meta(lastUpdated=eligibility_response.LastUpdated(datetime(2023, 1, 1, tzinfo=UTC))), processedSuggestions=[ - eligibility.ProcessedSuggestion( - condition=eligibility.ConditionName("ConditionA"), - status=eligibility.Status.actionable, - statusText=eligibility.StatusText("Go ahead"), + eligibility_response.ProcessedSuggestion( + condition=eligibility_response.ConditionName("ConditionA"), + status=eligibility_response.Status.actionable, + statusText=eligibility_response.StatusText("Go ahead"), eligibilityCohorts=[], suitabilityRules=[], actions=[ - eligibility.Action( - actionType=eligibility.ActionType("TYPE_A"), - actionCode=eligibility.ActionCode("CODE123"), - description=eligibility.Description("Contact GP"), - urlLink=eligibility.UrlLink("https://example.dummy/"), - urlLabel=eligibility.UrlLabel("GP contact"), + eligibility_response.Action( + actionType=eligibility_response.ActionType("TYPE_A"), + actionCode=eligibility_response.ActionCode("CODE123"), + description=eligibility_response.Description("Contact GP"), + urlLink=eligibility_response.UrlLink("https://example.dummy/"), + urlLabel=eligibility_response.UrlLabel("GP contact"), ) ], ) @@ -584,46 +421,6 @@ def test_build_response_include_values_that_are_not_null(client: FlaskClient): assert action["urlLabel"] == "GP contact" -@pytest.mark.parametrize( - ("headers", "expected_request_id"), - [ - ({"X-Request-ID": "test-request-id-123"}, "test-request-id-123"), - ( - {"X-Request-ID": ""}, - "", - ), - ( - {}, # No headers provided - None, - ), - ], -) -def test_request_id_from_header_logging_variants( - app: Flask, client: FlaskClient, caplog, headers: dict[str, str], expected_request_id: str -): - """ - This test checks that the x-request-ID is logged so that it can be used to correlate logs - with that of the logs from api-gateway - """ - with ( - get_app_container(app).override.service(EligibilityService, new=FakeEligibilityService()), - get_app_container(app).override.service(AuditService, new=FakeAuditService()), - ): - with caplog.at_level(logging.INFO): - response = client.get("/patient-check/12345", headers=headers) - - request_id_logged = False - for record in caplog.records: - request_id = getattr(record, "X-Request-ID", None) - - if request_id == expected_request_id: - request_id_logged = True - break - - assert request_id_logged - assert response.status_code == HTTPStatus.OK - - def test_get_or_default_query_params_with_no_args(app: Flask): with app.test_request_context("/patient-check"): result = get_or_default_query_params() From 5f7c914f38249827e771ea381af9727846cdb55a Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Tue, 12 Aug 2025 11:43:45 +0100 Subject: [PATCH 70/72] added ELI-399, 373 regression tests tidying the VITA int tests removed 320-12 as invalid removed old code added Sean handover stuff --- poetry.lock | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 641bd9b07..a133dd4c6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.4 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -1589,11 +1589,8 @@ files = [ {file = "lxml-5.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:7ce1a171ec325192c6a636b64c94418e71a1964f56d002cc28122fceff0b6121"}, {file = "lxml-5.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:795f61bcaf8770e1b37eec24edf9771b307df3af74d1d6f27d812e15a9ff3872"}, {file = "lxml-5.4.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:29f451a4b614a7b5b6c2e043d7b64a15bd8304d7e767055e8ab68387a8cacf4e"}, - {file = "lxml-5.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:891f7f991a68d20c75cb13c5c9142b2a3f9eb161f1f12a9489c82172d1f133c0"}, {file = "lxml-5.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4aa412a82e460571fad592d0f93ce9935a20090029ba08eca05c614f99b0cc92"}, - {file = "lxml-5.4.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:ac7ba71f9561cd7d7b55e1ea5511543c0282e2b6450f122672a2694621d63b7e"}, {file = "lxml-5.4.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:c5d32f5284012deaccd37da1e2cd42f081feaa76981f0eaa474351b68df813c5"}, - {file = "lxml-5.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:ce31158630a6ac85bddd6b830cffd46085ff90498b397bd0a259f59d27a12188"}, {file = "lxml-5.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:31e63621e073e04697c1b2d23fcb89991790eef370ec37ce4d5d469f40924ed6"}, {file = "lxml-5.4.0-cp37-cp37m-win32.whl", hash = "sha256:be2ba4c3c5b7900246a8f866580700ef0d538f2ca32535e991027bdaba944063"}, {file = "lxml-5.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:09846782b1ef650b321484ad429217f5154da4d6e786636c38e434fa32e94e49"}, From f63d69d24ec755daa028c2187f6a3f23e1b32134 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Tue, 12 Aug 2025 11:50:48 +0100 Subject: [PATCH 71/72] added ELI-399, 373 regression tests tidying the VITA int tests removed 320-12 as invalid removed old code added Sean handover stuff refomatting to pass checks --- .../ELI-220-371/AUTO_RSV_ELI-220-01.json | 176 ++-- .../ELI-220-371/AUTO_RSV_ELI-220-02.json | 176 ++-- .../ELI-220-371/AUTO_RSV_ELI-220-03.json | 174 ++-- .../ELI-220-371/AUTO_RSV_ELI-220-04.json | 174 ++-- .../ELI-220-371/AUTO_RSV_ELI-220-05.json | 174 ++-- .../ELI-220-371/AUTO_RSV_ELI-220-06.json | 174 ++-- .../ELI-274-371/AUTO_RSV_ELI-274-01.json | 208 ++-- .../ELI-274-371/AUTO_RSV_ELI-274-05.json | 208 ++-- .../ELI-274-371/AUTO_RSV_ELI-274-06.json | 208 ++-- .../ELI-274-371/AUTO_RSV_ELI-274-07.json | 208 ++-- .../AUTO_RSV_REG_001.json | 12 +- .../.temp/AUTO_RSV_SB_001.json | 12 +- .../.temp/AUTO_RSV_SB_008.json | 18 +- .../smokeTestConfigs/AUTO_RSV_SB_001.json | 12 +- .../smokeTestConfigs/AUTO_RSV_SB_008.json | 18 +- .../storyTestConfigs/AUTO_RSV_ELI-155.json | 12 +- .../storyTestConfigs/AUTO_RSV_ELI-216.json | 12 +- .../storyTestConfigs/AUTO_RSV_ELI-219-1.json | 12 +- .../storyTestConfigs/AUTO_RSV_ELI-219-2.json | 12 +- .../storyTestConfigs/AUTO_RSV_ELI-219-3.json | 12 +- .../storyTestConfigs/AUTO_RSV_ELI-221-01.json | 12 +- .../storyTestConfigs/AUTO_RSV_ELI-221-02.json | 12 +- .../storyTestConfigs/AUTO_RSV_ELI-221-03.json | 12 +- .../storyTestConfigs/AUTO_RSV_ELI-222.json | 12 +- .../storyTestConfigs/AUTO_RSV_ELI-236.json | 12 +- .../storyTestConfigs/AUTO_RSV_ELI-295-1.json | 12 +- .../storyTestConfigs/AUTO_RSV_ELI-295-2.json | 12 +- .../storyTestConfigs/AUTO_RSV_ELI-295-3.json | 12 +- .../storyTestConfigs/AUTO_RSV_ELI-295-4.json | 12 +- .../storyTestConfigs/AUTO_RSV_ELI-317-1.json | 12 +- .../storyTestConfigs/AUTO_RSV_ELI-317-2.json | 12 +- .../storyTestConfigs/AUTO_RSV_ELI-317-3.json | 12 +- .../storyTestConfigs/AUTO_RSV_ELI-317-4.json | 12 +- .../storyTestConfigs/AUTO_RSV_ELI-317-5.json | 15 +- .../AUTO_RSV_ELI-320-COVID.json | 12 +- .../AUTO_RSV_ELI-320-MMR.json | 12 +- .../AUTO_RSV_ELI-320-RSV.json | 13 +- .../AUTO_RSV_ELI-320-SCREENING-1.json | 13 +- .../AUTO_RSV_ELI-320-SCREENING-2.json | 13 +- .../storyTestConfigs/AUTO_RSV_ELI-365.json | 904 ++++++++--------- .../AUTO_RSV_ELI-365v0.5.json | 904 ++++++++--------- .../storyTestConfigs/AUTO_RSV_ELI-373-01.json | 908 +++++++++--------- .../storyTestConfigs/AUTO_RSV_ELI-373-02.json | 896 ++++++++--------- .../storyTestConfigs/AUTO_RSV_ELI-399-01.json | 136 +-- .../storyTestConfigs/AUTO_RSV_ELI-399-02.json | 368 +++---- .../storyTestConfigs/AUTO_RSV_ELI-399-03.json | 368 +++---- .../storyTestConfigs/AUTO_RSV_ELI-399-04.json | 368 +++---- .../storyTestConfigs/AUTO_RSV_ELI-399-05.json | 206 ++-- .../storyTestConfigs/AUTO_RSV_ELI-399-06.json | 208 ++-- .../storyTestConfigs/AUTO_RSV_ELI-399-07.json | 136 +-- .../vita_integration_test_config.json | 12 +- 51 files changed, 3982 insertions(+), 3678 deletions(-) diff --git a/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-01.json b/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-01.json index 595578249..f2d7de1dc 100644 --- a/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-01.json +++ b/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-01.json @@ -1,88 +1,94 @@ { - "CampaignConfig": { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", - "Version": 1, - "Name": "ELI-220-01-Iteration-Config", - "Type": "V", - "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], - "IterationFrequency": "X", - "IterationType": "O", - "IterationTime": "07:00:00", - "StartDate": "20250717", - "EndDate": "20350717", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", - "Iterations": [ - { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", - "DefaultCommsRouting": "", - "DefaultNotActionableRouting": "", - "DefaultNotEligibleRouting": "", - "Version": 1, - "Name": "ELI-220-01-Iteration-Config", - "IterationDate": "20240808", - "IterationNumber": 1, - "CommsType": "I", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Type": "O", - "IterationCohorts": [ - { - "CohortLabel": "rsv_eli_220_cohort_1", - "CohortGroup": "rsv_eli_220_cohort_group", - "PositiveDescription": "are a member of eli_220_cohort_group_10", - "NegativeDescription": "are not a member of eli_220_cohort_group_10", - "Priority": 10 - }, - { - "CohortLabel": "rsv_eli_220_cohort_2", - "CohortGroup": "rsv_eli_220_cohort_group", - "PositiveDescription": "are a member of eli_220_cohort_group_0", - "NegativeDescription": "are not a member of eli_220_cohort_group_0", - "Priority": 0 - }, + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-220-01-Iteration-Config", + "Type": "V", + "Target": "RSV", + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "ELI-220-01-Iteration-Config", + "IterationDate": "20240808", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ { - "CohortLabel": "rsv_eli_220_cohort_3", - "CohortGroup": "rsv_eli_220_cohort_group", - "PositiveDescription": "are a member of eli_220_cohort_group_40", - "NegativeDescription": "are not a member of eli_220_cohort_group_40", - "Priority": 40 - }, - { - "CohortLabel": "rsv_eli_220_cohort_4", - "CohortGroup": "rsv_eli_220_cohort_group_other", - "PositiveDescription": "are a member of eli_220_cohort_group_other", - "NegativeDescription": "are not a member of eli_220_cohort_group_other", - "Priority": 20 - } - ], - "IterationRules": [ - { - "Type": "S", - "Name": "NotActionable Reason 1", - "Description": "Description 1", - "Operator": "Y>", - "Comparator": "-75", - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "CohortLabel": "rsv_eli_220_cohort_1", - "Priority": 100 - } - ], - "ActionsMapper": { - "BOOK_NBS": { - "ExternalRoutingCode": "BookNBS", - "ActionDescription": "", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Continue to booking" - } - } - } - ] - } + "CohortLabel": "rsv_eli_220_cohort_1", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "are a member of eli_220_cohort_group_10", + "NegativeDescription": "are not a member of eli_220_cohort_group_10", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_220_cohort_2", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "are a member of eli_220_cohort_group_0", + "NegativeDescription": "are not a member of eli_220_cohort_group_0", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_220_cohort_3", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "are a member of eli_220_cohort_group_40", + "NegativeDescription": "are not a member of eli_220_cohort_group_40", + "Priority": 40 + }, + { + "CohortLabel": "rsv_eli_220_cohort_4", + "CohortGroup": "rsv_eli_220_cohort_group_other", + "PositiveDescription": "are a member of eli_220_cohort_group_other", + "NegativeDescription": "are not a member of eli_220_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason 1", + "Description": "Description 1", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_220_cohort_1", + "Priority": 100 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } } diff --git a/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-02.json b/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-02.json index 1fb2b6871..2f4049674 100644 --- a/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-02.json +++ b/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-02.json @@ -1,88 +1,94 @@ { - "CampaignConfig": { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", - "Version": 1, - "Name": "ELI-220-01-Iteration-Config", - "Type": "V", - "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], - "IterationFrequency": "X", - "IterationType": "O", - "IterationTime": "07:00:00", - "StartDate": "20250717", - "EndDate": "20350717", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", - "Iterations": [ - { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", - "DefaultCommsRouting": "", - "DefaultNotActionableRouting": "", - "DefaultNotEligibleRouting": "", - "Version": 1, - "Name": "ELI-220-01-Iteration-Config", - "IterationDate": "20250717", - "IterationNumber": 1, - "CommsType": "I", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Type": "O", - "IterationCohorts": [ - { - "CohortLabel": "rsv_eli_220_cohort_1", - "CohortGroup": "rsv_eli_220_cohort_group", - "PositiveDescription": "are a member of eli_220_cohort_group_10", - "NegativeDescription": "are not a member of eli_220_cohort_group_10", - "Priority": 10 - }, - { - "CohortLabel": "rsv_eli_220_cohort_2", - "CohortGroup": "rsv_eli_220_cohort_group", - "PositiveDescription": "", - "NegativeDescription": "are not a member of eli_220_cohort_group_0", - "Priority": 0 - }, + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-220-01-Iteration-Config", + "Type": "V", + "Target": "RSV", + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "ELI-220-01-Iteration-Config", + "IterationDate": "20250717", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ { - "CohortLabel": "rsv_eli_220_cohort_3", - "CohortGroup": "rsv_eli_220_cohort_group", - "PositiveDescription": "are a member of eli_220_cohort_group_40", - "NegativeDescription": "are not a member of eli_220_cohort_group_40", - "Priority": 40 - }, - { - "CohortLabel": "rsv_eli_220_cohort_4", - "CohortGroup": "rsv_eli_220_cohort_group_other", - "PositiveDescription": "are a member of eli_220_cohort_group_other", - "NegativeDescription": "are not a member of eli_220_cohort_group_other", - "Priority": 20 - } - ], - "IterationRules": [ - { - "Type": "S", - "Name": "NotActionable Reason 1", - "Description": "Description 1", - "Operator": "Y>", - "Comparator": "-75", - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "CohortLabel": "rsv_eli_220_cohort_1", - "Priority": 100 - } - ], - "ActionsMapper": { - "BOOK_NBS": { - "ExternalRoutingCode": "BookNBS", - "ActionDescription": "", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Continue to booking" - } - } - } - ] - } + "CohortLabel": "rsv_eli_220_cohort_1", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "are a member of eli_220_cohort_group_10", + "NegativeDescription": "are not a member of eli_220_cohort_group_10", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_220_cohort_2", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "", + "NegativeDescription": "are not a member of eli_220_cohort_group_0", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_220_cohort_3", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "are a member of eli_220_cohort_group_40", + "NegativeDescription": "are not a member of eli_220_cohort_group_40", + "Priority": 40 + }, + { + "CohortLabel": "rsv_eli_220_cohort_4", + "CohortGroup": "rsv_eli_220_cohort_group_other", + "PositiveDescription": "are a member of eli_220_cohort_group_other", + "NegativeDescription": "are not a member of eli_220_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason 1", + "Description": "Description 1", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_220_cohort_1", + "Priority": 100 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } } diff --git a/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-03.json b/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-03.json index d5d8a3570..23ce6e5f5 100644 --- a/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-03.json +++ b/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-03.json @@ -1,87 +1,93 @@ { - "CampaignConfig": { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", - "Version": 1, - "Name": "ELI-220-03-Iteration-Config", - "Type": "V", - "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], - "IterationFrequency": "X", - "IterationType": "O", - "IterationTime": "07:00:00", - "StartDate": "20250717", - "EndDate": "20350717", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", - "Iterations": [ - { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", - "DefaultCommsRouting": "", - "DefaultNotActionableRouting": "", - "DefaultNotEligibleRouting": "", - "Version": 1, - "Name": "ELI-220-03-Iteration-Config", - "IterationDate": "20250717", - "IterationNumber": 1, - "CommsType": "I", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Type": "O", - "IterationCohorts": [ - { - "CohortLabel": "rsv_eli_220_cohort_1", - "CohortGroup": "rsv_eli_220_cohort_group", - "PositiveDescription": "are a member of eli_220_cohort_group_10", - "NegativeDescription": "are not a member of eli_220_cohort_group_10", - "Priority": 10 - }, - { - "CohortLabel": "rsv_eli_220_cohort_2", - "CohortGroup": "rsv_eli_220_cohort_group", - "PositiveDescription": "are a member of eli_220_cohort_group_0", - "NegativeDescription": "are not a member of eli_220_cohort_group_0", - "Priority": 0 - }, + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-220-03-Iteration-Config", + "Type": "V", + "Target": "RSV", + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "ELI-220-03-Iteration-Config", + "IterationDate": "20250717", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ { - "CohortLabel": "rsv_eli_220_cohort_3", - "CohortGroup": "rsv_eli_220_cohort_group", - "PositiveDescription": "are a member of eli_220_cohort_group_40", - "NegativeDescription": "are not a member of eli_220_cohort_group_40", - "Priority": 40 - }, - { - "CohortLabel": "rsv_eli_220_cohort_4", - "CohortGroup": "rsv_eli_220_cohort_group_other", - "PositiveDescription": "are a member of eli_220_cohort_group_other", - "NegativeDescription": "are not a member of eli_220_cohort_group_other", - "Priority": 20 - } - ], - "IterationRules": [ - { - "Type": "S", - "Name": "NotActionable Reason 1", - "Description": "Description 1", - "Operator": "Y>", - "Comparator": "-75", - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Priority": 100 - } - ], - "ActionsMapper": { - "BOOK_NBS": { - "ExternalRoutingCode": "BookNBS", - "ActionDescription": "", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Continue to booking" - } - } - } - ] - } + "CohortLabel": "rsv_eli_220_cohort_1", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "are a member of eli_220_cohort_group_10", + "NegativeDescription": "are not a member of eli_220_cohort_group_10", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_220_cohort_2", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "are a member of eli_220_cohort_group_0", + "NegativeDescription": "are not a member of eli_220_cohort_group_0", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_220_cohort_3", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "are a member of eli_220_cohort_group_40", + "NegativeDescription": "are not a member of eli_220_cohort_group_40", + "Priority": 40 + }, + { + "CohortLabel": "rsv_eli_220_cohort_4", + "CohortGroup": "rsv_eli_220_cohort_group_other", + "PositiveDescription": "are a member of eli_220_cohort_group_other", + "NegativeDescription": "are not a member of eli_220_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason 1", + "Description": "Description 1", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Priority": 100 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } } diff --git a/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-04.json b/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-04.json index fed4ee02a..b2043bd7f 100644 --- a/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-04.json +++ b/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-04.json @@ -1,87 +1,93 @@ { - "CampaignConfig": { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", - "Version": 1, - "Name": "ELI-220-04-Iteration-Config", - "Type": "V", - "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], - "IterationFrequency": "X", - "IterationType": "O", - "IterationTime": "07:00:00", - "StartDate": "20250717", - "EndDate": "20350717", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", - "Iterations": [ - { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", - "DefaultCommsRouting": "", - "DefaultNotActionableRouting": "", - "DefaultNotEligibleRouting": "", - "Version": 1, - "Name": "ELI-220-04-Iteration-Config", - "IterationDate": "20250717", - "IterationNumber": 1, - "CommsType": "I", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Type": "O", - "IterationCohorts": [ - { - "CohortLabel": "rsv_eli_220_cohort_1", - "CohortGroup": "rsv_eli_220_cohort_group", - "PositiveDescription": "are a member of eli_220_cohort_group_10", - "NegativeDescription": "are not a member of eli_220_cohort_group_10", - "Priority": 10 - }, - { - "CohortLabel": "rsv_eli_220_cohort_2", - "CohortGroup": "rsv_eli_220_cohort_group", - "PositiveDescription": "", - "NegativeDescription": "are not a member of eli_220_cohort_group_0", - "Priority": 0 - }, + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-220-04-Iteration-Config", + "Type": "V", + "Target": "RSV", + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "ELI-220-04-Iteration-Config", + "IterationDate": "20250717", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ { - "CohortLabel": "rsv_eli_220_cohort_3", - "CohortGroup": "rsv_eli_220_cohort_group", - "PositiveDescription": "are a member of eli_220_cohort_group_40", - "NegativeDescription": "are not a member of eli_220_cohort_group_40", - "Priority": 40 - }, - { - "CohortLabel": "rsv_eli_220_cohort_4", - "CohortGroup": "rsv_eli_220_cohort_group_other", - "PositiveDescription": "are a member of eli_220_cohort_group_other", - "NegativeDescription": "are not a member of eli_220_cohort_group_other", - "Priority": 20 - } - ], - "IterationRules": [ - { - "Type": "S", - "Name": "NotActionable Reason 1", - "Description": "Description 1", - "Operator": "Y>", - "Comparator": "-75", - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Priority": 100 - } - ], - "ActionsMapper": { - "BOOK_NBS": { - "ExternalRoutingCode": "BookNBS", - "ActionDescription": "", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Continue to booking" - } - } - } - ] - } + "CohortLabel": "rsv_eli_220_cohort_1", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "are a member of eli_220_cohort_group_10", + "NegativeDescription": "are not a member of eli_220_cohort_group_10", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_220_cohort_2", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "", + "NegativeDescription": "are not a member of eli_220_cohort_group_0", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_220_cohort_3", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "are a member of eli_220_cohort_group_40", + "NegativeDescription": "are not a member of eli_220_cohort_group_40", + "Priority": 40 + }, + { + "CohortLabel": "rsv_eli_220_cohort_4", + "CohortGroup": "rsv_eli_220_cohort_group_other", + "PositiveDescription": "are a member of eli_220_cohort_group_other", + "NegativeDescription": "are not a member of eli_220_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason 1", + "Description": "Description 1", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Priority": 100 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } } diff --git a/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-05.json b/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-05.json index 2251bc9c0..b49084fac 100644 --- a/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-05.json +++ b/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-05.json @@ -1,87 +1,93 @@ { - "CampaignConfig": { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", - "Version": 1, - "Name": "ELI-220-05-Iteration-Config", - "Type": "V", - "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], - "IterationFrequency": "X", - "IterationType": "O", - "IterationTime": "07:00:00", - "StartDate": "20250717", - "EndDate": "20350717", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", - "Iterations": [ - { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", - "DefaultCommsRouting": "", - "DefaultNotActionableRouting": "", - "DefaultNotEligibleRouting": "", - "Version": 1, - "Name": "ELI-220-04-Iteration-Config", - "IterationDate": "20250717", - "IterationNumber": 1, - "CommsType": "I", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Type": "O", - "IterationCohorts": [ - { - "CohortLabel": "rsv_eli_220_cohort_1", - "CohortGroup": "rsv_eli_220_cohort_group", - "PositiveDescription": "are a member of eli_220_cohort_group_10", - "NegativeDescription": "are not a member of eli_220_cohort_group_10", - "Priority": 10 - }, - { - "CohortLabel": "rsv_eli_220_cohort_2", - "CohortGroup": "rsv_eli_220_cohort_group", - "PositiveDescription": "are a member of eli_220_cohort_group_0", - "NegativeDescription": "are not a member of eli_220_cohort_group_0", - "Priority": 0 - }, + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-220-05-Iteration-Config", + "Type": "V", + "Target": "RSV", + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "ELI-220-04-Iteration-Config", + "IterationDate": "20250717", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ { - "CohortLabel": "rsv_eli_220_cohort_3", - "CohortGroup": "rsv_eli_220_cohort_group", - "PositiveDescription": "are a member of eli_220_cohort_group_40", - "NegativeDescription": "are not a member of eli_220_cohort_group_40", - "Priority": 40 - }, - { - "CohortLabel": "rsv_eli_220_cohort_4", - "CohortGroup": "rsv_eli_220_cohort_group_other", - "PositiveDescription": "are a member of eli_220_cohort_group_other", - "NegativeDescription": "are not a member of eli_220_cohort_group_other", - "Priority": 20 - } - ], - "IterationRules": [ - { - "Type": "F", - "Name": "NotActionable Reason 1", - "Description": "Description 1", - "Operator": "Y>", - "Comparator": "-75", - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Priority": 100 - } - ], - "ActionsMapper": { - "BOOK_NBS": { - "ExternalRoutingCode": "BookNBS", - "ActionDescription": "", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Continue to booking" - } - } - } - ] - } + "CohortLabel": "rsv_eli_220_cohort_1", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "are a member of eli_220_cohort_group_10", + "NegativeDescription": "are not a member of eli_220_cohort_group_10", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_220_cohort_2", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "are a member of eli_220_cohort_group_0", + "NegativeDescription": "are not a member of eli_220_cohort_group_0", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_220_cohort_3", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "are a member of eli_220_cohort_group_40", + "NegativeDescription": "are not a member of eli_220_cohort_group_40", + "Priority": 40 + }, + { + "CohortLabel": "rsv_eli_220_cohort_4", + "CohortGroup": "rsv_eli_220_cohort_group_other", + "PositiveDescription": "are a member of eli_220_cohort_group_other", + "NegativeDescription": "are not a member of eli_220_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "NotActionable Reason 1", + "Description": "Description 1", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Priority": 100 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } } diff --git a/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-06.json b/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-06.json index 31153ebf6..71a6495c8 100644 --- a/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-06.json +++ b/tests/e2e/data/configs/inProgressTestConfigs/ELI-220-371/AUTO_RSV_ELI-220-06.json @@ -1,87 +1,93 @@ { - "CampaignConfig": { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", - "Version": 1, - "Name": "ELI-220-06-Iteration-Config", - "Type": "V", - "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], - "IterationFrequency": "X", - "IterationType": "O", - "IterationTime": "07:00:00", - "StartDate": "20250717", - "EndDate": "20350717", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", - "Iterations": [ - { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", - "DefaultCommsRouting": "", - "DefaultNotActionableRouting": "", - "DefaultNotEligibleRouting": "", - "Version": 1, - "Name": "ELI-220-04-Iteration-Config", - "IterationDate": "20250717", - "IterationNumber": 1, - "CommsType": "I", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Type": "O", - "IterationCohorts": [ - { - "CohortLabel": "rsv_eli_220_cohort_1", - "CohortGroup": "rsv_eli_220_cohort_group", - "PositiveDescription": "are a member of eli_220_cohort_group_10", - "NegativeDescription": "are not a member of eli_220_cohort_group_10", - "Priority": 10 - }, - { - "CohortLabel": "rsv_eli_220_cohort_2", - "CohortGroup": "rsv_eli_220_cohort_group", - "PositiveDescription": "are a member of eli_220_cohort_group_0", - "NegativeDescription": "", - "Priority": 0 - }, + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-220-06-Iteration-Config", + "Type": "V", + "Target": "RSV", + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "ELI-220-04-Iteration-Config", + "IterationDate": "20250717", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ { - "CohortLabel": "rsv_eli_220_cohort_3", - "CohortGroup": "rsv_eli_220_cohort_group", - "PositiveDescription": "are a member of eli_220_cohort_group_40", - "NegativeDescription": "are not a member of eli_220_cohort_group_40", - "Priority": 40 - }, - { - "CohortLabel": "rsv_eli_220_cohort_4", - "CohortGroup": "rsv_eli_220_cohort_group_other", - "PositiveDescription": "are a member of eli_220_cohort_group_other", - "NegativeDescription": "are not a member of eli_220_cohort_group_other", - "Priority": 20 - } - ], - "IterationRules": [ - { - "Type": "F", - "Name": "NotActionable Reason 1", - "Description": "Description 1", - "Operator": "Y>", - "Comparator": "-75", - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Priority": 100 - } - ], - "ActionsMapper": { - "BOOK_NBS": { - "ExternalRoutingCode": "BookNBS", - "ActionDescription": "", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Continue to booking" - } - } - } - ] - } + "CohortLabel": "rsv_eli_220_cohort_1", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "are a member of eli_220_cohort_group_10", + "NegativeDescription": "are not a member of eli_220_cohort_group_10", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_220_cohort_2", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "are a member of eli_220_cohort_group_0", + "NegativeDescription": "", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_220_cohort_3", + "CohortGroup": "rsv_eli_220_cohort_group", + "PositiveDescription": "are a member of eli_220_cohort_group_40", + "NegativeDescription": "are not a member of eli_220_cohort_group_40", + "Priority": 40 + }, + { + "CohortLabel": "rsv_eli_220_cohort_4", + "CohortGroup": "rsv_eli_220_cohort_group_other", + "PositiveDescription": "are a member of eli_220_cohort_group_other", + "NegativeDescription": "are not a member of eli_220_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "NotActionable Reason 1", + "Description": "Description 1", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Priority": 100 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } } diff --git a/tests/e2e/data/configs/inProgressTestConfigs/ELI-274-371/AUTO_RSV_ELI-274-01.json b/tests/e2e/data/configs/inProgressTestConfigs/ELI-274-371/AUTO_RSV_ELI-274-01.json index 566f1b1e2..2334d3619 100644 --- a/tests/e2e/data/configs/inProgressTestConfigs/ELI-274-371/AUTO_RSV_ELI-274-01.json +++ b/tests/e2e/data/configs/inProgressTestConfigs/ELI-274-371/AUTO_RSV_ELI-274-01.json @@ -1,103 +1,109 @@ { - "CampaignConfig": { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", - "Version": 1, - "Name": "ELI-274-01-Iteration-Config", - "Type": "V", - "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], - "IterationFrequency": "X", - "IterationType": "O", - "IterationTime": "07:00:00", - "StartDate": "20250717", - "EndDate": "20350717", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", - "Iterations": [ - { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", - "DefaultCommsRouting": "", - "DefaultNotActionableRouting": "", - "DefaultNotEligibleRouting": "", - "Version": 1, - "Name": "ELI-274-01-Iteration-Config", - "IterationDate": "20250717", - "IterationNumber": 1, - "CommsType": "I", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Type": "O", - "IterationCohorts": [ - { - "CohortLabel": "rsv_eli_274_cohort_1", - "CohortGroup": "rsv_eli_274_cohort_group", - "PositiveDescription": "are a member of eli_274_cohort_group", - "NegativeDescription": "are not a member of eli_274_cohort_group", - "Priority": 0 - }, - { - "CohortLabel": "rsv_eli_274_cohort_2", - "CohortGroup": "rsv_eli_274_cohort_group", - "PositiveDescription": "are a member of eli_274_cohort_group", - "NegativeDescription": "are not a member of eli_274_cohort_group", - "Priority": 10 - }, - { - "CohortLabel": "rsv_eli_274_cohort_3", - "CohortGroup": "rsv_eli_274_cohort_group_other", - "PositiveDescription": "are a member of eli_274_cohort_group_other", - "NegativeDescription": "are not a member of eli_274_cohort_group_other", - "Priority": 20 - } - ], - "IterationRules": [ - { - "Type": "S", - "Name": "NotActionable Reason 1", - "Description": "Description 1", - "Operator": "Y>", - "Comparator": "-75", - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "CohortLabel": "rsv_eli_274_cohort_1", - "Priority": 100 - }, - { - "Type": "S", - "Name": "NotActionable Reason 2", - "Description": "Description 2", - "Operator": "=", - "Comparator": "AAA", - "AttributeLevel": "PERSON", - "AttributeName": "ICB", - "CohortLabel": "rsv_eli_274_cohort_2", - "Priority": 110 - }, - { - "Type": "S", - "Name": "NotActionable Reason 3", - "Description": "Description 3", - "Operator": "=", - "Comparator": "ZZZ", - "AttributeLevel": "PERSON", - "AttributeName": "COMMISSIONING_REGION", - "CohortLabel": "rsv_eli_274_cohort_3", - "Priority": 120 - } - ], - "ActionsMapper": { - "BOOK_NBS": { - "ExternalRoutingCode": "BookNBS", - "ActionDescription": "", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Continue to booking" - } - } - } - ] - } + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-274-01-Iteration-Config", + "Type": "V", + "Target": "RSV", + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "ELI-274-01-Iteration-Config", + "IterationDate": "20250717", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_274_cohort_1", + "CohortGroup": "rsv_eli_274_cohort_group", + "PositiveDescription": "are a member of eli_274_cohort_group", + "NegativeDescription": "are not a member of eli_274_cohort_group", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_274_cohort_2", + "CohortGroup": "rsv_eli_274_cohort_group", + "PositiveDescription": "are a member of eli_274_cohort_group", + "NegativeDescription": "are not a member of eli_274_cohort_group", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_274_cohort_3", + "CohortGroup": "rsv_eli_274_cohort_group_other", + "PositiveDescription": "are a member of eli_274_cohort_group_other", + "NegativeDescription": "are not a member of eli_274_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason 1", + "Description": "Description 1", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_274_cohort_1", + "Priority": 100 + }, + { + "Type": "S", + "Name": "NotActionable Reason 2", + "Description": "Description 2", + "Operator": "=", + "Comparator": "AAA", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CohortLabel": "rsv_eli_274_cohort_2", + "Priority": 110 + }, + { + "Type": "S", + "Name": "NotActionable Reason 3", + "Description": "Description 3", + "Operator": "=", + "Comparator": "ZZZ", + "AttributeLevel": "PERSON", + "AttributeName": "COMMISSIONING_REGION", + "CohortLabel": "rsv_eli_274_cohort_3", + "Priority": 120 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } } diff --git a/tests/e2e/data/configs/inProgressTestConfigs/ELI-274-371/AUTO_RSV_ELI-274-05.json b/tests/e2e/data/configs/inProgressTestConfigs/ELI-274-371/AUTO_RSV_ELI-274-05.json index 7b8d58b68..e7346fc51 100644 --- a/tests/e2e/data/configs/inProgressTestConfigs/ELI-274-371/AUTO_RSV_ELI-274-05.json +++ b/tests/e2e/data/configs/inProgressTestConfigs/ELI-274-371/AUTO_RSV_ELI-274-05.json @@ -1,103 +1,109 @@ { - "CampaignConfig": { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", - "Version": 1, - "Name": "ELI-274-05-Iteration-Config", - "Type": "V", - "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], - "IterationFrequency": "X", - "IterationType": "O", - "IterationTime": "07:00:00", - "StartDate": "20250717", - "EndDate": "20350717", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", - "Iterations": [ - { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", - "DefaultCommsRouting": "", - "DefaultNotActionableRouting": "", - "DefaultNotEligibleRouting": "", - "Version": 1, - "Name": "ELI-274-05-Iteration-Config", - "IterationDate": "20250717", - "IterationNumber": 1, - "CommsType": "I", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Type": "O", - "IterationCohorts": [ - { - "CohortLabel": "rsv_eli_274_cohort_1", - "CohortGroup": "rsv_eli_274_cohort_group", - "PositiveDescription": "are a member of eli_274_cohort_group", - "NegativeDescription": "are not a member of eli_274_cohort_group", - "Priority": 0 - }, - { - "CohortLabel": "rsv_eli_274_cohort_2", - "CohortGroup": "rsv_eli_274_cohort_group", - "PositiveDescription": "are a member of eli_274_cohort_group", - "NegativeDescription": "are not a member of eli_274_cohort_group", - "Priority": 10 - }, - { - "CohortLabel": "rsv_eli_274_cohort_3", - "CohortGroup": "rsv_eli_274_cohort_group_other", - "PositiveDescription": "are a member of eli_274_cohort_group_other", - "NegativeDescription": "are not a member of eli_274_cohort_group_other", - "Priority": 20 - } - ], - "IterationRules": [ - { - "Type": "S", - "Name": "NotActionable Reason", - "Description": "Description 1", - "Operator": "Y>", - "Comparator": "-75", - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "CohortLabel": "rsv_eli_274_cohort_1", - "Priority": 100 - }, - { - "Type": "S", - "Name": "NotActionable Reason", - "Description": "Description 2", - "Operator": "=", - "Comparator": "AAA", - "AttributeLevel": "PERSON", - "AttributeName": "ICB", - "CohortLabel": "rsv_eli_274_cohort_2", - "Priority": 110 - }, - { - "Type": "S", - "Name": "NotActionable Reason 3", - "Description": "Description 3", - "Operator": "=", - "Comparator": "ZZZ", - "AttributeLevel": "PERSON", - "AttributeName": "COMMISSIONING_REGION", - "CohortLabel": "rsv_eli_274_cohort_3", - "Priority": 120 - } - ], - "ActionsMapper": { - "BOOK_NBS": { - "ExternalRoutingCode": "BookNBS", - "ActionDescription": "", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Continue to booking" - } - } - } - ] - } + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-274-05-Iteration-Config", + "Type": "V", + "Target": "RSV", + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "ELI-274-05-Iteration-Config", + "IterationDate": "20250717", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_274_cohort_1", + "CohortGroup": "rsv_eli_274_cohort_group", + "PositiveDescription": "are a member of eli_274_cohort_group", + "NegativeDescription": "are not a member of eli_274_cohort_group", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_274_cohort_2", + "CohortGroup": "rsv_eli_274_cohort_group", + "PositiveDescription": "are a member of eli_274_cohort_group", + "NegativeDescription": "are not a member of eli_274_cohort_group", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_274_cohort_3", + "CohortGroup": "rsv_eli_274_cohort_group_other", + "PositiveDescription": "are a member of eli_274_cohort_group_other", + "NegativeDescription": "are not a member of eli_274_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 1", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_274_cohort_1", + "Priority": 100 + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 2", + "Operator": "=", + "Comparator": "AAA", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CohortLabel": "rsv_eli_274_cohort_2", + "Priority": 110 + }, + { + "Type": "S", + "Name": "NotActionable Reason 3", + "Description": "Description 3", + "Operator": "=", + "Comparator": "ZZZ", + "AttributeLevel": "PERSON", + "AttributeName": "COMMISSIONING_REGION", + "CohortLabel": "rsv_eli_274_cohort_3", + "Priority": 120 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } } diff --git a/tests/e2e/data/configs/inProgressTestConfigs/ELI-274-371/AUTO_RSV_ELI-274-06.json b/tests/e2e/data/configs/inProgressTestConfigs/ELI-274-371/AUTO_RSV_ELI-274-06.json index 7b41662c0..43b686186 100644 --- a/tests/e2e/data/configs/inProgressTestConfigs/ELI-274-371/AUTO_RSV_ELI-274-06.json +++ b/tests/e2e/data/configs/inProgressTestConfigs/ELI-274-371/AUTO_RSV_ELI-274-06.json @@ -1,103 +1,109 @@ { - "CampaignConfig": { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", - "Version": 1, - "Name": "ELI-274-06-Iteration-Config", - "Type": "V", - "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], - "IterationFrequency": "X", - "IterationType": "O", - "IterationTime": "07:00:00", - "StartDate": "20250717", - "EndDate": "20350717", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", - "Iterations": [ - { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", - "DefaultCommsRouting": "", - "DefaultNotActionableRouting": "", - "DefaultNotEligibleRouting": "", - "Version": 1, - "Name": "ELI-274-06-Iteration-Config", - "IterationDate": "20250717", - "IterationNumber": 1, - "CommsType": "I", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Type": "O", - "IterationCohorts": [ - { - "CohortLabel": "rsv_eli_274_cohort_1", - "CohortGroup": "rsv_eli_274_cohort_group", - "PositiveDescription": "are a member of eli_274_cohort_group", - "NegativeDescription": "are not a member of eli_274_cohort_group", - "Priority": 0 - }, - { - "CohortLabel": "rsv_eli_274_cohort_2", - "CohortGroup": "rsv_eli_274_cohort_group", - "PositiveDescription": "are a member of eli_274_cohort_group", - "NegativeDescription": "are not a member of eli_274_cohort_group", - "Priority": 10 - }, - { - "CohortLabel": "rsv_eli_274_cohort_3", - "CohortGroup": "rsv_eli_274_cohort_group_other", - "PositiveDescription": "are a member of eli_274_cohort_group_other", - "NegativeDescription": "are not a member of eli_274_cohort_group_other", - "Priority": 20 - } - ], - "IterationRules": [ - { - "Type": "S", - "Name": "NotActionable Reason", - "Description": "Description 1", - "Operator": "Y>", - "Comparator": "-75", - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "CohortLabel": "rsv_eli_274_cohort_1", - "Priority": 100 - }, - { - "Type": "S", - "Name": "NotActionable Reason", - "Description": "Description 2", - "Operator": "=", - "Comparator": "AAA", - "AttributeLevel": "PERSON", - "AttributeName": "ICB", - "CohortLabel": "rsv_eli_274_cohort_2", - "Priority": 110 - }, - { - "Type": "S", - "Name": "NotActionable Reason", - "Description": "Description 3", - "Operator": "=", - "Comparator": "ZZZ", - "AttributeLevel": "PERSON", - "AttributeName": "COMMISSIONING_REGION", - "CohortLabel": "rsv_eli_274_cohort_3", - "Priority": 120 - } - ], - "ActionsMapper": { - "BOOK_NBS": { - "ExternalRoutingCode": "BookNBS", - "ActionDescription": "", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Continue to booking" - } - } - } - ] - } + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-274-06-Iteration-Config", + "Type": "V", + "Target": "RSV", + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "ELI-274-06-Iteration-Config", + "IterationDate": "20250717", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_274_cohort_1", + "CohortGroup": "rsv_eli_274_cohort_group", + "PositiveDescription": "are a member of eli_274_cohort_group", + "NegativeDescription": "are not a member of eli_274_cohort_group", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_274_cohort_2", + "CohortGroup": "rsv_eli_274_cohort_group", + "PositiveDescription": "are a member of eli_274_cohort_group", + "NegativeDescription": "are not a member of eli_274_cohort_group", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_274_cohort_3", + "CohortGroup": "rsv_eli_274_cohort_group_other", + "PositiveDescription": "are a member of eli_274_cohort_group_other", + "NegativeDescription": "are not a member of eli_274_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 1", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_274_cohort_1", + "Priority": 100 + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 2", + "Operator": "=", + "Comparator": "AAA", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CohortLabel": "rsv_eli_274_cohort_2", + "Priority": 110 + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 3", + "Operator": "=", + "Comparator": "ZZZ", + "AttributeLevel": "PERSON", + "AttributeName": "COMMISSIONING_REGION", + "CohortLabel": "rsv_eli_274_cohort_3", + "Priority": 120 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } } diff --git a/tests/e2e/data/configs/inProgressTestConfigs/ELI-274-371/AUTO_RSV_ELI-274-07.json b/tests/e2e/data/configs/inProgressTestConfigs/ELI-274-371/AUTO_RSV_ELI-274-07.json index 09b2fc893..9332b871a 100644 --- a/tests/e2e/data/configs/inProgressTestConfigs/ELI-274-371/AUTO_RSV_ELI-274-07.json +++ b/tests/e2e/data/configs/inProgressTestConfigs/ELI-274-371/AUTO_RSV_ELI-274-07.json @@ -1,104 +1,110 @@ { - "CampaignConfig": { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", - "Version": 1, - "Name": "ELI-274-07-Iteration-Config", - "Type": "V", - "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], - "IterationFrequency": "X", - "IterationType": "O", - "IterationTime": "07:00:00", - "StartDate": "20250717", - "EndDate": "20350717", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", - "Iterations": [ - { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", - "DefaultCommsRouting": "", - "DefaultNotActionableRouting": "", - "DefaultNotEligibleRouting": "", - "Version": 1, - "Name": "ELI-274-07-Iteration-Config", - "IterationDate": "20250717", - "IterationNumber": 1, - "CommsType": "I", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Type": "O", - "IterationCohorts": [ - { - "CohortLabel": "rsv_eli_274_cohort_1", - "CohortGroup": "rsv_eli_274_cohort_group", - "PositiveDescription": "are a member of eli_274_cohort_group", - "NegativeDescription": "are not a member of eli_274_cohort_group", - "Priority": 0 - }, - { - "CohortLabel": "rsv_eli_274_cohort_2", - "CohortGroup": "rsv_eli_274_cohort_group", - "PositiveDescription": "are a member of eli_274_cohort_group", - "NegativeDescription": "are not a member of eli_274_cohort_group", - "Priority": 10 - }, - { - "CohortLabel": "rsv_eli_274_cohort_3", - "CohortGroup": "rsv_eli_274_cohort_group_other", - "PositiveDescription": "are a member of eli_274_cohort_group_other", - "NegativeDescription": "are not a member of eli_274_cohort_group_other", - "Priority": 20 - } - ], - "IterationRules": [ - { - "Type": "S", - "Name": "NotActionable Reason", - "Description": "", - "Operator": "Y>", - "Comparator": "-75", - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "CohortLabel": "rsv_eli_274_cohort_1", - "Priority": 100, + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-274-07-Iteration-Config", + "Type": "V", + "Target": "RSV", + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "ELI-274-07-Iteration-Config", + "IterationDate": "20250717", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_274_cohort_1", + "CohortGroup": "rsv_eli_274_cohort_group", + "PositiveDescription": "are a member of eli_274_cohort_group", + "NegativeDescription": "are not a member of eli_274_cohort_group", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_274_cohort_2", + "CohortGroup": "rsv_eli_274_cohort_group", + "PositiveDescription": "are a member of eli_274_cohort_group", + "NegativeDescription": "are not a member of eli_274_cohort_group", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_274_cohort_3", + "CohortGroup": "rsv_eli_274_cohort_group_other", + "PositiveDescription": "are a member of eli_274_cohort_group_other", + "NegativeDescription": "are not a member of eli_274_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_274_cohort_1", + "Priority": 100, "RuleStop:": "Y" - }, - { - "Type": "S", - "Name": "NotActionable Reason", - "Description": "Description 4", - "Operator": "=", - "Comparator": "AAA", - "AttributeLevel": "PERSON", - "AttributeName": "ICB", - "CohortLabel": "rsv_eli_274_cohort_2", - "Priority": 110 - }, - { - "Type": "S", - "Name": "NotActionable Reason", - "Description": "Description 3", - "Operator": "=", - "Comparator": "ZZZ", - "AttributeLevel": "PERSON", - "AttributeName": "COMMISSIONING_REGION", - "CohortLabel": "rsv_eli_274_cohort_3", - "Priority": 120 - } - ], - "ActionsMapper": { - "BOOK_NBS": { - "ExternalRoutingCode": "BookNBS", - "ActionDescription": "", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Continue to booking" - } - } - } - ] - } + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 4", + "Operator": "=", + "Comparator": "AAA", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CohortLabel": "rsv_eli_274_cohort_2", + "Priority": 110 + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 3", + "Operator": "=", + "Comparator": "ZZZ", + "AttributeLevel": "PERSON", + "AttributeName": "COMMISSIONING_REGION", + "CohortLabel": "rsv_eli_274_cohort_3", + "Priority": 120 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } } diff --git a/tests/e2e/data/configs/regressionTestConfigs/AUTO_RSV_REG_001.json b/tests/e2e/data/configs/regressionTestConfigs/AUTO_RSV_REG_001.json index 5f96bf0cc..e8bc72ffa 100644 --- a/tests/e2e/data/configs/regressionTestConfigs/AUTO_RSV_REG_001.json +++ b/tests/e2e/data/configs/regressionTestConfigs/AUTO_RSV_REG_001.json @@ -5,9 +5,15 @@ "Name": "Automation RSV - Regression Test Config", "Type": "V", "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", diff --git a/tests/e2e/data/configs/smokeTestConfigs/.temp/AUTO_RSV_SB_001.json b/tests/e2e/data/configs/smokeTestConfigs/.temp/AUTO_RSV_SB_001.json index 3bae11c85..efae1b3ee 100644 --- a/tests/e2e/data/configs/smokeTestConfigs/.temp/AUTO_RSV_SB_001.json +++ b/tests/e2e/data/configs/smokeTestConfigs/.temp/AUTO_RSV_SB_001.json @@ -5,9 +5,15 @@ "Name": "Automation RSV - Smoke Test Config", "Type": "V", "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", diff --git a/tests/e2e/data/configs/smokeTestConfigs/.temp/AUTO_RSV_SB_008.json b/tests/e2e/data/configs/smokeTestConfigs/.temp/AUTO_RSV_SB_008.json index 918ebcf42..7a896ab08 100644 --- a/tests/e2e/data/configs/smokeTestConfigs/.temp/AUTO_RSV_SB_008.json +++ b/tests/e2e/data/configs/smokeTestConfigs/.temp/AUTO_RSV_SB_008.json @@ -5,9 +5,15 @@ "Name": "Automation RSV - Smoke Test Config", "Type": "V", "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -94,21 +100,21 @@ "UrlLink": "", "UrlLabel": "" }, - "BOOK_LOCAL": { + "BOOK_LOCAL": { "ExternalRoutingCode": "BookLocal", "ActionDescription": "##Getting the vaccine\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", "ActionType": "InfoText", "UrlLink": "", "UrlLabel": "" }, - "MANAGE_LOCAL": { + "MANAGE_LOCAL": { "ExternalRoutingCode": "ManageLocal", "ActionDescription": "##You have an RSV vaccination appointment\nContact your healthcare provider to change or cancel your appointment.", "ActionType": "CardWithText", "UrlLink": "", "UrlLabel": "" }, - "CHECK_CORRECT": { + "CHECK_CORRECT": { "ExternalRoutingCode": "CheckCorrect", "ActionDescription": "##If you think this is incorrect\\nIf you have not had this vaccination and you think you should, speak to your healthcare professional.", "ActionType": "InfoText", diff --git a/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_001.json b/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_001.json index a21f9240a..99db5bd94 100644 --- a/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_001.json +++ b/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_001.json @@ -5,9 +5,15 @@ "Name": "Automation RSV - Smoke Test Config", "Type": "V", "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", diff --git a/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_008.json b/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_008.json index 3bf657272..73e672d14 100644 --- a/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_008.json +++ b/tests/e2e/data/configs/smokeTestConfigs/AUTO_RSV_SB_008.json @@ -5,9 +5,15 @@ "Name": "Automation RSV - Smoke Test Config", "Type": "V", "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -209,21 +215,21 @@ "UrlLink": null, "UrlLabel": "" }, - "BOOK_LOCAL": { + "BOOK_LOCAL": { "ExternalRoutingCode": "BookLocal", "ActionDescription": "##Getting the vaccine\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", "ActionType": "InfoText", "UrlLink": null, "UrlLabel": "" }, - "MANAGE_LOCAL": { + "MANAGE_LOCAL": { "ExternalRoutingCode": "ManageLocal", "ActionDescription": "##You have an RSV vaccination appointment\nContact your healthcare provider to change or cancel your appointment.", "ActionType": "CardWithText", "UrlLink": null, "UrlLabel": "" }, - "CHECK_CORRECT": { + "CHECK_CORRECT": { "ExternalRoutingCode": "CheckCorrect", "ActionDescription": "##If you think this is incorrect\\nIf you have not had this vaccination and you think you should, speak to your healthcare professional.", "ActionType": "InfoText", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-155.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-155.json index be504d28a..83223a5e7 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-155.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-155.json @@ -5,9 +5,15 @@ "Name": "ELI-155 - cohort_label is not supported in R rules", "Type": "V", "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json index 119ab9025..0fb07fd7b 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-216.json @@ -5,9 +5,15 @@ "Name": "ELI-216 - NHS Number check (NHS login) - 9000000006", "Type": "V", "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-1.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-1.json index dae24bbe2..c487c6a4c 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-1.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-1.json @@ -5,9 +5,15 @@ "Name": "ELI-219 - Support Actions R Rules - 9000000002", "Type": "V", "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-2.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-2.json index 477e61b63..a693246e8 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-2.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-2.json @@ -5,9 +5,15 @@ "Name": "ELI-219 - Support Actions R Rules - 9000000003", "Type": "V", "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-3.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-3.json index 5fe1ccbcc..1700f134c 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-3.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-219-3.json @@ -5,9 +5,15 @@ "Name": "ELI-219 - Support Actions R Rules - 9000000004", "Type": "V", "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-01.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-01.json index 8d5eac5e0..2dc45815d 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-01.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-01.json @@ -5,9 +5,15 @@ "Name": "ELI-221 - Create Rule Stop function - Rule Stop on 2nd rule", "Type": "V", "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-02.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-02.json index 07075f5d5..ff612086f 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-02.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-02.json @@ -5,9 +5,15 @@ "Name": "ELI-221 - Create Rule Stop function - Rule Stop on Vaccinated - Rule Stop: N", "Type": "V", "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-03.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-03.json index 5ce13aa38..a4a6b7645 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-03.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-221-03.json @@ -5,9 +5,15 @@ "Name": "ELI-221 - Create Rule Stop function - Rule Stop on Vaccinated - No Rule Stop", "Type": "V", "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-222.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-222.json index ee1e2d028..7a9f6b9dc 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-222.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-222.json @@ -5,9 +5,15 @@ "Name": "ELI-222 - Implement magic cohort", "Type": "V", "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-236.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-236.json index 38cf3269e..c1c28aff2 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-236.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-236.json @@ -5,9 +5,15 @@ "Name": "ELI-236 - NVL Functionality", "Type": "V", "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-1.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-1.json index 0ba38160e..6b386935b 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-1.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-1.json @@ -5,9 +5,15 @@ "Name": "ELI-295 - include generic text for Not Eligible (X/Y Rules)", "Type": "V", "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-2.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-2.json index c064b96a9..dfea9b20b 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-2.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-2.json @@ -5,9 +5,15 @@ "Name": "ELI-295 - include generic text for Not Eligible (X/Y Rules)", "Type": "V", "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-3.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-3.json index 227009e35..0517bf379 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-3.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-3.json @@ -5,9 +5,15 @@ "Name": "ELI-295 - include generic text for Not Eligible (X/Y Rules)", "Type": "V", "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-4.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-4.json index c4ae151c2..598c6ecb9 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-4.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-295-4.json @@ -5,9 +5,15 @@ "Name": "ELI-295 - include generic text for Not Eligible (X/Y Rules)", "Type": "V", "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-1.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-1.json index a69840055..68bbfdb79 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-1.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-1.json @@ -5,9 +5,15 @@ "Name": "ELI-317-1 - Empty Actions - 9000000005", "Type": "V", "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-2.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-2.json index 17721a620..b3e4374fc 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-2.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-2.json @@ -5,9 +5,15 @@ "Name": "ELI-317 - Empty Actions - 9000000006", "Type": "V", "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-3.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-3.json index cab0cde65..a50ee43e1 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-3.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-3.json @@ -5,9 +5,15 @@ "Name": "ELI-317 - includeActions=N - 9000000008", "Type": "V", "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-4.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-4.json index 75677718b..c76268245 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-4.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-4.json @@ -5,9 +5,15 @@ "Name": "ELI-317 - includeActions=N - 9000000009", "Type": "V", "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-5.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-5.json index e860547a6..8f3423553 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-5.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-317-5.json @@ -5,9 +5,15 @@ "Name": "ELI-317 - Multiple Actions - 9000000010", "Type": "V", "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -59,7 +65,8 @@ "ActionType": "ActionOneActionType", "UrlLink": "http://www.actiononeurl.com", "UrlLabel": "ActiononeUrlLabel" - },"ACTION_TWO": { + }, + "ACTION_TWO": { "ExternalRoutingCode": "ActionTwoRoutingCode", "ActionDescription": "ActionTwoDescription", "ActionType": "ActionTwoActionType", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-COVID.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-COVID.json index 271e955a0..b31ff7883 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-COVID.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-COVID.json @@ -5,9 +5,15 @@ "Name": "ELI-320 - COVID Config", "Type": "V", "Target": "COVID", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-MMR.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-MMR.json index 188c61e9a..27b8282f3 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-MMR.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-MMR.json @@ -5,9 +5,15 @@ "Name": "ELI-320 - MMR Config", "Type": "V", "Target": "MMR", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-RSV.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-RSV.json index 81ffe627a..5576a951e 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-RSV.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-RSV.json @@ -5,9 +5,15 @@ "Name": "ELI-320 - RSV Config", "Type": "V", "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -51,7 +57,6 @@ "Operator": "D>=", "Comparator": "0", "CommsRouting": "AMEND_NBS" - } ], "ActionsMapper": { diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-SCREENING-1.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-SCREENING-1.json index 4cc81a50f..2ee8ba910 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-SCREENING-1.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-SCREENING-1.json @@ -5,9 +5,15 @@ "Name": "ELI-320 - FLU SCREENING Config", "Type": "S", "Target": "FLU", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -51,7 +57,6 @@ "Operator": "D>=", "Comparator": "0", "CommsRouting": "AMEND_NBS" - } ], "ActionsMapper": { diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-SCREENING-2.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-SCREENING-2.json index 18ab67e61..8502bedc6 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-SCREENING-2.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-320-SCREENING-2.json @@ -5,9 +5,15 @@ "Name": "ELI-320 - RSV SCREENING Config", "Type": "S", "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", @@ -51,7 +57,6 @@ "Operator": "D>=", "Comparator": "0", "CommsRouting": "AMEND_NBS" - } ], "ActionsMapper": { diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-365.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-365.json index 534320670..8b7148750 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-365.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-365.json @@ -1,451 +1,457 @@ { - "CampaignConfig": { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", - "Version": 1, - "Name": "EliD RSV example config", - "Type": "V", - "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], - "IterationFrequency": "X", - "IterationType": "O", - "IterationTime": "07:00:00", - "StartDate": "20250717", - "EndDate": "20350717", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", - "Iterations": [ - { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", - "DefaultCommsRouting": "BOOK_LOCAL|HELP_SUPPORT", - "DefaultNotActionableRouting": "", - "DefaultNotEligibleRouting": "CHECK_CORRECT_X", - "Version": 1, - "Name": "EliD RSV example config", - "IterationDate": "20250717", - "IterationNumber": 1, - "CommsType": "I", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Type": "O", - "IterationCohorts": [ - { - "CohortLabel": "rsv_75to79", - "CohortGroup": "rsv_age", - "PositiveDescription": "are aged 75 to 79 years old", - "NegativeDescription": "are not aged 75 to 79 years old", - "Priority": 0 - }, - { - "CohortLabel": "rsv_80_since_02_Sept_2024", - "CohortGroup": "rsv_age_catchup", - "PositiveDescription": "turned 80 after 1st September 2024", - "NegativeDescription": "did not turn 80 after 1 September 2024", - "Priority": 10 - }, - { - "CohortLabel": "elid_all_people", - "CohortGroup": "magic_cohort", - "PositiveDescription": "", - "NegativeDescription": "", - "Priority": 20 - } - ], - "IterationRules": [ - { - "Type": "F", - "Name": "Remove from magic cohort unless already vaccinated or have future booking", - "Description": "Remove anyone NOT already vaccinated within the last 25 years and do not have a future booking from the magic cohort", - "Operator": "Y<=", - "Comparator": "-25[[NVL:18000101]]", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "LAST_SUCCESSFUL_DATE", - "CohortLabel": "elid_all_people", - "Priority": 100 - }, - { - "Type": "F", - "Name": "Remove from magic cohort unless already vaccinated or have future booking", - "Description": "Remove anyone without a future booking from magic cohort", - "Operator": "D<", - "Comparator": "0[[NVL:18000101]]", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_DATE", - "CohortLabel": "elid_all_people", - "Priority": 100 - }, - { - "Type": "F", - "Name": "Remove under 75 Years on day of execution", - "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", - "Priority": 120, - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Operator": "Y>", - "Comparator": "-75", - "CohortLabel": "rsv_75to79" - }, - { - "Type": "F", - "Name": "Remove over 80 on day of execution", - "Description": "Exclude anyone who turned 80 before 2nd September 2024", - "Priority": 130, - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Operator": "<", - "Comparator": "19440902", - "CohortLabel": "rsv_75to79" - }, - { - "Type": "F", - "Name": "Remove under 75 Years on day of execution", - "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", - "Priority": 140, - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Operator": "Y>", - "Comparator": "-75", - "CohortLabel": "rsv_80_since_02_Sept_2024" - }, - { - "Type": "F", - "Name": "Remove over 80 on day of execution", - "Description": "Exclude anyone who turned 80 before 2nd September 2024", - "Priority": 150, - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Operator": "<", - "Comparator": "19440902", - "CohortLabel": "rsv_80_since_02_Sept_2024" - }, - { - "Type": "F", - "Name": "Remove from rsv 80 cohort if already vaccinated", - "Description": "Remove anyone already vaccinated from 80 cohort", - "Operator": "Y>=", - "Comparator": "-25", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "LAST_SUCCESSFUL_DATE", - "CohortLabel": "rsv_80_since_02_Sept_2024", - "Priority": 160 - }, - { - "Type": "F", - "Name": "Remove from rsv 80 cohort if future booking", - "Description": "Remove anyone with a future booking from RSV 80 cohort", - "Operator": "D>=", - "Comparator": "0", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_DATE", - "CohortLabel": "rsv_80_since_02_Sept_2024", - "Priority": 170 - }, - { - "Type": "F", - "Name": "Remove from rsv 75-79 cohort if already vaccinated", - "Description": "Remove anyone already vaccinated from 75-79 cohort", - "Operator": "Y>=", - "Comparator": "-25", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "LAST_SUCCESSFUL_DATE", - "CohortLabel": "rsv_75to79", - "Priority": 180 - }, - { - "Type": "F", - "Name": "Remove from rsv 75-79 cohort if future booking", - "Description": "Remove anyone with a future booking from RSV 75-79 cohort", - "Operator": "D>=", - "Comparator": "0", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_DATE", - "CohortLabel": "rsv_75to79", - "Priority": 190 - }, - { - "Type": "S", - "Name": "Already Vaccinated", - "Description": "## You've had your RSV vaccination\n\nWe believe you had your vaccination.", - "Priority": 200, - "AttributeLevel": "TARGET", - "AttributeTarget": "RSV", - "AttributeName": "LAST_SUCCESSFUL_DATE", - "Operator": "Y>=", - "Comparator": "-25", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "Other Setting", - "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", - "Priority": 510, - "AttributeLevel": "PERSON", - "AttributeName": "CARE_HOME_FLAG", - "Operator": "=", - "Comparator": "Y", - "CohortLabel": "rsv_80_since_02_Sept_2024", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "Other Setting", - "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", - "Priority": 520, - "AttributeLevel": "PERSON", - "AttributeName": "DE_FLAG", - "Operator": "=", - "Comparator": "Y", - "CohortLabel": "rsv_80_since_02_Sept_2024", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "Other Setting", - "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", - "Priority": 530, - "AttributeLevel": "PERSON", - "AttributeName": "13Q_FLAG", - "Operator": "=", - "Comparator": "Y", - "CohortLabel": "rsv_80_since_02_Sept_2024", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "Other Setting", - "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", - "Priority": 540, - "AttributeLevel": "PERSON", - "AttributeName": "CARE_HOME_FLAG", - "Operator": "=", - "Comparator": "Y", - "CohortLabel": "rsv_75to79", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "Other Setting with no future booking", - "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", - "Priority": 550, - "AttributeLevel": "PERSON", - "AttributeName": "DE_FLAG", - "Operator": "=", - "Comparator": "Y", - "CohortLabel": "rsv_75to79", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "Other Setting", - "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", - "Priority": 560, - "AttributeLevel": "PERSON", - "AttributeName": "13Q_FLAG", - "Operator": "=", - "Comparator": "Y", - "CohortLabel": "rsv_75to79", - "RuleStop": "Y" - }, - { - "Type": "R", - "Name": "Actionable Future Booked NBS Appointment", - "Description": "Amend NBS future booking", - "Priority": 1000, - "Operator": "D>=", - "Comparator": "0", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_DATE", - "CommsRouting": "AMEND_NBS" - }, - { - "Type": "R", - "Name": "Actionable Future Booked NBS Appointment", - "Description": "Amend NBS future booking", - "Priority": 1000, - "Operator": "=", - "Comparator": "NBS", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", - "CommsRouting": "AMEND_NBS" - }, - { - "Type": "R", - "Name": "Actionable Future Booked Local Appointment", - "Description": "Amend local future booking", - "Priority": 1100, - "Operator": "D>=", - "Comparator": "0", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_DATE", - "CommsRouting": "MANAGE_LOCAL" - }, - { - "Type": "R", - "Name": "Within CP Expansion ICB", - "Description": "Book an appointment on NBS as within CP expansion", - "Priority": 1200, - "Operator": "in", - "Comparator": "QH8,QJG", - "AttributeLevel": "PERSON", - "AttributeName": "ICB", - "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" - }, - { - "Type": "R", - "Name": "Within CP Expansion ICB not 80 plus", - "Description": "Book an appointment on NBS as within CP expansion", - "Priority": 1200, - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Operator": "Y>", - "Comparator": "-80", - "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" - }, - { - "Type": "R", - "Name": "Within CP Expansion Local Authority", - "Description": "Book an appointment on NBS as within CP expansion", - "Priority": 1300, - "Operator": "in", - "Comparator": "E08000028,E08000031,E08000025,E06000016,E06000008,E07000117,E07000120,E08000011,E08000012,E07000122,E07000123,E08000014,E07000126,E08000013,E07000127,E08000015,E07000128", - "AttributeLevel": "PERSON", - "AttributeName": "LOCAL_AUTHORITY", - "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" - }, - { - "Type": "R", - "Name": "Within CP Expansion ICB not 80 plus", - "Description": "Book an appointment on NBS as within CP expansion", - "Priority": 1300, - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Operator": "Y>", - "Comparator": "-80", - "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" - }, - { - "Type": "Y", - "Name": "Already vaccinated default text", - "Description": "Already vaccinated default text", - "Priority": 3000, - "AttributeLevel": "TARGET", - "AttributeTarget": "RSV", - "AttributeName": "LAST_SUCCESSFUL_DATE", - "Operator": "Y>=", - "Comparator": "-25", - "CommsRouting": "CHECK_CORRECT_ALREADY_VACCINATED" - }, - { - "Type": "Y", - "Name": "Other setting default text", - "Description": "Other setting default text", - "Priority": 3100, - "AttributeLevel": "PERSON", - "AttributeName": "CARE_HOME_FLAG", - "Operator": "=", - "Comparator": "Y", - "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" - }, - { - "Type": "Y", - "Name": "Other setting default text", - "Description": "Other setting default text", - "Priority": 3200, - "AttributeLevel": "PERSON", - "AttributeName": "DE_FLAG", - "Operator": "=", - "Comparator": "Y", - "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" - }, - { - "Type": "Y", - "Name": "Other setting default text", - "Description": "Other setting default text", - "Priority": 3300, - "AttributeLevel": "PERSON", - "AttributeName": "13Q_FLAG", - "Operator": "=", - "Comparator": "Y", - "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" - } - ], - "ActionsMapper": { - "BOOK_NBS": { - "ExternalRoutingCode": "BookNBS", - "ActionDescription": "", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Continue to booking" - }, - "AMEND_NBS": { - "ExternalRoutingCode": "AmendNBS", - "ActionDescription": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Manage your appointment" - }, - "CONTACT_GP": { - "ExternalRoutingCode": "ContactGP", - "ActionDescription": "Contact your GP", - "ActionType": "InfoText", - "UrlLink": null, - "UrlLabel": "" - }, - "BOOK_LOCAL": { - "ExternalRoutingCode": "BookLocal", - "ActionDescription": "##Getting the vaccine\n\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", - "ActionType": "InfoText", - "UrlLink": null, - "UrlLabel": "" - }, - "MANAGE_LOCAL": { - "ExternalRoutingCode": "ManageLocal", - "ActionDescription": "##You have an RSV vaccination appointment\n\nContact your healthcare provider to change or cancel your appointment.", - "ActionType": "CardWithText", - "UrlLink": null, - "UrlLabel": "" - }, - "HELP_SUPPORT": { - "ExternalRoutingCode": "HelpSupportInfo", - "ActionDescription": "## CONTENT TBC\n\nBlah blah blah.", - "ActionType": "InfoText", - "UrlLink": null, - "UrlLabel": "" - }, - "CHECK_CORRECT_X": { - "ExternalRoutingCode": "HealthcareProInfo", - "ActionDescription": "## If you think this is incorrect\n\nSpeak to your healthcare professional if you think you should be offered this vaccine.\n\nFor anything else, visit our help and support page. (ADD LINK)", - "ActionType": "InfoText", - "UrlLink": null, - "UrlLabel": "" - }, - "CHECK_CORRECT_ALREADY_VACCINATED": { - "ExternalRoutingCode": "AlreadyVaccinatedInfo", - "ActionDescription": "## If you think this is incorrect\n\nIf you believe you've not been vaccinated against RSV, speak to your healthcare professional.\n\nFor anything else please see our help and support page. (ADD LINK).", - "ActionType": "InfoText", - "UrlLink": null, - "UrlLabel": "" - }, - "CHECK_CORRECT_OTHER_SETTING": { - "ExternalRoutingCode": "ManagedSettingInfo", - "ActionDescription": "## If you think this is incorrect\n\nIf you have already had this vaccination or your personal details are wrong, visit our help and support page. (ADD LINK).", - "ActionType": "InfoText", - "UrlLink": null, - "UrlLabel": "" - } - } - } - ] - } + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "EliD RSV example config", + "Type": "V", + "Target": "RSV", + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "BOOK_LOCAL|HELP_SUPPORT", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "CHECK_CORRECT_X", + "Version": 1, + "Name": "EliD RSV example config", + "IterationDate": "20250717", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_75to79", + "CohortGroup": "rsv_age", + "PositiveDescription": "are aged 75 to 79 years old", + "NegativeDescription": "are not aged 75 to 79 years old", + "Priority": 0 + }, + { + "CohortLabel": "rsv_80_since_02_Sept_2024", + "CohortGroup": "rsv_age_catchup", + "PositiveDescription": "turned 80 after 1st September 2024", + "NegativeDescription": "did not turn 80 after 1 September 2024", + "Priority": 10 + }, + { + "CohortLabel": "elid_all_people", + "CohortGroup": "magic_cohort", + "PositiveDescription": "", + "NegativeDescription": "", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "Remove from magic cohort unless already vaccinated or have future booking", + "Description": "Remove anyone NOT already vaccinated within the last 25 years and do not have a future booking from the magic cohort", + "Operator": "Y<=", + "Comparator": "-25[[NVL:18000101]]", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "elid_all_people", + "Priority": 100 + }, + { + "Type": "F", + "Name": "Remove from magic cohort unless already vaccinated or have future booking", + "Description": "Remove anyone without a future booking from magic cohort", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CohortLabel": "elid_all_people", + "Priority": 100 + }, + { + "Type": "F", + "Name": "Remove under 75 Years on day of execution", + "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", + "Priority": 120, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75", + "CohortLabel": "rsv_75to79" + }, + { + "Type": "F", + "Name": "Remove over 80 on day of execution", + "Description": "Exclude anyone who turned 80 before 2nd September 2024", + "Priority": 130, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "<", + "Comparator": "19440902", + "CohortLabel": "rsv_75to79" + }, + { + "Type": "F", + "Name": "Remove under 75 Years on day of execution", + "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", + "Priority": 140, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75", + "CohortLabel": "rsv_80_since_02_Sept_2024" + }, + { + "Type": "F", + "Name": "Remove over 80 on day of execution", + "Description": "Exclude anyone who turned 80 before 2nd September 2024", + "Priority": 150, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "<", + "Comparator": "19440902", + "CohortLabel": "rsv_80_since_02_Sept_2024" + }, + { + "Type": "F", + "Name": "Remove from rsv 80 cohort if already vaccinated", + "Description": "Remove anyone already vaccinated from 80 cohort", + "Operator": "Y>=", + "Comparator": "-25", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "Priority": 160 + }, + { + "Type": "F", + "Name": "Remove from rsv 80 cohort if future booking", + "Description": "Remove anyone with a future booking from RSV 80 cohort", + "Operator": "D>=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "Priority": 170 + }, + { + "Type": "F", + "Name": "Remove from rsv 75-79 cohort if already vaccinated", + "Description": "Remove anyone already vaccinated from 75-79 cohort", + "Operator": "Y>=", + "Comparator": "-25", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "rsv_75to79", + "Priority": 180 + }, + { + "Type": "F", + "Name": "Remove from rsv 75-79 cohort if future booking", + "Description": "Remove anyone with a future booking from RSV 75-79 cohort", + "Operator": "D>=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CohortLabel": "rsv_75to79", + "Priority": 190 + }, + { + "Type": "S", + "Name": "Already Vaccinated", + "Description": "## You've had your RSV vaccination\n\nWe believe you had your vaccination.", + "Priority": 200, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "Y>=", + "Comparator": "-25", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Other Setting", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 510, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Other Setting", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 520, + "AttributeLevel": "PERSON", + "AttributeName": "DE_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Other Setting", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 530, + "AttributeLevel": "PERSON", + "AttributeName": "13Q_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Other Setting", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 540, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_75to79", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Other Setting with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 550, + "AttributeLevel": "PERSON", + "AttributeName": "DE_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_75to79", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Other Setting", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 560, + "AttributeLevel": "PERSON", + "AttributeName": "13Q_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_75to79", + "RuleStop": "Y" + }, + { + "Type": "R", + "Name": "Actionable Future Booked NBS Appointment", + "Description": "Amend NBS future booking", + "Priority": 1000, + "Operator": "D>=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked NBS Appointment", + "Description": "Amend NBS future booking", + "Priority": 1000, + "Operator": "=", + "Comparator": "NBS", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Local Appointment", + "Description": "Amend local future booking", + "Priority": 1100, + "Operator": "D>=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "MANAGE_LOCAL" + }, + { + "Type": "R", + "Name": "Within CP Expansion ICB", + "Description": "Book an appointment on NBS as within CP expansion", + "Priority": 1200, + "Operator": "in", + "Comparator": "QH8,QJG", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" + }, + { + "Type": "R", + "Name": "Within CP Expansion ICB not 80 plus", + "Description": "Book an appointment on NBS as within CP expansion", + "Priority": 1200, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-80", + "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" + }, + { + "Type": "R", + "Name": "Within CP Expansion Local Authority", + "Description": "Book an appointment on NBS as within CP expansion", + "Priority": 1300, + "Operator": "in", + "Comparator": "E08000028,E08000031,E08000025,E06000016,E06000008,E07000117,E07000120,E08000011,E08000012,E07000122,E07000123,E08000014,E07000126,E08000013,E07000127,E08000015,E07000128", + "AttributeLevel": "PERSON", + "AttributeName": "LOCAL_AUTHORITY", + "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" + }, + { + "Type": "R", + "Name": "Within CP Expansion ICB not 80 plus", + "Description": "Book an appointment on NBS as within CP expansion", + "Priority": 1300, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-80", + "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" + }, + { + "Type": "Y", + "Name": "Already vaccinated default text", + "Description": "Already vaccinated default text", + "Priority": 3000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "Y>=", + "Comparator": "-25", + "CommsRouting": "CHECK_CORRECT_ALREADY_VACCINATED" + }, + { + "Type": "Y", + "Name": "Other setting default text", + "Description": "Other setting default text", + "Priority": 3100, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y", + "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" + }, + { + "Type": "Y", + "Name": "Other setting default text", + "Description": "Other setting default text", + "Priority": 3200, + "AttributeLevel": "PERSON", + "AttributeName": "DE_FLAG", + "Operator": "=", + "Comparator": "Y", + "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" + }, + { + "Type": "Y", + "Name": "Other setting default text", + "Description": "Other setting default text", + "Priority": 3300, + "AttributeLevel": "PERSON", + "AttributeName": "13Q_FLAG", + "Operator": "=", + "Comparator": "Y", + "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + }, + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Manage your appointment" + }, + "CONTACT_GP": { + "ExternalRoutingCode": "ContactGP", + "ActionDescription": "Contact your GP", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "BOOK_LOCAL": { + "ExternalRoutingCode": "BookLocal", + "ActionDescription": "##Getting the vaccine\n\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "MANAGE_LOCAL": { + "ExternalRoutingCode": "ManageLocal", + "ActionDescription": "##You have an RSV vaccination appointment\n\nContact your healthcare provider to change or cancel your appointment.", + "ActionType": "CardWithText", + "UrlLink": null, + "UrlLabel": "" + }, + "HELP_SUPPORT": { + "ExternalRoutingCode": "HelpSupportInfo", + "ActionDescription": "## CONTENT TBC\n\nBlah blah blah.", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "CHECK_CORRECT_X": { + "ExternalRoutingCode": "HealthcareProInfo", + "ActionDescription": "## If you think this is incorrect\n\nSpeak to your healthcare professional if you think you should be offered this vaccine.\n\nFor anything else, visit our help and support page. (ADD LINK)", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "CHECK_CORRECT_ALREADY_VACCINATED": { + "ExternalRoutingCode": "AlreadyVaccinatedInfo", + "ActionDescription": "## If you think this is incorrect\n\nIf you believe you've not been vaccinated against RSV, speak to your healthcare professional.\n\nFor anything else please see our help and support page. (ADD LINK).", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "CHECK_CORRECT_OTHER_SETTING": { + "ExternalRoutingCode": "ManagedSettingInfo", + "ActionDescription": "## If you think this is incorrect\n\nIf you have already had this vaccination or your personal details are wrong, visit our help and support page. (ADD LINK).", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + } + } + } + ] + } } diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-365v0.5.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-365v0.5.json index 78f94df3c..e0deed583 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-365v0.5.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-365v0.5.json @@ -1,451 +1,457 @@ { - "CampaignConfig": { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", - "Version": 1, - "Name": "EliD RSV example config", - "Type": "V", - "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], - "IterationFrequency": "X", - "IterationType": "O", - "IterationTime": "07:00:00", - "StartDate": "20250717", - "EndDate": "20350717", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", - "Iterations": [ - { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", - "DefaultCommsRouting": "BOOK_LOCAL|HELP_SUPPORT", - "DefaultNotActionableRouting": "", - "DefaultNotEligibleRouting": "CHECK_CORRECT_X", - "Version": 1, - "Name": "EliD RSV example config", - "IterationDate": "20250717", - "IterationNumber": 1, - "CommsType": "I", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Type": "O", - "IterationCohorts": [ - { - "CohortLabel": "rsv_75to79", - "CohortGroup": "rsv_age", - "PositiveDescription": "are aged 75 to 79 years old", - "NegativeDescription": "are not aged 75 to 79 years old", - "Priority": 0 - }, - { - "CohortLabel": "rsv_80_since_02_Sept_2024", - "CohortGroup": "rsv_age_catchup", - "PositiveDescription": "turned 80 after 1st September 2024", - "NegativeDescription": "did not turn 80 after 1 September 2024", - "Priority": 10 - }, - { - "CohortLabel": "elid_all_people", - "CohortGroup": "magic_cohort", - "PositiveDescription": "", - "NegativeDescription": "", - "Priority": 20 - } - ], - "IterationRules": [ - { - "Type": "F", - "Name": "Remove from magic cohort unless already vaccinated or have future booking", - "Description": "Remove anyone NOT already vaccinated within the last 25 years and do not have a future booking from the magic cohort", - "Operator": "Y<=", - "Comparator": "-25[[NVL:18000101]]", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "LAST_SUCCESSFUL_DATE", - "CohortLabel": "elid_all_people", - "Priority": 100 - }, - { - "Type": "F", - "Name": "Remove from magic cohort unless already vaccinated or have future booking", - "Description": "Remove anyone without a future booking from magic cohort", - "Operator": "D<", - "Comparator": "0[[NVL:18000101]]", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_DATE", - "CohortLabel": "elid_all_people", - "Priority": 100 - }, - { - "Type": "F", - "Name": "Remove under 75 Years on day of execution", - "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", - "Priority": 120, - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Operator": "Y>", - "Comparator": "-75", - "CohortLabel": "rsv_75to79" - }, - { - "Type": "F", - "Name": "Remove over 80 on day of execution", - "Description": "Exclude anyone who turned 80 before 2nd September 2024", - "Priority": 130, - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Operator": "<", - "Comparator": "19440902", - "CohortLabel": "rsv_75to79" - }, - { - "Type": "F", - "Name": "Remove under 75 Years on day of execution", - "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", - "Priority": 140, - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Operator": "Y>", - "Comparator": "-75", - "CohortLabel": "rsv_80_since_02_Sept_2024" - }, - { - "Type": "F", - "Name": "Remove over 80 on day of execution", - "Description": "Exclude anyone who turned 80 before 2nd September 2024", - "Priority": 150, - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Operator": "<", - "Comparator": "19440902", - "CohortLabel": "rsv_80_since_02_Sept_2024" - }, - { - "Type": "F", - "Name": "Remove from rsv 80 cohort if already vaccinated", - "Description": "Remove anyone already vaccinated from 80 cohort", - "Operator": "Y>=", - "Comparator": "-25", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "LAST_SUCCESSFUL_DATE", - "CohortLabel": "rsv_80_since_02_Sept_2024", - "Priority": 160 - }, - { - "Type": "F", - "Name": "Remove from rsv 80 cohort if future booking", - "Description": "Remove anyone with a future booking from RSV 80 cohort", - "Operator": "D>=", - "Comparator": "0", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_DATE", - "CohortLabel": "rsv_80_since_02_Sept_2024", - "Priority": 170 - }, - { - "Type": "F", - "Name": "Remove from rsv 75-79 cohort if already vaccinated", - "Description": "Remove anyone already vaccinated from 75-79 cohort", - "Operator": "Y>=", - "Comparator": "-25", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "LAST_SUCCESSFUL_DATE", - "CohortLabel": "rsv_75to79", - "Priority": 180 - }, - { - "Type": "F", - "Name": "Remove from rsv 75-79 cohort if future booking", - "Description": "Remove anyone with a future booking from RSV 75-79 cohort", - "Operator": "D>=", - "Comparator": "0", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_DATE", - "CohortLabel": "rsv_75to79", - "Priority": 190 - }, - { - "Type": "S", - "Name": "Already Vaccinated", - "Description": "## You've had your RSV vaccination\n\n We believe you had your vaccination.", - "Priority": 200, - "AttributeLevel": "TARGET", - "AttributeTarget": "RSV", - "AttributeName": "LAST_SUCCESSFUL_DATE", - "Operator": "Y>=", - "Comparator": "-25", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "Other Setting", - "Description": "## Getting the vaccine\n\n We believe you're living in a setting where care is provided.\n\n Speak to a member of staff where you live about getting the RSV vaccine.", - "Priority": 510, - "AttributeLevel": "PERSON", - "AttributeName": "CARE_HOME_FLAG", - "Operator": "=", - "Comparator": "Y", - "CohortLabel": "rsv_80_since_02_Sept_2024", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "Other Setting", - "Description": "## Getting the vaccine\n\n We believe you're living in a setting where care is provided.\n\n Speak to a member of staff where you live about getting the RSV vaccine.", - "Priority": 520, - "AttributeLevel": "PERSON", - "AttributeName": "DE_FLAG", - "Operator": "=", - "Comparator": "Y", - "CohortLabel": "rsv_80_since_02_Sept_2024", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "Other Setting", - "Description": "## Getting the vaccine\n\n We believe you're living in a setting where care is provided.\n\n Speak to a member of staff where you live about getting the RSV vaccine.", - "Priority": 530, - "AttributeLevel": "PERSON", - "AttributeName": "13Q_FLAG", - "Operator": "=", - "Comparator": "Y", - "CohortLabel": "rsv_80_since_02_Sept_2024", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "Other Setting", - "Description": "## Getting the vaccine\n\n We believe you're living in a setting where care is provided.\n\n Speak to a member of staff where you live about getting the RSV vaccine.", - "Priority": 540, - "AttributeLevel": "PERSON", - "AttributeName": "CARE_HOME_FLAG", - "Operator": "=", - "Comparator": "Y", - "CohortLabel": "rsv_75to79", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "Other Setting with no future booking", - "Description": "## Getting the vaccine\n\n We believe you're living in a setting where care is provided.\n\n Speak to a member of staff where you live about getting the RSV vaccine.", - "Priority": 550, - "AttributeLevel": "PERSON", - "AttributeName": "DE_FLAG", - "Operator": "=", - "Comparator": "Y", - "CohortLabel": "rsv_75to79", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "Other Setting", - "Description": "## Getting the vaccine\n\n We believe you're living in a setting where care is provided.\n\n Speak to a member of staff where you live about getting the RSV vaccine.", - "Priority": 560, - "AttributeLevel": "PERSON", - "AttributeName": "13Q_FLAG", - "Operator": "=", - "Comparator": "Y", - "CohortLabel": "rsv_75to79", - "RuleStop": "Y" - }, - { - "Type": "R", - "Name": "Actionable Future Booked NBS Appointment", - "Description": "Amend NBS future booking", - "Priority": 1000, - "Operator": "D>=", - "Comparator": "0", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_DATE", - "CommsRouting": "AMEND_NBS" - }, - { - "Type": "R", - "Name": "Actionable Future Booked NBS Appointment", - "Description": "Amend NBS future booking", - "Priority": 1000, - "Operator": "=", - "Comparator": "NBS", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", - "CommsRouting": "AMEND_NBS" - }, - { - "Type": "R", - "Name": "Actionable Future Booked Local Appointment", - "Description": "Amend local future booking", - "Priority": 1100, - "Operator": "D>=", - "Comparator": "0", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_DATE", - "CommsRouting": "MANAGE_LOCAL" - }, - { - "Type": "R", - "Name": "Within CP Expansion ICB not 80 plus", - "Description": "Book an appointment on NBS as within CP expansion", - "Priority": 1200, - "Operator": "in", - "Comparator": "QH8,QJG", - "AttributeLevel": "PERSON", - "AttributeName": "ICB", - "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" - }, - { - "Type": "R", - "Name": "Within CP Expansion ICB not 80 plus", - "Description": "Book an appointment on NBS as within CP expansion", - "Priority": 1200, - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Operator": "Y>", - "Comparator": "-80", - "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" - }, - { - "Type": "R", - "Name": "Within CP Expansion Local Authority", - "Description": "Book an appointment on NBS as within CP expansion", - "Priority": 1300, - "Operator": "in", - "Comparator": "E08000028,E08000031,E08000025,E06000016,E06000008,E07000117,E07000120,E08000011,E08000012,E07000122,E07000123,E08000014,E07000126,E08000013,E07000127,E08000015,E07000128", - "AttributeLevel": "PERSON", - "AttributeName": "LOCAL_AUTHORITY", - "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" - }, - { - "Type": "R", - "Name": "Within CP Expansion ICB not 80 plus", - "Description": "Book an appointment on NBS as within CP expansion", - "Priority": 1300, - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Operator": "Y>", - "Comparator": "-80", - "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" - }, - { - "Type": "Y", - "Name": "Already vaccinated default text", - "Description": "Already vaccinated default text", - "Priority": 3000, - "AttributeLevel": "TARGET", - "AttributeTarget": "RSV", - "AttributeName": "LAST_SUCCESSFUL_DATE", - "Operator": "Y>=", - "Comparator": "-25", - "CommsRouting": "CHECK_CORRECT_ALREADY_VACCINATED" - }, - { - "Type": "Y", - "Name": "Other setting default text", - "Description": "Other setting default text", - "Priority": 3100, - "AttributeLevel": "PERSON", - "AttributeName": "CARE_HOME_FLAG", - "Operator": "=", - "Comparator": "Y", - "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" - }, - { - "Type": "Y", - "Name": "Other setting default text", - "Description": "Other setting default text", - "Priority": 3200, - "AttributeLevel": "PERSON", - "AttributeName": "DE_FLAG", - "Operator": "=", - "Comparator": "Y", - "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" - }, - { - "Type": "Y", - "Name": "Other setting default text", - "Description": "Other setting default text", - "Priority": 3300, - "AttributeLevel": "PERSON", - "AttributeName": "13Q_FLAG", - "Operator": "=", - "Comparator": "Y", - "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" - } - ], - "ActionsMapper": { - "BOOK_NBS": { - "ExternalRoutingCode": "BookNBS", - "ActionDescription": "", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Continue to booking" - }, - "AMEND_NBS": { - "ExternalRoutingCode": "AmendNBS", - "ActionDescription": "## You have an RSV vaccination appointment\n You can view, change or cancel your appointment below.", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Manage your appointment" - }, - "CONTACT_GP": { - "ExternalRoutingCode": "ContactGP", - "ActionDescription": "Contact your GP", - "ActionType": "InfoText", - "UrlLink": null, - "UrlLabel": "" - }, - "BOOK_LOCAL": { - "ExternalRoutingCode": "BookLocal", - "ActionDescription": "## Getting the vaccine\n\n You can get an RSV vaccination at your GP surgery.\n Your GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", - "ActionType": "InfoText", - "UrlLink": null, - "UrlLabel": "" - }, - "MANAGE_LOCAL": { - "ExternalRoutingCode": "ManageLocal", - "ActionDescription": "## You have an RSV vaccination appointment\n\n Contact your healthcare provider to change or cancel your appointment.", - "ActionType": "CardWithText", - "UrlLink": null, - "UrlLabel": "" - }, - "HELP_SUPPORT": { - "ExternalRoutingCode": "HelpSupportInfo", - "ActionDescription": "## If you think this is incorrect\n\n If you have already had this vaccination or your personal details are wrong, visit our [help and support page](https://digital.nhs.uk/services/eligibility-data-product-elid).", - "ActionType": "InfoText", - "UrlLink": null, - "UrlLabel": "" - }, - "CHECK_CORRECT_X": { - "ExternalRoutingCode": "HealthcareProInfo", - "ActionDescription": "## If you think this is incorrect\n\n Speak to your healthcare professional if you think you should be offered this vaccine.\n\nFor anything else, visit our [help and support page](https://digital.nhs.uk/services/eligibility-data-product-elid).", - "ActionType": "InfoText", - "UrlLink": null, - "UrlLabel": "" - }, - "CHECK_CORRECT_ALREADY_VACCINATED": { - "ExternalRoutingCode": "AlreadyVaccinatedInfo", - "ActionDescription": "## If you think this is incorrect\n\n If you believe you've not been vaccinated against RSV, speak to your healthcare professional.\n\nFor anything else please see our [help and support page](https://digital.nhs.uk/services/eligibility-data-product-elid).", - "ActionType": "InfoText", - "UrlLink": null, - "UrlLabel": "" - }, - "CHECK_CORRECT_OTHER_SETTING": { - "ExternalRoutingCode": "ManagedSettingInfo", - "ActionDescription": "## If you think this is incorrect\n\n If you have already had this vaccination or your personal details are wrong, visit our [help and support page](https://digital.nhs.uk/services/eligibility-data-product-elid).", - "ActionType": "InfoText", - "UrlLink": null, - "UrlLabel": "" - } - } - } - ] - } + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "EliD RSV example config", + "Type": "V", + "Target": "RSV", + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "BOOK_LOCAL|HELP_SUPPORT", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "CHECK_CORRECT_X", + "Version": 1, + "Name": "EliD RSV example config", + "IterationDate": "20250717", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_75to79", + "CohortGroup": "rsv_age", + "PositiveDescription": "are aged 75 to 79 years old", + "NegativeDescription": "are not aged 75 to 79 years old", + "Priority": 0 + }, + { + "CohortLabel": "rsv_80_since_02_Sept_2024", + "CohortGroup": "rsv_age_catchup", + "PositiveDescription": "turned 80 after 1st September 2024", + "NegativeDescription": "did not turn 80 after 1 September 2024", + "Priority": 10 + }, + { + "CohortLabel": "elid_all_people", + "CohortGroup": "magic_cohort", + "PositiveDescription": "", + "NegativeDescription": "", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "Remove from magic cohort unless already vaccinated or have future booking", + "Description": "Remove anyone NOT already vaccinated within the last 25 years and do not have a future booking from the magic cohort", + "Operator": "Y<=", + "Comparator": "-25[[NVL:18000101]]", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "elid_all_people", + "Priority": 100 + }, + { + "Type": "F", + "Name": "Remove from magic cohort unless already vaccinated or have future booking", + "Description": "Remove anyone without a future booking from magic cohort", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CohortLabel": "elid_all_people", + "Priority": 100 + }, + { + "Type": "F", + "Name": "Remove under 75 Years on day of execution", + "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", + "Priority": 120, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75", + "CohortLabel": "rsv_75to79" + }, + { + "Type": "F", + "Name": "Remove over 80 on day of execution", + "Description": "Exclude anyone who turned 80 before 2nd September 2024", + "Priority": 130, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "<", + "Comparator": "19440902", + "CohortLabel": "rsv_75to79" + }, + { + "Type": "F", + "Name": "Remove under 75 Years on day of execution", + "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", + "Priority": 140, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75", + "CohortLabel": "rsv_80_since_02_Sept_2024" + }, + { + "Type": "F", + "Name": "Remove over 80 on day of execution", + "Description": "Exclude anyone who turned 80 before 2nd September 2024", + "Priority": 150, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "<", + "Comparator": "19440902", + "CohortLabel": "rsv_80_since_02_Sept_2024" + }, + { + "Type": "F", + "Name": "Remove from rsv 80 cohort if already vaccinated", + "Description": "Remove anyone already vaccinated from 80 cohort", + "Operator": "Y>=", + "Comparator": "-25", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "Priority": 160 + }, + { + "Type": "F", + "Name": "Remove from rsv 80 cohort if future booking", + "Description": "Remove anyone with a future booking from RSV 80 cohort", + "Operator": "D>=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "Priority": 170 + }, + { + "Type": "F", + "Name": "Remove from rsv 75-79 cohort if already vaccinated", + "Description": "Remove anyone already vaccinated from 75-79 cohort", + "Operator": "Y>=", + "Comparator": "-25", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "rsv_75to79", + "Priority": 180 + }, + { + "Type": "F", + "Name": "Remove from rsv 75-79 cohort if future booking", + "Description": "Remove anyone with a future booking from RSV 75-79 cohort", + "Operator": "D>=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CohortLabel": "rsv_75to79", + "Priority": 190 + }, + { + "Type": "S", + "Name": "Already Vaccinated", + "Description": "## You've had your RSV vaccination\n\n We believe you had your vaccination.", + "Priority": 200, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "Y>=", + "Comparator": "-25", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Other Setting", + "Description": "## Getting the vaccine\n\n We believe you're living in a setting where care is provided.\n\n Speak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 510, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Other Setting", + "Description": "## Getting the vaccine\n\n We believe you're living in a setting where care is provided.\n\n Speak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 520, + "AttributeLevel": "PERSON", + "AttributeName": "DE_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Other Setting", + "Description": "## Getting the vaccine\n\n We believe you're living in a setting where care is provided.\n\n Speak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 530, + "AttributeLevel": "PERSON", + "AttributeName": "13Q_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Other Setting", + "Description": "## Getting the vaccine\n\n We believe you're living in a setting where care is provided.\n\n Speak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 540, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_75to79", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Other Setting with no future booking", + "Description": "## Getting the vaccine\n\n We believe you're living in a setting where care is provided.\n\n Speak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 550, + "AttributeLevel": "PERSON", + "AttributeName": "DE_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_75to79", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "Other Setting", + "Description": "## Getting the vaccine\n\n We believe you're living in a setting where care is provided.\n\n Speak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 560, + "AttributeLevel": "PERSON", + "AttributeName": "13Q_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_75to79", + "RuleStop": "Y" + }, + { + "Type": "R", + "Name": "Actionable Future Booked NBS Appointment", + "Description": "Amend NBS future booking", + "Priority": 1000, + "Operator": "D>=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked NBS Appointment", + "Description": "Amend NBS future booking", + "Priority": 1000, + "Operator": "=", + "Comparator": "NBS", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Local Appointment", + "Description": "Amend local future booking", + "Priority": 1100, + "Operator": "D>=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "MANAGE_LOCAL" + }, + { + "Type": "R", + "Name": "Within CP Expansion ICB not 80 plus", + "Description": "Book an appointment on NBS as within CP expansion", + "Priority": 1200, + "Operator": "in", + "Comparator": "QH8,QJG", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" + }, + { + "Type": "R", + "Name": "Within CP Expansion ICB not 80 plus", + "Description": "Book an appointment on NBS as within CP expansion", + "Priority": 1200, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-80", + "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" + }, + { + "Type": "R", + "Name": "Within CP Expansion Local Authority", + "Description": "Book an appointment on NBS as within CP expansion", + "Priority": 1300, + "Operator": "in", + "Comparator": "E08000028,E08000031,E08000025,E06000016,E06000008,E07000117,E07000120,E08000011,E08000012,E07000122,E07000123,E08000014,E07000126,E08000013,E07000127,E08000015,E07000128", + "AttributeLevel": "PERSON", + "AttributeName": "LOCAL_AUTHORITY", + "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" + }, + { + "Type": "R", + "Name": "Within CP Expansion ICB not 80 plus", + "Description": "Book an appointment on NBS as within CP expansion", + "Priority": 1300, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-80", + "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" + }, + { + "Type": "Y", + "Name": "Already vaccinated default text", + "Description": "Already vaccinated default text", + "Priority": 3000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "Y>=", + "Comparator": "-25", + "CommsRouting": "CHECK_CORRECT_ALREADY_VACCINATED" + }, + { + "Type": "Y", + "Name": "Other setting default text", + "Description": "Other setting default text", + "Priority": 3100, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y", + "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" + }, + { + "Type": "Y", + "Name": "Other setting default text", + "Description": "Other setting default text", + "Priority": 3200, + "AttributeLevel": "PERSON", + "AttributeName": "DE_FLAG", + "Operator": "=", + "Comparator": "Y", + "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" + }, + { + "Type": "Y", + "Name": "Other setting default text", + "Description": "Other setting default text", + "Priority": 3300, + "AttributeLevel": "PERSON", + "AttributeName": "13Q_FLAG", + "Operator": "=", + "Comparator": "Y", + "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + }, + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "## You have an RSV vaccination appointment\n You can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Manage your appointment" + }, + "CONTACT_GP": { + "ExternalRoutingCode": "ContactGP", + "ActionDescription": "Contact your GP", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "BOOK_LOCAL": { + "ExternalRoutingCode": "BookLocal", + "ActionDescription": "## Getting the vaccine\n\n You can get an RSV vaccination at your GP surgery.\n Your GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "MANAGE_LOCAL": { + "ExternalRoutingCode": "ManageLocal", + "ActionDescription": "## You have an RSV vaccination appointment\n\n Contact your healthcare provider to change or cancel your appointment.", + "ActionType": "CardWithText", + "UrlLink": null, + "UrlLabel": "" + }, + "HELP_SUPPORT": { + "ExternalRoutingCode": "HelpSupportInfo", + "ActionDescription": "## If you think this is incorrect\n\n If you have already had this vaccination or your personal details are wrong, visit our [help and support page](https://digital.nhs.uk/services/eligibility-data-product-elid).", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "CHECK_CORRECT_X": { + "ExternalRoutingCode": "HealthcareProInfo", + "ActionDescription": "## If you think this is incorrect\n\n Speak to your healthcare professional if you think you should be offered this vaccine.\n\nFor anything else, visit our [help and support page](https://digital.nhs.uk/services/eligibility-data-product-elid).", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "CHECK_CORRECT_ALREADY_VACCINATED": { + "ExternalRoutingCode": "AlreadyVaccinatedInfo", + "ActionDescription": "## If you think this is incorrect\n\n If you believe you've not been vaccinated against RSV, speak to your healthcare professional.\n\nFor anything else please see our [help and support page](https://digital.nhs.uk/services/eligibility-data-product-elid).", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "CHECK_CORRECT_OTHER_SETTING": { + "ExternalRoutingCode": "ManagedSettingInfo", + "ActionDescription": "## If you think this is incorrect\n\n If you have already had this vaccination or your personal details are wrong, visit our [help and support page](https://digital.nhs.uk/services/eligibility-data-product-elid).", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + } + } + } + ] + } } diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-373-01.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-373-01.json index c96b688bd..a0d7e8835 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-373-01.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-373-01.json @@ -1,453 +1,459 @@ { - "CampaignConfig": { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", - "Version": 1, - "Name": "EliD RSV example config", - "Type": "V", - "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], - "IterationFrequency": "X", - "IterationType": "O", - "IterationTime": "07:00:00", - "StartDate": "20250717", - "EndDate": "20350717", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", - "Iterations": [ - { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", - "DefaultCommsRouting": "BOOK_LOCAL|HELP_SUPPORT", - "DefaultNotActionableRouting": "", - "DefaultNotEligibleRouting": "CHECK_CORRECT_X", - "Version": 1, - "Name": "EliD RSV example config", - "IterationDate": "20250717", - "IterationNumber": 1, - "CommsType": "I", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Type": "O", - "IterationCohorts": [ - { - "CohortLabel": "rsv_75to79", - "CohortGroup": "rsv_age", - "PositiveDescription": "are aged 75 to 79 years old", - "NegativeDescription": "are not aged 75 to 79 years old", - "Priority": 0 - }, - { - "CohortLabel": "rsv_80_since_02_Sept_2024", - "CohortGroup": "rsv_age_catchup", - "PositiveDescription": "turned 80 after 1st September 2024", - "NegativeDescription": "did not turn 80 after 1 September 2024", - "Priority": 10 - }, - { - "CohortLabel": "elid_all_people", - "CohortGroup": "magic_cohort", - "PositiveDescription": "", - "NegativeDescription": "", - "Priority": 20 - } - ], - "IterationRules": [ - { - "Type": "F", - "Name": "Remove anyone NOT already vaccinated within the last 25 years and do not have a future booking from the magic cohort", - "Description": "Remove anyone NOT already vaccinated within the last 25 years and do not have a future booking from the magic cohort", - "Operator": "Y<=", - "Comparator": "-25[[NVL:18000101]]", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "LAST_SUCCESSFUL_DATE", - "CohortLabel": "elid_all_people", - "Priority": 100 - }, - { - "Type": "F", - "Name": "Remove anyone without a future booking from magic cohort", - "Description": "Remove anyone without a future booking from magic cohort", - "Operator": "D<", - "Comparator": "0[[NVL:18000101]]", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_DATE", - "CohortLabel": "elid_all_people", - "Priority": 100 - }, - { - "Type": "F", - "Name": "Under Age - Under 75 Years on day of execution", - "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", - "Priority": 120, - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Operator": "Y>", - "Comparator": "-75", - "CohortLabel": "rsv_75to79" - }, - { - "Type": "F", - "Name": "Exclude Too OLD", - "Description": "Exclude anyone who turned 80 before 2nd September 2024", - "Priority": 130, - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Operator": "<", - "Comparator": "19440902", - "CohortLabel": "rsv_75to79" - }, - { - "Type": "F", - "Name": "Under Age - Under 75 Years on day of execution", - "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", - "Priority": 140, - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Operator": "Y>", - "Comparator": "-75", - "CohortLabel": "rsv_80_since_02_Sept_2024" - }, - { - "Type": "F", - "Name": "Exclude Too OLD", - "Description": "Exclude anyone who turned 80 before 2nd September 2024", - "Priority": 150, - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Operator": "<", - "Comparator": "19440902", - "CohortLabel": "rsv_80_since_02_Sept_2024" - }, - { - "Type": "S", - "Name": "Already Vaccinated", - "Description": "## You've had your RSV vaccination\n\nWe believe you had your vaccination.", - "Priority": 200, - "AttributeLevel": "TARGET", - "AttributeTarget": "RSV", - "AttributeName": "LAST_SUCCESSFUL_DATE", - "Operator": "Y>=", - "Comparator": "-25", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "80 plus - Other Setting (Care Home) with no future booking", - "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", - "Priority": 510, - "AttributeLevel": "PERSON", - "AttributeName": "CARE_HOME_FLAG", - "Operator": "=", - "Comparator": "Y", - "CohortLabel": "rsv_80_since_02_Sept_2024", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "80 plus - Other Setting (Care Home) with no future booking", - "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", - "Priority": 510, - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_DATE", - "Operator": "D<", - "Comparator": "0[[NVL:18000101]]", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "80 plus - Other Setting (Detained Estates) with no future booking", - "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", - "Priority": 520, - "AttributeLevel": "PERSON", - "AttributeName": "DE_FLAG", - "Operator": "=", - "Comparator": "Y", - "CohortLabel": "rsv_80_since_02_Sept_2024", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "80 plus - Other Setting (Detained Estates) with no future booking", - "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", - "Priority": 520, - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_DATE", - "Operator": "D<", - "Comparator": "0[[NVL:18000101]]", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "80 plus - Other Setting (13Q) with no future booking", - "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", - "Priority": 530, - "AttributeLevel": "PERSON", - "AttributeName": "13Q_FLAG", - "Operator": "=", - "Comparator": "Y", - "CohortLabel": "rsv_80_since_02_Sept_2024", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "80 plus - Other Setting (13Q) with no future booking - 2", - "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", - "Priority": 530, - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_DATE", - "Operator": "D<", - "Comparator": "0[[NVL:18000101]]", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "75 to 79 - Other Setting (Care Home) with no future booking", - "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", - "Priority": 540, - "AttributeLevel": "PERSON", - "AttributeName": "CARE_HOME_FLAG", - "Operator": "=", - "Comparator": "Y", - "CohortLabel": "rsv_75to79", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "75 to 79 - Other Setting (Care Home) with no future booking", - "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", - "Priority": 540, - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_DATE", - "Operator": "D<", - "Comparator": "0[[NVL:18000101]]", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "75 to 79 - Other Setting (Detained Estates) with no future booking", - "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", - "Priority": 550, - "AttributeLevel": "PERSON", - "AttributeName": "DE_FLAG", - "Operator": "=", - "Comparator": "Y", - "CohortLabel": "rsv_75to79", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "75 to 79 - Other Setting (Detained Estates) with no future booking", - "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", - "Priority": 550, - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_DATE", - "Operator": "D<", - "Comparator": "0[[NVL:18000101]]", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "75 to 79 - Other Setting (13Q) with no future booking", - "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", - "Priority": 560, - "AttributeLevel": "PERSON", - "AttributeName": "13Q_FLAG", - "Operator": "=", - "Comparator": "Y", - "CohortLabel": "rsv_75to79", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "75 to 79 - Other Setting (13Q) with no future booking - 2", - "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", - "Priority": 560, - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_DATE", - "Operator": "D<", - "Comparator": "0[[NVL:18000101]]", - "RuleStop": "Y" - }, - { - "Type": "R", - "Name": "Actionable Future Booked NBS Appointment", - "Description": "Amend NBS future booking", - "Priority": 1000, - "Operator": "D>=", - "Comparator": "0", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_DATE", - "CommsRouting": "AMEND_NBS" - }, - { - "Type": "R", - "Name": "Actionable Future Booked NBS Appointment - 2", - "Description": "Amend NBS future booking", - "Priority": 1000, - "Operator": "=", - "Comparator": "NBS", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", - "CommsRouting": "AMEND_NBS" - }, - { - "Type": "R", - "Name": "Actionable Future Booked Local Appointment", - "Description": "Amend local future booking", - "Priority": 1100, - "Operator": "D>=", - "Comparator": "0", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_DATE", - "CommsRouting": "MANAGE_LOCAL" - }, - { - "Type": "R", - "Name": "Within CP Expansion ICB", - "Description": "Book an appointment on NBS as within CP expansion", - "Priority": 1200, - "Operator": "in", - "Comparator": "QH8,QJG", - "AttributeLevel": "PERSON", - "AttributeName": "ICB", - "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" - }, - { - "Type": "R", - "Name": "Within CP Expansion Local Authority", - "Description": "Book an appointment on NBS as within CP expansion", - "Priority": 1300, - "Operator": "in", - "Comparator": "E08000028,E08000031,E08000025,E06000016,E06000008,E07000117,E07000120,E08000011,E08000012,E07000122,E07000123,E08000014,E07000126,E08000013,E07000127,E08000015,E07000128", - "AttributeLevel": "PERSON", - "AttributeName": "LOCAL_AUTHORITY", - "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" - }, - { - "Type": "Y", - "Name": "Already vaccinated default text", - "Description": "Already vaccinated default text", - "Priority": 3000, - "AttributeLevel": "TARGET", - "AttributeTarget": "RSV", - "AttributeName": "LAST_SUCCESSFUL_DATE", - "Operator": "Y>=", - "Comparator": "-25", - "CommsRouting": "CHECK_CORRECT_ALREADY_VACCINATED" - }, - { - "Type": "Y", - "Name": "Other setting default text", - "Description": "Other setting default text", - "Priority": 3100, - "AttributeLevel": "PERSON", - "AttributeName": "CARE_HOME_FLAG", - "Operator": "=", - "Comparator": "Y", - "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" - }, - { - "Type": "Y", - "Name": "Other setting default text", - "Description": "Other setting default text", - "Priority": 3200, - "AttributeLevel": "PERSON", - "AttributeName": "DE_FLAG", - "Operator": "=", - "Comparator": "Y", - "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" - }, - { - "Type": "Y", - "Name": "Other setting default text", - "Description": "Other setting default text", - "Priority": 3300, - "AttributeLevel": "PERSON", - "AttributeName": "13Q_FLAG", - "Operator": "=", - "Comparator": "Y", - "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" - } - ], - "ActionsMapper": { - "BOOK_NBS": { - "ExternalRoutingCode": "BookNBS", - "ActionDescription": "", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Continue to booking" - }, - "AMEND_NBS": { - "ExternalRoutingCode": "AmendNBS", - "ActionDescription": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Manage your appointment" - }, - "CONTACT_GP": { - "ExternalRoutingCode": "ContactGP", - "ActionDescription": "Contact your GP", - "ActionType": "InfoText", - "UrlLink": null, - "UrlLabel": "" - }, - "BOOK_LOCAL": { - "ExternalRoutingCode": "BookLocal", - "ActionDescription": "##Getting the vaccine\n\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", - "ActionType": "InfoText", - "UrlLink": null, - "UrlLabel": "" - }, - "MANAGE_LOCAL": { - "ExternalRoutingCode": "ManageLocal", - "ActionDescription": "##You have an RSV vaccination appointment\n\nContact your healthcare provider to change or cancel your appointment.", - "ActionType": "CardWithText", - "UrlLink": null, - "UrlLabel": "" - }, - "HELP_SUPPORT": { - "ExternalRoutingCode": "HelpSupportInfo", - "ActionDescription": "## CONTENT TBC\n\nBlah blah blah.", - "ActionType": "InfoText", - "UrlLink": null, - "UrlLabel": "" - }, - "CHECK_CORRECT_X": { - "ExternalRoutingCode": "HealthcareProInfo", - "ActionDescription": "## If you think this is incorrect\n\nSpeak to your healthcare professional if you think you should be offered this vaccine.\n\nFor anything else, visit our help and support page. (ADD LINK)", - "ActionType": "InfoText", - "UrlLink": null, - "UrlLabel": "" - }, - "CHECK_CORRECT_ALREADY_VACCINATED": { - "ExternalRoutingCode": "AlreadyVaccinatedInfo", - "ActionDescription": "## If you think this is incorrect\n\nIf you believe you've not been vaccinated against RSV, speak to your healthcare professional.\n\nFor anything else please see our help and support page. (ADD LINK).", - "ActionType": "InfoText", - "UrlLink": null, - "UrlLabel": "" - }, - "CHECK_CORRECT_OTHER_SETTING": { - "ExternalRoutingCode": "ManagedSettingInfo", - "ActionDescription": "## If you think this is incorrect\n\nIf you have already had this vaccination or your personal details are wrong, visit our help and support page. (ADD LINK).", - "ActionType": "InfoText", - "UrlLink": null, - "UrlLabel": "" - } - } - } - ] - } + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "EliD RSV example config", + "Type": "V", + "Target": "RSV", + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "BOOK_LOCAL|HELP_SUPPORT", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "CHECK_CORRECT_X", + "Version": 1, + "Name": "EliD RSV example config", + "IterationDate": "20250717", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_75to79", + "CohortGroup": "rsv_age", + "PositiveDescription": "are aged 75 to 79 years old", + "NegativeDescription": "are not aged 75 to 79 years old", + "Priority": 0 + }, + { + "CohortLabel": "rsv_80_since_02_Sept_2024", + "CohortGroup": "rsv_age_catchup", + "PositiveDescription": "turned 80 after 1st September 2024", + "NegativeDescription": "did not turn 80 after 1 September 2024", + "Priority": 10 + }, + { + "CohortLabel": "elid_all_people", + "CohortGroup": "magic_cohort", + "PositiveDescription": "", + "NegativeDescription": "", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "Remove anyone NOT already vaccinated within the last 25 years and do not have a future booking from the magic cohort", + "Description": "Remove anyone NOT already vaccinated within the last 25 years and do not have a future booking from the magic cohort", + "Operator": "Y<=", + "Comparator": "-25[[NVL:18000101]]", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "elid_all_people", + "Priority": 100 + }, + { + "Type": "F", + "Name": "Remove anyone without a future booking from magic cohort", + "Description": "Remove anyone without a future booking from magic cohort", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CohortLabel": "elid_all_people", + "Priority": 100 + }, + { + "Type": "F", + "Name": "Under Age - Under 75 Years on day of execution", + "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", + "Priority": 120, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75", + "CohortLabel": "rsv_75to79" + }, + { + "Type": "F", + "Name": "Exclude Too OLD", + "Description": "Exclude anyone who turned 80 before 2nd September 2024", + "Priority": 130, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "<", + "Comparator": "19440902", + "CohortLabel": "rsv_75to79" + }, + { + "Type": "F", + "Name": "Under Age - Under 75 Years on day of execution", + "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", + "Priority": 140, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75", + "CohortLabel": "rsv_80_since_02_Sept_2024" + }, + { + "Type": "F", + "Name": "Exclude Too OLD", + "Description": "Exclude anyone who turned 80 before 2nd September 2024", + "Priority": 150, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "<", + "Comparator": "19440902", + "CohortLabel": "rsv_80_since_02_Sept_2024" + }, + { + "Type": "S", + "Name": "Already Vaccinated", + "Description": "## You've had your RSV vaccination\n\nWe believe you had your vaccination.", + "Priority": 200, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "Y>=", + "Comparator": "-25", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "80 plus - Other Setting (Care Home) with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 510, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "80 plus - Other Setting (Care Home) with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 510, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "80 plus - Other Setting (Detained Estates) with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 520, + "AttributeLevel": "PERSON", + "AttributeName": "DE_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "80 plus - Other Setting (Detained Estates) with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 520, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "80 plus - Other Setting (13Q) with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 530, + "AttributeLevel": "PERSON", + "AttributeName": "13Q_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "80 plus - Other Setting (13Q) with no future booking - 2", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 530, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "75 to 79 - Other Setting (Care Home) with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 540, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_75to79", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "75 to 79 - Other Setting (Care Home) with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 540, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "75 to 79 - Other Setting (Detained Estates) with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 550, + "AttributeLevel": "PERSON", + "AttributeName": "DE_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_75to79", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "75 to 79 - Other Setting (Detained Estates) with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 550, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "75 to 79 - Other Setting (13Q) with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 560, + "AttributeLevel": "PERSON", + "AttributeName": "13Q_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_75to79", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "75 to 79 - Other Setting (13Q) with no future booking - 2", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 560, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]", + "RuleStop": "Y" + }, + { + "Type": "R", + "Name": "Actionable Future Booked NBS Appointment", + "Description": "Amend NBS future booking", + "Priority": 1000, + "Operator": "D>=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked NBS Appointment - 2", + "Description": "Amend NBS future booking", + "Priority": 1000, + "Operator": "=", + "Comparator": "NBS", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Local Appointment", + "Description": "Amend local future booking", + "Priority": 1100, + "Operator": "D>=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "MANAGE_LOCAL" + }, + { + "Type": "R", + "Name": "Within CP Expansion ICB", + "Description": "Book an appointment on NBS as within CP expansion", + "Priority": 1200, + "Operator": "in", + "Comparator": "QH8,QJG", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" + }, + { + "Type": "R", + "Name": "Within CP Expansion Local Authority", + "Description": "Book an appointment on NBS as within CP expansion", + "Priority": 1300, + "Operator": "in", + "Comparator": "E08000028,E08000031,E08000025,E06000016,E06000008,E07000117,E07000120,E08000011,E08000012,E07000122,E07000123,E08000014,E07000126,E08000013,E07000127,E08000015,E07000128", + "AttributeLevel": "PERSON", + "AttributeName": "LOCAL_AUTHORITY", + "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" + }, + { + "Type": "Y", + "Name": "Already vaccinated default text", + "Description": "Already vaccinated default text", + "Priority": 3000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "Y>=", + "Comparator": "-25", + "CommsRouting": "CHECK_CORRECT_ALREADY_VACCINATED" + }, + { + "Type": "Y", + "Name": "Other setting default text", + "Description": "Other setting default text", + "Priority": 3100, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y", + "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" + }, + { + "Type": "Y", + "Name": "Other setting default text", + "Description": "Other setting default text", + "Priority": 3200, + "AttributeLevel": "PERSON", + "AttributeName": "DE_FLAG", + "Operator": "=", + "Comparator": "Y", + "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" + }, + { + "Type": "Y", + "Name": "Other setting default text", + "Description": "Other setting default text", + "Priority": 3300, + "AttributeLevel": "PERSON", + "AttributeName": "13Q_FLAG", + "Operator": "=", + "Comparator": "Y", + "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + }, + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Manage your appointment" + }, + "CONTACT_GP": { + "ExternalRoutingCode": "ContactGP", + "ActionDescription": "Contact your GP", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "BOOK_LOCAL": { + "ExternalRoutingCode": "BookLocal", + "ActionDescription": "##Getting the vaccine\n\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "MANAGE_LOCAL": { + "ExternalRoutingCode": "ManageLocal", + "ActionDescription": "##You have an RSV vaccination appointment\n\nContact your healthcare provider to change or cancel your appointment.", + "ActionType": "CardWithText", + "UrlLink": null, + "UrlLabel": "" + }, + "HELP_SUPPORT": { + "ExternalRoutingCode": "HelpSupportInfo", + "ActionDescription": "## CONTENT TBC\n\nBlah blah blah.", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "CHECK_CORRECT_X": { + "ExternalRoutingCode": "HealthcareProInfo", + "ActionDescription": "## If you think this is incorrect\n\nSpeak to your healthcare professional if you think you should be offered this vaccine.\n\nFor anything else, visit our help and support page. (ADD LINK)", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "CHECK_CORRECT_ALREADY_VACCINATED": { + "ExternalRoutingCode": "AlreadyVaccinatedInfo", + "ActionDescription": "## If you think this is incorrect\n\nIf you believe you've not been vaccinated against RSV, speak to your healthcare professional.\n\nFor anything else please see our help and support page. (ADD LINK).", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "CHECK_CORRECT_OTHER_SETTING": { + "ExternalRoutingCode": "ManagedSettingInfo", + "ActionDescription": "## If you think this is incorrect\n\nIf you have already had this vaccination or your personal details are wrong, visit our help and support page. (ADD LINK).", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + } + } + } + ] + } } diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-373-02.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-373-02.json index 0cc3ee248..4c546d253 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-373-02.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-373-02.json @@ -1,447 +1,453 @@ { - "CampaignConfig": { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", - "Version": 1, - "Name": "EliD RSV example config", - "Type": "V", - "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], - "IterationFrequency": "X", - "IterationType": "O", - "IterationTime": "07:00:00", - "StartDate": "20250717", - "EndDate": "20350717", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", - "Iterations": [ - { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", - "DefaultCommsRouting": "BOOK_LOCAL|HELP_SUPPORT", - "DefaultNotActionableRouting": "", - "DefaultNotEligibleRouting": "CHECK_CORRECT_X", - "Version": 1, - "Name": "EliD RSV example config", - "IterationDate": "20250717", - "IterationNumber": 1, - "CommsType": "I", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Type": "O", - "IterationCohorts": [ - { - "CohortLabel": "rsv_75to79", - "CohortGroup": "rsv_age", - "PositiveDescription": "are aged 75 to 79 years old", - "NegativeDescription": "are not aged 75 to 79 years old", - "Priority": 0 - }, - { - "CohortLabel": "rsv_80_since_02_Sept_2024", - "CohortGroup": "rsv_age_catchup", - "PositiveDescription": "turned 80 after 1st September 2024", - "NegativeDescription": "did not turn 80 after 1 September 2024", - "Priority": 10 - }, - { - "CohortLabel": "elid_all_people", - "CohortGroup": "magic_cohort", - "PositiveDescription": "", - "NegativeDescription": "", - "Priority": 20 - } - ], - "IterationRules": [ - { - "Type": "F", - "Name": "Remove anyone NOT already vaccinated within the last 25 years and do not have a future booking from the magic cohort", - "Description": "Remove anyone NOT already vaccinated within the last 25 years and do not have a future booking from the magic cohort", - "Operator": "Y<=", - "Comparator": "-25[[NVL:18000101]]", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "LAST_SUCCESSFUL_DATE", - "CohortLabel": "elid_all_people", - "Priority": 100 - }, - { - "Type": "F", - "Name": "Remove anyone without a future booking from magic cohort", - "Description": "Remove anyone without a future booking from magic cohort", - "Operator": "D<", - "Comparator": "0[[NVL:18000101]]", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_DATE", - "CohortLabel": "elid_all_people", - "Priority": 100 - }, - { - "Type": "F", - "Name": "Under Age - Under 75 Years on day of execution", - "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", - "Priority": 120, - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Operator": "Y>", - "Comparator": "-75", - "CohortLabel": "rsv_75to79" - }, - { - "Type": "F", - "Name": "Exclude Too OLD", - "Description": "Exclude anyone who turned 80 before 2nd September 2024", - "Priority": 130, - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Operator": "<", - "Comparator": "19440902", - "CohortLabel": "rsv_75to79" - }, - { - "Type": "F", - "Name": "Under Age - Under 75 Years on day of execution", - "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", - "Priority": 140, - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Operator": "Y>", - "Comparator": "-75", - "CohortLabel": "rsv_80_since_02_Sept_2024" - }, - { - "Type": "F", - "Name": "Exclude Too OLD", - "Description": "Exclude anyone who turned 80 before 2nd September 2024", - "Priority": 150, - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "Operator": "<", - "Comparator": "19440902", - "CohortLabel": "rsv_80_since_02_Sept_2024" - }, - { - "Type": "S", - "Name": "Already Vaccinated", - "Description": "## You've had your RSV vaccination\n\nWe believe you had your vaccination.", - "Priority": 200, - "AttributeLevel": "TARGET", - "AttributeTarget": "RSV", - "AttributeName": "LAST_SUCCESSFUL_DATE", - "Operator": "Y>=", - "Comparator": "-25", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "80 plus - Other Setting (Care Home) with no future booking", - "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", - "Priority": 510, - "AttributeLevel": "PERSON", - "AttributeName": "CARE_HOME_FLAG", - "Operator": "=", - "Comparator": "Y", - "CohortLabel": "rsv_80_since_02_Sept_2024", - "RuleStop": "N" - }, - { - "Type": "S", - "Name": "80 plus - Other Setting (Care Home) with no future booking", - "Description": "", - "Priority": 510, - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_DATE", - "Operator": "D<", - "Comparator": "0[[NVL:18000101]]" - }, - { - "Type": "S", - "Name": "80 plus - Other Setting (Detained Estates) with no future booking", - "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", - "Priority": 520, - "AttributeLevel": "PERSON", - "AttributeName": "DE_FLAG", - "Operator": "=", - "Comparator": "Y", - "CohortLabel": "rsv_80_since_02_Sept_2024", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "80 plus - Other Setting (Detained Estates) with no future booking", - "Description": "", - "Priority": 520, - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_DATE", - "Operator": "D<", - "Comparator": "0[[NVL:18000101]]" - }, - { - "Type": "S", - "Name": "80 plus - Other Setting (13Q) with no future booking", - "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", - "Priority": 530, - "AttributeLevel": "PERSON", - "AttributeName": "13Q_FLAG", - "Operator": "=", - "Comparator": "Y", - "CohortLabel": "rsv_80_since_02_Sept_2024", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "80 plus - Other Setting (13Q) with no future booking", - "Description": "", - "Priority": 530, - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_DATE", - "Operator": "D<", - "Comparator": "0[[NVL:18000101]]" - }, - { - "Type": "S", - "Name": "75 to 79 - Other Setting (Care Home) with no future booking", - "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", - "Priority": 540, - "AttributeLevel": "PERSON", - "AttributeName": "CARE_HOME_FLAG", - "Operator": "=", - "Comparator": "Y", - "CohortLabel": "rsv_75to79", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "75 to 79 - Other Setting (Care Home) with no future booking", - "Description": "", - "Priority": 540, - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_DATE", - "Operator": "D<", - "Comparator": "0[[NVL:18000101]]" - }, - { - "Type": "S", - "Name": "75 to 79 - Other Setting (Detained Estates) with no future booking", - "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", - "Priority": 550, - "AttributeLevel": "PERSON", - "AttributeName": "DE_FLAG", - "Operator": "=", - "Comparator": "Y", - "CohortLabel": "rsv_75to79" - }, - { - "Type": "S", - "Name": "75 to 79 - Other Setting (Detained Estates) with no future booking", - "Description": "", - "Priority": 550, - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_DATE", - "Operator": "D<", - "Comparator": "0[[NVL:18000101]]", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "75 to 79 - Other Setting (13Q) with no future booking", - "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", - "Priority": 560, - "AttributeLevel": "PERSON", - "AttributeName": "13Q_FLAG", - "Operator": "=", - "Comparator": "Y", - "CohortLabel": "rsv_75to79", - "RuleStop": "Y" - }, - { - "Type": "S", - "Name": "75 to 79 - Other Setting (13Q) with no future booking - 2", - "Description": "", - "Priority": 560, - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_DATE", - "Operator": "D<", - "Comparator": "0[[NVL:18000101]]" - }, - { - "Type": "R", - "Name": "Actionable Future Booked NBS Appointment", - "Description": "Amend NBS future booking", - "Priority": 1000, - "Operator": "D>=", - "Comparator": "0", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_DATE", - "CommsRouting": "AMEND_NBS" - }, - { - "Type": "R", - "Name": "Actionable Future Booked NBS Appointment - 2", - "Description": "Amend NBS future booking", - "Priority": 1000, - "Operator": "=", - "Comparator": "NBS", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", - "CommsRouting": "AMEND_NBS" - }, - { - "Type": "R", - "Name": "Actionable Future Booked Local Appointment", - "Description": "Amend local future booking", - "Priority": 1100, - "Operator": "D>=", - "Comparator": "0", - "AttributeTarget": "RSV", - "AttributeLevel": "TARGET", - "AttributeName": "BOOKED_APPOINTMENT_DATE", - "CommsRouting": "MANAGE_LOCAL" - }, - { - "Type": "R", - "Name": "Within CP Expansion ICB", - "Description": "Book an appointment on NBS as within CP expansion", - "Priority": 1200, - "Operator": "in", - "Comparator": "QH8,QJG", - "AttributeLevel": "PERSON", - "AttributeName": "ICB", - "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" - }, - { - "Type": "R", - "Name": "Within CP Expansion Local Authority", - "Description": "Book an appointment on NBS as within CP expansion", - "Priority": 1300, - "Operator": "in", - "Comparator": "E08000028,E08000031,E08000025,E06000016,E06000008,E07000117,E07000120,E08000011,E08000012,E07000122,E07000123,E08000014,E07000126,E08000013,E07000127,E08000015,E07000128", - "AttributeLevel": "PERSON", - "AttributeName": "LOCAL_AUTHORITY", - "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" - }, - { - "Type": "Y", - "Name": "Already vaccinated default text", - "Description": "Already vaccinated default text", - "Priority": 3000, - "AttributeLevel": "TARGET", - "AttributeTarget": "RSV", - "AttributeName": "LAST_SUCCESSFUL_DATE", - "Operator": "Y>=", - "Comparator": "-25", - "CommsRouting": "CHECK_CORRECT_ALREADY_VACCINATED" - }, - { - "Type": "Y", - "Name": "Other setting default text", - "Description": "Other setting default text", - "Priority": 3100, - "AttributeLevel": "PERSON", - "AttributeName": "CARE_HOME_FLAG", - "Operator": "=", - "Comparator": "Y", - "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" - }, - { - "Type": "Y", - "Name": "Other setting default text", - "Description": "Other setting default text", - "Priority": 3200, - "AttributeLevel": "PERSON", - "AttributeName": "DE_FLAG", - "Operator": "=", - "Comparator": "Y", - "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" - }, - { - "Type": "Y", - "Name": "Other setting default text", - "Description": "Other setting default text", - "Priority": 3300, - "AttributeLevel": "PERSON", - "AttributeName": "13Q_FLAG", - "Operator": "=", - "Comparator": "Y", - "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" - } - ], - "ActionsMapper": { - "BOOK_NBS": { - "ExternalRoutingCode": "BookNBS", - "ActionDescription": "", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Continue to booking" - }, - "AMEND_NBS": { - "ExternalRoutingCode": "AmendNBS", - "ActionDescription": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Manage your appointment" - }, - "CONTACT_GP": { - "ExternalRoutingCode": "ContactGP", - "ActionDescription": "Contact your GP", - "ActionType": "InfoText", - "UrlLink": null, - "UrlLabel": "" - }, - "BOOK_LOCAL": { - "ExternalRoutingCode": "BookLocal", - "ActionDescription": "##Getting the vaccine\n\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", - "ActionType": "InfoText", - "UrlLink": null, - "UrlLabel": "" - }, - "MANAGE_LOCAL": { - "ExternalRoutingCode": "ManageLocal", - "ActionDescription": "##You have an RSV vaccination appointment\n\nContact your healthcare provider to change or cancel your appointment.", - "ActionType": "CardWithText", - "UrlLink": null, - "UrlLabel": "" - }, - "HELP_SUPPORT": { - "ExternalRoutingCode": "HelpSupportInfo", - "ActionDescription": "## CONTENT TBC\n\nBlah blah blah.", - "ActionType": "InfoText", - "UrlLink": null, - "UrlLabel": "" - }, - "CHECK_CORRECT_X": { - "ExternalRoutingCode": "HealthcareProInfo", - "ActionDescription": "## If you think this is incorrect\n\nSpeak to your healthcare professional if you think you should be offered this vaccine.\n\nFor anything else, visit our help and support page. (ADD LINK)", - "ActionType": "InfoText", - "UrlLink": null, - "UrlLabel": "" - }, - "CHECK_CORRECT_ALREADY_VACCINATED": { - "ExternalRoutingCode": "AlreadyVaccinatedInfo", - "ActionDescription": "## If you think this is incorrect\n\nIf you believe you've not been vaccinated against RSV, speak to your healthcare professional.\n\nFor anything else please see our help and support page. (ADD LINK).", - "ActionType": "InfoText", - "UrlLink": null, - "UrlLabel": "" - }, - "CHECK_CORRECT_OTHER_SETTING": { - "ExternalRoutingCode": "ManagedSettingInfo", - "ActionDescription": "## If you think this is incorrect\n\nIf you have already had this vaccination or your personal details are wrong, visit our help and support page. (ADD LINK).", - "ActionType": "InfoText", - "UrlLink": null, - "UrlLabel": "" - } - } - } - ] - } + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "EliD RSV example config", + "Type": "V", + "Target": "RSV", + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20250717", + "EndDate": "20350717", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "BOOK_LOCAL|HELP_SUPPORT", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "CHECK_CORRECT_X", + "Version": 1, + "Name": "EliD RSV example config", + "IterationDate": "20250717", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_75to79", + "CohortGroup": "rsv_age", + "PositiveDescription": "are aged 75 to 79 years old", + "NegativeDescription": "are not aged 75 to 79 years old", + "Priority": 0 + }, + { + "CohortLabel": "rsv_80_since_02_Sept_2024", + "CohortGroup": "rsv_age_catchup", + "PositiveDescription": "turned 80 after 1st September 2024", + "NegativeDescription": "did not turn 80 after 1 September 2024", + "Priority": 10 + }, + { + "CohortLabel": "elid_all_people", + "CohortGroup": "magic_cohort", + "PositiveDescription": "", + "NegativeDescription": "", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "F", + "Name": "Remove anyone NOT already vaccinated within the last 25 years and do not have a future booking from the magic cohort", + "Description": "Remove anyone NOT already vaccinated within the last 25 years and do not have a future booking from the magic cohort", + "Operator": "Y<=", + "Comparator": "-25[[NVL:18000101]]", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "CohortLabel": "elid_all_people", + "Priority": 100 + }, + { + "Type": "F", + "Name": "Remove anyone without a future booking from magic cohort", + "Description": "Remove anyone without a future booking from magic cohort", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CohortLabel": "elid_all_people", + "Priority": 100 + }, + { + "Type": "F", + "Name": "Under Age - Under 75 Years on day of execution", + "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", + "Priority": 120, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75", + "CohortLabel": "rsv_75to79" + }, + { + "Type": "F", + "Name": "Exclude Too OLD", + "Description": "Exclude anyone who turned 80 before 2nd September 2024", + "Priority": 130, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "<", + "Comparator": "19440902", + "CohortLabel": "rsv_75to79" + }, + { + "Type": "F", + "Name": "Under Age - Under 75 Years on day of execution", + "Description": "Ensure anyone who has a PDS date of birth which determines their age to be less than 75 years is filtered out.", + "Priority": 140, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "Y>", + "Comparator": "-75", + "CohortLabel": "rsv_80_since_02_Sept_2024" + }, + { + "Type": "F", + "Name": "Exclude Too OLD", + "Description": "Exclude anyone who turned 80 before 2nd September 2024", + "Priority": 150, + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "Operator": "<", + "Comparator": "19440902", + "CohortLabel": "rsv_80_since_02_Sept_2024" + }, + { + "Type": "S", + "Name": "Already Vaccinated", + "Description": "## You've had your RSV vaccination\n\nWe believe you had your vaccination.", + "Priority": 200, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "Y>=", + "Comparator": "-25", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "80 plus - Other Setting (Care Home) with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 510, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "RuleStop": "N" + }, + { + "Type": "S", + "Name": "80 plus - Other Setting (Care Home) with no future booking", + "Description": "", + "Priority": 510, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]" + }, + { + "Type": "S", + "Name": "80 plus - Other Setting (Detained Estates) with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 520, + "AttributeLevel": "PERSON", + "AttributeName": "DE_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "80 plus - Other Setting (Detained Estates) with no future booking", + "Description": "", + "Priority": 520, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]" + }, + { + "Type": "S", + "Name": "80 plus - Other Setting (13Q) with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 530, + "AttributeLevel": "PERSON", + "AttributeName": "13Q_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_80_since_02_Sept_2024", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "80 plus - Other Setting (13Q) with no future booking", + "Description": "", + "Priority": 530, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]" + }, + { + "Type": "S", + "Name": "75 to 79 - Other Setting (Care Home) with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 540, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_75to79", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "75 to 79 - Other Setting (Care Home) with no future booking", + "Description": "", + "Priority": 540, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]" + }, + { + "Type": "S", + "Name": "75 to 79 - Other Setting (Detained Estates) with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 550, + "AttributeLevel": "PERSON", + "AttributeName": "DE_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_75to79" + }, + { + "Type": "S", + "Name": "75 to 79 - Other Setting (Detained Estates) with no future booking", + "Description": "", + "Priority": 550, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "75 to 79 - Other Setting (13Q) with no future booking", + "Description": "## Getting the vaccine\n\nWe believe you're living in a setting where care is provided.\n\nSpeak to a member of staff where you live about getting the RSV vaccine.", + "Priority": 560, + "AttributeLevel": "PERSON", + "AttributeName": "13Q_FLAG", + "Operator": "=", + "Comparator": "Y", + "CohortLabel": "rsv_75to79", + "RuleStop": "Y" + }, + { + "Type": "S", + "Name": "75 to 79 - Other Setting (13Q) with no future booking - 2", + "Description": "", + "Priority": 560, + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "Operator": "D<", + "Comparator": "0[[NVL:18000101]]" + }, + { + "Type": "R", + "Name": "Actionable Future Booked NBS Appointment", + "Description": "Amend NBS future booking", + "Priority": 1000, + "Operator": "D>=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked NBS Appointment - 2", + "Description": "Amend NBS future booking", + "Priority": 1000, + "Operator": "=", + "Comparator": "NBS", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_PROVIDER", + "CommsRouting": "AMEND_NBS" + }, + { + "Type": "R", + "Name": "Actionable Future Booked Local Appointment", + "Description": "Amend local future booking", + "Priority": 1100, + "Operator": "D>=", + "Comparator": "0", + "AttributeTarget": "RSV", + "AttributeLevel": "TARGET", + "AttributeName": "BOOKED_APPOINTMENT_DATE", + "CommsRouting": "MANAGE_LOCAL" + }, + { + "Type": "R", + "Name": "Within CP Expansion ICB", + "Description": "Book an appointment on NBS as within CP expansion", + "Priority": 1200, + "Operator": "in", + "Comparator": "QH8,QJG", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" + }, + { + "Type": "R", + "Name": "Within CP Expansion Local Authority", + "Description": "Book an appointment on NBS as within CP expansion", + "Priority": 1300, + "Operator": "in", + "Comparator": "E08000028,E08000031,E08000025,E06000016,E06000008,E07000117,E07000120,E08000011,E08000012,E07000122,E07000123,E08000014,E07000126,E08000013,E07000127,E08000015,E07000128", + "AttributeLevel": "PERSON", + "AttributeName": "LOCAL_AUTHORITY", + "CommsRouting": "BOOK_LOCAL|BOOK_NBS|HELP_SUPPORT" + }, + { + "Type": "Y", + "Name": "Already vaccinated default text", + "Description": "Already vaccinated default text", + "Priority": 3000, + "AttributeLevel": "TARGET", + "AttributeTarget": "RSV", + "AttributeName": "LAST_SUCCESSFUL_DATE", + "Operator": "Y>=", + "Comparator": "-25", + "CommsRouting": "CHECK_CORRECT_ALREADY_VACCINATED" + }, + { + "Type": "Y", + "Name": "Other setting default text", + "Description": "Other setting default text", + "Priority": 3100, + "AttributeLevel": "PERSON", + "AttributeName": "CARE_HOME_FLAG", + "Operator": "=", + "Comparator": "Y", + "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" + }, + { + "Type": "Y", + "Name": "Other setting default text", + "Description": "Other setting default text", + "Priority": 3200, + "AttributeLevel": "PERSON", + "AttributeName": "DE_FLAG", + "Operator": "=", + "Comparator": "Y", + "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" + }, + { + "Type": "Y", + "Name": "Other setting default text", + "Description": "Other setting default text", + "Priority": 3300, + "AttributeLevel": "PERSON", + "AttributeName": "13Q_FLAG", + "Operator": "=", + "Comparator": "Y", + "CommsRouting": "CHECK_CORRECT_OTHER_SETTING" + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + }, + "AMEND_NBS": { + "ExternalRoutingCode": "AmendNBS", + "ActionDescription": "##You have an RSV vaccination appointment\nYou can view, change or cancel your appointment below.", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Manage your appointment" + }, + "CONTACT_GP": { + "ExternalRoutingCode": "ContactGP", + "ActionDescription": "Contact your GP", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "BOOK_LOCAL": { + "ExternalRoutingCode": "BookLocal", + "ActionDescription": "##Getting the vaccine\n\nYou can get an RSV vaccination at your GP surgery.\nYour GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call, email or through the NHS App. You do not need to wait to be contacted before booking your vaccination.", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "MANAGE_LOCAL": { + "ExternalRoutingCode": "ManageLocal", + "ActionDescription": "##You have an RSV vaccination appointment\n\nContact your healthcare provider to change or cancel your appointment.", + "ActionType": "CardWithText", + "UrlLink": null, + "UrlLabel": "" + }, + "HELP_SUPPORT": { + "ExternalRoutingCode": "HelpSupportInfo", + "ActionDescription": "## CONTENT TBC\n\nBlah blah blah.", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "CHECK_CORRECT_X": { + "ExternalRoutingCode": "HealthcareProInfo", + "ActionDescription": "## If you think this is incorrect\n\nSpeak to your healthcare professional if you think you should be offered this vaccine.\n\nFor anything else, visit our help and support page. (ADD LINK)", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "CHECK_CORRECT_ALREADY_VACCINATED": { + "ExternalRoutingCode": "AlreadyVaccinatedInfo", + "ActionDescription": "## If you think this is incorrect\n\nIf you believe you've not been vaccinated against RSV, speak to your healthcare professional.\n\nFor anything else please see our help and support page. (ADD LINK).", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + }, + "CHECK_CORRECT_OTHER_SETTING": { + "ExternalRoutingCode": "ManagedSettingInfo", + "ActionDescription": "## If you think this is incorrect\n\nIf you have already had this vaccination or your personal details are wrong, visit our help and support page. (ADD LINK).", + "ActionType": "InfoText", + "UrlLink": null, + "UrlLabel": "" + } + } + } + ] + } } diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-01.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-01.json index 8e173675b..b16724007 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-01.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-01.json @@ -1,67 +1,73 @@ { - "CampaignConfig": { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", - "Version": 1, - "Name": "ELI-399-01-Iteration-Config", - "Type": "V", - "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], - "IterationFrequency": "X", - "IterationType": "O", - "IterationTime": "07:00:00", - "StartDate": "<>", - "EndDate": "<>", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", - "Iterations": [ - { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", - "DefaultCommsRouting": "", - "DefaultNotActionableRouting": "", - "DefaultNotEligibleRouting": "", - "Version": 1, - "Name": "ELI-399-01-Iteration-Config", - "IterationDate": "<>", - "IterationNumber": 1, - "CommsType": "I", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Type": "O", - "IterationCohorts": [ - { - "CohortLabel": "rsv_eli_399_cohort_1", - "CohortGroup": "rsv_eli_399_cohort_group", - "PositiveDescription": "are a member of eli_399_cohort_group_10", - "NegativeDescription": "are not a member of eli_399_cohort_group_10", - "Priority": 10 - } - ], - "IterationRules": [ - { - "Type": "S", - "Name": "NotActionable Reason 1", - "Description": "Description 1", - "Operator": "Y>", - "Comparator": "-75", - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "CohortLabel": "rsv_eli_399_cohort_1", - "Priority": 100 - } - ], - "ActionsMapper": { - "BOOK_NBS": { - "ExternalRoutingCode": "BookNBS", - "ActionDescription": "", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Continue to booking" - } - } - } - ] - } + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-399-01-Iteration-Config", + "Type": "V", + "Target": "RSV", + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "<>", + "EndDate": "<>", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "ELI-399-01-Iteration-Config", + "IterationDate": "<>", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_399_cohort_1", + "CohortGroup": "rsv_eli_399_cohort_group", + "PositiveDescription": "are a member of eli_399_cohort_group_10", + "NegativeDescription": "are not a member of eli_399_cohort_group_10", + "Priority": 10 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason 1", + "Description": "Description 1", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_399_cohort_1", + "Priority": 100 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } } diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-02.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-02.json index 848d93f32..32761d36c 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-02.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-02.json @@ -1,186 +1,192 @@ { - "CampaignConfig": { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", - "Version": 1, - "Name": "ELI-399-02-Iteration-Config", - "Type": "V", - "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], - "IterationFrequency": "X", - "IterationType": "O", - "IterationTime": "07:00:00", - "StartDate": "<>", - "EndDate": "<>", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", - "Iterations": [ - { - "ID": "inactive-future-iteration-id", - "DefaultCommsRouting": "", - "DefaultNotActionableRouting": "", - "DefaultNotEligibleRouting": "", - "Version": 1, - "Name": "inactive-future-iteration", - "IterationDate": "<>", - "IterationNumber": 1, - "CommsType": "I", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Type": "O", - "IterationCohorts": [ - { - "CohortLabel": "rsv_eli_399_inactive_cohort_1", - "CohortGroup": "rsv_eli_399_inactive_cohort_group", - "PositiveDescription": "are a member of eli_399_inactive_cohort_group", - "NegativeDescription": "are not a member of eli_399_inactive_cohort_group", - "Priority": 0 - }, - { - "CohortLabel": "rsv_eli_399_inactive_cohort_2", - "CohortGroup": "rsv_eli_399_inactive_cohort_group", - "PositiveDescription": "are a member of eli_399_inactive_cohort_group", - "NegativeDescription": "are not a member of eli_399_inactive_cohort_group", - "Priority": 10 - }, - { - "CohortLabel": "rsv_eli_399_inactive_cohort_3", - "CohortGroup": "rsv_eli_399_inactive_cohort_group_other", - "PositiveDescription": "are a member of eli_399_inactive_cohort_group_other", - "NegativeDescription": "are not a member of eli_399_inactive_cohort_group_other", - "Priority": 20 - } - ], - "IterationRules": [ - { - "Type": "S", - "Name": "NotActionable Reason", - "Description": "", - "Operator": "Y>", - "Comparator": "-75", - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "CohortLabel": "rsv_eli_399_inactive_cohort_1", - "Priority": 100, + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-399-02-Iteration-Config", + "Type": "V", + "Target": "RSV", + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "<>", + "EndDate": "<>", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "inactive-future-iteration-id", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "inactive-future-iteration", + "IterationDate": "<>", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_399_inactive_cohort_1", + "CohortGroup": "rsv_eli_399_inactive_cohort_group", + "PositiveDescription": "are a member of eli_399_inactive_cohort_group", + "NegativeDescription": "are not a member of eli_399_inactive_cohort_group", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_399_inactive_cohort_2", + "CohortGroup": "rsv_eli_399_inactive_cohort_group", + "PositiveDescription": "are a member of eli_399_inactive_cohort_group", + "NegativeDescription": "are not a member of eli_399_inactive_cohort_group", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_399_inactive_cohort_3", + "CohortGroup": "rsv_eli_399_inactive_cohort_group_other", + "PositiveDescription": "are a member of eli_399_inactive_cohort_group_other", + "NegativeDescription": "are not a member of eli_399_inactive_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_399_inactive_cohort_1", + "Priority": 100, "RuleStop:": "Y" - }, - { - "Type": "S", - "Name": "NotActionable Reason", - "Description": "Description 4", - "Operator": "=", - "Comparator": "AAB", - "AttributeLevel": "PERSON", - "AttributeName": "ICB", - "CohortLabel": "rsv_eli_399_inactive_cohort_2", - "Priority": 110 - }, - { - "Type": "S", - "Name": "NotActionable Reason", - "Description": "Description 3", - "Operator": "=", - "Comparator": "ZZY", - "AttributeLevel": "PERSON", - "AttributeName": "COMMISSIONING_REGION", - "CohortLabel": "rsv_eli_399_inactive_cohort_3", - "Priority": 120 - } - ], - "ActionsMapper": { - "BOOK_NBS": { - "ExternalRoutingCode": "BookNBS", - "ActionDescription": "", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Continue to booking" - } - } - }, + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 4", + "Operator": "=", + "Comparator": "AAB", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CohortLabel": "rsv_eli_399_inactive_cohort_2", + "Priority": 110 + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 3", + "Operator": "=", + "Comparator": "ZZY", + "AttributeLevel": "PERSON", + "AttributeName": "COMMISSIONING_REGION", + "CohortLabel": "rsv_eli_399_inactive_cohort_3", + "Priority": 120 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + }, { - "ID": "active-current-iteration-id", - "DefaultCommsRouting": "", - "DefaultNotActionableRouting": "", - "DefaultNotEligibleRouting": "", - "Version": 1, - "Name": "active-current-iteration", - "IterationDate": "<>", - "IterationNumber": 2, - "CommsType": "I", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Type": "O", - "IterationCohorts": [ - { - "CohortLabel": "rsv_eli_399_active_cohort_1", - "CohortGroup": "rsv_eli_399_active_cohort_group", - "PositiveDescription": "are a member of eli_399_active_cohort_group", - "NegativeDescription": "are not a member of eli_399_active_cohort_group", - "Priority": 0 - }, - { - "CohortLabel": "rsv_eli_399_active_cohort_2", - "CohortGroup": "rsv_eli_399_active_cohort_group", - "PositiveDescription": "are a member of eli_399_active_cohort_group", - "NegativeDescription": "are not a member of eli_399_active_cohort_group", - "Priority": 10 - }, - { - "CohortLabel": "rsv_eli_399_active_cohort_3", - "CohortGroup": "rsv_eli_399_active_cohort_group_other", - "PositiveDescription": "are a member of eli_399_active_cohort_group_other", - "NegativeDescription": "are not a member of eli_399_active_cohort_group_other", - "Priority": 20 - } - ], - "IterationRules": [ - { - "Type": "S", - "Name": "NotActionable Reason", - "Description": "", - "Operator": "Y>", - "Comparator": "-75", - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "CohortLabel": "rsv_eli_399_active_cohort_1", - "Priority": 100, + "ID": "active-current-iteration-id", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "active-current-iteration", + "IterationDate": "<>", + "IterationNumber": 2, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_399_active_cohort_1", + "CohortGroup": "rsv_eli_399_active_cohort_group", + "PositiveDescription": "are a member of eli_399_active_cohort_group", + "NegativeDescription": "are not a member of eli_399_active_cohort_group", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_399_active_cohort_2", + "CohortGroup": "rsv_eli_399_active_cohort_group", + "PositiveDescription": "are a member of eli_399_active_cohort_group", + "NegativeDescription": "are not a member of eli_399_active_cohort_group", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_399_active_cohort_3", + "CohortGroup": "rsv_eli_399_active_cohort_group_other", + "PositiveDescription": "are a member of eli_399_active_cohort_group_other", + "NegativeDescription": "are not a member of eli_399_active_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_399_active_cohort_1", + "Priority": 100, "RuleStop:": "Y" - }, - { - "Type": "S", - "Name": "NotActionable Reason", - "Description": "Description 4", - "Operator": "=", - "Comparator": "AAB", - "AttributeLevel": "PERSON", - "AttributeName": "ICB", - "CohortLabel": "rsv_eli_399_active_cohort_2", - "Priority": 110 - }, - { - "Type": "S", - "Name": "NotActionable Reason", - "Description": "Description 3", - "Operator": "=", - "Comparator": "ZZY", - "AttributeLevel": "PERSON", - "AttributeName": "COMMISSIONING_REGION", - "CohortLabel": "rsv_eli_399_active_cohort_3", - "Priority": 120 - } - ], - "ActionsMapper": { - "BOOK_NBS": { - "ExternalRoutingCode": "BookNBS", - "ActionDescription": "", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Continue to booking" - } - } - } - ] - } + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 4", + "Operator": "=", + "Comparator": "AAB", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CohortLabel": "rsv_eli_399_active_cohort_2", + "Priority": 110 + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 3", + "Operator": "=", + "Comparator": "ZZY", + "AttributeLevel": "PERSON", + "AttributeName": "COMMISSIONING_REGION", + "CohortLabel": "rsv_eli_399_active_cohort_3", + "Priority": 120 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } } diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-03.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-03.json index 47322f44c..c6e5c25c3 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-03.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-03.json @@ -1,186 +1,192 @@ { - "CampaignConfig": { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", - "Version": 1, - "Name": "ELI-399-03-Iteration-Config", - "Type": "V", - "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], - "IterationFrequency": "X", - "IterationType": "O", - "IterationTime": "07:00:00", - "StartDate": "<>", - "EndDate": "<>", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", - "Iterations": [ - { - "ID": "older-past-iteration-id", - "DefaultCommsRouting": "", - "DefaultNotActionableRouting": "", - "DefaultNotEligibleRouting": "", - "Version": 1, - "Name": "older-past-iteration", - "IterationDate": "<>", - "IterationNumber": 2, - "CommsType": "I", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Type": "O", - "IterationCohorts": [ - { - "CohortLabel": "rsv_eli_399_inactive_cohort_1", - "CohortGroup": "rsv_eli_399_inactive_cohort_group", - "PositiveDescription": "are a member of eli_399_inactive_cohort_group", - "NegativeDescription": "are not a member of eli_399_inactive_cohort_group", - "Priority": 0 - }, - { - "CohortLabel": "rsv_eli_399_inactive_cohort_2", - "CohortGroup": "rsv_eli_399_inactive_cohort_group", - "PositiveDescription": "are a member of eli_399_inactive_cohort_group", - "NegativeDescription": "are not a member of eli_399_inactive_cohort_group", - "Priority": 10 - }, - { - "CohortLabel": "rsv_eli_399_inactive_cohort_3", - "CohortGroup": "rsv_eli_399_inactive_cohort_group_other", - "PositiveDescription": "are a member of eli_399_inactive_cohort_group_other", - "NegativeDescription": "are not a member of eli_399_inactive_cohort_group_other", - "Priority": 20 - } - ], - "IterationRules": [ - { - "Type": "S", - "Name": "NotActionable Reason", - "Description": "", - "Operator": "Y>", - "Comparator": "-75", - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "CohortLabel": "rsv_eli_399_inactive_cohort_1", - "Priority": 100, + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-399-03-Iteration-Config", + "Type": "V", + "Target": "RSV", + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "<>", + "EndDate": "<>", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "older-past-iteration-id", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "older-past-iteration", + "IterationDate": "<>", + "IterationNumber": 2, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_399_inactive_cohort_1", + "CohortGroup": "rsv_eli_399_inactive_cohort_group", + "PositiveDescription": "are a member of eli_399_inactive_cohort_group", + "NegativeDescription": "are not a member of eli_399_inactive_cohort_group", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_399_inactive_cohort_2", + "CohortGroup": "rsv_eli_399_inactive_cohort_group", + "PositiveDescription": "are a member of eli_399_inactive_cohort_group", + "NegativeDescription": "are not a member of eli_399_inactive_cohort_group", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_399_inactive_cohort_3", + "CohortGroup": "rsv_eli_399_inactive_cohort_group_other", + "PositiveDescription": "are a member of eli_399_inactive_cohort_group_other", + "NegativeDescription": "are not a member of eli_399_inactive_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_399_inactive_cohort_1", + "Priority": 100, "RuleStop:": "Y" - }, - { - "Type": "S", - "Name": "NotActionable Reason", - "Description": "Description 4", - "Operator": "=", - "Comparator": "AAB", - "AttributeLevel": "PERSON", - "AttributeName": "ICB", - "CohortLabel": "rsv_eli_399_inactive_cohort_2", - "Priority": 110 - }, - { - "Type": "S", - "Name": "NotActionable Reason", - "Description": "Description 3", - "Operator": "=", - "Comparator": "ZZY", - "AttributeLevel": "PERSON", - "AttributeName": "COMMISSIONING_REGION", - "CohortLabel": "rsv_eli_399_inactive_cohort_3", - "Priority": 120 - } - ], - "ActionsMapper": { - "BOOK_NBS": { - "ExternalRoutingCode": "BookNBS", - "ActionDescription": "", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Continue to booking" - } - } - }, + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 4", + "Operator": "=", + "Comparator": "AAB", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CohortLabel": "rsv_eli_399_inactive_cohort_2", + "Priority": 110 + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 3", + "Operator": "=", + "Comparator": "ZZY", + "AttributeLevel": "PERSON", + "AttributeName": "COMMISSIONING_REGION", + "CohortLabel": "rsv_eli_399_inactive_cohort_3", + "Priority": 120 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + }, { - "ID": "active-current-iteration-id", - "DefaultCommsRouting": "", - "DefaultNotActionableRouting": "", - "DefaultNotEligibleRouting": "", - "Version": 1, - "Name": "active-current-iteration", - "IterationDate": "<>", - "IterationNumber": 1, - "CommsType": "I", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Type": "O", - "IterationCohorts": [ - { - "CohortLabel": "rsv_eli_399_active_cohort_1", - "CohortGroup": "rsv_eli_399_active_cohort_group", - "PositiveDescription": "are a member of eli_399_active_cohort_group", - "NegativeDescription": "are not a member of eli_399_active_cohort_group", - "Priority": 0 - }, - { - "CohortLabel": "rsv_eli_399_active_cohort_2", - "CohortGroup": "rsv_eli_399_active_cohort_group", - "PositiveDescription": "are a member of eli_399_active_cohort_group", - "NegativeDescription": "are not a member of eli_399_active_cohort_group", - "Priority": 10 - }, - { - "CohortLabel": "rsv_eli_399_active_cohort_3", - "CohortGroup": "rsv_eli_399_active_cohort_group_other", - "PositiveDescription": "are a member of eli_399_active_cohort_group_other", - "NegativeDescription": "are not a member of eli_399_active_cohort_group_other", - "Priority": 20 - } - ], - "IterationRules": [ - { - "Type": "S", - "Name": "NotActionable Reason", - "Description": "", - "Operator": "Y>", - "Comparator": "-75", - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "CohortLabel": "rsv_eli_399_active_cohort_1", - "Priority": 100, + "ID": "active-current-iteration-id", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "active-current-iteration", + "IterationDate": "<>", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_399_active_cohort_1", + "CohortGroup": "rsv_eli_399_active_cohort_group", + "PositiveDescription": "are a member of eli_399_active_cohort_group", + "NegativeDescription": "are not a member of eli_399_active_cohort_group", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_399_active_cohort_2", + "CohortGroup": "rsv_eli_399_active_cohort_group", + "PositiveDescription": "are a member of eli_399_active_cohort_group", + "NegativeDescription": "are not a member of eli_399_active_cohort_group", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_399_active_cohort_3", + "CohortGroup": "rsv_eli_399_active_cohort_group_other", + "PositiveDescription": "are a member of eli_399_active_cohort_group_other", + "NegativeDescription": "are not a member of eli_399_active_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_399_active_cohort_1", + "Priority": 100, "RuleStop:": "Y" - }, - { - "Type": "S", - "Name": "NotActionable Reason", - "Description": "Description 4", - "Operator": "=", - "Comparator": "AAB", - "AttributeLevel": "PERSON", - "AttributeName": "ICB", - "CohortLabel": "rsv_eli_399_active_cohort_2", - "Priority": 110 - }, - { - "Type": "S", - "Name": "NotActionable Reason", - "Description": "Description 3", - "Operator": "=", - "Comparator": "ZZY", - "AttributeLevel": "PERSON", - "AttributeName": "COMMISSIONING_REGION", - "CohortLabel": "rsv_eli_399_active_cohort_3", - "Priority": 120 - } - ], - "ActionsMapper": { - "BOOK_NBS": { - "ExternalRoutingCode": "BookNBS", - "ActionDescription": "", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Continue to booking" - } - } - } - ] - } + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 4", + "Operator": "=", + "Comparator": "AAB", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CohortLabel": "rsv_eli_399_active_cohort_2", + "Priority": 110 + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 3", + "Operator": "=", + "Comparator": "ZZY", + "AttributeLevel": "PERSON", + "AttributeName": "COMMISSIONING_REGION", + "CohortLabel": "rsv_eli_399_active_cohort_3", + "Priority": 120 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } } diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-04.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-04.json index 89e4bb72b..301a57279 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-04.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-04.json @@ -1,186 +1,192 @@ { - "CampaignConfig": { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", - "Version": 1, - "Name": "ELI-399-04-Iteration-Config", - "Type": "V", - "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], - "IterationFrequency": "X", - "IterationType": "O", - "IterationTime": "07:00:00", - "StartDate": "<>", - "EndDate": "<>", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", - "Iterations": [ - { - "ID": "future-iteration-id", - "DefaultCommsRouting": "", - "DefaultNotActionableRouting": "", - "DefaultNotEligibleRouting": "", - "Version": 1, - "Name": "future-iteration-iteration", - "IterationDate": "<>", - "IterationNumber": 2, - "CommsType": "I", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Type": "O", - "IterationCohorts": [ - { - "CohortLabel": "rsv_eli_399_inactive_cohort_1", - "CohortGroup": "rsv_eli_399_inactive_cohort_group", - "PositiveDescription": "are a member of eli_399_inactive_cohort_group", - "NegativeDescription": "are not a member of eli_399_inactive_cohort_group", - "Priority": 0 - }, - { - "CohortLabel": "rsv_eli_399_inactive_cohort_2", - "CohortGroup": "rsv_eli_399_inactive_cohort_group", - "PositiveDescription": "are a member of eli_399_inactive_cohort_group", - "NegativeDescription": "are not a member of eli_399_inactive_cohort_group", - "Priority": 10 - }, - { - "CohortLabel": "rsv_eli_399_inactive_cohort_3", - "CohortGroup": "rsv_eli_399_inactive_cohort_group_other", - "PositiveDescription": "are a member of eli_399_inactive_cohort_group_other", - "NegativeDescription": "are not a member of eli_399_inactive_cohort_group_other", - "Priority": 20 - } - ], - "IterationRules": [ - { - "Type": "S", - "Name": "NotActionable Reason", - "Description": "", - "Operator": "Y>", - "Comparator": "-75", - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "CohortLabel": "rsv_eli_399_inactive_cohort_1", - "Priority": 100, + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-399-04-Iteration-Config", + "Type": "V", + "Target": "RSV", + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "<>", + "EndDate": "<>", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "future-iteration-id", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "future-iteration-iteration", + "IterationDate": "<>", + "IterationNumber": 2, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_399_inactive_cohort_1", + "CohortGroup": "rsv_eli_399_inactive_cohort_group", + "PositiveDescription": "are a member of eli_399_inactive_cohort_group", + "NegativeDescription": "are not a member of eli_399_inactive_cohort_group", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_399_inactive_cohort_2", + "CohortGroup": "rsv_eli_399_inactive_cohort_group", + "PositiveDescription": "are a member of eli_399_inactive_cohort_group", + "NegativeDescription": "are not a member of eli_399_inactive_cohort_group", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_399_inactive_cohort_3", + "CohortGroup": "rsv_eli_399_inactive_cohort_group_other", + "PositiveDescription": "are a member of eli_399_inactive_cohort_group_other", + "NegativeDescription": "are not a member of eli_399_inactive_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_399_inactive_cohort_1", + "Priority": 100, "RuleStop:": "Y" - }, - { - "Type": "S", - "Name": "NotActionable Reason", - "Description": "Description 4", - "Operator": "=", - "Comparator": "AAB", - "AttributeLevel": "PERSON", - "AttributeName": "ICB", - "CohortLabel": "rsv_eli_399_inactive_cohort_2", - "Priority": 110 - }, - { - "Type": "S", - "Name": "NotActionable Reason", - "Description": "Description 3", - "Operator": "=", - "Comparator": "ZZY", - "AttributeLevel": "PERSON", - "AttributeName": "COMMISSIONING_REGION", - "CohortLabel": "rsv_eli_399_inactive_cohort_3", - "Priority": 120 - } - ], - "ActionsMapper": { - "BOOK_NBS": { - "ExternalRoutingCode": "BookNBS", - "ActionDescription": "", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Continue to booking" - } - } - }, + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 4", + "Operator": "=", + "Comparator": "AAB", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CohortLabel": "rsv_eli_399_inactive_cohort_2", + "Priority": 110 + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 3", + "Operator": "=", + "Comparator": "ZZY", + "AttributeLevel": "PERSON", + "AttributeName": "COMMISSIONING_REGION", + "CohortLabel": "rsv_eli_399_inactive_cohort_3", + "Priority": 120 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + }, { - "ID": "more-future-iteration-id", - "DefaultCommsRouting": "", - "DefaultNotActionableRouting": "", - "DefaultNotEligibleRouting": "", - "Version": 1, - "Name": "more-future-iteration", - "IterationDate": "<>", - "IterationNumber": 1, - "CommsType": "I", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Type": "O", - "IterationCohorts": [ - { - "CohortLabel": "rsv_eli_399_active_cohort_1", - "CohortGroup": "rsv_eli_399_active_cohort_group", - "PositiveDescription": "are a member of eli_399_active_cohort_group", - "NegativeDescription": "are not a member of eli_399_active_cohort_group", - "Priority": 0 - }, - { - "CohortLabel": "rsv_eli_399_active_cohort_2", - "CohortGroup": "rsv_eli_399_active_cohort_group_2", - "PositiveDescription": "are a member of eli_399_active_cohort_group", - "NegativeDescription": "are not a member of eli_399_active_cohort_group", - "Priority": 10 - }, - { - "CohortLabel": "rsv_eli_399_active_cohort_3", - "CohortGroup": "rsv_eli_399_active_cohort_group_other", - "PositiveDescription": "are a member of eli_399_active_cohort_group_other", - "NegativeDescription": "are not a member of eli_399_active_cohort_group_other", - "Priority": 20 - } - ], - "IterationRules": [ - { - "Type": "S", - "Name": "NotActionable Reason", - "Description": "", - "Operator": "Y>", - "Comparator": "-75", - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "CohortLabel": "rsv_eli_399_active_cohort_1", - "Priority": 100, + "ID": "more-future-iteration-id", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "more-future-iteration", + "IterationDate": "<>", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_399_active_cohort_1", + "CohortGroup": "rsv_eli_399_active_cohort_group", + "PositiveDescription": "are a member of eli_399_active_cohort_group", + "NegativeDescription": "are not a member of eli_399_active_cohort_group", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_399_active_cohort_2", + "CohortGroup": "rsv_eli_399_active_cohort_group_2", + "PositiveDescription": "are a member of eli_399_active_cohort_group", + "NegativeDescription": "are not a member of eli_399_active_cohort_group", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_399_active_cohort_3", + "CohortGroup": "rsv_eli_399_active_cohort_group_other", + "PositiveDescription": "are a member of eli_399_active_cohort_group_other", + "NegativeDescription": "are not a member of eli_399_active_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_399_active_cohort_1", + "Priority": 100, "RuleStop:": "Y" - }, - { - "Type": "S", - "Name": "NotActionable Reason", - "Description": "Description 4", - "Operator": "=", - "Comparator": "AAB", - "AttributeLevel": "PERSON", - "AttributeName": "ICB", - "CohortLabel": "rsv_eli_399_active_cohort_2", - "Priority": 110 - }, - { - "Type": "S", - "Name": "NotActionable Reason", - "Description": "Description 3", - "Operator": "=", - "Comparator": "ZZY", - "AttributeLevel": "PERSON", - "AttributeName": "COMMISSIONING_REGION", - "CohortLabel": "rsv_eli_399_active_cohort_3", - "Priority": 120 - } - ], - "ActionsMapper": { - "BOOK_NBS": { - "ExternalRoutingCode": "BookNBS", - "ActionDescription": "", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Continue to booking" - } - } - } - ] - } + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 4", + "Operator": "=", + "Comparator": "AAB", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CohortLabel": "rsv_eli_399_active_cohort_2", + "Priority": 110 + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 3", + "Operator": "=", + "Comparator": "ZZY", + "AttributeLevel": "PERSON", + "AttributeName": "COMMISSIONING_REGION", + "CohortLabel": "rsv_eli_399_active_cohort_3", + "Priority": 120 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } } diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-05.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-05.json index cd794bc49..f17eb2053 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-05.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-05.json @@ -1,104 +1,110 @@ { - "CampaignConfig": { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", - "Version": 1, - "Name": "ELI-399-05-Iteration-Config", - "Type": "V", - "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], - "IterationFrequency": "X", - "IterationType": "O", - "IterationTime": "07:00:00", - "StartDate": "<>", + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-399-05-Iteration-Config", + "Type": "V", + "Target": "RSV", + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "<>", "EndDate": "<>", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", - "Iterations": [ - { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", - "DefaultCommsRouting": "", - "DefaultNotActionableRouting": "", - "DefaultNotEligibleRouting": "", - "Version": 1, - "Name": "ELI-399-05-Iteration", - "IterationDate": "<>", - "IterationNumber": 1, - "CommsType": "I", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Type": "O", - "IterationCohorts": [ - { - "CohortLabel": "rsv_eli_399_cohort_1", - "CohortGroup": "rsv_eli_399_cohort_group", - "PositiveDescription": "are a member of eli_399_cohort_group", - "NegativeDescription": "are not a member of eli_399_cohort_group", - "Priority": 0 - }, - { - "CohortLabel": "rsv_eli_399_cohort_2", - "CohortGroup": "rsv_eli_399_cohort_group", - "PositiveDescription": "are a member of eli_399_cohort_group", - "NegativeDescription": "are not a member of eli_399_cohort_group", - "Priority": 10 - }, - { - "CohortLabel": "rsv_eli_399_cohort_3", - "CohortGroup": "rsv_eli_399_cohort_group_other", - "PositiveDescription": "are a member of eli_399_cohort_group_other", - "NegativeDescription": "are not a member of eli_399_cohort_group_other", - "Priority": 20 - } - ], - "IterationRules": [ - { - "Type": "S", - "Name": "NotActionable Reason", - "Description": "", - "Operator": "Y>", - "Comparator": "-75", - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "CohortLabel": "rsv_eli_399_cohort_1", - "Priority": 100, + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "ELI-399-05-Iteration", + "IterationDate": "<>", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_399_cohort_1", + "CohortGroup": "rsv_eli_399_cohort_group", + "PositiveDescription": "are a member of eli_399_cohort_group", + "NegativeDescription": "are not a member of eli_399_cohort_group", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_399_cohort_2", + "CohortGroup": "rsv_eli_399_cohort_group", + "PositiveDescription": "are a member of eli_399_cohort_group", + "NegativeDescription": "are not a member of eli_399_cohort_group", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_399_cohort_3", + "CohortGroup": "rsv_eli_399_cohort_group_other", + "PositiveDescription": "are a member of eli_399_cohort_group_other", + "NegativeDescription": "are not a member of eli_399_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_399_cohort_1", + "Priority": 100, "RuleStop:": "Y" - }, - { - "Type": "S", - "Name": "NotActionable Reason", - "Description": "Description 4", - "Operator": "=", - "Comparator": "AAB", - "AttributeLevel": "PERSON", - "AttributeName": "ICB", - "CohortLabel": "rsv_eli_399_cohort_2", - "Priority": 110 - }, - { - "Type": "S", - "Name": "NotActionable Reason", - "Description": "Description 3", - "Operator": "=", - "Comparator": "ZZY", - "AttributeLevel": "PERSON", - "AttributeName": "COMMISSIONING_REGION", - "CohortLabel": "rsv_eli_399_cohort_3", - "Priority": 120 - } - ], - "ActionsMapper": { - "BOOK_NBS": { - "ExternalRoutingCode": "BookNBS", - "ActionDescription": "", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Continue to booking" - } - } - } - ] - } + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 4", + "Operator": "=", + "Comparator": "AAB", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CohortLabel": "rsv_eli_399_cohort_2", + "Priority": 110 + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 3", + "Operator": "=", + "Comparator": "ZZY", + "AttributeLevel": "PERSON", + "AttributeName": "COMMISSIONING_REGION", + "CohortLabel": "rsv_eli_399_cohort_3", + "Priority": 120 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } } diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-06.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-06.json index 6cd224880..5c57bb551 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-06.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-06.json @@ -1,104 +1,110 @@ { - "CampaignConfig": { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", - "Version": 1, - "Name": "ELI-399-06-Iteration-Config", - "Type": "V", - "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], - "IterationFrequency": "X", - "IterationType": "O", - "IterationTime": "07:00:00", - "StartDate": "20300801", - "EndDate": "20350807", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", - "Iterations": [ - { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", - "DefaultCommsRouting": "", - "DefaultNotActionableRouting": "", - "DefaultNotEligibleRouting": "", - "Version": 1, - "Name": "ELI-399-06-Iteration", - "IterationDate": "20300805", - "IterationNumber": 1, - "CommsType": "I", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Type": "O", - "IterationCohorts": [ - { - "CohortLabel": "rsv_eli_399_cohort_1", - "CohortGroup": "rsv_eli_399_cohort_group", - "PositiveDescription": "are a member of eli_399_cohort_group", - "NegativeDescription": "are not a member of eli_399_cohort_group", - "Priority": 0 - }, - { - "CohortLabel": "rsv_eli_399_cohort_2", - "CohortGroup": "rsv_eli_399_cohort_group", - "PositiveDescription": "are a member of eli_399_cohort_group", - "NegativeDescription": "are not a member of eli_399_cohort_group", - "Priority": 10 - }, - { - "CohortLabel": "rsv_eli_399_cohort_3", - "CohortGroup": "rsv_eli_399_cohort_group_other", - "PositiveDescription": "are a member of eli_399_cohort_group_other", - "NegativeDescription": "are not a member of eli_399_cohort_group_other", - "Priority": 20 - } - ], - "IterationRules": [ - { - "Type": "S", - "Name": "NotActionable Reason", - "Description": "", - "Operator": "Y>", - "Comparator": "-75", - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "CohortLabel": "rsv_eli_399_cohort_1", - "Priority": 100, + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-399-06-Iteration-Config", + "Type": "V", + "Target": "RSV", + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "20300801", + "EndDate": "20350807", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "ELI-399-06-Iteration", + "IterationDate": "20300805", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_399_cohort_1", + "CohortGroup": "rsv_eli_399_cohort_group", + "PositiveDescription": "are a member of eli_399_cohort_group", + "NegativeDescription": "are not a member of eli_399_cohort_group", + "Priority": 0 + }, + { + "CohortLabel": "rsv_eli_399_cohort_2", + "CohortGroup": "rsv_eli_399_cohort_group", + "PositiveDescription": "are a member of eli_399_cohort_group", + "NegativeDescription": "are not a member of eli_399_cohort_group", + "Priority": 10 + }, + { + "CohortLabel": "rsv_eli_399_cohort_3", + "CohortGroup": "rsv_eli_399_cohort_group_other", + "PositiveDescription": "are a member of eli_399_cohort_group_other", + "NegativeDescription": "are not a member of eli_399_cohort_group_other", + "Priority": 20 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_399_cohort_1", + "Priority": 100, "RuleStop:": "Y" - }, - { - "Type": "S", - "Name": "NotActionable Reason", - "Description": "Description 4", - "Operator": "=", - "Comparator": "AAB", - "AttributeLevel": "PERSON", - "AttributeName": "ICB", - "CohortLabel": "rsv_eli_399_cohort_2", - "Priority": 110 - }, - { - "Type": "S", - "Name": "NotActionable Reason", - "Description": "Description 3", - "Operator": "=", - "Comparator": "ZZY", - "AttributeLevel": "PERSON", - "AttributeName": "COMMISSIONING_REGION", - "CohortLabel": "rsv_eli_399_cohort_3", - "Priority": 120 - } - ], - "ActionsMapper": { - "BOOK_NBS": { - "ExternalRoutingCode": "BookNBS", - "ActionDescription": "", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Continue to booking" - } - } - } - ] - } + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 4", + "Operator": "=", + "Comparator": "AAB", + "AttributeLevel": "PERSON", + "AttributeName": "ICB", + "CohortLabel": "rsv_eli_399_cohort_2", + "Priority": 110 + }, + { + "Type": "S", + "Name": "NotActionable Reason", + "Description": "Description 3", + "Operator": "=", + "Comparator": "ZZY", + "AttributeLevel": "PERSON", + "AttributeName": "COMMISSIONING_REGION", + "CohortLabel": "rsv_eli_399_cohort_3", + "Priority": 120 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } } diff --git a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-07.json b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-07.json index 84ad72a53..4e901dce6 100644 --- a/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-07.json +++ b/tests/e2e/data/configs/storyTestConfigs/AUTO_RSV_ELI-399-07.json @@ -1,67 +1,73 @@ { - "CampaignConfig": { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", - "Version": 1, - "Name": "ELI-399-07-Iteration-Config", - "Type": "V", - "Target": "COVID", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], - "IterationFrequency": "X", - "IterationType": "O", - "IterationTime": "07:00:00", - "StartDate": "<>", - "EndDate": "<>", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", - "Iterations": [ - { - "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", - "DefaultCommsRouting": "", - "DefaultNotActionableRouting": "", - "DefaultNotEligibleRouting": "", - "Version": 1, - "Name": "Active Covid Iteration", - "IterationDate": "<>", - "IterationNumber": 1, - "CommsType": "I", - "ApprovalMinimum": 0, - "ApprovalMaximum": 0, - "Type": "O", - "IterationCohorts": [ - { - "CohortLabel": "rsv_eli_399_cohort_1", - "CohortGroup": "rsv_eli_399_cohort_group", - "PositiveDescription": "are a member of eli_399_cohort_group_10", - "NegativeDescription": "are not a member of eli_399_cohort_group_10", - "Priority": 10 - } - ], - "IterationRules": [ - { - "Type": "S", - "Name": "NotActionable Reason 1", - "Description": "Description 1", - "Operator": "Y>", - "Comparator": "-75", - "AttributeLevel": "PERSON", - "AttributeName": "DATE_OF_BIRTH", - "CohortLabel": "rsv_eli_399_cohort_1", - "Priority": 100 - } - ], - "ActionsMapper": { - "BOOK_NBS": { - "ExternalRoutingCode": "BookNBS", - "ActionDescription": "", - "ActionType": "ButtonWithAuthLink", - "UrlLink": "http://www.nhs.uk/book-rsv", - "UrlLabel": "Continue to booking" - } - } - } - ] - } + "CampaignConfig": { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d0", + "Version": 1, + "Name": "ELI-399-07-Iteration-Config", + "Type": "V", + "Target": "COVID", + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], + "IterationFrequency": "X", + "IterationType": "O", + "IterationTime": "07:00:00", + "StartDate": "<>", + "EndDate": "<>", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "DefaultCommsRouting": "PLACEHOLDER_COMMS_ROUTING", + "Iterations": [ + { + "ID": "8fcb742b-45fa-4e0d-8f2f-9c2efb1f46d1", + "DefaultCommsRouting": "", + "DefaultNotActionableRouting": "", + "DefaultNotEligibleRouting": "", + "Version": 1, + "Name": "Active Covid Iteration", + "IterationDate": "<>", + "IterationNumber": 1, + "CommsType": "I", + "ApprovalMinimum": 0, + "ApprovalMaximum": 0, + "Type": "O", + "IterationCohorts": [ + { + "CohortLabel": "rsv_eli_399_cohort_1", + "CohortGroup": "rsv_eli_399_cohort_group", + "PositiveDescription": "are a member of eli_399_cohort_group_10", + "NegativeDescription": "are not a member of eli_399_cohort_group_10", + "Priority": 10 + } + ], + "IterationRules": [ + { + "Type": "S", + "Name": "NotActionable Reason 1", + "Description": "Description 1", + "Operator": "Y>", + "Comparator": "-75", + "AttributeLevel": "PERSON", + "AttributeName": "DATE_OF_BIRTH", + "CohortLabel": "rsv_eli_399_cohort_1", + "Priority": 100 + } + ], + "ActionsMapper": { + "BOOK_NBS": { + "ExternalRoutingCode": "BookNBS", + "ActionDescription": "", + "ActionType": "ButtonWithAuthLink", + "UrlLink": "http://www.nhs.uk/book-rsv", + "UrlLabel": "Continue to booking" + } + } + } + ] + } } diff --git a/tests/e2e/data/configs/vitaIntegrationTestConfigs/vita_integration_test_config.json b/tests/e2e/data/configs/vitaIntegrationTestConfigs/vita_integration_test_config.json index 405bcd949..f92afbfed 100644 --- a/tests/e2e/data/configs/vitaIntegrationTestConfigs/vita_integration_test_config.json +++ b/tests/e2e/data/configs/vitaIntegrationTestConfigs/vita_integration_test_config.json @@ -5,9 +5,15 @@ "Name": "EliD RSV example config", "Type": "V", "Target": "RSV", - "Manager": ["person1@nhs.net"], - "Approver": ["person1@nhs.net"], - "Reviewer": ["person1@nhs.net"], + "Manager": [ + "person1@nhs.net" + ], + "Approver": [ + "person1@nhs.net" + ], + "Reviewer": [ + "person1@nhs.net" + ], "IterationFrequency": "X", "IterationType": "O", "IterationTime": "07:00:00", From 88be99365057c0bd03750e22b3f883200a1d1a17 Mon Sep 17 00:00:00 2001 From: Adam Waller <176666249+adam-wallernhs1@users.noreply.github.com> Date: Tue, 12 Aug 2025 11:53:38 +0100 Subject: [PATCH 72/72] added ELI-399, 373 regression tests tidying the VITA int tests removed 320-12 as invalid removed old code added Sean handover stuff refomatting to pass checks *2 --- .../ELI-376-405/AUTO_RSV_ELI-376-02.json | 2 +- .../ELI-274-371/AUTO_RSV_ELI-274_001.json | 12 +++-- .../ELI-274-371/AUTO_RSV_ELI-274_002.json | 12 +++-- .../ELI-274-371/AUTO_RSV_ELI-274_003.json | 12 +++-- .../ELI-274-371/AUTO_RSV_ELI-274_004.json | 12 +++-- .../ELI-274-371/AUTO_RSV_ELI-274_005.json | 12 +++-- .../ELI-274-371/AUTO_RSV_ELI-274_006.json | 12 +++-- .../ELI-274-371/AUTO_RSV_ELI-274_007.json | 12 +++-- .../regressionTestData/AUTO_RSV_REG_001.json | 8 +++- .../regressionTestData/AUTO_RSV_REG_002.json | 10 ++-- .../regressionTestData/AUTO_RSV_REG_003.json | 12 +++-- .../AUTO_RSV_REG_010-1.json | 8 +++- .../regressionTestData/AUTO_RSV_REG_010.json | 8 +++- .../regressionTestData/AUTO_RSV_REG_011.json | 8 +++- .../regressionTestData/AUTO_RSV_REG_012.json | 8 +++- .../regressionTestData/AUTO_RSV_REG_013.json | 8 +++- .../regressionTestData/AUTO_RSV_REG_050.json | 8 +++- .../.ignore_folder/AUTO_RSV_ELI-216-2.json | 4 +- .../storyTestData/AUTO_RSV_ELI-219-3.json | 2 +- .../storyTestData/AUTO_RSV_ELI-221-01.json | 4 +- .../storyTestData/AUTO_RSV_ELI-221-02.json | 6 ++- .../storyTestData/AUTO_RSV_ELI-221-03.json | 4 +- .../storyTestData/AUTO_RSV_ELI-222-1.json | 4 +- .../storyTestData/AUTO_RSV_ELI-236-01.json | 2 +- .../storyTestData/AUTO_RSV_ELI-295-01.json | 4 +- .../storyTestData/AUTO_RSV_ELI-295-02.json | 6 ++- .../storyTestData/AUTO_RSV_ELI-295-03.json | 4 +- .../storyTestData/AUTO_RSV_ELI-295-04.json | 6 ++- .../storyTestData/AUTO_RSV_ELI-295-05.json | 6 ++- .../storyTestData/AUTO_RSV_ELI-295-07.json | 6 ++- .../storyTestData/AUTO_RSV_ELI-295-08.json | 6 ++- .../storyTestData/AUTO_RSV_ELI-295-09.json | 2 +- .../storyTestData/AUTO_RSV_ELI-295-10.json | 6 ++- .../storyTestData/AUTO_RSV_ELI-295-11.json | 2 +- .../storyTestData/AUTO_RSV_ELI-295-12.json | 2 +- .../storyTestData/AUTO_RSV_ELI-295-13.json | 6 ++- .../storyTestData/AUTO_RSV_ELI-295-14.json | 6 ++- .../storyTestData/AUTO_RSV_ELI-295-15.json | 2 +- .../storyTestData/AUTO_RSV_ELI-295-16.json | 4 +- .../storyTestData/AUTO_RSV_ELI-295-17.json | 4 +- .../storyTestData/AUTO_RSV_ELI-320-11.json | 7 ++- .../storyTestData/AUTO_RSV_ELI-320-2.json | 6 ++- .../storyTestData/AUTO_RSV_ELI-320-3.json | 6 ++- .../storyTestData/AUTO_RSV_ELI-320-4.json | 6 ++- .../storyTestData/AUTO_RSV_ELI-320-5.json | 6 ++- .../storyTestData/AUTO_RSV_ELI-320-7.json | 7 ++- .../storyTestData/AUTO_RSV_ELI-320-8.json | 7 ++- .../storyTestData/AUTO_RSV_ELI-320-9.json | 7 ++- .../storyTestData/AUTO_RSV_ELI-365_001.json | 4 +- .../storyTestData/AUTO_RSV_ELI-365_002.json | 4 +- .../storyTestData/AUTO_RSV_ELI-365_003.json | 4 +- .../storyTestData/AUTO_RSV_ELI-365_004.json | 4 +- .../storyTestData/AUTO_RSV_ELI-365_005.json | 4 +- .../storyTestData/AUTO_RSV_ELI-365_006.json | 4 +- .../storyTestData/AUTO_RSV_ELI-365_007.json | 4 +- .../storyTestData/AUTO_RSV_ELI-365_008.json | 4 +- .../storyTestData/AUTO_RSV_ELI-365_009.json | 6 ++- .../storyTestData/AUTO_RSV_ELI-365_010.json | 6 ++- .../storyTestData/AUTO_RSV_ELI-365_011.json | 6 ++- .../storyTestData/AUTO_RSV_ELI-365_012.json | 6 ++- .../storyTestData/AUTO_RSV_ELI-365_013.json | 6 ++- .../storyTestData/AUTO_RSV_ELI-365_014.json | 6 ++- .../storyTestData/AUTO_RSV_ELI-365_015.json | 6 ++- .../storyTestData/AUTO_RSV_ELI-365_016.json | 8 ++-- .../storyTestData/AUTO_RSV_ELI-365_017.json | 6 ++- .../storyTestData/AUTO_RSV_ELI-365_018.json | 6 ++- .../storyTestData/AUTO_RSV_ELI-365_019.json | 6 ++- .../storyTestData/AUTO_RSV_ELI-365_020.json | 6 ++- .../storyTestData/AUTO_RSV_ELI-365_021.json | 6 ++- .../storyTestData/AUTO_RSV_ELI-365_022.json | 6 ++- .../storyTestData/AUTO_RSV_ELI-373_005.json | 2 +- .../AUTO_RSV_VITA_INT_001.json | 4 +- .../AUTO_RSV_VITA_INT_002.json | 4 +- .../AUTO_RSV_VITA_INT_003.json | 6 ++- .../AUTO_RSV_VITA_INT_004.json | 4 +- .../AUTO_RSV_VITA_INT_005.json | 4 +- .../AUTO_RSV_VITA_INT_006.json | 4 +- .../AUTO_RSV_VITA_INT_007.json | 4 +- .../AUTO_RSV_VITA_INT_008.json | 4 +- .../AUTO_RSV_VITA_INT_009.json | 4 +- .../AUTO_RSV_VITA_INT_011.json | 6 ++- .../AUTO_RSV_VITA_INT_012.json | 4 +- .../AUTO_RSV_VITA_INT_013.json | 5 +- .../AUTO_RSV_VITA_INT_014.json | 4 +- .../AUTO_RSV_VITA_INT_015.json | 4 +- .../AUTO_RSV_ELI-365_024.json | 2 +- .../AUTO_RSV_ELI-365_025.json | 2 +- .../AUTO_RSV_ELI-365_026.json | 2 +- .../AUTO_RSV_ELI-365_027.json | 2 +- .../AUTO_RSV_ELI-365_028.json | 2 +- .../AUTO_RSV_VITA_INT_500.json | 46 +++++++++---------- 91 files changed, 378 insertions(+), 173 deletions(-) diff --git a/tests/e2e/data/configs/inProgressTestConfigs/ELI-376-405/AUTO_RSV_ELI-376-02.json b/tests/e2e/data/configs/inProgressTestConfigs/ELI-376-405/AUTO_RSV_ELI-376-02.json index f3d526bc6..a6ee1a28f 100644 --- a/tests/e2e/data/configs/inProgressTestConfigs/ELI-376-405/AUTO_RSV_ELI-376-02.json +++ b/tests/e2e/data/configs/inProgressTestConfigs/ELI-376-405/AUTO_RSV_ELI-376-02.json @@ -90,7 +90,7 @@ } ], "ActionsMapper": { - "TEST_ACTION": { + "TEST_ACTION": { "ExternalRoutingCode": "TestAction", "ActionDescription": "TestAction Description", "ActionType": "ButtonWithAuthLink", diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_001.json b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_001.json index 7738a29f5..e27daa61a 100644 --- a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_001.json +++ b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_001.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900247001" }, - "config_filenames": ["AUTO_RSV_ELI-274-01.json"], + "config_filenames": [ + "AUTO_RSV_ELI-274-01.json" + ], "data": [ { "NHS_NUMBER": "9900247001", @@ -12,10 +14,12 @@ { "COHORT_LABEL": "rsv_eli_274_cohort_1", "DATE_JOINED": "20231020" - },{ + }, + { "COHORT_LABEL": "rsv_eli_274_cohort_2", "DATE_JOINED": "20231020" - },{ + }, + { "COHORT_LABEL": "rsv_eli_274_cohort_3", "DATE_JOINED": "20231020" } @@ -43,7 +47,7 @@ "NHS_NUMBER": "9900247001", "ATTRIBUTE_TYPE": "RSV", "LAST_SUCCESSFUL_DATE": null, - "BOOKED_APPOINTMENT_DATE" : "<>", + "BOOKED_APPOINTMENT_DATE": "<>", "BOOKED_APPOINTMENT_PROVIDER": "NBS" } ] diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_002.json b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_002.json index b835e909b..aadf9602c 100644 --- a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_002.json +++ b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_002.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900247002" }, - "config_filenames": ["AUTO_RSV_ELI-274-01.json"], + "config_filenames": [ + "AUTO_RSV_ELI-274-01.json" + ], "data": [ { "NHS_NUMBER": "9900247002", @@ -12,10 +14,12 @@ { "COHORT_LABEL": "rsv_eli_274_cohort_1", "DATE_JOINED": "20231020" - },{ + }, + { "COHORT_LABEL": "rsv_eli_274_cohort_2", "DATE_JOINED": "20231020" - },{ + }, + { "COHORT_LABEL": "rsv_eli_274_cohort_3", "DATE_JOINED": "20231020" } @@ -43,7 +47,7 @@ "NHS_NUMBER": "9900247002", "ATTRIBUTE_TYPE": "RSV", "LAST_SUCCESSFUL_DATE": null, - "BOOKED_APPOINTMENT_DATE" : "<>", + "BOOKED_APPOINTMENT_DATE": "<>", "BOOKED_APPOINTMENT_PROVIDER": "NBS" } ] diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_003.json b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_003.json index ac3b7758e..1c4184309 100644 --- a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_003.json +++ b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_003.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900247003" }, - "config_filenames": ["AUTO_RSV_ELI-274-01.json"], + "config_filenames": [ + "AUTO_RSV_ELI-274-01.json" + ], "data": [ { "NHS_NUMBER": "9900247003", @@ -12,10 +14,12 @@ { "COHORT_LABEL": "rsv_eli_274_cohort_1", "DATE_JOINED": "20231020" - },{ + }, + { "COHORT_LABEL": "rsv_eli_274_cohort_2", "DATE_JOINED": "20231020" - },{ + }, + { "COHORT_LABEL": "rsv_eli_274_cohort_3", "DATE_JOINED": "20231020" } @@ -43,7 +47,7 @@ "NHS_NUMBER": "9900247003", "ATTRIBUTE_TYPE": "RSV", "LAST_SUCCESSFUL_DATE": null, - "BOOKED_APPOINTMENT_DATE" : "<>", + "BOOKED_APPOINTMENT_DATE": "<>", "BOOKED_APPOINTMENT_PROVIDER": "NBS" } ] diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_004.json b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_004.json index 86f7616e5..472e91fc0 100644 --- a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_004.json +++ b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_004.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900247004" }, - "config_filenames": ["AUTO_RSV_ELI-274-01.json"], + "config_filenames": [ + "AUTO_RSV_ELI-274-01.json" + ], "data": [ { "NHS_NUMBER": "9900247004", @@ -12,10 +14,12 @@ { "COHORT_LABEL": "rsv_eli_274_cohort_1", "DATE_JOINED": "20231020" - },{ + }, + { "COHORT_LABEL": "rsv_eli_274_cohort_2", "DATE_JOINED": "20231020" - },{ + }, + { "COHORT_LABEL": "rsv_eli_274_cohort_3", "DATE_JOINED": "20231020" } @@ -43,7 +47,7 @@ "NHS_NUMBER": "9900247004", "ATTRIBUTE_TYPE": "RSV", "LAST_SUCCESSFUL_DATE": null, - "BOOKED_APPOINTMENT_DATE" : "<>", + "BOOKED_APPOINTMENT_DATE": "<>", "BOOKED_APPOINTMENT_PROVIDER": "NBS" } ] diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_005.json b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_005.json index 527c94b26..b07b7a3ad 100644 --- a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_005.json +++ b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_005.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900247005" }, - "config_filenames": ["AUTO_RSV_ELI-274-05.json"], + "config_filenames": [ + "AUTO_RSV_ELI-274-05.json" + ], "data": [ { "NHS_NUMBER": "9900247005", @@ -12,10 +14,12 @@ { "COHORT_LABEL": "rsv_eli_274_cohort_1", "DATE_JOINED": "20231020" - },{ + }, + { "COHORT_LABEL": "rsv_eli_274_cohort_2", "DATE_JOINED": "20231020" - },{ + }, + { "COHORT_LABEL": "rsv_eli_274_cohort_3", "DATE_JOINED": "20231020" } @@ -43,7 +47,7 @@ "NHS_NUMBER": "9900247005", "ATTRIBUTE_TYPE": "RSV", "LAST_SUCCESSFUL_DATE": null, - "BOOKED_APPOINTMENT_DATE" : "<>", + "BOOKED_APPOINTMENT_DATE": "<>", "BOOKED_APPOINTMENT_PROVIDER": "NBS" } ] diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_006.json b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_006.json index 5ba02d6a1..92f7668e9 100644 --- a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_006.json +++ b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_006.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900247006" }, - "config_filenames": ["AUTO_RSV_ELI-274-06.json"], + "config_filenames": [ + "AUTO_RSV_ELI-274-06.json" + ], "data": [ { "NHS_NUMBER": "9900247006", @@ -12,10 +14,12 @@ { "COHORT_LABEL": "rsv_eli_274_cohort_1", "DATE_JOINED": "20231020" - },{ + }, + { "COHORT_LABEL": "rsv_eli_274_cohort_2", "DATE_JOINED": "20231020" - },{ + }, + { "COHORT_LABEL": "rsv_eli_274_cohort_3", "DATE_JOINED": "20231020" } @@ -43,7 +47,7 @@ "NHS_NUMBER": "9900247006", "ATTRIBUTE_TYPE": "RSV", "LAST_SUCCESSFUL_DATE": null, - "BOOKED_APPOINTMENT_DATE" : "<>", + "BOOKED_APPOINTMENT_DATE": "<>", "BOOKED_APPOINTMENT_PROVIDER": "NBS" } ] diff --git a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_007.json b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_007.json index 6f5c27e22..615a82e60 100644 --- a/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_007.json +++ b/tests/e2e/data/dynamoDB/inProgressTestData/ELI-274-371/AUTO_RSV_ELI-274_007.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900247007" }, - "config_filenames": ["AUTO_RSV_ELI-274-07.json"], + "config_filenames": [ + "AUTO_RSV_ELI-274-07.json" + ], "data": [ { "NHS_NUMBER": "9900247007", @@ -12,10 +14,12 @@ { "COHORT_LABEL": "rsv_eli_274_cohort_1", "DATE_JOINED": "20231020" - },{ + }, + { "COHORT_LABEL": "rsv_eli_274_cohort_2", "DATE_JOINED": "20231020" - },{ + }, + { "COHORT_LABEL": "rsv_eli_274_cohort_3", "DATE_JOINED": "20231020" } @@ -43,7 +47,7 @@ "NHS_NUMBER": "9900247007", "ATTRIBUTE_TYPE": "RSV", "LAST_SUCCESSFUL_DATE": null, - "BOOKED_APPOINTMENT_DATE" : "<>", + "BOOKED_APPOINTMENT_DATE": "<>", "BOOKED_APPOINTMENT_PROVIDER": "NBS" } ] diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_001.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_001.json index 0aec6aed8..dc4879b1a 100644 --- a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_001.json +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_001.json @@ -1,7 +1,11 @@ { "scenario_name": "RSV - Regression - Actionable, Single Eligible Cohort", - "request_headers": {"nhs-login-nhs-number": "1000000001"}, - "config_filenames": ["AUTO_RSV_REG_001.json"], + "request_headers": { + "nhs-login-nhs-number": "1000000001" + }, + "config_filenames": [ + "AUTO_RSV_REG_001.json" + ], "data": [ { "NHS_NUMBER": "1000000001", diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_002.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_002.json index 8aeb280fd..8a649222e 100644 --- a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_002.json +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_002.json @@ -1,7 +1,11 @@ { "scenario_name": "RSV - Regression - Actionable, Two Eligible Cohorts, Different Groups", - "request_headers": {"nhs-login-nhs-number": "1000000002"}, - "config_filenames": ["AUTO_RSV_REG_001.json"], + "request_headers": { + "nhs-login-nhs-number": "1000000002" + }, + "config_filenames": [ + "AUTO_RSV_REG_001.json" + ], "data": [ { "NHS_NUMBER": "1000000002", @@ -11,7 +15,7 @@ "COHORT_LABEL": "rsv_cohort_2", "DATE_JOINED": "20231020" }, - { + { "COHORT_LABEL": "rsv_cohort_3", "DATE_JOINED": "20231020" } diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_003.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_003.json index c473c3ce7..360107070 100644 --- a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_003.json +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_003.json @@ -1,9 +1,13 @@ { "scenario_name": "RSV - Regression - Actionable, Two Eligible Cohorts, Same Group", - "request_headers": {"nhs-login-nhs-number": "1000000003"}, - "config_filenames": ["AUTO_RSV_REG_001.json"], + "request_headers": { + "nhs-login-nhs-number": "1000000003" + }, + "config_filenames": [ + "AUTO_RSV_REG_001.json" + ], "data": [ - { + { "NHS_NUMBER": "1000000003", "ATTRIBUTE_TYPE": "COHORTS", "COHORT_MEMBERSHIPS": [ @@ -11,7 +15,7 @@ "COHORT_LABEL": "rsv_cohort_2", "DATE_JOINED": "20231020" }, - { + { "COHORT_LABEL": "rsv_cohort_1", "DATE_JOINED": "20231020" } diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_010-1.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_010-1.json index bcfa1ccf4..542c97dfc 100644 --- a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_010-1.json +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_010-1.json @@ -1,7 +1,11 @@ { "scenario_name": "RSV - Regression - Not Eligible, In Cohort - Future Date", - "request_headers": {"nhs-login-nhs-number": "1100000010"}, - "config_filenames": ["AUTO_RSV_REG_001.json"], + "request_headers": { + "nhs-login-nhs-number": "1100000010" + }, + "config_filenames": [ + "AUTO_RSV_REG_001.json" + ], "data": [ { "NHS_NUMBER": "1100000010", diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_010.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_010.json index 52321d5ed..ecf3c2fc7 100644 --- a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_010.json +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_010.json @@ -1,7 +1,11 @@ { "scenario_name": "RSV - Regression - Not Eligible, Not in Cohort", - "request_headers": {"nhs-login-nhs-number": "1000000010"}, - "config_filenames": ["AUTO_RSV_REG_001.json"], + "request_headers": { + "nhs-login-nhs-number": "1000000010" + }, + "config_filenames": [ + "AUTO_RSV_REG_001.json" + ], "data": [ { "NHS_NUMBER": "1000000010", diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_011.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_011.json index 4fb2d7298..196f37f9f 100644 --- a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_011.json +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_011.json @@ -1,7 +1,11 @@ { "scenario_name": "RSV - Regression - Not Eligible, In Cohort, Filter Rule", - "request_headers": {"nhs-login-nhs-number": "1000000011"}, - "config_filenames": ["AUTO_RSV_REG_001.json"], + "request_headers": { + "nhs-login-nhs-number": "1000000011" + }, + "config_filenames": [ + "AUTO_RSV_REG_001.json" + ], "data": [ { "NHS_NUMBER": "1000000011", diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_012.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_012.json index 90ad0dc3f..8fdd7062c 100644 --- a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_012.json +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_012.json @@ -1,7 +1,11 @@ { "scenario_name": "RSV - Regression - Not Eligible, In Magic Cohort, Filtered out Rule", - "request_headers": {"nhs-login-nhs-number": "1000000012"}, - "config_filenames": ["AUTO_RSV_REG_001.json"], + "request_headers": { + "nhs-login-nhs-number": "1000000012" + }, + "config_filenames": [ + "AUTO_RSV_REG_001.json" + ], "data": [ { "NHS_NUMBER": "1000000012", diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_013.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_013.json index 18442da51..e0abd3e5f 100644 --- a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_013.json +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_013.json @@ -1,7 +1,11 @@ { "scenario_name": "RSV - Regression - Eligible, In Magic Cohort", - "request_headers": {"nhs-login-nhs-number": "1000000013"}, - "config_filenames": ["AUTO_RSV_REG_001.json"], + "request_headers": { + "nhs-login-nhs-number": "1000000013" + }, + "config_filenames": [ + "AUTO_RSV_REG_001.json" + ], "data": [ { "NHS_NUMBER": "1000000013", diff --git a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_050.json b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_050.json index d99d67dca..940fd5c71 100644 --- a/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_050.json +++ b/tests/e2e/data/dynamoDB/regressionTestData/AUTO_RSV_REG_050.json @@ -1,7 +1,11 @@ { "scenario_name": "RSV - Regression - NotActionable, Rule Stop Suppression", - "request_headers": {"nhs-login-nhs-number": "1000000050"}, - "config_filenames": ["AUTO_RSV_REG_001.json"], + "request_headers": { + "nhs-login-nhs-number": "1000000050" + }, + "config_filenames": [ + "AUTO_RSV_REG_001.json" + ], "data": [ { "NHS_NUMBER": "1000000050", diff --git a/tests/e2e/data/dynamoDB/storyTestData/.ignore_folder/AUTO_RSV_ELI-216-2.json b/tests/e2e/data/dynamoDB/storyTestData/.ignore_folder/AUTO_RSV_ELI-216-2.json index e6f6b01d0..ce3de9e8b 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/.ignore_folder/AUTO_RSV_ELI-216-2.json +++ b/tests/e2e/data/dynamoDB/storyTestData/.ignore_folder/AUTO_RSV_ELI-216-2.json @@ -1,7 +1,9 @@ { "scenario_name": "ELI-216 - NHS Number check (NHS login) - Incorrect NHS_number", "config_filename": "AUTO_RSV_ELI-216.json", - "request_headers": {"nhs-login-nhs-number": "9000000001"}, + "request_headers": { + "nhs-login-nhs-number": "9000000001" + }, "expected_response_code": 403, "data": [ { diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-3.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-3.json index b1ed5a453..ab3094335 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-3.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-219-3.json @@ -7,7 +7,7 @@ "nhs-login-nhs-number": "9900021903" }, "data": [ -{ + { "NHS_NUMBER": "9900021903", "ATTRIBUTE_TYPE": "COHORTS", "COHORT_MEMBERSHIPS": [ diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-01.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-01.json index 1d8be93a2..711c81c38 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-01.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-01.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900022101" }, - "config_filenames": ["AUTO_RSV_ELI-221-01.json"], + "config_filenames": [ + "AUTO_RSV_ELI-221-01.json" + ], "data": [ { "NHS_NUMBER": "9900022101", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-02.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-02.json index 8d3a10155..390ed4621 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-02.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-02.json @@ -3,9 +3,11 @@ "request_headers": { "nhs-login-nhs-number": "9900022102" }, - "config_filenames": ["AUTO_RSV_ELI-221-02.json"], + "config_filenames": [ + "AUTO_RSV_ELI-221-02.json" + ], "data": [ - { + { "NHS_NUMBER": "9900022102", "ATTRIBUTE_TYPE": "COHORTS", "COHORT_MEMBERSHIPS": [ diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-03.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-03.json index 843aa899c..89b1eb68c 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-03.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-221-03.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900022103" }, - "config_filenames": ["AUTO_RSV_ELI-221-03.json"], + "config_filenames": [ + "AUTO_RSV_ELI-221-03.json" + ], "data": [ { "NHS_NUMBER": "9900022103", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-1.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-1.json index e36d9878a..849c4cc94 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-1.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-222-1.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900022201" }, - "config_filenames": ["AUTO_RSV_ELI-222.json"], + "config_filenames": [ + "AUTO_RSV_ELI-222.json" + ], "data": [ { "NHS_NUMBER": "9900022201", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-236-01.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-236-01.json index c8d0ca3c3..734a9ae50 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-236-01.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-236-01.json @@ -7,7 +7,7 @@ "nhs-login-nhs-number": "9900023601" }, "data": [ - { + { "NHS_NUMBER": "9900023601", "ATTRIBUTE_TYPE": "COHORTS", "COHORT_MEMBERSHIPS": [ diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-01.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-01.json index 60a39caec..30d6495e7 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-01.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-01.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900029501" }, - "config_filenames": ["AUTO_RSV_ELI-295-1.json"], + "config_filenames": [ + "AUTO_RSV_ELI-295-1.json" + ], "data": [ { "NHS_NUMBER": "9900029501", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-02.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-02.json index 210e2e359..0fd8fb4b5 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-02.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-02.json @@ -3,9 +3,11 @@ "request_headers": { "nhs-login-nhs-number": "9900029502" }, - "config_filenames": ["AUTO_RSV_ELI-295-1.json"], + "config_filenames": [ + "AUTO_RSV_ELI-295-1.json" + ], "data": [ - { + { "NHS_NUMBER": "9900029502", "ATTRIBUTE_TYPE": "COHORTS", "COHORT_MEMBERSHIPS": [ diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-03.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-03.json index ca35dc601..418e0d19c 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-03.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-03.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900029503" }, - "config_filenames": ["AUTO_RSV_ELI-295-2.json"], + "config_filenames": [ + "AUTO_RSV_ELI-295-2.json" + ], "data": [ { "NHS_NUMBER": "9900029503", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-04.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-04.json index f5c8e01da..a049d6a19 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-04.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-04.json @@ -4,9 +4,11 @@ "request_headers": { "nhs-login-nhs-number": "9900029504" }, - "config_filenames": ["AUTO_RSV_ELI-295-2.json"], + "config_filenames": [ + "AUTO_RSV_ELI-295-2.json" + ], "data": [ - { + { "NHS_NUMBER": "9900029504", "ATTRIBUTE_TYPE": "COHORTS", "COHORT_MEMBERSHIPS": [ diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-05.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-05.json index 73def4d00..0de52991d 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-05.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-05.json @@ -3,9 +3,11 @@ "request_headers": { "nhs-login-nhs-number": "9900029505" }, - "config_filenames": ["AUTO_RSV_ELI-295-2.json"], + "config_filenames": [ + "AUTO_RSV_ELI-295-2.json" + ], "data": [ - { + { "NHS_NUMBER": "9900029505", "ATTRIBUTE_TYPE": "COHORTS", "COHORT_MEMBERSHIPS": [ diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-07.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-07.json index 96f3e9fc5..14fdd0b20 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-07.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-07.json @@ -3,9 +3,11 @@ "request_headers": { "nhs-login-nhs-number": "9900029507" }, - "config_filenames": ["AUTO_RSV_ELI-295-2.json"], + "config_filenames": [ + "AUTO_RSV_ELI-295-2.json" + ], "data": [ - { + { "NHS_NUMBER": "9900029507", "ATTRIBUTE_TYPE": "COHORTS", "COHORT_MEMBERSHIPS": [ diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-08.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-08.json index 471991d14..cf57ec142 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-08.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-08.json @@ -3,9 +3,11 @@ "request_headers": { "nhs-login-nhs-number": "9900029508" }, - "config_filenames": ["AUTO_RSV_ELI-295-2.json"], + "config_filenames": [ + "AUTO_RSV_ELI-295-2.json" + ], "data": [ - { + { "NHS_NUMBER": "9900029508", "ATTRIBUTE_TYPE": "COHORTS", "COHORT_MEMBERSHIPS": [ diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-09.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-09.json index 7887476fc..06bb1f9e4 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-09.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-09.json @@ -7,7 +7,7 @@ "AUTO_RSV_ELI-295-2.json" ], "data": [ - { + { "NHS_NUMBER": "9900029509", "ATTRIBUTE_TYPE": "COHORTS", "COHORT_MEMBERSHIPS": [ diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-10.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-10.json index 4257bd7bf..1228230ad 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-10.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-10.json @@ -6,9 +6,11 @@ "query_params": { "includeActions": "N" }, - "config_filenames": ["AUTO_RSV_ELI-295-2.json"], + "config_filenames": [ + "AUTO_RSV_ELI-295-2.json" + ], "data": [ - { + { "NHS_NUMBER": "9900029510", "ATTRIBUTE_TYPE": "COHORTS", "COHORT_MEMBERSHIPS": [ diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-11.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-11.json index 0ca834700..8ef6cdfc9 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-11.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-11.json @@ -10,7 +10,7 @@ "AUTO_RSV_ELI-295-2.json" ], "data": [ - { + { "NHS_NUMBER": "9900029511", "ATTRIBUTE_TYPE": "COHORTS", "COHORT_MEMBERSHIPS": [ diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-12.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-12.json index d87edd044..0672281d5 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-12.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-12.json @@ -10,7 +10,7 @@ "AUTO_RSV_ELI-295-2.json" ], "data": [ - { + { "NHS_NUMBER": "9900029512", "ATTRIBUTE_TYPE": "COHORTS", "COHORT_MEMBERSHIPS": [ diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-13.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-13.json index 23da335b6..ff2d8fde6 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-13.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-13.json @@ -3,9 +3,11 @@ "request_headers": { "nhs-login-nhs-number": "9900029513" }, - "config_filenames": ["AUTO_RSV_ELI-295-3.json"], + "config_filenames": [ + "AUTO_RSV_ELI-295-3.json" + ], "data": [ - { + { "NHS_NUMBER": "9900029513", "ATTRIBUTE_TYPE": "COHORTS", "COHORT_MEMBERSHIPS": [ diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-14.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-14.json index c17b1f561..524d8ea13 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-14.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-14.json @@ -3,9 +3,11 @@ "request_headers": { "nhs-login-nhs-number": "9900029514" }, - "config_filenames": ["AUTO_RSV_ELI-295-3.json"], + "config_filenames": [ + "AUTO_RSV_ELI-295-3.json" + ], "data": [ - { + { "NHS_NUMBER": "9900029514", "ATTRIBUTE_TYPE": "COHORTS", "COHORT_MEMBERSHIPS": [ diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-15.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-15.json index fadba0dec..a5119e63c 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-15.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-15.json @@ -7,7 +7,7 @@ "AUTO_RSV_ELI-295-3.json" ], "data": [ - { + { "NHS_NUMBER": "9900029515", "ATTRIBUTE_TYPE": "COHORTS", "COHORT_MEMBERSHIPS": [ diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-16.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-16.json index e64c15e90..b308eed7b 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-16.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-16.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900029516" }, - "config_filenames": ["AUTO_RSV_ELI-295-4.json"], + "config_filenames": [ + "AUTO_RSV_ELI-295-4.json" + ], "data": [ { "NHS_NUMBER": "9900029516", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-17.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-17.json index 93b329923..b08c5a223 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-17.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-295-17.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900029517" }, - "config_filenames": ["AUTO_RSV_ELI-295-4.json"], + "config_filenames": [ + "AUTO_RSV_ELI-295-4.json" + ], "data": [ { "NHS_NUMBER": "9900029517", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-11.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-11.json index 31a7c5ea2..770ba1830 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-11.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-11.json @@ -7,7 +7,12 @@ "category": "VACCINATIONS", "conditions": "HPV" }, - "config_filenames": ["AUTO_RSV_ELI-320-COVID.json","AUTO_RSV_ELI-320-MMR.json", "AUTO_RSV_ELI-320-SCREENING-1.json", "AUTO_RSV_ELI-320-SCREENING-2.json"], + "config_filenames": [ + "AUTO_RSV_ELI-320-COVID.json", + "AUTO_RSV_ELI-320-MMR.json", + "AUTO_RSV_ELI-320-SCREENING-1.json", + "AUTO_RSV_ELI-320-SCREENING-2.json" + ], "data": [ { "NHS_NUMBER": "9900032011", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-2.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-2.json index 6545acd11..a852a6fb8 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-2.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-2.json @@ -6,7 +6,11 @@ "query_params": { "conditions": "ALL" }, - "config_filenames": ["AUTO_RSV_ELI-320-COVID.json","AUTO_RSV_ELI-320-RSV.json", "AUTO_RSV_ELI-320-MMR.json"], + "config_filenames": [ + "AUTO_RSV_ELI-320-COVID.json", + "AUTO_RSV_ELI-320-RSV.json", + "AUTO_RSV_ELI-320-MMR.json" + ], "data": [ { "NHS_NUMBER": "9990032020", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-3.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-3.json index bbb95d4d5..beb538c32 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-3.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-3.json @@ -6,7 +6,11 @@ "query_params": { "conditions": "covid" }, - "config_filenames": ["AUTO_RSV_ELI-320-COVID.json","AUTO_RSV_ELI-320-RSV.json", "AUTO_RSV_ELI-320-MMR.json"], + "config_filenames": [ + "AUTO_RSV_ELI-320-COVID.json", + "AUTO_RSV_ELI-320-RSV.json", + "AUTO_RSV_ELI-320-MMR.json" + ], "data": [ { "NHS_NUMBER": "9990032030", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-4.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-4.json index 6cbdc1698..26dcebca6 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-4.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-4.json @@ -6,7 +6,11 @@ "query_params": { "conditions": "MMR" }, - "config_filenames": ["AUTO_RSV_ELI-320-COVID.json","AUTO_RSV_ELI-320-RSV.json", "AUTO_RSV_ELI-320-MMR.json"], + "config_filenames": [ + "AUTO_RSV_ELI-320-COVID.json", + "AUTO_RSV_ELI-320-RSV.json", + "AUTO_RSV_ELI-320-MMR.json" + ], "data": [ { "NHS_NUMBER": "9990032040", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-5.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-5.json index fa39d5bc1..55e37ebe4 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-5.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-5.json @@ -6,7 +6,11 @@ "query_params": { "conditions": "RSV,MMR" }, - "config_filenames": ["AUTO_RSV_ELI-320-COVID.json","AUTO_RSV_ELI-320-RSV.json", "AUTO_RSV_ELI-320-MMR.json"], + "config_filenames": [ + "AUTO_RSV_ELI-320-COVID.json", + "AUTO_RSV_ELI-320-RSV.json", + "AUTO_RSV_ELI-320-MMR.json" + ], "data": [ { "NHS_NUMBER": "9990032050", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-7.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-7.json index 09d976aa8..4c4b88da9 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-7.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-7.json @@ -6,7 +6,12 @@ "query_params": { "category": "all" }, - "config_filenames": ["AUTO_RSV_ELI-320-COVID.json","AUTO_RSV_ELI-320-MMR.json", "AUTO_RSV_ELI-320-SCREENING-1.json", "AUTO_RSV_ELI-320-SCREENING-2.json"], + "config_filenames": [ + "AUTO_RSV_ELI-320-COVID.json", + "AUTO_RSV_ELI-320-MMR.json", + "AUTO_RSV_ELI-320-SCREENING-1.json", + "AUTO_RSV_ELI-320-SCREENING-2.json" + ], "data": [ { "NHS_NUMBER": "9990032070", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-8.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-8.json index 2082f3188..ed17315b7 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-8.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-8.json @@ -6,7 +6,12 @@ "query_params": { "category": "screening" }, - "config_filenames": ["AUTO_RSV_ELI-320-COVID.json","AUTO_RSV_ELI-320-MMR.json", "AUTO_RSV_ELI-320-SCREENING-1.json", "AUTO_RSV_ELI-320-SCREENING-2.json"], + "config_filenames": [ + "AUTO_RSV_ELI-320-COVID.json", + "AUTO_RSV_ELI-320-MMR.json", + "AUTO_RSV_ELI-320-SCREENING-1.json", + "AUTO_RSV_ELI-320-SCREENING-2.json" + ], "data": [ { "NHS_NUMBER": "9990032080", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-9.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-9.json index 348263932..64be5dfd3 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-9.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-320-9.json @@ -6,7 +6,12 @@ "query_params": { "category": "VACCINATIONS" }, - "config_filenames": ["AUTO_RSV_ELI-320-COVID.json","AUTO_RSV_ELI-320-MMR.json", "AUTO_RSV_ELI-320-SCREENING-1.json", "AUTO_RSV_ELI-320-SCREENING-2.json"], + "config_filenames": [ + "AUTO_RSV_ELI-320-COVID.json", + "AUTO_RSV_ELI-320-MMR.json", + "AUTO_RSV_ELI-320-SCREENING-1.json", + "AUTO_RSV_ELI-320-SCREENING-2.json" + ], "data": [ { "NHS_NUMBER": "9900032090", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_001.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_001.json index 3ca2b715e..0b08b65d5 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_001.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_001.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900036501" }, - "config_filenames": ["AUTO_RSV_ELI-365.json"], + "config_filenames": [ + "AUTO_RSV_ELI-365.json" + ], "data": [ { "NHS_NUMBER": "9900036501", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_002.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_002.json index 8db7b0832..0fff27492 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_002.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_002.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900036502" }, - "config_filenames": ["AUTO_RSV_ELI-365.json"], + "config_filenames": [ + "AUTO_RSV_ELI-365.json" + ], "data": [ { "NHS_NUMBER": "9900036502", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_003.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_003.json index 96c386845..51b29ed9c 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_003.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_003.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900036503" }, - "config_filenames": ["AUTO_RSV_ELI-365.json"], + "config_filenames": [ + "AUTO_RSV_ELI-365.json" + ], "data": [ { "NHS_NUMBER": "9900036503", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_004.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_004.json index 9b5be5a51..999c96cac 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_004.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_004.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900036504" }, - "config_filenames": ["AUTO_RSV_ELI-365.json"], + "config_filenames": [ + "AUTO_RSV_ELI-365.json" + ], "data": [ { "NHS_NUMBER": "9900036504", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_005.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_005.json index b9fb7b290..57b9d2ed6 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_005.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_005.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900036505" }, - "config_filenames": ["AUTO_RSV_ELI-365.json"], + "config_filenames": [ + "AUTO_RSV_ELI-365.json" + ], "data": [ { "NHS_NUMBER": "9900036505", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_006.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_006.json index f6f33380d..f4a3c24a2 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_006.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_006.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900036506" }, - "config_filenames": ["AUTO_RSV_ELI-365.json"], + "config_filenames": [ + "AUTO_RSV_ELI-365.json" + ], "data": [ { "NHS_NUMBER": "9900036506", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_007.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_007.json index a59e0aca1..62812795e 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_007.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_007.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900036507" }, - "config_filenames": ["AUTO_RSV_ELI-365.json"], + "config_filenames": [ + "AUTO_RSV_ELI-365.json" + ], "data": [ { "NHS_NUMBER": "9900036507", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_008.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_008.json index e6f4d6880..669b8b55e 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_008.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_008.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900036508" }, - "config_filenames": ["AUTO_RSV_ELI-365.json"], + "config_filenames": [ + "AUTO_RSV_ELI-365.json" + ], "data": [ { "NHS_NUMBER": "9900036508", diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_009.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_009.json index ed1357e09..2201d5b31 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_009.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_009.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900036509" }, - "config_filenames": ["AUTO_RSV_ELI-365.json"], + "config_filenames": [ + "AUTO_RSV_ELI-365.json" + ], "data": [ { "NHS_NUMBER": "9900036509", @@ -37,7 +39,7 @@ "NHS_NUMBER": "9900036509", "ATTRIBUTE_TYPE": "RSV", "LAST_SUCCESSFUL_DATE": null, - "BOOKED_APPOINTMENT_DATE" : null, + "BOOKED_APPOINTMENT_DATE": null, "BOOKED_APPOINTMENT_PROVIDER": null } ] diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_010.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_010.json index 67f80d2b7..96f1a407a 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_010.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_010.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900036510" }, - "config_filenames": ["AUTO_RSV_ELI-365.json"], + "config_filenames": [ + "AUTO_RSV_ELI-365.json" + ], "data": [ { "NHS_NUMBER": "9900036510", @@ -37,7 +39,7 @@ "NHS_NUMBER": "9900036510", "ATTRIBUTE_TYPE": "RSV", "LAST_SUCCESSFUL_DATE": null, - "BOOKED_APPOINTMENT_DATE" : null, + "BOOKED_APPOINTMENT_DATE": null, "BOOKED_APPOINTMENT_PROVIDER": null } ] diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_011.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_011.json index 1fb932066..fe6b07b59 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_011.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_011.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900036511" }, - "config_filenames": ["AUTO_RSV_ELI-365.json"], + "config_filenames": [ + "AUTO_RSV_ELI-365.json" + ], "data": [ { "NHS_NUMBER": "9900036511", @@ -37,7 +39,7 @@ "NHS_NUMBER": "9900036511", "ATTRIBUTE_TYPE": "RSV", "LAST_SUCCESSFUL_DATE": null, - "BOOKED_APPOINTMENT_DATE" : null, + "BOOKED_APPOINTMENT_DATE": null, "BOOKED_APPOINTMENT_PROVIDER": null } ] diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_012.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_012.json index fd13c856b..a37e23ee7 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_012.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_012.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900036512" }, - "config_filenames": ["AUTO_RSV_ELI-365.json"], + "config_filenames": [ + "AUTO_RSV_ELI-365.json" + ], "data": [ { "NHS_NUMBER": "9900036512", @@ -37,7 +39,7 @@ "NHS_NUMBER": "9900036512", "ATTRIBUTE_TYPE": "RSV", "LAST_SUCCESSFUL_DATE": null, - "BOOKED_APPOINTMENT_DATE" : null, + "BOOKED_APPOINTMENT_DATE": null, "BOOKED_APPOINTMENT_PROVIDER": null } ] diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_013.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_013.json index ac210c779..ca0510b12 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_013.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_013.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900036513" }, - "config_filenames": ["AUTO_RSV_ELI-365.json"], + "config_filenames": [ + "AUTO_RSV_ELI-365.json" + ], "data": [ { "NHS_NUMBER": "9900036513", @@ -37,7 +39,7 @@ "NHS_NUMBER": "9900036513", "ATTRIBUTE_TYPE": "RSV", "LAST_SUCCESSFUL_DATE": "<>", - "BOOKED_APPOINTMENT_DATE" : null, + "BOOKED_APPOINTMENT_DATE": null, "BOOKED_APPOINTMENT_PROVIDER": null } ] diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_014.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_014.json index d297874f8..ca394208c 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_014.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_014.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900036514" }, - "config_filenames": ["AUTO_RSV_ELI-365.json"], + "config_filenames": [ + "AUTO_RSV_ELI-365.json" + ], "data": [ { "NHS_NUMBER": "9900036514", @@ -37,7 +39,7 @@ "NHS_NUMBER": "9900036514", "ATTRIBUTE_TYPE": "RSV", "LAST_SUCCESSFUL_DATE": null, - "BOOKED_APPOINTMENT_DATE" : null, + "BOOKED_APPOINTMENT_DATE": null, "BOOKED_APPOINTMENT_PROVIDER": null } ] diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_015.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_015.json index 29db6c359..405635d1c 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_015.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_015.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900036515" }, - "config_filenames": ["AUTO_RSV_ELI-365.json"], + "config_filenames": [ + "AUTO_RSV_ELI-365.json" + ], "data": [ { "NHS_NUMBER": "9900036515", @@ -37,7 +39,7 @@ "NHS_NUMBER": "9900036515", "ATTRIBUTE_TYPE": "RSV", "LAST_SUCCESSFUL_DATE": null, - "BOOKED_APPOINTMENT_DATE" : null, + "BOOKED_APPOINTMENT_DATE": null, "BOOKED_APPOINTMENT_PROVIDER": null } ] diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_016.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_016.json index 50b4f6965..0a84624d8 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_016.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_016.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900036516" }, - "config_filenames": ["AUTO_RSV_ELI-365.json"], + "config_filenames": [ + "AUTO_RSV_ELI-365.json" + ], "data": [ { "NHS_NUMBER": "9900036516", @@ -37,14 +39,14 @@ "NHS_NUMBER": "9900036516", "ATTRIBUTE_TYPE": "RSV", "LAST_SUCCESSFUL_DATE": null, - "BOOKED_APPOINTMENT_DATE" : null, + "BOOKED_APPOINTMENT_DATE": null, "BOOKED_APPOINTMENT_PROVIDER": null }, { "NHS_NUMBER": "9900036516", "ATTRIBUTE_TYPE": "COVID", "LAST_SUCCESSFUL_DATE": null, - "BOOKED_APPOINTMENT_DATE" : null, + "BOOKED_APPOINTMENT_DATE": null, "BOOKED_APPOINTMENT_PROVIDER": null } ] diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_017.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_017.json index 21074c9be..901917b13 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_017.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_017.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900036517" }, - "config_filenames": ["AUTO_RSV_ELI-365.json"], + "config_filenames": [ + "AUTO_RSV_ELI-365.json" + ], "data": [ { "NHS_NUMBER": "9900036517", @@ -37,7 +39,7 @@ "NHS_NUMBER": "9900036517", "ATTRIBUTE_TYPE": "RSV", "LAST_SUCCESSFUL_DATE": null, - "BOOKED_APPOINTMENT_DATE" : "<>", + "BOOKED_APPOINTMENT_DATE": "<>", "BOOKED_APPOINTMENT_PROVIDER": "ACC" } ] diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_018.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_018.json index deccd2359..dc5de3881 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_018.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_018.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900036518" }, - "config_filenames": ["AUTO_RSV_ELI-365.json"], + "config_filenames": [ + "AUTO_RSV_ELI-365.json" + ], "data": [ { "NHS_NUMBER": "9900036518", @@ -37,7 +39,7 @@ "NHS_NUMBER": "9900036518", "ATTRIBUTE_TYPE": "RSV", "LAST_SUCCESSFUL_DATE": null, - "BOOKED_APPOINTMENT_DATE" : "<>", + "BOOKED_APPOINTMENT_DATE": "<>", "BOOKED_APPOINTMENT_PROVIDER": "NBS" } ] diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_019.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_019.json index 530a79359..6f38bac43 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_019.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_019.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900036519" }, - "config_filenames": ["AUTO_RSV_ELI-365.json"], + "config_filenames": [ + "AUTO_RSV_ELI-365.json" + ], "data": [ { "NHS_NUMBER": "9900036519", @@ -37,7 +39,7 @@ "NHS_NUMBER": "9900036519", "ATTRIBUTE_TYPE": "RSV", "LAST_SUCCESSFUL_DATE": null, - "BOOKED_APPOINTMENT_DATE" : "<>", + "BOOKED_APPOINTMENT_DATE": "<>", "BOOKED_APPOINTMENT_PROVIDER": "NBS" } ] diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_020.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_020.json index 2c77fc177..6b8c81428 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_020.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_020.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900036520" }, - "config_filenames": ["AUTO_RSV_ELI-365.json"], + "config_filenames": [ + "AUTO_RSV_ELI-365.json" + ], "data": [ { "NHS_NUMBER": "9900036520", @@ -37,7 +39,7 @@ "NHS_NUMBER": "9900036520", "ATTRIBUTE_TYPE": "RSV", "LAST_SUCCESSFUL_DATE": null, - "BOOKED_APPOINTMENT_DATE" : null, + "BOOKED_APPOINTMENT_DATE": null, "BOOKED_APPOINTMENT_PROVIDER": null } ] diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_021.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_021.json index 1f953ddb8..33696fc63 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_021.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_021.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900036521" }, - "config_filenames": ["AUTO_RSV_ELI-365.json"], + "config_filenames": [ + "AUTO_RSV_ELI-365.json" + ], "data": [ { "NHS_NUMBER": "9900036521", @@ -38,7 +40,7 @@ "NHS_NUMBER": "9900036521", "ATTRIBUTE_TYPE": "RSV", "LAST_SUCCESSFUL_DATE": null, - "BOOKED_APPOINTMENT_DATE" : null, + "BOOKED_APPOINTMENT_DATE": null, "BOOKED_APPOINTMENT_PROVIDER": null } ] diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_022.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_022.json index 17be92a34..a4286be82 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_022.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-365_022.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9900036522" }, - "config_filenames": ["AUTO_RSV_ELI-365.json"], + "config_filenames": [ + "AUTO_RSV_ELI-365.json" + ], "data": [ { "NHS_NUMBER": "9900036522", @@ -38,7 +40,7 @@ "NHS_NUMBER": "9900036522", "ATTRIBUTE_TYPE": "RSV", "LAST_SUCCESSFUL_DATE": null, - "BOOKED_APPOINTMENT_DATE" : null, + "BOOKED_APPOINTMENT_DATE": null, "BOOKED_APPOINTMENT_PROVIDER": null } ] diff --git a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_005.json b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_005.json index c372a1afa..39c5b2be8 100644 --- a/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_005.json +++ b/tests/e2e/data/dynamoDB/storyTestData/AUTO_RSV_ELI-373_005.json @@ -39,7 +39,7 @@ "NHS_NUMBER": "9900373005", "ATTRIBUTE_TYPE": "RSV", "LAST_SUCCESSFUL_DATE": null, - "BOOKED_APPOINTMENT_DATE":null, + "BOOKED_APPOINTMENT_DATE": null, "BOOKED_APPOINTMENT_PROVIDER": null } ] diff --git a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_001.json b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_001.json index e7e0b5b37..c744f8e5b 100644 --- a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_001.json +++ b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_001.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9686368973" }, - "config_filenames": ["vita_integration_test_config.json"], + "config_filenames": [ + "vita_integration_test_config.json" + ], "data": [ { "NHS_NUMBER": "9686368973", diff --git a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_002.json b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_002.json index 2ac0aaa1a..35bcb3efc 100644 --- a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_002.json +++ b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_002.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9686368906" }, - "config_filenames": ["vita_integration_test_config.json"], + "config_filenames": [ + "vita_integration_test_config.json" + ], "data": [ { "NHS_NUMBER": "9686368906", diff --git a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_003.json b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_003.json index aa1be4725..0e8cdd257 100644 --- a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_003.json +++ b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_003.json @@ -3,9 +3,11 @@ "request_headers": { "nhs-login-nhs-number": "9658218873" }, - "config_filenames": ["vita_integration_test_config.json"], + "config_filenames": [ + "vita_integration_test_config.json" + ], "data": [ - { + { "NHS_NUMBER": "9658218873", "ATTRIBUTE_TYPE": "COHORTS", "COHORT_MEMBERSHIPS": [ diff --git a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_004.json b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_004.json index 01e433f44..ed20d7d35 100644 --- a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_004.json +++ b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_004.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9658218881" }, - "config_filenames": ["vita_integration_test_config.json"], + "config_filenames": [ + "vita_integration_test_config.json" + ], "data": [ { "NHS_NUMBER": "9658218881", diff --git a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_005.json b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_005.json index ca4128ae6..2ef47a620 100644 --- a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_005.json +++ b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_005.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9658218903" }, - "config_filenames": ["vita_integration_test_config.json"], + "config_filenames": [ + "vita_integration_test_config.json" + ], "data": [ { "NHS_NUMBER": "9658218903", diff --git a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_006.json b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_006.json index 192d57f6e..9cda11395 100644 --- a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_006.json +++ b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_006.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9658218989" }, - "config_filenames": ["vita_integration_test_config.json"], + "config_filenames": [ + "vita_integration_test_config.json" + ], "data": [ { "NHS_NUMBER": "9658218989", diff --git a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_007.json b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_007.json index 8013cf75f..e7b4d9a30 100644 --- a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_007.json +++ b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_007.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9658218997" }, - "config_filenames": ["vita_integration_test_config.json"], + "config_filenames": [ + "vita_integration_test_config.json" + ], "data": [ { "NHS_NUMBER": "9658218997", diff --git a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_008.json b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_008.json index 2f4cb8d8b..1ff7adb41 100644 --- a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_008.json +++ b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_008.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9658219004" }, - "config_filenames": ["vita_integration_test_config.json"], + "config_filenames": [ + "vita_integration_test_config.json" + ], "data": [ { "NHS_NUMBER": "9658219004", diff --git a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_009.json b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_009.json index 5d2ca0ea6..f0c0971a2 100644 --- a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_009.json +++ b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_009.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9658219012" }, - "config_filenames": ["vita_integration_test_config.json"], + "config_filenames": [ + "vita_integration_test_config.json" + ], "data": [ { "NHS_NUMBER": "9658219012", diff --git a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_011.json b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_011.json index 5c7a7eb8e..6ebd3beb1 100644 --- a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_011.json +++ b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_011.json @@ -3,9 +3,11 @@ "request_headers": { "nhs-login-nhs-number": "9658220142" }, - "config_filenames": ["vita_integration_test_config.json"], + "config_filenames": [ + "vita_integration_test_config.json" + ], "data": [ - { + { "NHS_NUMBER": "9658220142", "ATTRIBUTE_TYPE": "COHORTS", "COHORT_MEMBERSHIPS": [ diff --git a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_012.json b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_012.json index a8f8ade05..a91993317 100644 --- a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_012.json +++ b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_012.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9658220150" }, - "config_filenames": ["vita_integration_test_config.json"], + "config_filenames": [ + "vita_integration_test_config.json" + ], "data": [ { "NHS_NUMBER": "9658220150", diff --git a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_013.json b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_013.json index 039686abf..77057e50b 100644 --- a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_013.json +++ b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_013.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9450114080" }, - "config_filenames": ["vita_integration_test_config.json"], + "config_filenames": [ + "vita_integration_test_config.json" + ], "data": [ { "NHS_NUMBER": "9450114080", @@ -28,6 +30,5 @@ "ATTRIBUTE_TYPE": "RSV", "LAST_SUCCESSFUL_DATE": "<>" } - ] } diff --git a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_014.json b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_014.json index 0f8b3c63f..7ec5f92d9 100644 --- a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_014.json +++ b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_014.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9466447939" }, - "config_filenames": ["vita_integration_test_config.json"], + "config_filenames": [ + "vita_integration_test_config.json" + ], "data": [ { "NHS_NUMBER": "9466447939", diff --git a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_015.json b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_015.json index bf6397698..2f663767e 100644 --- a/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_015.json +++ b/tests/e2e/data/dynamoDB/vitaIntegrationTestData/AUTO_RSV_VITA_INT_015.json @@ -3,7 +3,9 @@ "request_headers": { "nhs-login-nhs-number": "9657933617" }, - "config_filenames": ["vita_integration_test_config.json"], + "config_filenames": [ + "vita_integration_test_config.json" + ], "data": [ { "NHS_NUMBER": "9657933617", diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_024.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_024.json index c0bb698d4..0dc4f9323 100644 --- a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_024.json +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_024.json @@ -31,7 +31,7 @@ "eligibilityCohorts": [ { "cohortCode": "rsv_age_catchup", - "cohortStatus": "Actionable", + "cohortStatus": "Actionable", "cohortText": "turned 80 after 1st September 2024" } ], diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_025.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_025.json index c0bb698d4..0dc4f9323 100644 --- a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_025.json +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_025.json @@ -31,7 +31,7 @@ "eligibilityCohorts": [ { "cohortCode": "rsv_age_catchup", - "cohortStatus": "Actionable", + "cohortStatus": "Actionable", "cohortText": "turned 80 after 1st September 2024" } ], diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_026.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_026.json index 2800f4974..3367c8f54 100644 --- a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_026.json +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_026.json @@ -24,7 +24,7 @@ "eligibilityCohorts": [ { "cohortCode": "rsv_age_catchup", - "cohortStatus": "Actionable", + "cohortStatus": "Actionable", "cohortText": "turned 80 after 1st September 2024" } ], diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_027.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_027.json index 2800f4974..3367c8f54 100644 --- a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_027.json +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_027.json @@ -24,7 +24,7 @@ "eligibilityCohorts": [ { "cohortCode": "rsv_age_catchup", - "cohortStatus": "Actionable", + "cohortStatus": "Actionable", "cohortText": "turned 80 after 1st September 2024" } ], diff --git a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_028.json b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_028.json index 2800f4974..3367c8f54 100644 --- a/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_028.json +++ b/tests/e2e/data/responses/storyTestResponses/AUTO_RSV_ELI-365_028.json @@ -24,7 +24,7 @@ "eligibilityCohorts": [ { "cohortCode": "rsv_age_catchup", - "cohortStatus": "Actionable", + "cohortStatus": "Actionable", "cohortText": "turned 80 after 1st September 2024" } ], diff --git a/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_500.json b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_500.json index fd6e101bf..c6b5dcd75 100644 --- a/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_500.json +++ b/tests/e2e/data/responses/vitaIntegrationTestResponses/AUTO_RSV_VITA_INT_500.json @@ -1,23 +1,23 @@ - { - "resourceType": "OperationOutcome", - "id": "a4639b8e-4359-4f17-8016-d325846bbc2a", - "meta": { - "lastUpdated": "2025-08-01T08:16:47.379654Z" - }, - "issue": [ - { - "severity": "error", - "code": "processing", - "details": { - "coding": [ - { - "system": "https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1", - "code": "INTERNAL_SERVER_ERROR", - "display": "An unexpected internal server error occurred." - } - ] - }, - "diagnostics": "An unexpected error occurred." - } - ] - } +{ + "resourceType": "OperationOutcome", + "id": "a4639b8e-4359-4f17-8016-d325846bbc2a", + "meta": { + "lastUpdated": "2025-08-01T08:16:47.379654Z" + }, + "issue": [ + { + "severity": "error", + "code": "processing", + "details": { + "coding": [ + { + "system": "https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1", + "code": "INTERNAL_SERVER_ERROR", + "display": "An unexpected internal server error occurred." + } + ] + }, + "diagnostics": "An unexpected error occurred." + } + ] +}