From 9dc2cd45aa3a006496be1ca64b7d7a6400af1978 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Wed, 8 Apr 2026 07:17:12 +0000 Subject: [PATCH] CodeRabbit Generated Unit Tests: Add unit tests for PR changes --- ...er_autoscaling_conditional_test.tftest.hcl | 166 +++++++++++++++++ .../autoscaling_behavior_test.tftest.hcl | 173 ++++++++++++++++++ ...pdate_policy_opportunistic_test.tftest.hcl | 127 +++++++++++++ 3 files changed, 466 insertions(+) create mode 100644 terraform/environments/prod/tests/worker_autoscaling_conditional_test.tftest.hcl create mode 100644 terraform/modules/compute/tests/autoscaling_behavior_test.tftest.hcl create mode 100644 terraform/modules/compute/tests/update_policy_opportunistic_test.tftest.hcl diff --git a/terraform/environments/prod/tests/worker_autoscaling_conditional_test.tftest.hcl b/terraform/environments/prod/tests/worker_autoscaling_conditional_test.tftest.hcl new file mode 100644 index 0000000..1420347 --- /dev/null +++ b/terraform/environments/prod/tests/worker_autoscaling_conditional_test.tftest.hcl @@ -0,0 +1,166 @@ +# ============================================================================= +# Tests: Worker node autoscaling conditional expression +# +# PR Change (prod/compute.tf, module "k8s_worker_nodes"): +# +# Before: +# enable_autoscaling = var.enable_autoscaling +# +# After: +# enable_autoscaling = var.k8s_worker_instance_group_size > 0 ? var.enable_autoscaling : false +# +# Intent: When the worker MIG target_size is 0 (fully drained), the autoscaler +# must be disabled unconditionally so GCP does not fight the deliberately-empty +# group. The autoscaler_id output is null when no autoscaler is created. +# +# Tests: +# 1. size > 0, enable_autoscaling=true → autoscaler_id is non-null +# 2. size > 0, enable_autoscaling=false → autoscaler_id is null +# 3. size = 0, enable_autoscaling=true → autoscaler_id is null (conditional override) +# 4. size = 0, enable_autoscaling=false → autoscaler_id is null +# 5. size = 1 (boundary), enable_autoscaling=true → autoscaler_id is non-null +# 6. Master nodes: autoscaler_id always null (hardcoded enable_autoscaling=false) +# ============================================================================= + +mock_provider "google" {} + +# --------------------------------------------------------------------------- +# Test 1: Normal operation — workers with size > 0 and autoscaling enabled. +# The conditional passes var.enable_autoscaling through: true → true. +# autoscaler_id should be a non-null string (resource was created). +# --------------------------------------------------------------------------- +run "worker_autoscaling_enabled_when_group_size_positive" { + command = plan + + variables { + k8s_worker_instance_group_size = 2 + enable_autoscaling = true + autoscaling_min_replicas = 2 + autoscaling_max_replicas = 5 + + service_account_email = "sa@test-project.iam.gserviceaccount.com" + } + + # size(2) > 0 and enable_autoscaling=true → conditional evaluates to true → autoscaler created + assert { + condition = module.k8s_worker_nodes.autoscaler_id != null + error_message = "Worker autoscaler must be created when group size > 0 and enable_autoscaling = true." + } +} + +# --------------------------------------------------------------------------- +# Test 2: Workers with size > 0 but autoscaling explicitly disabled. +# The conditional passes var.enable_autoscaling through: false → false. +# autoscaler_id should be null. +# --------------------------------------------------------------------------- +run "worker_autoscaling_disabled_when_group_size_positive_but_autoscaling_false" { + command = plan + + variables { + k8s_worker_instance_group_size = 2 + enable_autoscaling = false + autoscaling_min_replicas = 2 + autoscaling_max_replicas = 5 + + service_account_email = "sa@test-project.iam.gserviceaccount.com" + } + + # size(2) > 0 but enable_autoscaling=false → conditional evaluates to false → no autoscaler + assert { + condition = module.k8s_worker_nodes.autoscaler_id == null + error_message = "Worker autoscaler must not be created when enable_autoscaling = false, even with positive group size." + } +} + +# --------------------------------------------------------------------------- +# Test 3 (KEY NEW BEHAVIOR): Workers with size = 0 and enable_autoscaling=true. +# The conditional overrides enable_autoscaling to false, preventing the +# autoscaler from conflicting with a deliberately-empty MIG. +# autoscaler_id should be null despite enable_autoscaling=true. +# --------------------------------------------------------------------------- +run "worker_autoscaling_forced_false_when_group_size_is_zero" { + command = plan + + variables { + k8s_worker_instance_group_size = 0 + enable_autoscaling = true # user flag is true, but group is empty + autoscaling_min_replicas = 2 + autoscaling_max_replicas = 5 + + service_account_email = "sa@test-project.iam.gserviceaccount.com" + } + + # size(0) is NOT > 0 → conditional resolves to false → no autoscaler created + assert { + condition = module.k8s_worker_nodes.autoscaler_id == null + error_message = "Worker autoscaler must be suppressed when group size is 0, regardless of enable_autoscaling value." + } +} + +# --------------------------------------------------------------------------- +# Test 4: Workers with size = 0 and enable_autoscaling = false. +# Both the explicit value and the conditional produce false; autoscaler_id null. +# --------------------------------------------------------------------------- +run "worker_autoscaling_null_when_group_size_zero_and_autoscaling_false" { + command = plan + + variables { + k8s_worker_instance_group_size = 0 + enable_autoscaling = false + autoscaling_min_replicas = 2 + autoscaling_max_replicas = 5 + + service_account_email = "sa@test-project.iam.gserviceaccount.com" + } + + assert { + condition = module.k8s_worker_nodes.autoscaler_id == null + error_message = "No autoscaler when group size is 0 and enable_autoscaling is false." + } +} + +# --------------------------------------------------------------------------- +# Test 5: Boundary — size = 1 is the minimum value for the conditional to +# evaluate to true (size > 0). Autoscaling should be enabled. +# --------------------------------------------------------------------------- +run "worker_autoscaling_enabled_at_minimum_nonzero_boundary" { + command = plan + + variables { + k8s_worker_instance_group_size = 1 # boundary: exactly one instance + enable_autoscaling = true + autoscaling_min_replicas = 1 + autoscaling_max_replicas = 5 + + service_account_email = "sa@test-project.iam.gserviceaccount.com" + } + + # size(1) > 0 and enable_autoscaling=true → conditional evaluates to true + assert { + condition = module.k8s_worker_nodes.autoscaler_id != null + error_message = "Autoscaler should be created when group size is exactly 1 (minimum non-zero boundary)." + } +} + +# --------------------------------------------------------------------------- +# Test 6: Master nodes always have enable_autoscaling = false (hardcoded). +# Regression: The PR did not change the master node autoscaling policy. +# Master autoscaler_id must remain null even when enable_autoscaling=true globally. +# --------------------------------------------------------------------------- +run "master_autoscaling_always_disabled_regardless_of_global_enable_autoscaling" { + command = plan + + variables { + k8s_master_instance_group_size = 1 + enable_autoscaling = true # global flag is true, but masters ignore it + k8s_worker_instance_group_size = 2 + + service_account_email = "sa@test-project.iam.gserviceaccount.com" + } + + # Master nodes have enable_autoscaling = false hardcoded in compute.tf → no autoscaler + assert { + condition = module.k8s_master_nodes.autoscaler_id == null + error_message = "Master nodes must never have an autoscaler; enable_autoscaling is hardcoded to false in compute.tf." + } +} \ No newline at end of file diff --git a/terraform/modules/compute/tests/autoscaling_behavior_test.tftest.hcl b/terraform/modules/compute/tests/autoscaling_behavior_test.tftest.hcl new file mode 100644 index 0000000..4b96e56 --- /dev/null +++ b/terraform/modules/compute/tests/autoscaling_behavior_test.tftest.hcl @@ -0,0 +1,173 @@ +# ============================================================================= +# Tests: enable_autoscaling resource creation behavior +# +# PR Change: Worker nodes now use a conditional for enable_autoscaling: +# var.k8s_worker_instance_group_size > 0 ? var.enable_autoscaling : false +# +# The conditional is evaluated in the calling environment (prod/compute.tf) +# before being passed into this module. These module-level tests verify that +# the module correctly creates/omits the autoscaler based on the value it +# receives, validating the downstream behavior of that conditional. +# +# Tests: +# 1. enable_autoscaling=true → google_compute_autoscaler is created (count=1) +# 2. enable_autoscaling=false → google_compute_autoscaler is NOT created (count=0) +# 3. enable_autoscaling=true with cpu_target=0.7 → CPU policy is applied +# 4. Boundary: min_replicas=1 (minimum valid value) +# ============================================================================= + +mock_provider "google" {} + +# --------------------------------------------------------------------------- +# Test 1: enable_autoscaling=true creates exactly one autoscaler resource. +# --------------------------------------------------------------------------- +run "autoscaler_created_when_autoscaling_enabled" { + command = plan + + variables { + name_prefix = "test-workers" + network = "projects/test-project/global/networks/test-vpc" + subnetwork = "projects/test-project/regions/asia-northeast3/subnetworks/test-subnet" + create_instance_template = true + create_instance_group = true + instance_group_zone = "asia-northeast3-a" + instance_group_target_size = 2 + update_policy_type = "OPPORTUNISTIC" + enable_autoscaling = true + autoscaling_min_replicas = 2 + autoscaling_max_replicas = 5 + autoscaling_cpu_target = 0.7 + } + + assert { + condition = length(google_compute_autoscaler.autoscaler) == 1 + error_message = "Exactly one autoscaler should be created when enable_autoscaling is true." + } + + assert { + condition = google_compute_autoscaler.autoscaler[0].autoscaling_policy[0].min_replicas == 2 + error_message = "Autoscaler min_replicas should match autoscaling_min_replicas variable." + } + + assert { + condition = google_compute_autoscaler.autoscaler[0].autoscaling_policy[0].max_replicas == 5 + error_message = "Autoscaler max_replicas should match autoscaling_max_replicas variable." + } +} + +# --------------------------------------------------------------------------- +# Test 2: enable_autoscaling=false creates NO autoscaler. +# This is the value produced when k8s_worker_instance_group_size == 0, +# because the conditional forces enable_autoscaling to false. +# Also mirrors the permanent behavior of k8s_master_nodes. +# --------------------------------------------------------------------------- +run "autoscaler_not_created_when_autoscaling_disabled" { + command = plan + + variables { + name_prefix = "test-workers-empty" + network = "projects/test-project/global/networks/test-vpc" + subnetwork = "projects/test-project/regions/asia-northeast3/subnetworks/test-subnet" + create_instance_template = true + create_instance_group = true + instance_group_zone = "asia-northeast3-a" + instance_group_target_size = 0 + update_policy_type = "OPPORTUNISTIC" + # The conditional in prod/compute.tf resolves to false when target_size == 0. + # Here we pass false directly to test the module-level behavior. + enable_autoscaling = false + } + + assert { + condition = length(google_compute_autoscaler.autoscaler) == 0 + error_message = "No autoscaler should be created when enable_autoscaling is false (as when group size is 0)." + } +} + +# --------------------------------------------------------------------------- +# Test 3: CPU utilization target of 0.7 is applied when autoscaling is on. +# Worker nodes use autoscaling_cpu_target = 0.7 (hardcoded in compute.tf). +# --------------------------------------------------------------------------- +run "autoscaler_cpu_target_applied_at_0_7" { + command = plan + + variables { + name_prefix = "test-workers-cpu" + network = "projects/test-project/global/networks/test-vpc" + subnetwork = "projects/test-project/regions/asia-northeast3/subnetworks/test-subnet" + create_instance_template = true + create_instance_group = true + instance_group_zone = "asia-northeast3-a" + instance_group_target_size = 2 + update_policy_type = "OPPORTUNISTIC" + enable_autoscaling = true + autoscaling_min_replicas = 2 + autoscaling_max_replicas = 5 + autoscaling_cpu_target = 0.7 + } + + assert { + condition = google_compute_autoscaler.autoscaler[0].autoscaling_policy[0].cpu_utilization[0].target == 0.7 + error_message = "CPU utilization target should be 0.7 as configured in compute.tf." + } +} + +# --------------------------------------------------------------------------- +# Test 4: Boundary — minimum valid non-zero group size (size=1) with +# autoscaling enabled results in autoscaler being created. +# This corresponds to the boundary of the conditional expression (size > 0). +# --------------------------------------------------------------------------- +run "autoscaler_created_at_minimum_nonzero_group_size" { + command = plan + + variables { + name_prefix = "test-workers-single" + network = "projects/test-project/global/networks/test-vpc" + subnetwork = "projects/test-project/regions/asia-northeast3/subnetworks/test-subnet" + create_instance_template = true + create_instance_group = true + instance_group_zone = "asia-northeast3-a" + instance_group_target_size = 1 + update_policy_type = "OPPORTUNISTIC" + enable_autoscaling = true + autoscaling_min_replicas = 1 + autoscaling_max_replicas = 5 + autoscaling_cpu_target = 0.7 + } + + assert { + condition = length(google_compute_autoscaler.autoscaler) == 1 + error_message = "Autoscaler should be created for minimum non-zero group size (size=1)." + } +} + +# --------------------------------------------------------------------------- +# Test 5: Master node configuration — autoscaling hardcoded to false, +# update_policy_type = "OPPORTUNISTIC". No autoscaler regardless of group size. +# Regression test: master nodes must never get an autoscaler. +# --------------------------------------------------------------------------- +run "master_nodes_never_have_autoscaler" { + command = plan + + variables { + name_prefix = "test-masters" + network = "projects/test-project/global/networks/test-vpc" + subnetwork = "projects/test-project/regions/asia-northeast3/subnetworks/test-subnet" + create_instance_template = true + create_instance_group = true + instance_group_zone = "asia-northeast3-a" + instance_group_target_size = 1 + update_policy_type = "OPPORTUNISTIC" + enable_autoscaling = false # Master nodes: always false + } + + assert { + condition = length(google_compute_autoscaler.autoscaler) == 0 + error_message = "Master nodes must never have an autoscaler (enable_autoscaling is hardcoded to false)." + } + + assert { + condition = google_compute_instance_group_manager.instance_group[0].update_policy[0].type == "OPPORTUNISTIC" + error_message = "Master node MIG must use OPPORTUNISTIC update policy." + } +} \ No newline at end of file diff --git a/terraform/modules/compute/tests/update_policy_opportunistic_test.tftest.hcl b/terraform/modules/compute/tests/update_policy_opportunistic_test.tftest.hcl new file mode 100644 index 0000000..99d4d01 --- /dev/null +++ b/terraform/modules/compute/tests/update_policy_opportunistic_test.tftest.hcl @@ -0,0 +1,127 @@ +# ============================================================================= +# Tests: update_policy_type = "OPPORTUNISTIC" +# +# PR Change: Both master and worker node module calls now explicitly set +# update_policy_type = "OPPORTUNISTIC" (previously used the default "PROACTIVE"). +# +# These tests verify that: +# 1. The compute module correctly propagates update_policy_type to the MIG. +# 2. OPPORTUNISTIC is accepted as a valid value (no validation error). +# 3. The default value "PROACTIVE" still works (regression check). +# 4. Boundary: switching from default PROACTIVE to OPPORTUNISTIC is safe. +# ============================================================================= + +mock_provider "google" {} + +# --------------------------------------------------------------------------- +# Test 1: update_policy_type = "OPPORTUNISTIC" is accepted and plan succeeds. +# This mirrors what both k8s_master_nodes and k8s_worker_nodes now use. +# --------------------------------------------------------------------------- +run "update_policy_type_opportunistic_is_accepted" { + command = plan + + variables { + name_prefix = "test-prefix" + network = "projects/test-project/global/networks/test-vpc" + subnetwork = "projects/test-project/regions/asia-northeast3/subnetworks/test-subnet" + create_instance_template = true + create_instance_group = true + instance_group_zone = "asia-northeast3-a" + instance_group_target_size = 1 + update_policy_type = "OPPORTUNISTIC" + enable_autoscaling = false + } + + assert { + condition = google_compute_instance_group_manager.instance_group[0].update_policy[0].type == "OPPORTUNISTIC" + error_message = "Expected update_policy_type to be OPPORTUNISTIC but got a different value." + } +} + +# --------------------------------------------------------------------------- +# Test 2: update_policy_type defaults to "PROACTIVE" when not specified. +# Regression test to ensure the default is preserved for other callers. +# --------------------------------------------------------------------------- +run "update_policy_type_defaults_to_proactive" { + command = plan + + variables { + name_prefix = "test-prefix" + network = "projects/test-project/global/networks/test-vpc" + subnetwork = "projects/test-project/regions/asia-northeast3/subnetworks/test-subnet" + create_instance_template = true + create_instance_group = true + instance_group_zone = "asia-northeast3-a" + instance_group_target_size = 1 + # update_policy_type not set — should default to "PROACTIVE" + enable_autoscaling = false + } + + assert { + condition = google_compute_instance_group_manager.instance_group[0].update_policy[0].type == "PROACTIVE" + error_message = "Expected default update_policy_type to be PROACTIVE." + } +} + +# --------------------------------------------------------------------------- +# Test 3: OPPORTUNISTIC update policy with autoscaling enabled. +# Mirrors the k8s_worker_nodes configuration: OPPORTUNISTIC + autoscaling. +# --------------------------------------------------------------------------- +run "update_policy_opportunistic_with_autoscaling_enabled" { + command = plan + + variables { + name_prefix = "test-workers" + network = "projects/test-project/global/networks/test-vpc" + subnetwork = "projects/test-project/regions/asia-northeast3/subnetworks/test-subnet" + create_instance_template = true + create_instance_group = true + instance_group_zone = "asia-northeast3-a" + instance_group_target_size = 2 + update_policy_type = "OPPORTUNISTIC" + enable_autoscaling = true + autoscaling_min_replicas = 2 + autoscaling_max_replicas = 5 + autoscaling_cpu_target = 0.7 + } + + assert { + condition = google_compute_instance_group_manager.instance_group[0].update_policy[0].type == "OPPORTUNISTIC" + error_message = "Worker node MIG should use OPPORTUNISTIC update policy." + } + + assert { + condition = length(google_compute_autoscaler.autoscaler) == 1 + error_message = "Autoscaler should be created when enable_autoscaling is true." + } +} + +# --------------------------------------------------------------------------- +# Test 4: OPPORTUNISTIC update policy with autoscaling disabled. +# Mirrors the k8s_master_nodes configuration: OPPORTUNISTIC + no autoscaling. +# --------------------------------------------------------------------------- +run "update_policy_opportunistic_with_autoscaling_disabled" { + command = plan + + variables { + name_prefix = "test-masters" + network = "projects/test-project/global/networks/test-vpc" + subnetwork = "projects/test-project/regions/asia-northeast3/subnetworks/test-subnet" + create_instance_template = true + create_instance_group = true + instance_group_zone = "asia-northeast3-a" + instance_group_target_size = 1 + update_policy_type = "OPPORTUNISTIC" + enable_autoscaling = false + } + + assert { + condition = google_compute_instance_group_manager.instance_group[0].update_policy[0].type == "OPPORTUNISTIC" + error_message = "Master node MIG should use OPPORTUNISTIC update policy." + } + + assert { + condition = length(google_compute_autoscaler.autoscaler) == 0 + error_message = "No autoscaler should be created when enable_autoscaling is false." + } +} \ No newline at end of file