Skip to content

Commit ca368e6

Browse files
Testcases for Policy (#2062)
* feat(tfpolicy): TF-33301 TF-33306: Added: init setup for terraform-policy * feat(tfpolicy): TF-33301: Added: changelog * feat(tfpolicy): TF-33301: Refactored: variables cleanup * feat(tfpolicy): TF-33301: Bumped: terraform-schema * feat(tfpolicy): TF-33301 TF-33309: Added: readme * prepend core:: to functions * Add test for policy feature * Remove unused test data file * Cleanup redundant test data * Update terraform-schema version * Update version to beta * Added changelog --------- Co-authored-by: Anubhav Goel <replyag96@gmail.com>
1 parent 534a605 commit ca368e6

File tree

9 files changed

+483
-4
lines changed

9 files changed

+483
-4
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
kind: ENHANCEMENTS
2+
body: Add test cases for policy feature
3+
time: 2026-02-09T14:52:17.472152+05:30
4+
custom:
5+
Issue: "2062"
6+
Repository: terraform-ls

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ require (
1919
github.com/hashicorp/terraform-exec v0.24.0
2020
github.com/hashicorp/terraform-json v0.27.2
2121
github.com/hashicorp/terraform-registry-address v0.4.0
22-
github.com/hashicorp/terraform-schema v0.0.0-20260205154820-1fa83aeadfe8
22+
github.com/hashicorp/terraform-schema v0.0.0-20260209091111-f9f8837ceba2
2323
github.com/mcuadros/go-defaults v1.2.0
2424
github.com/mh-cbon/go-fmt-fail v0.0.0-20160815164508-67765b3fbcb5
2525
github.com/mitchellh/cli v1.1.5

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ github.com/hashicorp/terraform-registry-address v0.4.0 h1:S1yCGomj30Sao4l5BMPjTG
128128
github.com/hashicorp/terraform-registry-address v0.4.0/go.mod h1:LRS1Ay0+mAiRkUyltGT+UHWkIqTFvigGn/LbMshfflE=
129129
github.com/hashicorp/terraform-schema v0.0.0-20260205154820-1fa83aeadfe8 h1:PPVlY6fBiz9Cryk5QjKOU+bjXhMzLgIlyNF8jtsvcY8=
130130
github.com/hashicorp/terraform-schema v0.0.0-20260205154820-1fa83aeadfe8/go.mod h1:pXOhxvzIKX/rL0weM3CzswnU8WBEMSElvC0MYsc8FzQ=
131+
github.com/hashicorp/terraform-schema v0.0.0-20260209091111-f9f8837ceba2 h1:QbBEhzv5Fl7b+IL9JcikW3g+b+/Puw3y2+oywQvzCbI=
132+
github.com/hashicorp/terraform-schema v0.0.0-20260209091111-f9f8837ceba2/go.mod h1:pXOhxvzIKX/rL0weM3CzswnU8WBEMSElvC0MYsc8FzQ=
131133
github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ=
132134
github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc=
133135
github.com/hexops/autogold v1.3.1 h1:YgxF9OHWbEIUjhDbpnLhgVsjUDsiHDTyDfy2lrfdlzo=

internal/features/policy/decoder/functions.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,14 @@ func functionsForPolicy(policy *state.PolicyRecord, stateReader CombinedReader)
1717
}
1818

1919
func mustFunctionsForVersion(v *version.Version) map[string]schema.FunctionSignature {
20-
s, err := tfschema.FunctionsForVersion(v)
20+
fs, err := tfschema.FunctionsForVersion(v)
2121
if err != nil {
2222
// this should never happen
2323
panic(err)
2424
}
25-
return s
25+
coreFunctions := make(map[string]schema.FunctionSignature, len(fs))
26+
for name, signature := range fs {
27+
coreFunctions["core::"+name] = signature
28+
}
29+
return coreFunctions
2630
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Copyright IBM Corp. 2020, 2026
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package jobs
5+
6+
import (
7+
"context"
8+
"path/filepath"
9+
"testing"
10+
11+
lsctx "github.com/hashicorp/terraform-ls/internal/context"
12+
"github.com/hashicorp/terraform-ls/internal/features/policy/ast"
13+
"github.com/hashicorp/terraform-ls/internal/features/policy/state"
14+
"github.com/hashicorp/terraform-ls/internal/filesystem"
15+
"github.com/hashicorp/terraform-ls/internal/job"
16+
ilsp "github.com/hashicorp/terraform-ls/internal/lsp"
17+
globalState "github.com/hashicorp/terraform-ls/internal/state"
18+
globalAst "github.com/hashicorp/terraform-ls/internal/terraform/ast"
19+
"github.com/hashicorp/terraform-ls/internal/uri"
20+
)
21+
22+
func TestParsePolicyConfiguration(t *testing.T) {
23+
ctx := context.Background()
24+
gs, err := globalState.NewStateStore()
25+
if err != nil {
26+
t.Fatal(err)
27+
}
28+
29+
ps, err := state.NewPolicyStore(gs.ChangeStore)
30+
if err != nil {
31+
t.Fatal(err)
32+
}
33+
34+
testData, err := filepath.Abs("testdata")
35+
if err != nil {
36+
t.Fatal(err)
37+
}
38+
testFs := filesystem.NewFilesystem(gs.DocumentStore)
39+
40+
simplePolicyPath := filepath.Join(testData, "simple-policy")
41+
42+
err = ps.Add(simplePolicyPath)
43+
if err != nil {
44+
t.Fatal(err)
45+
}
46+
47+
ctx = lsctx.WithDocumentContext(ctx, lsctx.Document{})
48+
err = ParsePolicyConfiguration(ctx, testFs, ps, simplePolicyPath)
49+
if err != nil {
50+
t.Fatal(err)
51+
}
52+
53+
before, err := ps.PolicyRecordByPath(simplePolicyPath)
54+
if err != nil {
55+
t.Fatal(err)
56+
}
57+
58+
if len(before.ParsedPolicyFiles) == 0 {
59+
t.Fatal("expected parsed policy files, got none")
60+
}
61+
62+
// Verify specific files are in the store
63+
mainFile := ast.PolicyFilename("config.policy.hcl")
64+
65+
if _, exists := before.ParsedPolicyFiles[mainFile]; !exists {
66+
t.Fatalf("expected %s to be parsed", mainFile)
67+
}
68+
69+
// ignore job state for next test
70+
ctx = job.WithIgnoreState(ctx, true)
71+
72+
mainURI, err := filepath.Abs(filepath.Join(simplePolicyPath, "config.policy.hcl"))
73+
if err != nil {
74+
t.Fatal(err)
75+
}
76+
77+
// Simulate a didChange request for one file
78+
changeCtx := lsctx.WithDocumentContext(ctx, lsctx.Document{
79+
Method: "textDocument/didChange",
80+
LanguageID: ilsp.Policy.String(),
81+
URI: uri.FromPath(mainURI),
82+
})
83+
84+
err = ParsePolicyConfiguration(changeCtx, testFs, ps, simplePolicyPath)
85+
if err != nil {
86+
t.Fatal(err)
87+
}
88+
89+
after, err := ps.PolicyRecordByPath(simplePolicyPath)
90+
if err != nil {
91+
t.Fatal(err)
92+
}
93+
94+
// config.policy.hcl should have been updated (new pointer)
95+
if before.ParsedPolicyFiles[mainFile] == after.ParsedPolicyFiles[mainFile] {
96+
t.Errorf("%s should have been re-parsed (new pointer expected)", mainFile)
97+
}
98+
99+
// Verify diagnostics were updated for the changed file
100+
if _, ok := after.PolicyDiagnostics[globalAst.HCLParsingSource][mainFile]; !ok {
101+
t.Fatal("expected diagnostics for config.policy.hcl")
102+
}
103+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
policy {
2+
consumer = terraform
3+
4+
consumer_config {
5+
required_version = ">=1.12"
6+
}
7+
}
8+
9+
locals {
10+
parent_value = core::getresources("test_resource", {
11+
id = attrs.dependent
12+
}).value
13+
}
14+
15+
resource_policy "test_resource" "policy" {
16+
filter = attrs.value == "child"
17+
18+
enforce {
19+
condition = local.parent_value == "parent"
20+
error_message = "Child resource must link to a 'parent' resource."
21+
}
22+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// Copyright IBM Corp. 2020, 2026
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package jobs
5+
6+
import (
7+
"context"
8+
"path/filepath"
9+
"testing"
10+
11+
"github.com/hashicorp/go-version"
12+
lsctx "github.com/hashicorp/terraform-ls/internal/context"
13+
"github.com/hashicorp/terraform-ls/internal/features/policy/ast"
14+
"github.com/hashicorp/terraform-ls/internal/features/policy/state"
15+
"github.com/hashicorp/terraform-ls/internal/filesystem"
16+
ilsp "github.com/hashicorp/terraform-ls/internal/lsp"
17+
globalState "github.com/hashicorp/terraform-ls/internal/state"
18+
globalAst "github.com/hashicorp/terraform-ls/internal/terraform/ast"
19+
"github.com/hashicorp/terraform-ls/internal/uri"
20+
tfmod "github.com/hashicorp/terraform-schema/module"
21+
)
22+
23+
// --- Mocks & Helpers ---
24+
25+
type PolicyRootReaderMock struct{}
26+
27+
func (r PolicyRootReaderMock) InstalledModuleCalls(modPath string) (map[string]tfmod.InstalledModuleCall, error) {
28+
return nil, nil
29+
}
30+
func (r PolicyRootReaderMock) TerraformVersion(modPath string) *version.Version {
31+
return nil
32+
}
33+
func (r PolicyRootReaderMock) InstalledModulePath(rootPath string, normalizedSource string) (string, bool) {
34+
return "", false
35+
}
36+
37+
func setupTestEnv(t *testing.T) (*state.PolicyStore, *filesystem.Filesystem, string) {
38+
gs, err := globalState.NewStateStore()
39+
if err != nil {
40+
t.Fatal(err)
41+
}
42+
43+
ps, err := state.NewPolicyStore(gs.ChangeStore)
44+
if err != nil {
45+
t.Fatal(err)
46+
}
47+
48+
testData, err := filepath.Abs("testdata")
49+
if err != nil {
50+
t.Fatal(err)
51+
}
52+
53+
policyPath := filepath.Join(testData, "simple-policy")
54+
err = ps.Add(policyPath)
55+
if err != nil {
56+
t.Fatal(err)
57+
}
58+
59+
fs := filesystem.NewFilesystem(gs.DocumentStore)
60+
return ps, fs, policyPath
61+
}
62+
63+
func TestParsePolicy_FullPolicy(t *testing.T) {
64+
ps, fs, policyPath := setupTestEnv(t)
65+
ctx := lsctx.WithDocumentContext(context.Background(), lsctx.Document{
66+
Method: "textDocument/didOpen",
67+
LanguageID: ilsp.Policy.String(),
68+
})
69+
70+
// Run Parse
71+
if err := ParsePolicyConfiguration(ctx, fs, ps, policyPath); err != nil {
72+
t.Fatalf("Full parse failed: %v", err)
73+
}
74+
75+
// Run Validation
76+
if err := SchemaPolicyValidation(ctx, ps, PolicyRootReaderMock{}, policyPath); err != nil {
77+
t.Fatalf("Full schema validation failed: %v", err)
78+
}
79+
80+
record, _ := ps.PolicyRecordByPath(policyPath)
81+
if len(record.ParsedPolicyFiles) == 0 {
82+
t.Error("Expected files to be parsed, but map is empty")
83+
}
84+
}
85+
86+
func TestParsePolicy_SingleFile(t *testing.T) {
87+
ps, fs, policyPath := setupTestEnv(t)
88+
89+
mainFile := "config.policy.hcl"
90+
absPath, _ := filepath.Abs(filepath.Join(policyPath, mainFile))
91+
mainURI := uri.FromPath(absPath)
92+
93+
ctx := lsctx.WithDocumentContext(context.Background(), lsctx.Document{
94+
Method: "textDocument/didChange",
95+
LanguageID: ilsp.Policy.String(),
96+
URI: mainURI,
97+
})
98+
99+
if err := ParsePolicyConfiguration(ctx, fs, ps, policyPath); err != nil {
100+
t.Fatalf("Incremental parse failed: %v", err)
101+
}
102+
103+
if err := SchemaPolicyValidation(ctx, ps, PolicyRootReaderMock{}, policyPath); err != nil {
104+
t.Fatalf("Incremental schema validation failed: %v", err)
105+
}
106+
107+
record, _ := ps.PolicyRecordByPath(policyPath)
108+
filename := ast.PolicyFilename(mainFile)
109+
110+
if _, ok := record.PolicyDiagnostics[globalAst.SchemaValidationSource][filename]; !ok {
111+
t.Errorf("Diagnostic for %s missing from store", filename)
112+
}
113+
}

0 commit comments

Comments
 (0)