# Tgc Fix Handwritten Resources Tests Skill

> Fix integration tests for handwritten resources in TGC. Use when you need to fix integration tests for TGC.

- Skill: `googlecloudplatform/tgc-fix-handwritten-resources-tests-skill` (Agent Skill)
- Install (CLI): `npx skillmds@latest add googlecloudplatform/tgc-fix-handwritten-resources-tests-skill`
- Raw SKILL.md: https://api.skillmd.com/api/skills/googlecloudplatform/tgc-fix-handwritten-resources-tests-skill/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: GoogleCloudPlatform (https://skillmd.com/u/googlecloudplatform)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/googlecloudplatform/tgc-fix-handwritten-resources-tests-skill

---


# tgc-fix-handwritten-resources-tests-skill

When you need to fix integration tests for handwritten resources in TGC, use this skill.

## When to Use This Skill

- Use this when fixing integration tests for handwritten resources in TGC.
- This is helpful when you need to address build and testing failures for any Terraform Google Conversion (TGC) resource.

---

## How to Use It

### General Rules for Fixing Tests
When troubleshooting and resolving test failures, adhere to these constraints:
- **DON'T** modify `mmv1/api/resource.go` to hardcode or manually append ignored fields for tests. If there is a field path mapping mismatch (e.g., due to singular/plural name differences), rename the property name in the resource YAML configuration (e.g., `Instance.yaml`) and use `api_name` to map it to the API field name.
- **DON'T** modify shared provider templates/files in `mmv1/third_party/terraform`. All changes must be localized to TGC Next code under `mmv1/third_party/tgc_next/`.
- **DON'T** modify legacy TGC code in `mmv1/third_party/cai2hcl` or `mmv1/third_party/tgc`. All changes must be made to TGC Next code in `mmv1/third_party/tgc_next/`.
- **DO** add a comment for each fix in the YAML file or other files to explain the root cause and the solution.

### Steps
1. Find the error message
2. Fix the tests in magic-modules by following the guidelines and playbook below:


#### File Naming Conventions for Handwritten Tests

When adding or fixing tests for a resource, ensure that the handwritten test files in `mmv1/third_party/terraform/services/<product>/` follow the expected naming convention: `resource_<product>_<resource_name>_test.go`.

If the file name does not match (e.g., it uses a legacy name or has extra suffixes), the generator function `addTestsFromHandwrittenTests` will fail to find it and will log a message like `no handwritten test file found for <resource>`.

**Solution:**
Rename the file to match the expected convention so the generator can discover it and include its tests in the generated TGC test suite.

#### Example: Fixing Omitempty JSON Tag Issue with ForceSendFields

**Failing Test:** `TestAccContainerCluster_auto_ipam_config_enabled_step3_roundtrip.tf`

The test might fail during the evaluation of the `terraform plan` phase with an error indicating a missing argument inside a generated block, such as:

```text
Error: Missing required argument

The argument "enabled" is required, but no definition was found in auto_ipam_config.
```


If a boolean or zero-value field (e.g., `enabled = false`) is silently omitted during the API export process, it can cause Terraform to fail with missing required fields (e.g., returning an empty block `auto_ipam_config {}` instead of `auto_ipam_config { enabled = false }`).

**Solution:**
To fix issues where zero-value boolean fields are omitted:
1. **tfplan2cai (Expander):** Ensure that `ForceSendFields` is explicitly specified in the expander function (e.g., `expandAutoIpamConfig`), so that the converted CAI asset in `roundtrip.json` includes the zero value (e.g., `enabled: false`). For example, add `ForceSendFields: []string{"Enabled"}` to ensure the 0-value is not dropped by the Go `omitempty` JSON struct tag.

After adding these, rebuild TGC and run the respective integration test to verify the fix.

#### Example: Fixing Terraform Validation Error with Unrecognized CAI Enum Values

TestAccContainerCluster_withLoggingConfig.tf

The test might fail during the evaluation of `terraform plan` phase with the following error:

```
expected pod_autoscaling.0.hpa_profile to be one of ["NONE" "PERFORMANCE"], got HPA_PROFILE_UNSPECIFIED
```

The underlying issue is that the CAI export contains a default/unspecified enumeration value (`HPA_PROFILE_UNSPECIFIED`) that Terraform's internal schema explicitly forbids.

