• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pantsbuild / pants / 18847018991

27 Oct 2025 03:45PM UTC coverage: 92.254% (+12.0%) from 80.282%
18847018991

Pull #22816

github

web-flow
Merge f1312fa87 into 06e216752
Pull Request #22816: Update Pants internal Python to 3.14

39 of 40 new or added lines in 11 files covered. (97.5%)

382 existing lines in 22 files now uncovered.

89230 of 96722 relevant lines covered (92.25%)

3.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

50.0
/src/python/pants/backend/terraform/dependencies_test.py
1
# Copyright 2023 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3
from __future__ import annotations
1✔
4

5
import dataclasses
1✔
6
import json
1✔
7
import textwrap
1✔
8
from pathlib import Path
1✔
9

10
from pants.backend.terraform.dependencies import (
1✔
11
    TerraformDependenciesResponse,
12
    TerraformInitRequest,
13
    TerraformInvocationRequirements,
14
)
15
from pants.backend.terraform.goals.deploy import DeployTerraformFieldSet
1✔
16
from pants.backend.terraform.testutil import (
1✔
17
    StandardDeployment,
18
    rule_runner_with_auto_approve,
19
    standard_deployment,
20
    terraform_lockfile,
21
)
22
from pants.engine.fs import DigestContents, DigestEntries, FileContent, SymlinkEntry
1✔
23
from pants.engine.internals.native_engine import Address
1✔
24
from pants.testutil.rule_runner import RuleRunner
1✔
25

26
rule_runner = rule_runner_with_auto_approve
1✔
27
standard_deployment = standard_deployment
1✔
28

29

30
def _do_init_terraform(
1✔
31
    rule_runner: RuleRunner, standard_deployment: StandardDeployment, initialise_backend: bool
32
) -> tuple[DigestContents, DigestEntries]:
33
    rule_runner.write_files(standard_deployment.files)
1✔
34
    target = rule_runner.get_target(standard_deployment.target)
1✔
35
    field_set = DeployTerraformFieldSet.create(target)
1✔
36

37
    init = rule_runner.request(
1✔
38
        TerraformInvocationRequirements,
39
        [
40
            TerraformInitRequest(
41
                field_set.root_module,
42
                field_set.dependencies,
43
                initialise_backend=initialise_backend,
44
            )
45
        ],
46
    )
47

UNCOV
48
    result = rule_runner.request(
×
49
        TerraformDependenciesResponse,
50
        [init.init_cmd],
51
    )
UNCOV
52
    initialised_files = rule_runner.request(DigestContents, [result.digest])
×
UNCOV
53
    initialised_entries = rule_runner.request(DigestEntries, [result.digest])
×
UNCOV
54
    assert isinstance(initialised_files, DigestContents)
×
UNCOV
55
    return initialised_files, initialised_entries
×
56

57

58
def find_file(files: DigestContents, pattern: str) -> FileContent | None:
1✔
UNCOV
59
    return next((file for file in files if Path(file.path).match(pattern)), None)
×
60

61

62
def find_link(entries: DigestEntries, pattern: str) -> SymlinkEntry | None:
1✔
UNCOV
63
    for entry in entries:
×
UNCOV
64
        if not isinstance(entry, SymlinkEntry):
×
UNCOV
65
            continue
×
66

UNCOV
67
        if Path(entry.path).match(pattern):
×
68
            # allow any prefix to account for absolute targets
UNCOV
69
            return entry
×
70

71
    return None
×
72

73

74
def test_init_terraform(rule_runner: RuleRunner, standard_deployment: StandardDeployment) -> None:
1✔
75
    """Test for the happy path of initialising Terraform with a backend config."""
76
    initialised_files, initialised_links = _do_init_terraform(
1✔
77
        rule_runner, standard_deployment, initialise_backend=True
78
    )
79

80
    # Assert uses backend by checking that the overrides in the backend file are present in the local stub state file
UNCOV
81
    stub_tfstate_raw = find_file(initialised_files, "src/tf/.terraform/terraform.tfstate")
×
UNCOV
82
    assert stub_tfstate_raw
×
UNCOV
83
    stub_tfstate = json.loads(stub_tfstate_raw.content)
×
UNCOV
84
    assert stub_tfstate["backend"]["config"]["path"] == str(standard_deployment.state_file)
×
85

86
    # Assert dependencies are initialised by checking for the dependency itself
UNCOV
87
    assert find_link(
×
88
        initialised_links,
89
        ".terraform/providers/registry.terraform.io/hashicorp/null/*/*",
90
    ), "Did not find expected provider"
91

92
    # Assert lockfile is included
UNCOV
93
    assert find_file(initialised_files, ".terraform.lock.hcl"), "Did not find expected provider"
×
94

95

96
def test_init_terraform_uses_lockfiles(
1✔
97
    rule_runner: RuleRunner, standard_deployment: StandardDeployment
98
) -> None:
99
    """Test that we can use generated lockfiles."""
