diff --git a/pkg/storage/unified/resource/storage_backend.go b/pkg/storage/unified/resource/storage_backend.go index 24bb98c1f8b..2f11da20f48 100644 --- a/pkg/storage/unified/resource/storage_backend.go +++ b/pkg/storage/unified/resource/storage_backend.go @@ -737,14 +737,7 @@ func (k *kvStorageBackend) listModifiedSinceEventStore(ctx context.Context, key } seen[evtKey.Name] = struct{}{} - value, err := k.getValueFromDataStore(ctx, DataKey{ - Group: evtKey.Group, - Resource: evtKey.Resource, - Namespace: evtKey.Namespace, - Name: evtKey.Name, - ResourceVersion: evtKey.ResourceVersion, - Action: evtKey.Action, - }) + value, err := k.getValueFromDataStore(ctx, DataKey(evtKey)) if err != nil { yield(&ModifiedResource{}, err) return diff --git a/pkg/storage/unified/resource/storage_backend_test.go b/pkg/storage/unified/resource/storage_backend_test.go index 0a1c263d216..8b1143db6d1 100644 --- a/pkg/storage/unified/resource/storage_backend_test.go +++ b/pkg/storage/unified/resource/storage_backend_test.go @@ -785,6 +785,111 @@ func seedBackend(t *testing.T, backend *kvStorageBackend, ctx context.Context, n return expectations } +func TestKvStorageBackend_ListModifiedSince_WithFolder(t *testing.T) { + backend := setupTestStorageBackend(t) + ctx := context.Background() + + // Use a unique resource type to avoid conflicts with other tests + ns := NamespacedResource{ + Namespace: "test-folder-ns", + Group: "test.folder.app", + Resource: "test-resources", + } + + // Create test objects with folder field set + testObj1, err := createTestObjectWithName("dashboard-1", ns, "data-1") + require.NoError(t, err) + metaAccessor1, err := utils.MetaAccessor(testObj1) + require.NoError(t, err) + metaAccessor1.SetFolder("folder-abc") + + testObj2, err := createTestObjectWithName("dashboard-2", ns, "data-2") + require.NoError(t, err) + metaAccessor2, err := utils.MetaAccessor(testObj2) + require.NoError(t, err) + metaAccessor2.SetFolder("folder-xyz") + + // Write first dashboard + writeEvent1 := WriteEvent{ + Type: resourcepb.WatchEvent_ADDED, + Key: &resourcepb.ResourceKey{ + Namespace: ns.Namespace, + Group: ns.Group, + Resource: ns.Resource, + Name: "dashboard-1", + }, + Value: objectToJSONBytes(t, testObj1), + Object: metaAccessor1, + PreviousRV: 0, + } + rv1, err := backend.WriteEvent(ctx, writeEvent1) + require.NoError(t, err) + + // Write second dashboard + writeEvent2 := WriteEvent{ + Type: resourcepb.WatchEvent_ADDED, + Key: &resourcepb.ResourceKey{ + Namespace: ns.Namespace, + Group: ns.Group, + Resource: ns.Resource, + Name: "dashboard-2", + }, + Value: objectToJSONBytes(t, testObj2), + Object: metaAccessor2, + PreviousRV: 0, + } + rv2, err := backend.WriteEvent(ctx, writeEvent2) + require.NoError(t, err) + + tests := []struct { + name string + sinceRV func() int64 + codePath string + }{ + { + name: "via event store (recent RV < 1 hour)", + sinceRV: func() int64 { return rv1 - 1 }, + codePath: "listModifiedSinceEventStore", + }, + { + name: "via data store (old RV > 1 hour)", + sinceRV: func() int64 { return generateOldSnowflake(t) }, + codePath: "listModifiedSinceDataStore", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + sinceRV := tt.sinceRV() + + // List resources + rv, seq := backend.ListModifiedSince(ctx, ns, sinceRV) + + changes := make(map[string]*ModifiedResource) + for mr, err := range seq { + require.NoError(t, err, "Should not get error when Folder field is included") + require.Equal(t, ns.Group, mr.Key.Group) + require.Equal(t, ns.Namespace, mr.Key.Namespace) + require.Equal(t, ns.Resource, mr.Key.Resource) + changes[mr.Key.Name] = mr + } + + require.Greater(t, rv, sinceRV) + require.Len(t, changes, 2, "Should return 2 resources") + + // Verify dashboard-1 + require.Contains(t, changes, "dashboard-1") + require.Equal(t, rv1, changes["dashboard-1"].ResourceVersion) + require.Equal(t, objectToJSONBytes(t, testObj1), changes["dashboard-1"].Value) + + // Verify dashboard-2 + require.Contains(t, changes, "dashboard-2") + require.Equal(t, rv2, changes["dashboard-2"].ResourceVersion) + require.Equal(t, objectToJSONBytes(t, testObj2), changes["dashboard-2"].Value) + }) + } +} + func createAndSaveTestObject(t *testing.T, backend *kvStorageBackend, ctx context.Context, ns NamespacedResource, uniqueStringGen func() string, updates int, deleted bool) *ModifiedResource { name := uniqueStringGen() action := resourcepb.WatchEvent_ADDED