**Solution:**
To fix issues where an unspecified enum fails Terraform validation:
1. **cai2hcl (Flattener):** Handle this explicitly in the `cai2hcl.go` flattener (e.g., `flattenPodAutoscaling`). When the underlying map value matches an unspecified enum value (or is an empty string), discard the block entirely by returning `nil`. This correctly implies the default absent state back into the HCL config block mapping.

#### Example: Emitting Default Enums for Expected CAI Payload Values

When user provides no terraform configuration or supplies a completely empty block for a struct that expects a CAI enum (like `HpaProfile` expecting `HPA_PROFILE_UNSPECIFIED` rather than nil), it may produce missing field differences in the `asset` verification checks.

**Solution:**
1. **tfplan2cai (Expander):** Explicitly check if the configured value is `nil` or empty. If so, return a new struct with the correct default CAI enum explicitly assigned (e.g. `&container.PodAutoscaling{HpaProfile: "HPA_PROFILE_UNSPECIFIED"}`) rather than directly returning `nil`. This forces the proper default to be populated into the CAI export payload generated by TGC.

### Example: Fixing Missing Required Fields inside Nested Blocks by Emitting Default Configurations

TestAccContainerCluster_withCostManagementConfig.tf

The test might fail during the evaluation of `terraform plan` phase with the following error:

```
Error: Missing required argument "enabled" in cost_management_config
```

The underlying issue is that the CAI-to-HCL flattener was generating an empty block (e.g., `cost_management_config {}`) instead of properly defaulting the required fields or omitting the block. Since `enabled` is a required attribute within `cost_management_config` in the Terraform schema, generating an empty block triggers schema validation errors.

**Solution:**
To fix issues where a nested block requires a specific field value to pass validation when generated without explicit data:
1. **cai2hcl (Flattener):** Handle this explicitly in the `cai2hcl.go` flattener (e.g., `flattenManagementConfig`). Instead of returning an empty map when the CAI asset lacks the fields, explicitly default the required attributes to their default values (e.g., set `enabled` to `false`). This ensures the emitted HCL block meets schema requirements natively.
2. **tfplan2cai (Expander):** Ensure the expander generates the exact equivalent CAI API empty object (e.g. `costManagementConfig: {}`) instead of omitting the field entirely. The expander (e.g., `expandCostManagementConfig`) must return a pointer to an empty struct (`&container.CostManagementConfig{}`) rather than `nil` when the user configuration block is absent or disabled. This ensures structural equality in `roundtrip.json` tests.

#### Example: Fixing Missing Required Fields for Empty Strings Dropped by API

**Failing Test:** `TestAccContainerCluster_withAuthenticatorGroupsConfig.tf`

The test might fail during the evaluation of `terraform plan` phase with the following error:

```text
Error: Missing required argument

The argument "security_group" is required, but no definition was found.
```

The underlying issue is that the user specified an empty string for a required field (e.g. `security_group = ""`) in a nested block. During the CAI export, this empty string may be dropped by the `omitempty` JSON tag, causing the flattener to interpret the field as missing completely rather than as an empty string. This generates an empty block `{}` which fails schema validation for the required field.

**Solution:**
To fix issues where required fields are dropped because they are empty strings:
1. **cai2hcl (Flattener):** Handle this explicitly in the `cai2hcl.go` flattener (e.g., `flattenAuthenticatorGroupsConfig`). Check if the payload contains the field; if not, explicitly fall back to an empty string `""` instead of returning a missing value map. This correctly satisfies the `Required: true` HCL schema.

#### Example: Resolving cty.Value Unmarshaling Errors for Missing Schema Attributes

**Failing Test:** `TestAccContainerCluster_withAddons`

The test might fail during the test framework's asset assertions with an error:

```text
error when converting the export assets into export config: &errors.errorString{s:"error unmarshaling JSON as cty.Value: unsupported attribute \"slice_controller_config\""}
```

This error occurs when the CAI-to-HCL flattener correctly exports a nested field (e.g., `slice_controller_config`), but the associated exact Terraform schema block is missing from the downstream `terraform-google-conversion` generated codebase. When the framework attempts to decode the output JSON into a `cty.Value` to verify matches against the TF plan, it fails to recognize the exported attribute because the schema is stale or incomplete.