100
    requested_version = "3.2.0"
1✔
101

102
    deployment_with_lockfile = dataclasses.replace(
1✔
103
        standard_deployment,
104
        files={**standard_deployment.files, **{"src/tf/.terraform.lock.hcl": terraform_lockfile}},
105
    )
106

107
    initialised_files, initialised_entries = _do_init_terraform(
1✔
108
        rule_runner, deployment_with_lockfile, initialise_backend=True
109
    )
110

111
    # Assert lockfile is not regenerated
UNCOV
112
    result_lockfile = find_file(initialised_files, ".terraform.lock.hcl")
×
UNCOV
113
    assert result_lockfile, "Did not find lockfile"
×
UNCOV
114
    assert f'version     = "{requested_version}"' in result_lockfile.content.decode(), (
×
115
        "version in lockfile has changed, we should not have regenerated the lockfile"
116
    )
117

118
    # Assert dependencies are initialised to the older version
UNCOV
119
    result_provider = find_link(
×
120
        initialised_entries,
121
        ".terraform/providers/registry.terraform.io/hashicorp/null/*/*",
122
    )
UNCOV
123
    assert result_provider, "Did not find any providers, did we initialise them successfully?"
×
UNCOV
124
    assert requested_version in result_provider.path, (
×
125
        "initialised provider did not have our requested version, did the lockfile show up and did we regenerate it?"
126
    )
127

128

129
def test_init_terraform_without_backends(
1✔
130
    rule_runner: RuleRunner, standard_deployment: StandardDeployment
131
) -> None:
132
    initialised_files, initialised_entries = _do_init_terraform(
1✔
133
        rule_runner, standard_deployment, initialise_backend=False
134
    )
135

136
    # Not initialising the backend means that ./.terraform/.terraform.tfstate will not be present
UNCOV
137
    assert not find_file(initialised_files, "**/*.tfstate"), (
×
138
        "Terraform state file should not be present if the request was to not initialise the backend"
139
    )
140

141
    # The dependencies should still be present
UNCOV
142
    assert find_link(
×
143
        initialised_entries,
144
        ".terraform/providers/registry.terraform.io/hashicorp/null/*/*",
145
    ), "Did not find expected provider"
146

147

148
def assert_init_module(modules, target_module_id: str, message: str) -> None:
1✔
UNCOV
149
    assert target_module_id in modules, (
×
150
        f"{message}: Did not find {target_module_id} in modules.json. Found modules are {list(modules.items())}"
151
    )
152

153

154
def test_init_terraform_with_transitive_module(rule_runner: RuleRunner, tmpdir) -> None:
1✔
155
    deployment_files = {
1✔
156
        "src/tf/deployment/BUILD": textwrap.dedent(
157
            """\
158
            terraform_deployment(name="root", root_module=":mod")
159
            terraform_module(name="mod")
160
        """
161
        ),
162
        "src/tf/deployment/main.tf": textwrap.dedent(
163
            """\
164
            module "mod0" {
165
              source = "../module/"
166
            }
167
        """
168
        ),
169
    }
170
    module_files = {
1✔
171
        "src/tf/module/BUILD": "terraform_module()",
172
        "src/tf/module/main.tf": 'module "transitive" { source = "../transitive/" }',
173
    }
174
    transitive_module_files = {
1✔
175
        "src/tf/transitive/BUILD": "terraform_module()",
176
        "src/tf/transitive/main.tf": 'resource "null_resource" "dep" {}',
177
    }
178

179
    deployment = StandardDeployment(
1✔
180
        {**deployment_files, **module_files, **transitive_module_files},
181
        Path(str(tmpdir.mkdir(".terraform").join("state.json"))),
182
        Address("src/tf/deployment", target_name="root"),
183
    )
184
    initialised_files, initialised_entries = _do_init_terraform(
1✔
185
        rule_runner, deployment, initialise_backend=True
186
    )
187

UNCOV
188
    assert initialised_files
×
189
    # Assert that init succeeded and created the modules mapping
UNCOV
190
    modules_file_raw = find_file(initialised_files, ".terraform/modules/modules.json")
×
UNCOV
191
    assert modules_file_raw
×
192

UNCOV
193
    modules_file = json.loads(modules_file_raw.content)
×
UNCOV
194
    modules = {module["Key"]: module for module in modules_file["Modules"]}
×
195

UNCOV
196
    assert_init_module(
×
197
        modules, "mod0", message="Assert that the deployment pulled in it root module"
198
    )
UNCOV
199
    assert_init_module(
×
200
        modules, "mod0.transitive", message="Assert that the root module pulled in its dependents"
201
    )
202

203
    # Assert that the provider dependency was initialised
UNCOV
204
    assert find_link(
×
205
        initialised_entries,
206
        ".terraform/providers/registry.terraform.io/hashicorp/null/*/*",
207
    ), (
208
        "Did not find expected provider contained in module, did we successfully include it in the files passed to `init`?"
209
    )
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc