Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP - File scoped context for Terraform Test files #1889

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ require (
github.com/hashicorp/go-uuid v1.0.3
github.com/hashicorp/go-version v1.7.0
github.com/hashicorp/hc-install v0.9.0
github.com/hashicorp/hcl-lang v0.0.0-20241115124434-9d252ff73a68
github.com/hashicorp/hcl-lang v0.0.0-20241206161211-32066ec5d37c
github.com/hashicorp/hcl/v2 v2.23.0
github.com/hashicorp/terraform-exec v0.21.0
github.com/hashicorp/terraform-json v0.23.0
github.com/hashicorp/terraform-registry-address v0.2.3
github.com/hashicorp/terraform-schema v0.0.0-20241128095320-1392f740c4fe
github.com/hashicorp/terraform-schema v0.0.0-20241206161747-7d864b0dfb07
github.com/mcuadros/go-defaults v1.2.0
github.com/mh-cbon/go-fmt-fail v0.0.0-20160815164508-67765b3fbcb5
github.com/mitchellh/cli v1.1.5
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,8 @@ github.com/hashicorp/hc-install v0.9.0 h1:2dIk8LcvANwtv3QZLckxcjyF5w8KVtiMxu6G6e
github.com/hashicorp/hc-install v0.9.0/go.mod h1:+6vOP+mf3tuGgMApVYtmsnDoKWMDcFXeTxCACYZ8SFg=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/hcl-lang v0.0.0-20241115124434-9d252ff73a68 h1:AhKd5zK/+uiSRCmjpyQqZ8nSDBwiIz2fF5D4nXIYdys=
github.com/hashicorp/hcl-lang v0.0.0-20241115124434-9d252ff73a68/go.mod h1:3HWmoYgqN9HnX3GXCIPbfjLNT48F/0dqY5SP8V9cmIs=
github.com/hashicorp/hcl-lang v0.0.0-20241206161211-32066ec5d37c h1:veJ7q9pXKHKoEkZ9ymSAWv1yUc20X5+qTrwTtuBwYIA=
github.com/hashicorp/hcl-lang v0.0.0-20241206161211-32066ec5d37c/go.mod h1:5NUxE6UGpjm8iW/pY70BJWunDMkc73d7HTtvefU8B/c=
github.com/hashicorp/hcl/v2 v2.23.0 h1:Fphj1/gCylPxHutVSEOf2fBOh1VE4AuLV7+kbJf3qos=
github.com/hashicorp/hcl/v2 v2.23.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA=
github.com/hashicorp/terraform-exec v0.21.0 h1:uNkLAe95ey5Uux6KJdua6+cv8asgILFVWkd/RG0D2XQ=
Expand All @@ -238,8 +238,8 @@ github.com/hashicorp/terraform-json v0.23.0 h1:sniCkExU4iKtTADReHzACkk8fnpQXrdD2
github.com/hashicorp/terraform-json v0.23.0/go.mod h1:MHdXbBAbSg0GvzuWazEGKAn/cyNfIB7mN6y7KJN6y2c=
github.com/hashicorp/terraform-registry-address v0.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTVcLZRu7JseiXNRHbOAyoTI=
github.com/hashicorp/terraform-registry-address v0.2.3/go.mod h1:lFHA76T8jfQteVfT7caREqguFrW3c4MFSPhZB7HHgUM=
github.com/hashicorp/terraform-schema v0.0.0-20241128095320-1392f740c4fe h1:2pVtzihaLjn6PTIyKom+X491QlLupxGoLRf5Ik8zpYM=
github.com/hashicorp/terraform-schema v0.0.0-20241128095320-1392f740c4fe/go.mod h1:3vDqHlpaMuTeBXSC4LWDM/m2QdEe9DmC90IgyuhdgZw=
github.com/hashicorp/terraform-schema v0.0.0-20241206161747-7d864b0dfb07 h1:jd5luM1tdSpxTk9oY4NrssfFdpprG+nKF9HMpnEQ7to=
github.com/hashicorp/terraform-schema v0.0.0-20241206161747-7d864b0dfb07/go.mod h1:7nHB+a9/PpOTwfdJe/KlYO1VZwGINkv8o7emxaIl3Q4=
github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ=
github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc=
github.com/hexops/autogold v1.3.1 h1:YgxF9OHWbEIUjhDbpnLhgVsjUDsiHDTyDfy2lrfdlzo=
Expand Down
72 changes: 51 additions & 21 deletions internal/features/tests/decoder/path_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/hashicorp/hcl-lang/decoder"
"github.com/hashicorp/hcl-lang/lang"
"github.com/hashicorp/hcl-lang/reference"
"github.com/hashicorp/hcl-lang/schema"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/terraform-ls/internal/features/tests/ast"
"github.com/hashicorp/terraform-ls/internal/features/tests/state"
Expand Down Expand Up @@ -52,14 +53,20 @@ var _ decoder.PathReader = &PathReader{}

// PathContext returns a PathContext for the given path based on the language ID
func (pr *PathReader) PathContext(path lang.Path) (*decoder.PathContext, error) {
if path.File == "" {
panic("file is empty")
// TODO: make this nicer after testing is done
// return nil, fmt.Errorf("file is empty")
}

record, err := pr.StateReader.TestRecordByPath(path.Path)
if err != nil {
return nil, err
}

switch path.LanguageID {
case ilsp.Test.String():
return testPathContext(record, CombinedReader{
return testPathContext(record, path.File, CombinedReader{
StateReader: pr.StateReader,
ModuleReader: pr.ModuleReader,
RootReader: pr.RootReader,
Expand All @@ -75,7 +82,7 @@ func (pr *PathReader) PathContext(path lang.Path) (*decoder.PathContext, error)
return nil, fmt.Errorf("unknown language ID: %q", path.LanguageID)
}

func testPathContext(record *state.TestRecord, stateReader CombinedReader) (*decoder.PathContext, error) {
func testPathContext(record *state.TestRecord, filename string, stateReader CombinedReader) (*decoder.PathContext, error) {
// TODO! this should only work for terraform 1.6 and above
version := stateReader.TerraformVersion(record.Path())
if version == nil {
Expand All @@ -91,40 +98,41 @@ func testPathContext(record *state.TestRecord, stateReader CombinedReader) (*dec
sm.SetStateReader(stateReader)

meta := &tftest.Meta{
Path: record.Path(),
Filenames: record.Meta.Filenames,
Path: record.Path(),
}

mergedSchema, err := sm.SchemaForTest(meta)
if err != nil {
return nil, err
}

functions, err := functionsForTest(record, version, stateReader)
if err != nil {
return nil, err
}

pathCtx := &decoder.PathContext{
Schema: mergedSchema,
ReferenceOrigins: make(reference.Origins, 0),
ReferenceTargets: make(reference.Targets, 0),
Files: make(map[string]*hcl.File, 0),
Validators: validators,
// TODO? functions TFECO-7480
Functions: functions,
}

for _, origin := range record.RefOrigins {
for _, origin := range record.RefOrigins[filename] {
if ast.IsTestFilename(origin.OriginRange().Filename) {
pathCtx.ReferenceOrigins = append(pathCtx.ReferenceOrigins, origin)
}
}
for _, target := range record.RefTargets {
for _, target := range record.RefTargets[filename] {
if target.RangePtr != nil && ast.IsTestFilename(target.RangePtr.Filename) {
pathCtx.ReferenceTargets = append(pathCtx.ReferenceTargets, target)
}
}

for name, f := range record.ParsedFiles {
if _, ok := name.(ast.TestFilename); ok {
pathCtx.Files[name.String()] = f
}
}
// only one file in this context
pathCtx.Files[filename] = record.ParsedFiles[ast.TestFilename(filename)]

return pathCtx, nil
}
Expand All @@ -145,32 +153,44 @@ func mockPathContext(record *state.TestRecord, stateReader CombinedReader) (*dec
sm.SetStateReader(stateReader)

meta := &tftest.Meta{
Path: record.Path(),
Filenames: record.Meta.Filenames,
Path: record.Path(),
}

// TODO: the schema for mock files gets all the schemas from the test files combined
// while we could know in which test files mocks are used, we want to keep them open
// to all the test files as far as completions go

mergedSchema, err := sm.SchemaForMock(meta)
if err != nil {
return nil, err
}

functions, err := functionsForTest(record, version, stateReader)
if err != nil {
return nil, err
}

pathCtx := &decoder.PathContext{
Schema: mergedSchema,
ReferenceOrigins: make(reference.Origins, 0),
ReferenceTargets: make(reference.Targets, 0),
Files: make(map[string]*hcl.File, 0),
Validators: validators,
// TODO? functions TFECO-7480
Functions: functions,
}

for _, origin := range record.RefOrigins {
if ast.IsMockFilename(origin.OriginRange().Filename) {
pathCtx.ReferenceOrigins = append(pathCtx.ReferenceOrigins, origin)
for _, origins := range record.RefOrigins {
for _, origin := range origins {
if ast.IsMockFilename(origin.OriginRange().Filename) {
pathCtx.ReferenceOrigins = append(pathCtx.ReferenceOrigins, origin)
}
}
}
for _, target := range record.RefTargets {
if target.RangePtr != nil && ast.IsMockFilename(target.RangePtr.Filename) {
pathCtx.ReferenceTargets = append(pathCtx.ReferenceTargets, target)
for _, targets := range record.RefTargets {
for _, target := range targets {
if target.RangePtr != nil && ast.IsMockFilename(target.RangePtr.Filename) {
pathCtx.ReferenceTargets = append(pathCtx.ReferenceTargets, target)
}
}
}

Expand Down Expand Up @@ -207,6 +227,7 @@ func (pr *PathReader) Paths(ctx context.Context) []lang.Path {
paths = append(paths, lang.Path{
Path: record.Path(),
LanguageID: ilsp.Test.String(),
// TODO: add filename!
})
}
if foundMock {
Expand All @@ -219,3 +240,12 @@ func (pr *PathReader) Paths(ctx context.Context) []lang.Path {

return paths
}

func mustFunctionsForVersion(v *version.Version) map[string]schema.FunctionSignature {
s, err := tfschema.FunctionsForVersion(v)
if err != nil {
// this should never happen
panic(err)
}
return s
}
53 changes: 53 additions & 0 deletions internal/features/tests/decoder/test_functions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package decoder

import (
"github.com/hashicorp/go-version"
"github.com/hashicorp/hcl-lang/schema"
"github.com/hashicorp/terraform-ls/internal/features/tests/state"
tfaddr "github.com/hashicorp/terraform-registry-address"
tfmod "github.com/hashicorp/terraform-schema/module"
tfschema "github.com/hashicorp/terraform-schema/schema"
)

func functionsForTest(record *state.TestRecord, version *version.Version, stateReader CombinedReader) (map[string]schema.FunctionSignature, error) {
fm := tfschema.NewFunctionsMerger(mustFunctionsForVersion(version))
fm.SetTerraformVersion(version)
fm.SetStateReader(stateReader)

// providers used := stateReader.ModuleReader.LocalModuleMeta(record.Path()) ...
// get module meta for "root" module under test (possibly in parent dir)
// get module meta for all modules used in run blocks in record (Or the keyed on for the current test file!)

// maybe persist the "module under test"-path in state? makes it just a single lookup then
// needs to happen in a job that depends on module discovery done (so we know whether there really isn't code in the same dir e.g.)

// FIXME: PathContext assumes all hcl files in a dir are merged, but that's not the case for TF test
// something probably needs a change in hcl lang so it cleanly supports hcl files that are NOT merged

// TODO: re-enable once we have a way to get provider requirements
// We have to create the provider requirements and references based on the types the functions merger expects
providerRequirements := make(tfmod.ProviderRequirements) //, len(record.Meta.ProviderRequirements))
providerReferences := make(map[tfmod.ProviderRef]tfaddr.Provider) //, len(record.Meta.ProviderRequirements))
// for localName, req := range record.Meta.ProviderRequirements {
// providerRequirements[req.Source] = req.VersionConstraints
// providerReferences[tfmod.ProviderRef{LocalName: localName}] = req.Source
// }

// TODO: steps
// 1. find the related terraform module as that's defining which providers are available for functions
// (needs to be in terraform sources, i think; todo: check if implicit provider refs also work)

// given a test record (which points to a test directory),
//

mMeta := &tfmod.Meta{
Path: record.Path(),
ProviderRequirements: providerRequirements,
ProviderReferences: providerReferences,
}

return fm.FunctionsForModule(mMeta)
}
11 changes: 10 additions & 1 deletion internal/features/tests/jobs/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ package jobs
import (
"context"

"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/terraform-ls/internal/document"
"github.com/hashicorp/terraform-ls/internal/features/tests/state"
"github.com/hashicorp/terraform-ls/internal/job"
"github.com/hashicorp/terraform-ls/internal/terraform/module/operation"
earlydecoder "github.com/hashicorp/terraform-schema/earlydecoder/tests"
"github.com/hashicorp/terraform-schema/test"
)

// LoadTestMetadata loads data about the test in a version-independent
Expand All @@ -34,8 +36,15 @@ func LoadTestMetadata(ctx context.Context, testStore *state.TestStore, testPath
return err
}

var diags hcl.Diagnostics
meta := map[string]*test.Meta{}
for filename, file := range record.ParsedFiles.AsMap() {
fMeta, fDiags := earlydecoder.LoadTest(record.Path(), filename, file)
diags = append(diags, fDiags...)
meta[filename] = fMeta
}

var mErr error
meta, diags := earlydecoder.LoadTest(record.Path(), record.ParsedFiles.AsMap())
if len(diags) > 0 {
mErr = diags
}
Expand Down
Loading
Loading