**Solution:**
To fix `unsupported attribute` unmarshaling errors for newly mapped CAI fields:
1. **Schema Update:** Locate the schema definition in the appropriate handwritten or generated file (e.g. `mmv1/third_party/tgc_next/pkg/services/container/resource_container_cluster.go` or `mmv1/products/...`) and ensure the new attribute block is declared structurally within its parent schema type definition map.
2. **Rebuild TGC Generation Correctly:** Ensure you rebuild the codebase by providing the exact absolute path to the downstream repository so your target schema file gets cleanly overwritten. A relative path from the Magic Modules root will write the generated files to the wrong place since generation runs inside the `mmv1` directory!
   ```bash
   make tgc OUTPUT_PATH="/path/to/downstream/terraform-google-conversion"
   ```
3. Re-build the conversion binaries downstream (`make mod-clean && make build`) and re-run the integration test.

#### Example: Resolving Missing Fields Due to Map Type Assertion with Nil Values

**Failing Test:** `TestAccContainerCluster_withReleaseChannelEnabled.tf`

The test might fail during the evaluation phase with the following error:

```text
TestAccContainerCluster_withReleaseChannelEnabled_step3: missing fields in resource google_container_cluster.with_release_channel after cai2hcl conversion:
[release_channel.channel]
```

The underlying issue is that the GKE API considers `UNSPECIFIED` as the zero state for `releaseChannel.channel` and omits it entirely from the JSON response. When the field is omitted, the CAI asset contains an empty map `{}`. If the flattener checks `if c, ok := v.(map[string]interface{}); ok && c["channel"] != ""`, the missing key evaluates to `nil`. In Go, `interface{}(nil) != interface{}("")` evaluates to `true`. This incorrectly maps a `nil` back into the HCL configuration, which eventually drops during map serialization, causing a missing field discrepancy.

**Solution:**
To fix `missing fields` errors caused by nil map values bypassing string interface comparisons:
1. **cai2hcl (Flattener):** Update the `cai2hcl.go` flattener (e.g., `flattenReleaseChannel`) to explicitly perform a string type assertion (`if ch, ok := c["channel"].(string); ok && ch != ""`) before assignment. This forces missing or non-string configurations to properly fallback to the default HCL value (e.g., `"UNSPECIFIED"`).
2. **tfplan2cai (Expander):** If the upstream API returns an empty object (`{}`) for the zero-state (like `releaseChannel: {}` when `"UNSPECIFIED"`), ensure the expander matches this behavior. Configure the expander (e.g., `expandReleaseChannel`) to return an empty struct pointer (`&container.ReleaseChannel{}`) when the zero-state is detected. Because internal fields like `Channel` use the `omitempty` JSON tag, the empty struct will correctly serialize into an empty JSON object (`{}`), ensuring the `tfplan2cai` roundtrip payload perfectly matches the upstream `cai2hcl` conversion output.

#### Example: Resolving "Value for unconfigurable attribute" Validation Errors

**Failing Test:** `TestAccContainerCluster_deleteExclusionWindow.tf`

The test might fail during the evaluation of the `terraform plan` phase against the generated output with an error indicating an attribute cannot be explicitly set:

```text
Error: Value for unconfigurable attribute
Can't configure a value for "maintenance_policy.0.daily_maintenance_window.0.duration": its value will be decided automatically based on the result of applying this configuration.
```

The underlying issue is that the Terraform schema defines the attribute (e.g. `duration`) strictly as `Computed: true` (read-only output from the API). Because it is generated dynamically by the upstream API, Terraform forbids explicitly defining or configuring it within `.tf` blocks. However, during the `cai2hcl` conversion mapping process, the flattener unconditionally exported the duration value from the CAI asset into the generated struct, populating an unconfigurable field and triggering schema validation rejection.

**Solution:**
To fix validation errors caused by unconfigurable computed attributes being populated in HCL maps:
1. **cai2hcl (Flattener):** Locate the flattener associated with the failing block (e.g. `flattenMaintenancePolicy`). Find where the nested properties are assigned from the API map into the `transformed` JSON equivalent. Surgically omit mapping the offending `Computed` attribute (e.g. `duration`). By mapping only the configurable fields (e.g., `start_time`), Terraform gracefully allows the implicitly computed value to pass without complaining about manually configured read-only attributes.

#### Example: Resolving Shared Parameter Collisions in CAI Resource Identification

**Failing Test:** Multiple generated resources mapping to the same API endpoints

