// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

// #skiplint: import_testing_only_in_tests
package precompilebind

// tmplSourcePrecompileConfigGo is the Go precompiled config source template.
const tmplSourcePrecompileContractTestGo = `
// Code generated
// This file is a generated precompile contract test with the skeleton of test functions.
// The file is generated by a template. Please inspect every code and comment in this file before use.

package {{.Package}}

import (
	"testing"
	"math/big"

	{{- if .Contract.AllowList}}
	"github.com/ava-labs/subnet-evm/precompile/allowlist/allowlisttest"
	{{- end}}
	"github.com/ava-labs/subnet-evm/precompile/precompiletest"
	"github.com/ava-labs/libevm/common"
	"github.com/ava-labs/libevm/core/vm"
	"github.com/stretchr/testify/require"
)

var (
	_ = vm.ErrOutOfGas
	_ = big.NewInt
	_ = common.Big0
	_ = require.New
)

// These tests are run against the precompile contract directly with
// the given input and expected output. They're just a guide to
// help you write your own tests. These tests are for general cases like
// allowlist, readOnly behaviour, and gas cost. You should write your own
// tests for specific cases.
var(
	tests = map[string]precompiletest.PrecompileTest{
		{{- $contract := .Contract}}
		{{- $structs := .Structs}}
		{{- range .Contract.Funcs}}
		{{- $func := .}}
		{{- if $contract.AllowList}}
		{{- $roles := mkList "NoRole" "Enabled" "Manager" "Admin"}}
		{{- range $role := $roles}}
		{{- $fail := and (not $func.Original.IsConstant) (eq $role "NoRole")}}
		"calling {{decapitalise $func.Normalized.Name}} from {{$role}} should {{- if $fail}} fail {{- else}} succeed{{- end}}":  {
			Caller:     allowlisttest.Test{{$role}}Addr,
			BeforeHook: allowlisttest.SetDefaultRoles(Module.Address),
			InputFn: func(t testing.TB) []byte {
				{{- if len $func.Normalized.Inputs | lt 1}}
				// CUSTOM CODE STARTS HERE
				// populate test input here
				testInput := {{capitalise $func.Normalized.Name}}Input{}
				input, err := Pack{{$func.Normalized.Name}}(testInput)
				{{- else if len $func.Normalized.Inputs | eq 1 }}
				{{- $input := index $func.Normalized.Inputs 0}}
				// CUSTOM CODE STARTS HERE
				// set test input to a value here
				var testInput {{bindtype $input.Type $structs}}
				testInput = {{bindtypenew $input.Type $structs}}
				input, err := Pack{{$func.Normalized.Name}}(testInput)
				{{- else}}
				input, err := Pack{{$func.Normalized.Name}}()
				{{- end}}
				require.NoError(t, err)
				return input
			},
			{{- if not $fail}}
			// This test is for a successful call. You can set the expected output here.
			// CUSTOM CODE STARTS HERE
			ExpectedRes: func() []byte{
				{{- if len $func.Normalized.Outputs | eq 0}}
				// this function does not return an output, leave this one as is
				packedOutput := []byte{}
				{{- else}}
				{{- if len $func.Normalized.Outputs | lt 1}}
				var output {{capitalise $func.Normalized.Name}}Output // CUSTOM CODE FOR AN OUTPUT
				{{- else }}
				{{$output := index $func.Normalized.Outputs 0}}
				var output {{bindtype $output.Type $structs}} // CUSTOM CODE FOR AN OUTPUT
				output = {{bindtypenew $output.Type $structs}} // CUSTOM CODE FOR AN OUTPUT
				{{- end}}
				packedOutput, err := Pack{{$func.Normalized.Name}}Output(output)
				if err != nil {
					panic(err)
				}
				{{- end}}
				return packedOutput
			}(),
			{{- end}}
			SuppliedGas: {{$func.Normalized.Name}}GasCost,
			ReadOnly:    false,
			ExpectedErr: {{if $fail}} ErrCannot{{$func.Normalized.Name}}.Error() {{- else}} "" {{- end}},
		},
		{{- end}}
		{{- end}}
		{{- if not $func.Original.IsConstant}}
		"readOnly {{decapitalise $func.Normalized.Name}} should fail": {
			Caller:	common.Address{1},
			InputFn: func(t testing.TB) []byte {
				{{- if len $func.Normalized.Inputs | lt 1}}
				// CUSTOM CODE STARTS HERE
				// populate test input here
				testInput := {{capitalise $func.Normalized.Name}}Input{}
				input, err := Pack{{$func.Normalized.Name}}(testInput)
				{{- else if len $func.Normalized.Inputs | eq 1 }}
				{{- $input := index $func.Normalized.Inputs 0}}
				// CUSTOM CODE STARTS HERE
				// set test input to a value here
				var testInput {{bindtype $input.Type $structs}}
				testInput = {{bindtypenew $input.Type $structs}}
				input, err := Pack{{$func.Normalized.Name}}(testInput)
				{{- else}}
				input, err := Pack{{$func.Normalized.Name}}()
				{{- end}}
				require.NoError(t, err)
				return input
			},
			SuppliedGas:  {{$func.Normalized.Name}}GasCost,
			ReadOnly:    true,
			ExpectedErr: vm.ErrWriteProtection.Error(),
		},
		{{- end}}
		"insufficient gas for {{decapitalise $func.Normalized.Name}} should fail": {
			Caller:	common.Address{1},
			InputFn: func(t testing.TB) []byte {
				{{- if len $func.Normalized.Inputs | lt 1}}
				// CUSTOM CODE STARTS HERE
				// populate test input here
				testInput := {{capitalise $func.Normalized.Name}}Input{}
				input, err := Pack{{$func.Normalized.Name}}(testInput)
				{{- else if len $func.Normalized.Inputs | eq 1 }}
				{{- $input := index $func.Normalized.Inputs 0}}
				// CUSTOM CODE STARTS HERE
				// set test input to a value here
				var testInput {{bindtype $input.Type $structs}}
				testInput = {{bindtypenew $input.Type $structs}}
				input, err := Pack{{$func.Normalized.Name}}(testInput)
				{{- else}}
				input, err := Pack{{$func.Normalized.Name}}()
				{{- end}}
				require.NoError(t, err)
				return input
			},
			SuppliedGas: {{$func.Normalized.Name}}GasCost - 1,
			ReadOnly:    false,
			ExpectedErr: vm.ErrOutOfGas.Error(),
		},
		{{- end}}
		{{- if .Contract.Fallback}}
		"insufficient gas for fallback should fail": {
			Caller:	common.Address{1},
			Input: []byte{},
			SuppliedGas: {{.Contract.Type}}FallbackGasCost - 1,
			ReadOnly:    false,
			ExpectedErr: vm.ErrOutOfGas.Error(),
		},
		"readOnly fallback should fail": {
			Caller:	common.Address{1},
			Input: []byte{},
			SuppliedGas: {{.Contract.Type}}FallbackGasCost,
			ReadOnly:    true,
			ExpectedErr: vm.ErrWriteProtection.Error(),
		},
		"fallback should succeed": {
			Caller:	common.Address{1},
			Input: []byte{},
			SuppliedGas: {{.Contract.Type}}FallbackGasCost,
			ReadOnly:    false,
			ExpectedErr: "",
			// CUSTOM CODE STARTS HERE
			// set expected output here
			ExpectedRes: []byte{},
		},
		{{- end}}
	}
)

// Test{{.Contract.Type}}Run tests the Run function of the precompile contract.
func Test{{.Contract.Type}}Run(t *testing.T) {
	{{- if .Contract.AllowList}}
	// Run tests with allowlist tests.
	// This adds allowlist tests to your custom tests
	// and runs them all together.
	// Even if you don't add any custom tests, keep this. This will still
	// run the default allowlist tests.
			allowlisttest.RunPrecompileWithAllowListTests(t, Module, tests)
	{{- else}}
	// Run tests.
	for name, test := range tests {
		t.Run(name, func(t *testing.T) {
			test.Run(t, Module)
		})
	}
	{{- end}}
}

{{range .Contract.Events}}
{{$hasData := false}}
{{range .Normalized.Inputs}}
{{ if not .Indexed}}
{{ $hasData = true}}
{{end}}
{{end}}
{{ if $hasData}}
// TestPackUnpack{{.Normalized.Name}}EventData tests the Pack/Unpack{{.Normalized.Name}}EventData.
func TestPackUnpack{{.Normalized.Name}}EventData(t *testing.T) {
	// CUSTOM CODE STARTS HERE
	// set test inputs with proper values here
	{{- range .Normalized.Inputs}}
		{{if .Indexed}}var {{decapitalise .Name}}Input {{bindtype .Type $structs}} = {{bindtypenew .Type $structs}}{{end}}
	{{- end}}
	{{if $hasData}} dataInput := {{.Normalized.Name}}EventData{{end}}{
		{{- range .Normalized.Inputs}}
			{{- if not .Indexed}}
				{{capitalise .Name}}: {{bindtypenew .Type $structs}},
			{{- end}}
		{{- end}}
	}

	_, data, err := Pack{{.Normalized.Name}}Event(
		{{- range .Normalized.Inputs}}
			{{- if .Indexed}}
				{{decapitalise .Name}}Input,
			{{- end}}
		{{- end}}
		{{- if $hasData}}
			dataInput,
		{{- end}}
	)
	require.NoError(t, err)

	unpacked, err := Unpack{{.Normalized.Name}}EventData(data)
	require.NoError(t, err)
	require.Equal(t, dataInput, unpacked)
}
{{end}}
{{end}}
`
