// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package integrationtest

import (
	"testing"

	"github.com/hashicorp/hcl/v2"
	"github.com/hashicorp/hcl/v2/ext/typeexpr"
	"github.com/hashicorp/hcl/v2/hclsyntax"
	"github.com/zclconf/go-cty/cty"
	"github.com/zclconf/go-cty/cty/function"
)

// TestTypeConvertFunc is an integration test of all of the layers involved
// in making the type conversion function from ext/typeexpr work.
//
// This requires co-operation between the hclsyntax package, the ext/typeexpr
// package, and the underlying cty functionality in order to work correctly.
//
// There are unit tests for the function implementation itself in the
// ext/typeexpr package, so this test is focused on making sure the function
// is given the opportunity to decode the second argument as a type expression
// when the function is called from HCL native syntax.
func TestTypeConvertFunc(t *testing.T) {
	// The convert function is special because it takes a type expression
	// rather than a value expression as its second argument. In this case,
	// we're asking it to convert a tuple into a list of strings:
	const exprSrc = `convert(["hello"], list(string))`
	// It achieves this by marking that second argument as being of a custom
	// type (a "capsule type", in cty terminology) that has a special
	// annotation which hclsyntax.FunctionCallExpr understands as allowing
	// the type to handle the analysis of the unevaluated expression, instead
	// of evaluating it as normal.
	//
	// To see more details of how this works, look at the definitions of
	// typexpr.TypeConstraintType and typeexpr.ConvertFunc, and at the
	// implementation of hclsyntax.FunctionCallExpr.Value.

	expr, diags := hclsyntax.ParseExpression([]byte(exprSrc), "", hcl.Pos{Line: 1, Column: 1})
	if diags.HasErrors() {
		t.Fatalf("unexpected problems: %s", diags.Error())
	}

	ctx := &hcl.EvalContext{
		Functions: map[string]function.Function{
			"convert": typeexpr.ConvertFunc,
		},
	}
	got, diags := expr.Value(ctx)
	if diags.HasErrors() {
		t.Fatalf("unexpected problems: %s", diags.Error())
	}
	want := cty.ListVal([]cty.Value{cty.StringVal("hello")})
	if !want.RawEquals(got) {
		t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, want)
	}
}
