Skip to content

Commit

Permalink
Add hooks tests
Browse files Browse the repository at this point in the history
  • Loading branch information
dbanck committed Jan 8, 2024
1 parent 141874b commit 25d912b
Showing 1 changed file with 251 additions and 0 deletions.
251 changes: 251 additions & 0 deletions decoder/expression_candidates_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package decoder

import (
"context"
"fmt"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/hashicorp/hcl-lang/lang"
"github.com/hashicorp/hcl-lang/schema"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"
"github.com/zclconf/go-cty/cty"
)

func TestLegacyDecoder_CandidateAtPos_expressions_Hooks(t *testing.T) {
ctx := context.Background()
testCases := []struct {
testName string
attrSchema map[string]*schema.AttributeSchema
cfg string
pos hcl.Pos
completionHooks CompletionFuncMap
expectedCandidates lang.Candidates
}{
{
"simple hook",
map[string]*schema.AttributeSchema{
"attr": {
Constraint: schema.LiteralType{Type: cty.String},
CompletionHooks: lang.CompletionHooks{
{
Name: "TestCompletionHook",
},
},
},
},
`attr =
`,
hcl.Pos{Line: 1, Column: 8, Byte: 7},
CompletionFuncMap{
"TestCompletionHook": func(ctx context.Context, value cty.Value) ([]Candidate, error) {
candidates := []Candidate{
{
Label: "\"label\"",
Detail: "detail",
Kind: lang.StringCandidateKind,
Description: lang.PlainText("description"),
RawInsertText: "\"insertText\"",
},
}
return candidates, nil
},
},
lang.IncompleteCandidates([]lang.Candidate{
{
Label: "\"label\"",
Detail: "detail",
Kind: lang.StringCandidateKind,
Description: lang.PlainText("description"),
TextEdit: lang.TextEdit{
NewText: "\"insertText\"",
Snippet: "\"insertText\"",
Range: hcl.Range{
Filename: "test.tf",
Start: hcl.Pos{Line: 1, Column: 8, Byte: 7},
End: hcl.Pos{Line: 1, Column: 8, Byte: 7},
},
},
},
}),
},
{
"hook with prefix",
map[string]*schema.AttributeSchema{
"attr": {
Constraint: schema.LiteralType{Type: cty.String},
CompletionHooks: lang.CompletionHooks{
{
Name: "TestCompletionHook",
},
},
},
},
`attr = "pa"
`,
hcl.Pos{Line: 1, Column: 11, Byte: 10},
CompletionFuncMap{
"TestCompletionHook": func(ctx context.Context, value cty.Value) ([]Candidate, error) {
candidates := []Candidate{
{
Label: value.AsString(),
Kind: lang.StringCandidateKind,
RawInsertText: value.AsString(),
},
}
return candidates, nil
},
},
lang.IncompleteCandidates([]lang.Candidate{
{
Label: "pa",
Kind: lang.StringCandidateKind,
TextEdit: lang.TextEdit{
NewText: "pa",
Snippet: "pa",
Range: hcl.Range{
Filename: "test.tf",
Start: hcl.Pos{Line: 1, Column: 8, Byte: 7},
End: hcl.Pos{Line: 1, Column: 12, Byte: 11},
},
},
},
}),
},
{
"incomplete attr value",
map[string]*schema.AttributeSchema{
"attr": {
Constraint: schema.LiteralType{Type: cty.String},
CompletionHooks: lang.CompletionHooks{
{
Name: "TestCompletionHook",
},
},
},
},
`attr = "pa
`,
hcl.Pos{Line: 1, Column: 11, Byte: 10},
CompletionFuncMap{
"TestCompletionHook": func(ctx context.Context, value cty.Value) ([]Candidate, error) {
candidates := []Candidate{
{
Label: value.AsString(),
Kind: lang.StringCandidateKind,
RawInsertText: value.AsString(),
},
}
return candidates, nil
},
},
lang.IncompleteCandidates([]lang.Candidate{
{
Label: "pa",
Kind: lang.StringCandidateKind,
TextEdit: lang.TextEdit{
NewText: "pa",
Snippet: "pa",
Range: hcl.Range{
Filename: "test.tf",
Start: hcl.Pos{Line: 1, Column: 8, Byte: 7},
End: hcl.Pos{Line: 1, Column: 11, Byte: 10},
},
},
},
}),
},
}

for i, tc := range testCases {
t.Run(fmt.Sprintf("%d-%s", i, tc.testName), func(t *testing.T) {
bodySchema := &schema.BodySchema{
Attributes: tc.attrSchema,
}

// We're ignoring diagnostics here, since some test cases may contain invalid HCL
f, _ := hclsyntax.ParseConfig([]byte(tc.cfg), "test.tf", hcl.InitialPos)
d := testPathDecoder(t, &PathContext{
Schema: bodySchema,
Files: map[string]*hcl.File{
"test.tf": f,
},
})
for n, h := range tc.completionHooks {
d.decoderCtx.CompletionHooks[n] = h
}

candidates, err := d.CompletionAtPos(ctx, "test.tf", tc.pos)
if err != nil {
t.Fatal(err)
}

if diff := cmp.Diff(tc.expectedCandidates, candidates); diff != "" {
t.Fatalf("unexpected candidates: %s", diff)
}
})
}
}

func TestLegacyDecoder_CandidateAtPos_maxCandidates(t *testing.T) {
ctx := context.Background()
bodySchema := &schema.BodySchema{
Attributes: map[string]*schema.AttributeSchema{
"attr": {
Constraint: schema.LiteralType{Type: cty.String},
CompletionHooks: lang.CompletionHooks{
{
Name: "TestCompletionHook50",
},
{
Name: "TestCompletionHook70",
},
},
},
},
}

// We're ignoring diagnostics here, since our config contains invalid HCL
f, _ := hclsyntax.ParseConfig([]byte(`attr = `), "test.tf", hcl.InitialPos)
d := testPathDecoder(t, &PathContext{
Schema: bodySchema,
Files: map[string]*hcl.File{
"test.tf": f,
},
})
d.decoderCtx.CompletionHooks["TestCompletionHook50"] = func(ctx context.Context, value cty.Value) ([]Candidate, error) {
candidates := make([]Candidate, 0)
for i := 0; i < 50; i++ {
candidates = append(candidates, Candidate{
Label: fmt.Sprintf("\"Label %d\"", i),
Kind: lang.StringCandidateKind,
})
}
return candidates, nil
}
d.decoderCtx.CompletionHooks["TestCompletionHook70"] = func(ctx context.Context, value cty.Value) ([]Candidate, error) {
candidates := make([]Candidate, 0)
for i := 0; i < 70; i++ {
candidates = append(candidates, Candidate{
Label: fmt.Sprintf("\"Label %d\"", i),
Kind: lang.StringCandidateKind,
})
}
return candidates, nil
}

candidates, err := d.CompletionAtPos(ctx, "test.tf", hcl.Pos{Line: 1, Column: 8, Byte: 7})
if err != nil {
t.Fatal(err)
}
count := len(candidates.List)

if uint(count) != d.maxCandidates {
t.Fatalf("unexpected candidates count: %d", count)
}
}

0 comments on commit 25d912b

Please sign in to comment.