Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
c928884
feat: trying to improve running speed of the retry code in e2e tests
SamRobinson75684 Aug 29, 2025
daa5fb9
Merge branch 'main' into feat/DTOSS-10703-improving-test-speed
SamRobinson75684 Aug 29, 2025
792df7a
fix: removing unwanted changes
SamRobinson75684 Aug 29, 2025
10807fc
Merge branch 'main' into feat/DTOSS-10703-improving-test-speed
SamRobinson75684 Sep 1, 2025
27dd36a
chore: addressing comments on PR
SamRobinson75684 Sep 1, 2025
d292ea8
chore: adding new config items to env example
SamRobinson75684 Sep 1, 2025
856494e
fix: trying to make sure retry is actually working
SamRobinson75684 Sep 1, 2025
f507d9f
Merge branch 'main' into feat/DTOSS-10703-improving-test-speed
SamRobinson75684 Sep 1, 2025
2b1c594
fix: trying to make sure retry is actually working
SamRobinson75684 Sep 1, 2025
9c7ec6d
Merge branch 'main' into feat/DTOSS-10703-improving-test-speed
SamRobinson75684 Sep 1, 2025
c5e5ce2
Merge branch 'main' into feat/DTOSS-10703-improving-test-speed
SamRobinson75684 Sep 3, 2025
9790d59
fix: making sure tests are not flaky
SamRobinson75684 Sep 4, 2025
497b4e4
Merge branch 'main' into feat/DTOSS-10703-improving-test-speed
SamRobinson75684 Sep 4, 2025
0522a0d
fix: trying to make sure that both all tests run the same. (this comm…
SamRobinson75684 Sep 5, 2025
f67a4f0
fix: trying to get local debugging working
SamRobinson75684 Sep 8, 2025
4a625f1
Merge branch 'main' into feat/DTOSS-10703-improving-test-speed
SamRobinson75684 Sep 9, 2025
009671a
fix: code clean up
SamRobinson75684 Sep 10, 2025
49ef0c4
Merge branch 'main' into feat/DTOSS-10703-improving-test-speed
SamRobinson75684 Sep 10, 2025
f6fb844
Merge branch 'main' into feat/DTOSS-10703-improving-test-speed
SamRobinson75684 Sep 11, 2025
e7fa30e
fix: adding tests back in
SamRobinson75684 Sep 11, 2025
0b69ff4
fix: creating pick test types function
SamRobinson75684 Sep 11, 2025
fe6b061
fix: removing unwanted code
SamRobinson75684 Sep 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,4 @@ application/CohortManager/src/Functions/*.json
# Act
.act.secrets
.act.vars
tests/.vscode/settings.json
4 changes: 4 additions & 0 deletions application/CohortManager/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ SERVICENOW_CLIENT_ID=
SERVICENOW_CLIENT_SECRET=
SERVICENOW_REFRESH_TOKEN=


MAX_NUMBER_OF_RETRIES=""
TIME_BETWEEN_RETRIES_IN_SECONDS=""

# CAAS Manage Subscription (local/dev overrides)
# Optional overrides for CAAS mailbox IDs used by ManageCaasSubscription Subscribe stub
CAAS_SUBSCRIBE_TO_MAILBOX=
Expand Down
144 changes: 144 additions & 0 deletions tests/playwright-tests/src/api/RetryCore/Retry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { APIResponse, expect } from "@playwright/test";
import { config } from "../../config/env";
import { ApiResponse } from "../core/types";
import { fetchApiResponse, findMatchingObject, validateFields } from "../apiHelper";

const NHS_NUMBER_KEY = config.nhsNumberKey;
const NHS_NUMBER_KEY_EXCEPTION_DEMOGRAPHIC = config.nhsNumberKeyExceptionDemographic;


export async function validateApiResponse(validationJson: any, request: any): Promise<{ status: boolean; errorTrace?: any }> {
let endpoint = "";
let errorTrace: any = undefined;
let status = false;

let resultFromPolling: { apiResponse: APIResponse, status: boolean, errorTrace: any} | null = null;

for (const apiValidation of validationJson) {
endpoint = apiValidation.validations.apiEndpoint;
resultFromPolling = await pollAPI(endpoint, apiValidation, request);
}

if(resultFromPolling) {
status = resultFromPolling.status
errorTrace = resultFromPolling.errorTrace
}
return {status, errorTrace };
}


async function pollAPI(endpoint: string, apiValidation: any, request: any): Promise<{ apiResponse: APIResponse, status: boolean, errorTrace: any}> {
let apiResponse: APIResponse | null = null;
let i = 0;
let errorTrace: any = undefined;

let maxNumberOfRetries = config.maxNumberOfRetries;
let maxTimeBetweenRequests = config.maxTimeBetweenRequests;
let status = false;
console.info(`now trying request for ${maxNumberOfRetries} retries`);
while (i < Number(maxNumberOfRetries)) {
try{
apiResponse = await fetchApiResponse(endpoint, request);
switch(apiResponse.status()) {
case 204:
console.info("now handling no content response");
const expectedCount = apiValidation.validations.expectedCount;
status = await HandleNoContentResponse(expectedCount, apiValidation, endpoint);
break;
case 200:
console.info("now handling OK response");
status = await handleOKResponse(apiValidation, endpoint, apiResponse);
break;
default:
console.error("there was an error when handling response from ");
break;
}
console.log("api status code is: ", apiResponse.status());
if(status) {
break;
}
i++;

console.info(`http response completed ${i}/${maxNumberOfRetries} of number of retries`);
await new Promise(res => setTimeout(res, maxTimeBetweenRequests));

} catch (error) {
const errorMsg = `Endpoint: ${endpoint}, Status: ${apiResponse?.status?.()}, Error: ${error instanceof Error ? error.stack || error.message : error}`;
errorTrace = errorMsg;
if (apiResponse?.status?.() === 204) {
console.info(`ℹ️\t Status 204: No data found in the table using endpoint ${endpoint}`);
}
}
}


if (!apiResponse) {
throw new Error("apiResponse was never assigned");
}

return {apiResponse, status, errorTrace};
}

async function HandleNoContentResponse(expectedCount: number, apiValidation: any, endpoint: string): Promise<boolean> {
if (expectedCount !== undefined && Number(expectedCount) === 0) {
console.info(`✅ Status 204: Expected 0 records for endpoint ${endpoint}`);

// Get NHS number for validation
const nhsNumber = apiValidation.validations.NHSNumber ||
apiValidation.validations.NhsNumber ||
apiValidation.validations[NHS_NUMBER_KEY] ||
apiValidation.validations[NHS_NUMBER_KEY_EXCEPTION_DEMOGRAPHIC];

console.info(`Validating fields using 🅰️\t🅿️\tℹ️\t ${endpoint}`);
console.info(`From Response: null (204 No Content - 0 records as expected)`);
let status = await validateFields(apiValidation, null, nhsNumber, []);
return status;
} else {
// 204 is unexpected, log error and return false to trigger retry
console.warn(`Status 204: No data found in the table using endpoint ${endpoint}`);
return false;
}
}

async function handleOKResponse(apiValidation: any, endpoint: string, response: any ) : Promise<boolean>{
// Normal response handling (200, etc.)
expect(response.ok()).toBeTruthy();
const responseBody = await response.json();
expect(Array.isArray(responseBody)).toBeTruthy();
const { matchingObject, nhsNumber, matchingObjects } = await findMatchingObject(endpoint, responseBody, apiValidation);
console.info(`Validating fields using 🅰️\t🅿️\tℹ️\t ${endpoint}`);
console.info(`From Response ${JSON.stringify(matchingObject, null, 2)}`);
let status = await validateFields(apiValidation, matchingObject, nhsNumber, matchingObjects);

return status;
}

export async function pollApiForOKResponse(httpRequest: () => Promise<ApiResponse>): Promise<ApiResponse>{
let apiResponse: ApiResponse | null = null;
let i = 0;
let maxNumberOfRetries = config.maxNumberOfRetries;
let maxTimeBetweenRequests = config.maxTimeBetweenRequests;

console.info(`now trying request for ${maxNumberOfRetries} retries`);
while (i < Number(maxNumberOfRetries)) {
try {
apiResponse = await httpRequest();
if (apiResponse.status == 200) {
console.info("200 response found")
break;
}
}
catch(exception) {
console.error("Error reading request body:", exception);
}
i++;

console.info(`http response completed ${i}/${maxNumberOfRetries} of number of retries`);
await new Promise(res => setTimeout(res, maxTimeBetweenRequests));
}

if (!apiResponse) {
throw new Error("apiResponse was never assigned");
}
return apiResponse;
};
Loading
Loading