The TGC code generation loops through all known CAI endpoints to match a converted asset back to a single unique HCL block name. Previously, it searched for a single strictly unique `IdentityParam` string segment (e.g. `"global"` or `"projects"`). When multiple resources within a group (e.g. `compute.googleapis.com/FirewallPolicy`) shared these single unique segments (`_region_network_firewall_policy` has `"projects"`, and `_firewall_policy` has `"global"`), the deterministic identifier fallback misclassified the asset resource type.

**Solution:**
To fix CAI mapping collisions between similar resources:
1. **terraform_tgc_next (Generator):** The generator natively supports an array-based `IdentityParams` slice. Instead of identifying a single unique segment, `removeSharedElements` strips common URL components across a product, leaving *all* remaining distinct parameters. The generator natively evaluates all segments concurrently (e.g. `contains(projects) && contains(global)`), resulting in perfect multi-parameter exact-path checking for highly nested collision-prone resources.

#### Example: Fixing Missing Fields Not Present in CAI Asset via YAML

##### Debugging Order of Operations (Handling Missing Fields)
When a field is reported as missing or failing in a handwritten resource's integration test, you must follow this strict logical order of operations rather than immediately marking it as ignored:

1. **Verify Resource Type**: Confirm the resource is indeed a handwritten/custom resource (e.g., GKE Cluster or GKE NodePool) and not generated from a standard MM YAML.
2. **Verify Go-Level Conversion Support**: Check the `tgc_next` handwritten Go files (e.g., `node_config.go`) to see if the schema, flattener, and expander for the missing field are already implemented.
   - **If missing**: 
      - **Check if the field is only mapped/set in the standard downstream Terraform Provider when the field exists in the Terraform state or configuration (e.g., via checks like `d.GetOk("field")`).**
         - **If so**: **do NOT convert it in TGC**. Only implement Go-level flatteners in TGC for fields that are unconditionally mapped/set from the live API response.
         - **Otherwise**: Implement the conversion code in Go first. Do NOT skip this step; doing so would mask a missing feature with a false-positive ignore rule.
3. **Verify Post-Code Test Behavior**: Rebuild and re-run the integration test.
4. **Apply Ignored Metadata**: If the field is *still* reported as missing during validation, check if it's because of a permanent API-level omission in the CAI asset payload. Only after proving a CAI-level omission, add `is_missing_in_cai: true` to the MM YAML (e.g., `NodePool.yaml`) to gracefully bypass validation.

##### Solution for API-Level CAI Omissions:
1. Add the field to the resource's YAML file under the appropriate property block.
2. Set `is_missing_in_cai: true`.
This ensures that the validation framework gracefully ignores the CAI-level API omission during testing, while still allowing your Go-level conversion code (schema, flattener, and expander) to execute normally when properties are converted.

> [!TIP]
> **GKE CAI Parity Warning:** When dealing with nested configuration blocks shared between parent resources (like GKE Cluster) and standalone child resources (like GKE NodePool), do not assume their API CAI exporters share the same level of parity. For example, while GKE Clusters return `crashLoopBackOff` in CAI, the standalone GKE NodePool CAI asset (`container.googleapis.com/NodePool`) has a permanent omission and *never* returns `crashLoopBackOff`. Always verify target CAI asset types specifically.

#### Example: Resolving Missing Fields by Implementing Go-Level Flatteners

**Failing Test:** `TestAccComputeInstance_schedulingTerminationTime`

The test fails during the CAI-to-HCL validation check because of a missing field:

```text
TestAccComputeInstance_schedulingTerminationTime_step1: Failed after 5 attempts. First real error: retryable: missing fields: [scheduling.termination_time]
```

**Analysis:**
1. Inspect the raw CAI asset (`TestAccComputeInstance_schedulingTerminationTime_step1.json`). If the property (`terminationTime`) is present in the CAI payload, it is NOT missing from CAI. Thus, `is_missing_in_cai: true` must NOT be used.
2. For handwritten resources (e.g., `google_compute_instance`), look at the custom conversion files (e.g., `compute_instance_cai2hcl.go`). If the property flattener is missing the mapping code, the property won't be written to the exported HCL.

**Solution:**
1. **cai2hcl (Flattener):** Locate the flattener function mapping the nested block (e.g., `flattenSchedulingTgcNext`). Add Go code to extract the value from the CAI response map and write it to the HCL schema map:
   ```go
   if tt, ok := resp["terminationTime"].(string); ok && tt != "" {
       schedulingMap["termination_time"] = tt
   }
   ```

