Path: blob/main/pkg/flow/internal/controller/value_cache_test.go
4095 views
package controller12import (3"testing"45"github.com/stretchr/testify/require"6)78type fooArgs struct {9Something bool `river:"something,attr"`10}11type fooExports struct {12SomethingElse bool `river:"something_else,attr"`13}1415type barArgs struct {16Number int `river:"number,attr"`17}1819func TestValueCache(t *testing.T) {20vc := newValueCache()2122// Emulate values from the following River file:23//24// foo {25// something = true26//27// // Exported fields:28// // something_else = true29// }30//31// bar "label_a" {32// number = 1233// }34//35// bar "label_b" {36// number = 3437// }38//39// and expects to generate the equivalent to the following River object:40//41// {42// foo = {43// something_else = true,44// },45//46// bar = {47// label_a = {},48// label_b = {},49// }50// }51//52// For now, only exports are placed in generated objects, which is why the53// bar values are empty and the foo object only contains the exports.5455vc.CacheArguments(ComponentID{"foo"}, fooArgs{Something: true})56vc.CacheExports(ComponentID{"foo"}, fooExports{SomethingElse: true})57vc.CacheArguments(ComponentID{"bar", "label_a"}, barArgs{Number: 12})58vc.CacheArguments(ComponentID{"bar", "label_b"}, barArgs{Number: 34})5960res := vc.BuildContext()6162var (63expectKeys = []string{"foo", "bar"}64actualKeys []string65)66for varName := range res.Variables {67actualKeys = append(actualKeys, varName)68}69require.ElementsMatch(t, expectKeys, actualKeys)7071type object = map[string]interface{}7273expectFoo := fooExports{SomethingElse: true}74expectBar := object{75"label_a": object{}, // no exports for bar76"label_b": object{}, // no exports for bar77}78require.Equal(t, expectFoo, res.Variables["foo"])79require.Equal(t, expectBar, res.Variables["bar"])80}8182func TestExportValueCache(t *testing.T) {83vc := newValueCache()84vc.CacheModuleExportValue("t1", 1)85index := 086require.True(t, vc.ExportChangeIndex() != index)87index = vc.ExportChangeIndex()88require.False(t, vc.ExportChangeIndex() != index)8990vc.CacheModuleExportValue("t1", 2)91require.True(t, vc.ExportChangeIndex() != index)92index = vc.ExportChangeIndex()93require.False(t, vc.ExportChangeIndex() != index)9495vc.CacheModuleExportValue("t1", 2)96require.False(t, vc.ExportChangeIndex() != index)9798index = vc.ExportChangeIndex()99vc.CacheModuleExportValue("t2", "test")100require.True(t, vc.ExportChangeIndex() != index)101102index = vc.ExportChangeIndex()103vc.ClearModuleExports()104require.True(t, vc.ExportChangeIndex() != index)105}106107func TestModuleArgumentCache(t *testing.T) {108tt := []struct {109name string110argValue any111}{112{113name: "Nil",114argValue: nil,115},116{117name: "Number",118argValue: 1,119},120{121name: "String",122argValue: "string",123},124{125name: "Bool",126argValue: true,127},128{129name: "Map",130argValue: map[string]any{"test": "map"},131},132{133name: "Capsule",134argValue: fooExports{SomethingElse: true},135},136}137138for _, tc := range tt {139t.Run(tc.name, func(t *testing.T) {140// Create and cache the argument141vc := newValueCache()142vc.CacheModuleArgument("arg", tc.argValue)143144// Build the scope and validate it145res := vc.BuildContext()146expected := map[string]any{"arg": map[string]any{"value": tc.argValue}}147require.Equal(t, expected, res.Variables["argument"])148149// Sync arguments where the arg shouldn't change150syncArgs := map[string]any{"arg": tc.argValue}151vc.SyncModuleArgs(syncArgs)152res = vc.BuildContext()153require.Equal(t, expected, res.Variables["argument"])154155// Sync arguments where the arg should clear out156syncArgs = map[string]any{}157vc.SyncModuleArgs(syncArgs)158res = vc.BuildContext()159require.Equal(t, map[string]any{}, res.Variables)160})161}162}163164165