diff --git a/.github/workflows/deploy_fastapi_backend.yml b/.github/workflows/deploy_fastapi_backend.yml index 6ebb6cef..645d6bc1 100644 --- a/.github/workflows/deploy_fastapi_backend.yml +++ b/.github/workflows/deploy_fastapi_backend.yml @@ -1,170 +1,173 @@ -name: Fast Api Backend Deploy - -on: - push: - branches: [ dev, prod ] - -jobs: - deploy: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: 3.10.12 - - - name: Install Serverless and plugins - run: | - npm install -g serverless@^3.38.0 - npm install -g serverless-domain-manager@^7.3.8 - npm install -g serverless-python-requirements - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - - - name: AWS credentials for dev - if: github.ref == 'refs/heads/dev' - uses: aws-actions/configure-aws-credentials@v1 - with: - aws-access-key-id: ${{ secrets.DEV_AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }} - aws-region: eu-west-2 - - - name: AWS credentials for prod - if: github.ref == 'refs/heads/prod' - uses: aws-actions/configure-aws-credentials@v1 - with: - aws-access-key-id: ${{ secrets.PROD_AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.PROD_AWS_SECRET_ACCESS_KEY }} - aws-region: eu-west-2 - - - name: Set domain name - id: set_domain - run: echo "::set-output name=domain::${{ secrets[format('{0}_DOMAIN_NAME', github.ref_name)] }}" - - - name: Set EPC auth token - id: set_auth_token - run: echo "::set-output name=auth_token::${{ secrets[format('{0}_EPC_AUTH_TOKEN', github.ref_name)] }}" - - - name: Set Open EPC API token - id: set_open_epc_token - run: echo "::set-output name=open_epc_token::${{ secrets[format('{0}_OPEN_EPC_API_TOKEN', github.ref_name)] }}" - - # Store port, name and host in github secrets - - name: Set DB credentials - id: set_db_credentials - run: | - echo "::set-output name=db_host::${{ secrets[format('{0}_DB_HOST', github.ref_name)] }}" - echo "::set-output name=db_port::${{ secrets[format('{0}_DB_PORT', github.ref_name)] }}" - echo "::set-output name=db_name::${{ secrets[format('{0}_DB_NAME', github.ref_name)] }}" - - - name: Set ECR credentials - id: set_ecr_credentials - run: | - echo "::set-output name=ecr_uri::${{ secrets[format('{0}_ECR_URI', github.ref_name)] }}" - - - name: Set Secrets - id: set_api_secrets - run: | - echo "::set-output name=sap_predictions_bucket::${{ secrets[format('{0}_SAP_PREDICTIONS_BUCKET', github.ref_name)] }}" - echo "::set-output name=carbon_predictions_bucket::${{ secrets[format('{0}_CARBON_PREDICTIONS_BUCKET', github.ref_name)] }}" - echo "::set-output name=heat_predictions_bucket::${{ secrets[format('{0}_HEAT_PREDICTIONS_BUCKET', github.ref_name)] }}" - echo "::set-output name=lighting_cost_predictions_bucket::${{ secrets[format('{0}_LIGHTING_COST_PREDICTIONS_BUCKET', github.ref_name)] }}" - echo "::set-output name=heating_cost_predictions_bucket::${{ secrets[format('{0}_HEATING_COST_PREDICTIONS_BUCKET', github.ref_name)] }}" - echo "::set-output name=hot_water_cost_predictions_bucket::${{ secrets[format('{0}_HOT_WATER_COST_PREDICTIONS_BUCKET', github.ref_name)] }}" - echo "::set-output name=heating_kwh_predictions_bucket::${{ secrets[format('{0}_HEATING_KWH_PREDICTIONS_BUCKET', github.ref_name)] }}" - echo "::set-output name=hotwater_kwh_predictions_bucket::${{ secrets[format('{0}_HOTWATER_KWH_PREDICTIONS_BUCKET', github.ref_name)] }}" - echo "::set-output name=energy_asessments_bucket::${{ secrets[format('{0}_ENERGY_ASSESSMENTS_BUCKET', github.ref_name)] }}" - echo "::set-output name=google_solar_api_key::${{ secrets[format('{0}_GOOGLE_SOLAR_API_KEY', github.ref_name)] }}" - echo "::set-output name=sap_baseline_predictions_bucket::${{ secrets[format('{0}_SAP_BASELINE_PREDICTIONS_BUCKET', github.ref_name)] }}" - echo "::set-output name=carbon_baseline_predictions_bucket::${{ secrets[format('{0}_CARBON_BASELINE_PREDICTIONS_BUCKET', github.ref_name)] }}" - echo "::set-output name=heat_baseline_predictions_bucket::${{ secrets[format('{0}_HEAT_BASELINE_PREDICTIONS_BUCKET', github.ref_name)] }}" - - - name: Setup Docker - uses: docker/setup-buildx-action@v1 - - # - name: Setup Docker Buildx - # run: | - # docker buildx create --use - - - name: Build Docker Image For Engine - run: | - docker buildx build \ - --platform linux/amd64 \ - --provenance=false \ - --output=type=docker \ - -t fastapi-lambda-image:${{ github.sha }} \ - -f backend/docker/engine.Dockerfile \ - . - - - name: Login to ECR - run: | - aws ecr get-login-password --region eu-west-2 | docker login --username AWS --password-stdin ${{ steps.set_ecr_credentials.outputs.ecr_uri }} - - - name: Tag and Push Docker Image to ECR - run: | - docker tag fastapi-lambda-image:${{ github.sha }} ${{ steps.set_ecr_credentials.outputs.ecr_uri }}:${{ github.sha }} - docker push ${{ steps.set_ecr_credentials.outputs.ecr_uri }}:${{ github.sha }} - - - name: Deploy to AWS Lambda via Serverless - env: - API_KEY: ${{ secrets.FASTAPI_API_KEY }} - ENVIRONMENT: ${{ github.ref_name }} - SECRET_KEY: ${{ secrets.NEXTAUTH_SECRET }} - PLAN_TRIGGER_BUCKET: 'retrofit-plan-inputs-${{ github.ref_name }}' - DATA_BUCKET: 'retrofit-data-${{ github.ref_name }}' - PREDICTIONS_BUCKET: 'retrofit-sap-predictions-${{ github.ref_name }}' - SAP_PREDICTIONS_BUCKET: ${{ steps.set_api_secrets.outputs.sap_predictions_bucket }} - CARBON_PREDICTIONS_BUCKET: ${{ steps.set_api_secrets.outputs.carbon_predictions_bucket }} - HEAT_PREDICTIONS_BUCKET: ${{ steps.set_api_secrets.outputs.heat_predictions_bucket }} - LIGHTING_COST_PREDICTIONS_BUCKET: ${{ steps.set_api_secrets.outputs.lighting_cost_predictions_bucket }} - HEATING_COST_PREDICTIONS_BUCKET: ${{ steps.set_api_secrets.outputs.heating_cost_predictions_bucket }} - HOT_WATER_COST_PREDICTIONS_BUCKET: ${{ steps.set_api_secrets.outputs.hot_water_cost_predictions_bucket }} - HEATING_KWH_PREDICTIONS_BUCKET: ${{ steps.set_api_secrets.outputs.heating_kwh_predictions_bucket }} - HOTWATER_KWH_PREDICTIONS_BUCKET: ${{ steps.set_api_secrets.outputs.hotwater_kwh_predictions_bucket }} - ENERGY_ASSESSMENTS_BUCKET: ${{ steps.set_api_secrets.outputs.energy_asessments_bucket }} - GOOGLE_SOLAR_API_KEY: ${{ steps.set_api_secrets.outputs.google_solar_api_key }} - DOMAIN_NAME: ${{ steps.set_domain.outputs.domain }} - EPC_AUTH_TOKEN: ${{ steps.set_auth_token.outputs.auth_token }} - OPEN_EPC_API_TOKEN: ${{ steps.set_open_epc_token.outputs.open_epc_token }} - DB_HOST: ${{ steps.set_db_credentials.outputs.db_host }} - DB_PORT: ${{ steps.set_db_credentials.outputs.db_port }} - DB_NAME: ${{ steps.set_db_credentials.outputs.db_name }} - ECR_URI: ${{ steps.set_ecr_credentials.outputs.ecr_uri }} - GITHUB_SHA: ${{ github.sha }} - SAP_BASELINE_PREDICTIONS_BUCKET: ${{ steps.set_api_secrets.outputs.sap_baseline_predictions_bucket }} - CARBON_BASELINE_PREDICTIONS_BUCKET: ${{ steps.set_api_secrets.outputs.carbon_baseline_predictions_bucket }} - HEAT_BASELINE_PREDICTIONS_BUCKET: ${{ steps.set_api_secrets.outputs.heat_baseline_predictions_bucket }} - run: | - # Fetch database credentials from AWS Secrets Manager - SECRET_VALUE=$(aws secretsmanager get-secret-value --secret-id ${{ github.ref_name }}/assessment_model/db_credentials --query SecretString) - DB_USERNAME=$(echo "$SECRET_VALUE" | jq -r '. | fromjson | .db_assessment_model_username') - DB_PASSWORD=$(echo "$SECRET_VALUE" | jq -r '. | fromjson | .db_assessment_model_password') - - # Set the database credentials as environment variables - export DB_USERNAME - export DB_PASSWORD - - # Deploy to AWS Lambda via Serverless - sls deploy --stage ${{ github.ref_name }} --verbose - - - name: Smoke test deployed /health - env: - EXPECTED_SHA: ${{ github.sha }} - HEALTH_URL: https://api.${{ steps.set_domain.outputs.domain }}/health - run: | - set -euo pipefail - echo "Probing $HEALTH_URL" - RESPONSE=$(curl -fsSL --max-time 30 --retry 3 --retry-delay 5 --retry-connrefused "$HEALTH_URL") - echo "Response: $RESPONSE" - ACTUAL_SHA=$(echo "$RESPONSE" | jq -r '.sha') - if [[ "$ACTUAL_SHA" != "$EXPECTED_SHA" ]]; then - echo "::error::SHA mismatch. expected=$EXPECTED_SHA actual=$ACTUAL_SHA" - exit 1 - fi - echo "Health check passed. sha=$ACTUAL_SHA" +# Temporarily disabled — the FastAPI backend deploy pipeline (deploys must be run manually via `sls deploy` while disabled). +# Commented out to cut GitHub Actions minutes; uncomment to re-enable. +# +# name: Fast Api Backend Deploy +# +# on: +# push: +# branches: [ dev, prod ] +# +# jobs: +# deploy: +# runs-on: ubuntu-latest +# +# steps: +# - name: Checkout code +# uses: actions/checkout@v3 +# +# - name: Set up Python +# uses: actions/setup-python@v2 +# with: +# python-version: 3.10.12 +# +# - name: Install Serverless and plugins +# run: | +# npm install -g serverless@^3.38.0 +# npm install -g serverless-domain-manager@^7.3.8 +# npm install -g serverless-python-requirements +# +# - name: Install dependencies +# run: | +# python -m pip install --upgrade pip +# +# - name: AWS credentials for dev +# if: github.ref == 'refs/heads/dev' +# uses: aws-actions/configure-aws-credentials@v1 +# with: +# aws-access-key-id: ${{ secrets.DEV_AWS_ACCESS_KEY_ID }} +# aws-secret-access-key: ${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }} +# aws-region: eu-west-2 +# +# - name: AWS credentials for prod +# if: github.ref == 'refs/heads/prod' +# uses: aws-actions/configure-aws-credentials@v1 +# with: +# aws-access-key-id: ${{ secrets.PROD_AWS_ACCESS_KEY_ID }} +# aws-secret-access-key: ${{ secrets.PROD_AWS_SECRET_ACCESS_KEY }} +# aws-region: eu-west-2 +# +# - name: Set domain name +# id: set_domain +# run: echo "::set-output name=domain::${{ secrets[format('{0}_DOMAIN_NAME', github.ref_name)] }}" +# +# - name: Set EPC auth token +# id: set_auth_token +# run: echo "::set-output name=auth_token::${{ secrets[format('{0}_EPC_AUTH_TOKEN', github.ref_name)] }}" +# +# - name: Set Open EPC API token +# id: set_open_epc_token +# run: echo "::set-output name=open_epc_token::${{ secrets[format('{0}_OPEN_EPC_API_TOKEN', github.ref_name)] }}" +# +# # Store port, name and host in github secrets +# - name: Set DB credentials +# id: set_db_credentials +# run: | +# echo "::set-output name=db_host::${{ secrets[format('{0}_DB_HOST', github.ref_name)] }}" +# echo "::set-output name=db_port::${{ secrets[format('{0}_DB_PORT', github.ref_name)] }}" +# echo "::set-output name=db_name::${{ secrets[format('{0}_DB_NAME', github.ref_name)] }}" +# +# - name: Set ECR credentials +# id: set_ecr_credentials +# run: | +# echo "::set-output name=ecr_uri::${{ secrets[format('{0}_ECR_URI', github.ref_name)] }}" +# +# - name: Set Secrets +# id: set_api_secrets +# run: | +# echo "::set-output name=sap_predictions_bucket::${{ secrets[format('{0}_SAP_PREDICTIONS_BUCKET', github.ref_name)] }}" +# echo "::set-output name=carbon_predictions_bucket::${{ secrets[format('{0}_CARBON_PREDICTIONS_BUCKET', github.ref_name)] }}" +# echo "::set-output name=heat_predictions_bucket::${{ secrets[format('{0}_HEAT_PREDICTIONS_BUCKET', github.ref_name)] }}" +# echo "::set-output name=lighting_cost_predictions_bucket::${{ secrets[format('{0}_LIGHTING_COST_PREDICTIONS_BUCKET', github.ref_name)] }}" +# echo "::set-output name=heating_cost_predictions_bucket::${{ secrets[format('{0}_HEATING_COST_PREDICTIONS_BUCKET', github.ref_name)] }}" +# echo "::set-output name=hot_water_cost_predictions_bucket::${{ secrets[format('{0}_HOT_WATER_COST_PREDICTIONS_BUCKET', github.ref_name)] }}" +# echo "::set-output name=heating_kwh_predictions_bucket::${{ secrets[format('{0}_HEATING_KWH_PREDICTIONS_BUCKET', github.ref_name)] }}" +# echo "::set-output name=hotwater_kwh_predictions_bucket::${{ secrets[format('{0}_HOTWATER_KWH_PREDICTIONS_BUCKET', github.ref_name)] }}" +# echo "::set-output name=energy_asessments_bucket::${{ secrets[format('{0}_ENERGY_ASSESSMENTS_BUCKET', github.ref_name)] }}" +# echo "::set-output name=google_solar_api_key::${{ secrets[format('{0}_GOOGLE_SOLAR_API_KEY', github.ref_name)] }}" +# echo "::set-output name=sap_baseline_predictions_bucket::${{ secrets[format('{0}_SAP_BASELINE_PREDICTIONS_BUCKET', github.ref_name)] }}" +# echo "::set-output name=carbon_baseline_predictions_bucket::${{ secrets[format('{0}_CARBON_BASELINE_PREDICTIONS_BUCKET', github.ref_name)] }}" +# echo "::set-output name=heat_baseline_predictions_bucket::${{ secrets[format('{0}_HEAT_BASELINE_PREDICTIONS_BUCKET', github.ref_name)] }}" +# +# - name: Setup Docker +# uses: docker/setup-buildx-action@v1 +# +# # - name: Setup Docker Buildx +# # run: | +# # docker buildx create --use +# +# - name: Build Docker Image For Engine +# run: | +# docker buildx build \ +# --platform linux/amd64 \ +# --provenance=false \ +# --output=type=docker \ +# -t fastapi-lambda-image:${{ github.sha }} \ +# -f backend/docker/engine.Dockerfile \ +# . +# +# - name: Login to ECR +# run: | +# aws ecr get-login-password --region eu-west-2 | docker login --username AWS --password-stdin ${{ steps.set_ecr_credentials.outputs.ecr_uri }} +# +# - name: Tag and Push Docker Image to ECR +# run: | +# docker tag fastapi-lambda-image:${{ github.sha }} ${{ steps.set_ecr_credentials.outputs.ecr_uri }}:${{ github.sha }} +# docker push ${{ steps.set_ecr_credentials.outputs.ecr_uri }}:${{ github.sha }} +# +# - name: Deploy to AWS Lambda via Serverless +# env: +# API_KEY: ${{ secrets.FASTAPI_API_KEY }} +# ENVIRONMENT: ${{ github.ref_name }} +# SECRET_KEY: ${{ secrets.NEXTAUTH_SECRET }} +# PLAN_TRIGGER_BUCKET: 'retrofit-plan-inputs-${{ github.ref_name }}' +# DATA_BUCKET: 'retrofit-data-${{ github.ref_name }}' +# PREDICTIONS_BUCKET: 'retrofit-sap-predictions-${{ github.ref_name }}' +# SAP_PREDICTIONS_BUCKET: ${{ steps.set_api_secrets.outputs.sap_predictions_bucket }} +# CARBON_PREDICTIONS_BUCKET: ${{ steps.set_api_secrets.outputs.carbon_predictions_bucket }} +# HEAT_PREDICTIONS_BUCKET: ${{ steps.set_api_secrets.outputs.heat_predictions_bucket }} +# LIGHTING_COST_PREDICTIONS_BUCKET: ${{ steps.set_api_secrets.outputs.lighting_cost_predictions_bucket }} +# HEATING_COST_PREDICTIONS_BUCKET: ${{ steps.set_api_secrets.outputs.heating_cost_predictions_bucket }} +# HOT_WATER_COST_PREDICTIONS_BUCKET: ${{ steps.set_api_secrets.outputs.hot_water_cost_predictions_bucket }} +# HEATING_KWH_PREDICTIONS_BUCKET: ${{ steps.set_api_secrets.outputs.heating_kwh_predictions_bucket }} +# HOTWATER_KWH_PREDICTIONS_BUCKET: ${{ steps.set_api_secrets.outputs.hotwater_kwh_predictions_bucket }} +# ENERGY_ASSESSMENTS_BUCKET: ${{ steps.set_api_secrets.outputs.energy_asessments_bucket }} +# GOOGLE_SOLAR_API_KEY: ${{ steps.set_api_secrets.outputs.google_solar_api_key }} +# DOMAIN_NAME: ${{ steps.set_domain.outputs.domain }} +# EPC_AUTH_TOKEN: ${{ steps.set_auth_token.outputs.auth_token }} +# OPEN_EPC_API_TOKEN: ${{ steps.set_open_epc_token.outputs.open_epc_token }} +# DB_HOST: ${{ steps.set_db_credentials.outputs.db_host }} +# DB_PORT: ${{ steps.set_db_credentials.outputs.db_port }} +# DB_NAME: ${{ steps.set_db_credentials.outputs.db_name }} +# ECR_URI: ${{ steps.set_ecr_credentials.outputs.ecr_uri }} +# GITHUB_SHA: ${{ github.sha }} +# SAP_BASELINE_PREDICTIONS_BUCKET: ${{ steps.set_api_secrets.outputs.sap_baseline_predictions_bucket }} +# CARBON_BASELINE_PREDICTIONS_BUCKET: ${{ steps.set_api_secrets.outputs.carbon_baseline_predictions_bucket }} +# HEAT_BASELINE_PREDICTIONS_BUCKET: ${{ steps.set_api_secrets.outputs.heat_baseline_predictions_bucket }} +# run: | +# # Fetch database credentials from AWS Secrets Manager +# SECRET_VALUE=$(aws secretsmanager get-secret-value --secret-id ${{ github.ref_name }}/assessment_model/db_credentials --query SecretString) +# DB_USERNAME=$(echo "$SECRET_VALUE" | jq -r '. | fromjson | .db_assessment_model_username') +# DB_PASSWORD=$(echo "$SECRET_VALUE" | jq -r '. | fromjson | .db_assessment_model_password') +# +# # Set the database credentials as environment variables +# export DB_USERNAME +# export DB_PASSWORD +# +# # Deploy to AWS Lambda via Serverless +# sls deploy --stage ${{ github.ref_name }} --verbose +# +# - name: Smoke test deployed /health +# env: +# EXPECTED_SHA: ${{ github.sha }} +# HEALTH_URL: https://api.${{ steps.set_domain.outputs.domain }}/health +# run: | +# set -euo pipefail +# echo "Probing $HEALTH_URL" +# RESPONSE=$(curl -fsSL --max-time 30 --retry 3 --retry-delay 5 --retry-connrefused "$HEALTH_URL") +# echo "Response: $RESPONSE" +# ACTUAL_SHA=$(echo "$RESPONSE" | jq -r '.sha') +# if [[ "$ACTUAL_SHA" != "$EXPECTED_SHA" ]]; then +# echo "::error::SHA mismatch. expected=$EXPECTED_SHA actual=$ACTUAL_SHA" +# exit 1 +# fi +# echo "Health check passed. sha=$ACTUAL_SHA" diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index 6093e249..374bf94e 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -1,35 +1,38 @@ -name: Rebaselining Integration Test - -on: - pull_request: - branches: - - main - -jobs: - rebaselining-integration-test: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v4 - with: - python-version: '3.11' - - - name: Install tox via Makefile - run: | - make setup - - - name: Configure AWS credentials for dev - uses: aws-actions/configure-aws-credentials@v1 - with: - aws-access-key-id: ${{ secrets.DEV_AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }} - aws-region: eu-west-2 - - - name: Run only rebaselining integration test - env: - EPC_AUTH_TOKEN: ${{ secrets.DEV_EPC_AUTH_TOKEN }} - run: | - make test ARGS="-m integration" +# Temporarily disabled — the rebaselining integration suite. +# Commented out to cut GitHub Actions minutes; uncomment to re-enable. +# +# name: Rebaselining Integration Test +# +# on: +# pull_request: +# branches: +# - main +# +# jobs: +# rebaselining-integration-test: +# runs-on: ubuntu-latest +# steps: +# - name: Checkout code +# uses: actions/checkout@v4 +# +# - name: Set up Python 3.11 +# uses: actions/setup-python@v4 +# with: +# python-version: '3.11' +# +# - name: Install tox via Makefile +# run: | +# make setup +# +# - name: Configure AWS credentials for dev +# uses: aws-actions/configure-aws-credentials@v1 +# with: +# aws-access-key-id: ${{ secrets.DEV_AWS_ACCESS_KEY_ID }} +# aws-secret-access-key: ${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }} +# aws-region: eu-west-2 +# +# - name: Run only rebaselining integration test +# env: +# EPC_AUTH_TOKEN: ${{ secrets.DEV_EPC_AUTH_TOKEN }} +# run: | +# make test ARGS="-m integration" diff --git a/.github/workflows/protect_releases.yml b/.github/workflows/protect_releases.yml index cbd08e2f..7c8c7258 100644 --- a/.github/workflows/protect_releases.yml +++ b/.github/workflows/protect_releases.yml @@ -1,17 +1,20 @@ -name: Restrict PR source - -on: - pull_request: - branches: - - dev - -jobs: - check-source-branch: - runs-on: ubuntu-latest - steps: - - name: Fail if PR is not from main - run: | - if [[ "${{ github.head_ref }}" != "main" ]]; then - echo "Only PRs from main are allowed into dev" - exit 1 - fi \ No newline at end of file +# Temporarily disabled — the main→dev PR-source guardrail. +# Commented out to cut GitHub Actions minutes; uncomment to re-enable. +# +# name: Restrict PR source +# +# on: +# pull_request: +# branches: +# - dev +# +# jobs: +# check-source-branch: +# runs-on: ubuntu-latest +# steps: +# - name: Fail if PR is not from main +# run: | +# if [[ "${{ github.head_ref }}" != "main" ]]; then +# echo "Only PRs from main are allowed into dev" +# exit 1 +# fi diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 12d836d0..bd022a2a 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -1,67 +1,70 @@ -name: Run unit tests - -on: - pull_request: - branches: - - "**" - - -jobs: - test-docker: - name: Tests (Docker) - runs-on: ubuntu-latest - - services: - postgres: - image: postgres:15 - env: - POSTGRES_USER: test - POSTGRES_PASSWORD: test - POSTGRES_DB: test - ports: - - 5432:5432 - options: >- - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Build test image - run: docker build -f Dockerfile.test -t model-test . - - - name: Initialise database schema - run: | - docker run --rm \ - --network host \ - -e DB_HOST=localhost \ - -e DB_NAME=test \ - -e DB_USERNAME=test \ - -e DB_PASSWORD=test \ - -e DB_PORT=5432 \ - model-test python scripts/init_db.py - - - name: Run tests - run: | - docker run --rm \ - --network host \ - -e EPC_AUTH_TOKEN=${{ secrets.DEV_EPC_AUTH_TOKEN }} \ - -e OPEN_EPC_API_TOKEN=${{ secrets.DEV_OPEN_EPC_API_TOKEN }} \ - -e HUBSPOT_API_KEY=${{ secrets.HUBSPOT_API_KEY }} \ - -e AWS_ACCESS_KEY_ID=${{ secrets.DEV_AWS_ACCESS_KEY_ID }} \ - -e AWS_SECRET_ACCESS_KEY=${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }} \ - -e AWS_DEFAULT_REGION=${{ secrets.DEV_AWS_REGION }} \ - -e DB_HOST=localhost \ - -e DB_NAME=test \ - -e DB_USERNAME=test \ - -e DB_PASSWORD=test \ - -e DB_PORT=5432 \ - model-test pytest -vv -m 'not integration' - - # The DDD rewrite (tests/) runs in its own workflow (ddd_tests.yml): its - # SQLModel table classes map to the same physical tables as the legacy - # backend models and share the one global SQLModel.metadata, so the two - # suites cannot be imported into the same pytest process. +# Temporarily disabled — this Docker-based suite was consuming too many GitHub +# Actions minutes. Uncomment to re-enable. +# +# name: Run unit tests +# +# on: +# pull_request: +# branches: +# - "**" +# +# +# jobs: +# test-docker: +# name: Tests (Docker) +# runs-on: ubuntu-latest +# +# services: +# postgres: +# image: postgres:15 +# env: +# POSTGRES_USER: test +# POSTGRES_PASSWORD: test +# POSTGRES_DB: test +# ports: +# - 5432:5432 +# options: >- +# --health-cmd pg_isready +# --health-interval 10s +# --health-timeout 5s +# --health-retries 5 +# +# steps: +# - name: Checkout code +# uses: actions/checkout@v4 +# +# - name: Build test image +# run: docker build -f Dockerfile.test -t model-test . +# +# - name: Initialise database schema +# run: | +# docker run --rm \ +# --network host \ +# -e DB_HOST=localhost \ +# -e DB_NAME=test \ +# -e DB_USERNAME=test \ +# -e DB_PASSWORD=test \ +# -e DB_PORT=5432 \ +# model-test python scripts/init_db.py +# +# - name: Run tests +# run: | +# docker run --rm \ +# --network host \ +# -e EPC_AUTH_TOKEN=${{ secrets.DEV_EPC_AUTH_TOKEN }} \ +# -e OPEN_EPC_API_TOKEN=${{ secrets.DEV_OPEN_EPC_API_TOKEN }} \ +# -e HUBSPOT_API_KEY=${{ secrets.HUBSPOT_API_KEY }} \ +# -e AWS_ACCESS_KEY_ID=${{ secrets.DEV_AWS_ACCESS_KEY_ID }} \ +# -e AWS_SECRET_ACCESS_KEY=${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }} \ +# -e AWS_DEFAULT_REGION=${{ secrets.DEV_AWS_REGION }} \ +# -e DB_HOST=localhost \ +# -e DB_NAME=test \ +# -e DB_USERNAME=test \ +# -e DB_PASSWORD=test \ +# -e DB_PORT=5432 \ +# model-test pytest -vv -m 'not integration' +# +# # The DDD rewrite (tests/) runs in its own workflow (ddd_tests.yml): its +# # SQLModel table classes map to the same physical tables as the legacy +# # backend models and share the one global SQLModel.metadata, so the two +# # suites cannot be imported into the same pytest process. diff --git a/datatypes/epc/domain/mapper.py b/datatypes/epc/domain/mapper.py index 2f2bd00b..1658383f 100644 --- a/datatypes/epc/domain/mapper.py +++ b/datatypes/epc/domain/mapper.py @@ -2150,7 +2150,10 @@ class EpcPropertyDataMapper: door_count=schema.door_count, habitable_rooms_count=schema.habitable_room_count, heated_rooms_count=schema.heated_room_count, - wet_rooms_count=schema.wet_rooms_count, + # 21.0.1 lodges wet_rooms_count as Optional; None violates the + # epc_property NOT-NULL column (and the calc's `> 0` check). Coalesce + # to 0 like every other mapper (RdSAP "not lodged" → calc minimum 1). + wet_rooms_count=schema.wet_rooms_count or 0, extensions_count=schema.extensions_count, open_chimneys_count=schema.open_chimneys_count, insulated_door_count=schema.insulated_door_count, diff --git a/datatypes/epc/domain/tests/test_from_rdsap_schema.py b/datatypes/epc/domain/tests/test_from_rdsap_schema.py index e0c6b1ba..023f7c60 100644 --- a/datatypes/epc/domain/tests/test_from_rdsap_schema.py +++ b/datatypes/epc/domain/tests/test_from_rdsap_schema.py @@ -379,6 +379,22 @@ class TestFromRdSapSchema21_0_1: # --- general --- + def test_omitted_wet_rooms_count_defaults_to_zero_not_none(self) -> None: + # 21.0.1 lodges wet_rooms_count as Optional, so a cert that omits it + # mapped to EpcPropertyData.wet_rooms_count=None. That None then + # violated the epc_property.wet_rooms_count NOT-NULL constraint when a + # predicted EPC (which deep-copies a comparable template) was persisted, + # and would crash the calc's `wet_rooms_count > 0` check. 37 modelling_e2e + # properties failed on the 2026-06-23 run for this reason. Default to 0, + # matching every other mapper (RdSAP 0 → calc's Table-?? minimum 1 fan). + data = load("21_0_1.json") + data.pop("wet_rooms_count", None) + schema = from_dict(RdSapSchema21_0_1, data) + + result = EpcPropertyDataMapper.from_rdsap_schema_21_0_1(schema) + + assert result.wet_rooms_count == 0 + def test_uprn(self, result: EpcPropertyData) -> None: assert result.uprn == 12457