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, - }, - }); -});