diff --git a/.github/actions/acceptance-tests/action.yaml b/.github/actions/acceptance-tests/action.yaml
index 3b76f9bb8..d6735f77a 100644
--- a/.github/actions/acceptance-tests/action.yaml
+++ b/.github/actions/acceptance-tests/action.yaml
@@ -21,6 +21,11 @@ inputs:
description: Name of the component under test
default: dl
+ shard:
+ description: "Playwright shard in format index/total (e.g. 1/4). If empty, runs all tests."
+ required: false
+ default: ""
+
runs:
using: "composite"
@@ -38,7 +43,20 @@ runs:
shell: bash
run: |
npm ci
+ - name: Cache generated dependencies
+ id: cache-generated
+ uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
+ with:
+ path: |
+ schemas/digital-letters/
+ output/digital-letters/
+ src/digital-letters-events/types/
+ src/digital-letters-events/validators/
+ src/digital-letters-events/guard-functions/
+ src/digital-letters-events/digital_letters_events/models/
+ key: generated-deps-${{ runner.os }}-${{ hashFiles('src/cloudevents/**', 'src/typescript-schema-generator/**', 'src/python-schema-generator/**') }}
- name: "Generate dependencies"
+ if: steps.cache-generated.outputs.cache-hit != 'true'
shell: bash
run: |
npm run generate-dependencies
@@ -58,6 +76,7 @@ runs:
env:
TEST_TYPE: ${{ inputs.testType }}
ENVIRONMENT: ${{ inputs.targetEnvironment }}
+ PLAYWRIGHT_SHARD: ${{ inputs.shard }}
- name: Archive integration test results
if: ${{ inputs.testType == 'integration' }}
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
diff --git a/.github/scripts/dispatch_internal_repo_workflow.sh b/.github/scripts/dispatch_internal_repo_workflow.sh
index a52c1bbee..d3166ba5c 100755
--- a/.github/scripts/dispatch_internal_repo_workflow.sh
+++ b/.github/scripts/dispatch_internal_repo_workflow.sh
@@ -80,6 +80,10 @@ while [[ $# -gt 0 ]]; do
overrideRoleName="$2"
shift 2
;;
+ --shardCount) # Number of parallel shards to split tests across (optional)
+ shardCount="$2"
+ shift 2
+ ;;
*)
echo "[ERROR] Unknown argument: $1"
exit 1
@@ -167,6 +171,7 @@ echo " overrides: $overrides"
echo " overrideProjectName: $overrideProjectName"
echo " overrideRoleName: $overrideRoleName"
echo " targetProject: $targetProject"
+echo " shardCount: ${shardCount:-}"
DISPATCH_EVENT=$(jq -ncM \
--arg infraRepoName "$infraRepoName" \
@@ -180,6 +185,7 @@ DISPATCH_EVENT=$(jq -ncM \
--arg overrideProjectName "$overrideProjectName" \
--arg overrideRoleName "$overrideRoleName" \
--arg targetProject "$targetProject" \
+ --arg shardCount "${shardCount:-}" \
'{
"ref": "'"$internalRef"'",
"inputs": (
@@ -188,12 +194,13 @@ DISPATCH_EVENT=$(jq -ncM \
(if $overrideProjectName != "" then { "overrideProjectName": $overrideProjectName } else {} end) +
(if $overrideRoleName != "" then { "overrideRoleName": $overrideRoleName } else {} end) +
(if $targetProject != "" then { "targetProject": $targetProject } else {} end) +
+ (if $shardCount != "" then { "shardCount": $shardCount } else {} end) +
{
"releaseVersion": $releaseVersion,
"targetEnvironment": $targetEnvironment,
"targetAccountGroup": $targetAccountGroup,
"targetComponent": $targetComponent,
- "overrides": $overrides,
+ "overrides": $overrides
}
)
}')
diff --git a/.github/workflows/cicd-1-pull-request.yaml b/.github/workflows/cicd-1-pull-request.yaml
index 9309aec55..7e2c7faef 100644
--- a/.github/workflows/cicd-1-pull-request.yaml
+++ b/.github/workflows/cicd-1-pull-request.yaml
@@ -180,6 +180,7 @@ jobs:
--terraformAction "apply" \
--overrideProjectName "nhs" \
--overrideRoleName "nhs-main-acct-digital-letters-github-deploy" \
+ --internalRef "feature/enable-component-test-sharding" \
--overrides "branch_name=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}"
acceptance-stage: # Recommended maximum execution time is 10 minutes
name: "Acceptance stage"
@@ -189,6 +190,7 @@ jobs:
with:
target_environment: "pr${{ needs.metadata.outputs.pr_number }}"
target_account_group: nhs-notify-digital-letters-dev
+ internal_ref: "feature/enable-component-test-sharding"
secrets:
APP_PEM_FILE: ${{ secrets.APP_PEM_FILE }}
APP_CLIENT_ID: ${{ secrets.APP_CLIENT_ID }}
diff --git a/.github/workflows/stage-4-acceptance.yaml b/.github/workflows/stage-4-acceptance.yaml
index cbd3b1551..062dab891 100644
--- a/.github/workflows/stage-4-acceptance.yaml
+++ b/.github/workflows/stage-4-acceptance.yaml
@@ -20,6 +20,10 @@ on:
description: "Account for the environment being tested"
required: true
type: string
+ internal_ref:
+ description: "Branch or ref to use in nhs-notify-internal (defaults to main)"
+ required: false
+ type: string
jobs:
test-security:
@@ -84,7 +88,9 @@ jobs:
--targetWorkflow "dispatch-contextual-tests-dynamic-env.yaml" \
--targetEnvironment "$TARGET_ENVIRONMENT" \
--targetAccountGroup "$TARGET_ACCOUNT_GROUP" \
- --targetComponent "dl"
+ --targetComponent "dl" \
+ --internalRef "${{ inputs.internal_ref || 'main' }}" \
+ --shardCount "6"
test-accessibility:
name: "Accessibility test"
runs-on: ubuntu-latest
diff --git a/infrastructure/terraform/components/dl/README.md b/infrastructure/terraform/components/dl/README.md
index 01d1be1fe..ef7158d26 100644
--- a/infrastructure/terraform/components/dl/README.md
+++ b/infrastructure/terraform/components/dl/README.md
@@ -34,6 +34,8 @@ No requirements.
| [event\_anomaly\_period](#input\_event\_anomaly\_period) | The period in seconds over which the specified statistic is applied for anomaly detection. Minimum 300 seconds (5 minutes). Recommended: 300-600. | `number` | `300` | no |
| [eventpub\_control\_plane\_bus\_arn](#input\_eventpub\_control\_plane\_bus\_arn) | Event publisher control plane | `string` | n/a | yes |
| [eventpub\_data\_plane\_bus\_arn](#input\_eventpub\_data\_plane\_bus\_arn) | Event publisher data plane | `string` | n/a | yes |
+| [firehose\_destination\_buffer\_interval](#input\_firehose\_destination\_buffer\_interval) | The Firehose destination buffer interval in seconds. Lower values reduce latency for tests but increase costs. Minimum is 60, default (Terraform) is 300. | `number` | `300` | no |
+| [firehose\_processor\_buffer\_interval](#input\_firehose\_processor\_buffer\_interval) | The Firehose Lambda processor buffer interval in seconds. Should be greater than firehose\_destination\_buffer\_interval. Minimum is 61, default is 301. | `number` | `301` | no |
| [force\_destroy](#input\_force\_destroy) | Flag to force deletion of S3 buckets | `bool` | `false` | no |
| [force\_lambda\_code\_deploy](#input\_force\_lambda\_code\_deploy) | If the lambda package in s3 has the same commit id tag as the terraform build branch, the lambda will not update automatically. Set to True if making changes to Lambda code from on the same commit for example during development | `bool` | `false` | no |
| [group](#input\_group) | The group variables are being inherited from (often synonmous with account short-name) | `string` | n/a | yes |
diff --git a/infrastructure/terraform/components/dl/kinesis_firehose_delivery_stream_to_s3_reporting.tf b/infrastructure/terraform/components/dl/kinesis_firehose_delivery_stream_to_s3_reporting.tf
index 86efee7f4..0be07006e 100644
--- a/infrastructure/terraform/components/dl/kinesis_firehose_delivery_stream_to_s3_reporting.tf
+++ b/infrastructure/terraform/components/dl/kinesis_firehose_delivery_stream_to_s3_reporting.tf
@@ -11,7 +11,7 @@ resource "aws_kinesis_firehose_delivery_stream" "to_s3_reporting" {
error_output_prefix = "${local.firehose_output_path_prefix}/errors/!{timestamp:yyyy}-!{timestamp:MM}-!{timestamp:dd}-!{timestamp:HH}/!{firehose:error-output-type}/"
buffering_size = 128
- buffering_interval = 300
+ buffering_interval = var.firehose_destination_buffer_interval
dynamic_partitioning_configuration {
enabled = true
@@ -37,7 +37,7 @@ resource "aws_kinesis_firehose_delivery_stream" "to_s3_reporting" {
}
parameters {
parameter_name = "BufferIntervalInSeconds"
- parameter_value = 301
+ parameter_value = var.firehose_processor_buffer_interval
}
}
}
diff --git a/infrastructure/terraform/components/dl/pre.sh b/infrastructure/terraform/components/dl/pre.sh
index 2be656250..eeb0c88ce 100755
--- a/infrastructure/terraform/components/dl/pre.sh
+++ b/infrastructure/terraform/components/dl/pre.sh
@@ -14,6 +14,13 @@ $ROOT_DIR/scripts/set-github-token.sh
echo "Completed."
+# Skip dependency installation and build steps when only reading Terraform output.
+# terraform output reads from S3 state backend and does not require built artefacts.
+if [[ "${ACTION:-}" == "output" ]]; then
+ echo "Skipping dependency installation and build steps for 'output' action."
+ return 0
+fi
+
npm ci
npm run generate-dependencies
diff --git a/infrastructure/terraform/components/dl/variables.tf b/infrastructure/terraform/components/dl/variables.tf
index 2f9406c3d..0f3784b02 100644
--- a/infrastructure/terraform/components/dl/variables.tf
+++ b/infrastructure/terraform/components/dl/variables.tf
@@ -319,3 +319,15 @@ variable "event_anomaly_band_width" {
error_message = "Band width must be between 2 and 10"
}
}
+
+variable "firehose_destination_buffer_interval" {
+ type = number
+ description = "The Firehose destination buffer interval in seconds. Lower values reduce latency for tests but increase costs. Minimum is 60, default (Terraform) is 300."
+ default = 300
+}
+
+variable "firehose_processor_buffer_interval" {
+ type = number
+ description = "The Firehose Lambda processor buffer interval in seconds. Should be greater than firehose_destination_buffer_interval. Minimum is 61, default is 301."
+ default = 301
+}
diff --git a/scripts/tests/integration.sh b/scripts/tests/integration.sh
index 893c4efc2..03994c53d 100755
--- a/scripts/tests/integration.sh
+++ b/scripts/tests/integration.sh
@@ -9,4 +9,4 @@ npx playwright install --with-deps > /dev/null
cd tests/playwright
-npm run test:component
+npm run test:component${PLAYWRIGHT_SHARD:+ -- --shard="$PLAYWRIGHT_SHARD"}
diff --git a/tests/playwright/config/component/component.config.ts b/tests/playwright/config/component/component.config.ts
index 87ca74faa..340e7bb3a 100644
--- a/tests/playwright/config/component/component.config.ts
+++ b/tests/playwright/config/component/component.config.ts
@@ -13,19 +13,10 @@ export default defineConfig({
name: 'senders:setup',
testMatch: 'senders.setup.ts',
},
- {
- name: 'firehose:setup',
- testMatch: 'firehose.setup.ts',
- teardown: 'firehose:teardown',
- },
- {
- name: 'firehose:teardown',
- testMatch: 'firehose.teardown.ts',
- },
{
name: 'component',
testMatch: '*.component.spec.ts',
- dependencies: ['senders:setup', 'firehose:setup'],
+ dependencies: ['senders:setup'],
teardown: 'component:teardown',
},
{
diff --git a/tests/playwright/config/component/firehose.setup.ts b/tests/playwright/config/component/firehose.setup.ts
deleted file mode 100644
index f7ecf537e..000000000
--- a/tests/playwright/config/component/firehose.setup.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { test as setup } from '@playwright/test';
-import {
- MINIMUM_DESTINATION_BUFFER_INTERVAL,
- MINIMUM_PROCESSOR_BUFFER_INTERVAL,
- TERRAFORM_DESTINATION_BUFFER_INTERVAL,
- TERRAFORM_PROCESSOR_BUFFER_INTERVAL,
-} from 'constants/backend-constants';
-import { alterFirehoseBufferIntervals } from 'helpers/data-firehose-helpers';
-
-setup('Reduce Firehose buffer intervals', async () => {
- await alterFirehoseBufferIntervals({
- expected: {
- destination: TERRAFORM_DESTINATION_BUFFER_INTERVAL,
- processor: TERRAFORM_PROCESSOR_BUFFER_INTERVAL,
- },
- update: {
- destination: MINIMUM_DESTINATION_BUFFER_INTERVAL,
- processor: MINIMUM_PROCESSOR_BUFFER_INTERVAL,
- },
- });
-});
diff --git a/tests/playwright/config/component/firehose.teardown.ts b/tests/playwright/config/component/firehose.teardown.ts
deleted file mode 100644
index 11844458c..000000000
--- a/tests/playwright/config/component/firehose.teardown.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { test as teardown } from '@playwright/test';
-import {
- MINIMUM_DESTINATION_BUFFER_INTERVAL,
- MINIMUM_PROCESSOR_BUFFER_INTERVAL,
- TERRAFORM_DESTINATION_BUFFER_INTERVAL,
- TERRAFORM_PROCESSOR_BUFFER_INTERVAL,
-} from 'constants/backend-constants';
-import { alterFirehoseBufferIntervals } from 'helpers/data-firehose-helpers';
-
-teardown('Restore Firehose buffer intervals', async () => {
- await alterFirehoseBufferIntervals({
- expected: {
- destination: MINIMUM_DESTINATION_BUFFER_INTERVAL,
- processor: MINIMUM_PROCESSOR_BUFFER_INTERVAL,
- },
- update: {
- destination: TERRAFORM_DESTINATION_BUFFER_INTERVAL,
- processor: TERRAFORM_PROCESSOR_BUFFER_INTERVAL,
- },
- });
-});