Path: blob/main/pkg/river/internal/value/value_test.go
4096 views
package value_test12import (3"fmt"4"io"5"testing"67"github.com/grafana/agent/pkg/river/internal/value"8"github.com/stretchr/testify/require"9)1011// TestEncodeKeyLookup tests where Go values are retained correctly12// throughout values with a key lookup.13func TestEncodeKeyLookup(t *testing.T) {14type Body struct {15Data pointerMarshaler `river:"data,attr"`16}1718tt := []struct {19name string20encodeTarget any21key string2223expectBodyType value.Type24expectKeyExists bool25expectKeyValue value.Value26expectKeyType value.Type27}{28{29name: "Struct Encode data Key",30encodeTarget: &Body{},31key: "data",32expectBodyType: value.TypeObject,33expectKeyExists: true,34expectKeyValue: value.String("Hello, world!"),35expectKeyType: value.TypeString,36},37{38name: "Struct Encode Missing Key",39encodeTarget: &Body{},40key: "missing",41expectBodyType: value.TypeObject,42expectKeyExists: false,43expectKeyValue: value.Null,44expectKeyType: value.TypeNull,45},46{47name: "Map Encode data Key",48encodeTarget: map[string]string{"data": "Hello, world!"},49key: "data",50expectBodyType: value.TypeObject,51expectKeyExists: true,52expectKeyValue: value.String("Hello, world!"),53expectKeyType: value.TypeString,54},55{56name: "Map Encode Missing Key",57encodeTarget: map[string]string{"data": "Hello, world!"},58key: "missing",59expectBodyType: value.TypeObject,60expectKeyExists: false,61expectKeyValue: value.Null,62expectKeyType: value.TypeNull,63},64{65name: "Map Encode empty value Key",66encodeTarget: map[string]string{"data": ""},67key: "data",68expectBodyType: value.TypeObject,69expectKeyExists: true,70expectKeyValue: value.String(""),71expectKeyType: value.TypeString,72},73}7475for _, tc := range tt {76t.Run(tc.name, func(t *testing.T) {77bodyVal := value.Encode(tc.encodeTarget)78require.Equal(t, tc.expectBodyType, bodyVal.Type())7980val, ok := bodyVal.Key(tc.key)81require.Equal(t, tc.expectKeyExists, ok)82require.Equal(t, tc.expectKeyType, val.Type())83switch val.Type() {84case value.TypeString:85require.Equal(t, tc.expectKeyValue.Text(), val.Text())86case value.TypeNull:87require.Equal(t, tc.expectKeyValue, val)88default:89require.Fail(t, "unexpected value type (this switch can be expanded)")90}91})92}93}9495// TestEncodeNoKeyLookup tests where Go values are retained correctly96// throughout values without a key lookup.97func TestEncodeNoKeyLookup(t *testing.T) {98tt := []struct {99name string100encodeTarget any101key string102103expectBodyType value.Type104expectBodyText string105}{106{107name: "Encode",108encodeTarget: &pointerMarshaler{},109expectBodyType: value.TypeString,110expectBodyText: "Hello, world!",111},112}113114for _, tc := range tt {115t.Run(tc.name, func(t *testing.T) {116bodyVal := value.Encode(tc.encodeTarget)117require.Equal(t, tc.expectBodyType, bodyVal.Type())118require.Equal(t, "Hello, world!", bodyVal.Text())119})120}121}122123type pointerMarshaler struct{}124125func (*pointerMarshaler) MarshalText() ([]byte, error) {126return []byte("Hello, world!"), nil127}128129func TestValue_Call(t *testing.T) {130t.Run("simple", func(t *testing.T) {131add := func(a, b int) int { return a + b }132addVal := value.Encode(add)133134res, err := addVal.Call(135value.Int(15),136value.Int(43),137)138require.NoError(t, err)139require.Equal(t, int64(15+43), res.Int())140})141142t.Run("fully variadic", func(t *testing.T) {143add := func(nums ...int) int {144var sum int145for _, num := range nums {146sum += num147}148return sum149}150addVal := value.Encode(add)151152t.Run("no args", func(t *testing.T) {153res, err := addVal.Call()154require.NoError(t, err)155require.Equal(t, int64(0), res.Int())156})157158t.Run("one arg", func(t *testing.T) {159res, err := addVal.Call(value.Int(32))160require.NoError(t, err)161require.Equal(t, int64(32), res.Int())162})163164t.Run("many args", func(t *testing.T) {165res, err := addVal.Call(166value.Int(32),167value.Int(59),168value.Int(12),169)170require.NoError(t, err)171require.Equal(t, int64(32+59+12), res.Int())172})173})174175t.Run("partially variadic", func(t *testing.T) {176add := func(firstNum int, nums ...int) int {177sum := firstNum178for _, num := range nums {179sum += num180}181return sum182}183addVal := value.Encode(add)184185t.Run("no variadic args", func(t *testing.T) {186res, err := addVal.Call(value.Int(52))187require.NoError(t, err)188require.Equal(t, int64(52), res.Int())189})190191t.Run("one variadic arg", func(t *testing.T) {192res, err := addVal.Call(value.Int(52), value.Int(32))193require.NoError(t, err)194require.Equal(t, int64(52+32), res.Int())195})196197t.Run("many variadic args", func(t *testing.T) {198res, err := addVal.Call(199value.Int(32),200value.Int(59),201value.Int(12),202)203require.NoError(t, err)204require.Equal(t, int64(32+59+12), res.Int())205})206})207208t.Run("returns error", func(t *testing.T) {209failWhenTrue := func(val bool) (int, error) {210if val {211return 0, fmt.Errorf("function failed for a very good reason")212}213return 0, nil214}215funcVal := value.Encode(failWhenTrue)216217t.Run("no error", func(t *testing.T) {218res, err := funcVal.Call(value.Bool(false))219require.NoError(t, err)220require.Equal(t, int64(0), res.Int())221})222223t.Run("error", func(t *testing.T) {224_, err := funcVal.Call(value.Bool(true))225require.EqualError(t, err, "function failed for a very good reason")226})227})228}229230func TestValue_Interface_In_Array(t *testing.T) {231type Container struct {232Field io.Closer `river:"field,attr"`233}234235val := value.Encode(Container{Field: io.NopCloser(nil)})236fieldVal, ok := val.Key("field")237require.True(t, ok, "field not found in object")238require.Equal(t, value.TypeCapsule, fieldVal.Type())239240arrVal := value.Array(fieldVal)241require.Equal(t, value.TypeCapsule, arrVal.Index(0).Type())242}243244245