github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/internal/graph/lookupsubjects_reducers_test.go (about) 1 package graph 2 3 import ( 4 "context" 5 "sort" 6 "testing" 7 8 "github.com/stretchr/testify/require" 9 10 "github.com/authzed/spicedb/internal/dispatch" 11 v1 "github.com/authzed/spicedb/pkg/proto/dispatch/v1" 12 ) 13 14 func TestLookupSubjectsUnion(t *testing.T) { 15 ctx := context.Background() 16 17 cds := dispatch.NewCollectingDispatchStream[*v1.DispatchLookupSubjectsResponse](ctx) 18 ci, err := newCursorInformation(nil, newLimitTracker(0), 1) 19 require.NoError(t, err) 20 21 reducer := newLookupSubjectsUnion(cds, ci) 22 23 first := reducer.ForIndex(ctx, 0) 24 second := reducer.ForIndex(ctx, 1) 25 third := reducer.ForIndex(ctx, 2) 26 27 err = first.Publish(&v1.DispatchLookupSubjectsResponse{ 28 Metadata: emptyMetadata, 29 FoundSubjectsByResourceId: map[string]*v1.FoundSubjects{ 30 "resource1": { 31 FoundSubjects: []*v1.FoundSubject{ 32 {SubjectId: "subject1"}, 33 {SubjectId: "subject2"}, 34 }, 35 }, 36 "resource2": { 37 FoundSubjects: []*v1.FoundSubject{ 38 {SubjectId: "subject42"}, 39 }, 40 }, 41 }, 42 }) 43 require.NoError(t, err) 44 45 err = second.Publish(&v1.DispatchLookupSubjectsResponse{ 46 Metadata: emptyMetadata, 47 FoundSubjectsByResourceId: map[string]*v1.FoundSubjects{ 48 "resource2": { 49 FoundSubjects: []*v1.FoundSubject{ 50 {SubjectId: "subject1"}, 51 {SubjectId: "subject3"}, 52 }, 53 }, 54 }, 55 }) 56 require.NoError(t, err) 57 58 err = third.Publish(&v1.DispatchLookupSubjectsResponse{ 59 Metadata: emptyMetadata, 60 FoundSubjectsByResourceId: map[string]*v1.FoundSubjects{ 61 "resource3": { 62 FoundSubjects: []*v1.FoundSubject{ 63 {SubjectId: "subject2"}, 64 {SubjectId: "subject3"}, 65 }, 66 }, 67 "resource4": { 68 FoundSubjects: []*v1.FoundSubject{ 69 {SubjectId: "subject4"}, 70 {SubjectId: "subject1"}, 71 }, 72 }, 73 }, 74 }) 75 require.NoError(t, err) 76 77 err = reducer.CompletedChildOperations() 78 require.NoError(t, err) 79 80 resp := cds.Results() 81 require.Len(t, resp, 1) 82 83 result := resp[0] 84 85 for _, foundSubjects := range result.FoundSubjectsByResourceId { 86 sort.Slice(foundSubjects.FoundSubjects, func(i, j int) bool { 87 return foundSubjects.FoundSubjects[i].SubjectId < foundSubjects.FoundSubjects[j].SubjectId 88 }) 89 } 90 91 require.Equal(t, map[string]*v1.FoundSubjects{ 92 "resource1": { 93 FoundSubjects: []*v1.FoundSubject{ 94 {SubjectId: "subject1"}, 95 {SubjectId: "subject2"}, 96 }, 97 }, 98 "resource2": { 99 FoundSubjects: []*v1.FoundSubject{ 100 {SubjectId: "subject1"}, 101 {SubjectId: "subject3"}, 102 {SubjectId: "subject42"}, 103 }, 104 }, 105 "resource3": { 106 FoundSubjects: []*v1.FoundSubject{ 107 {SubjectId: "subject2"}, 108 {SubjectId: "subject3"}, 109 }, 110 }, 111 "resource4": { 112 FoundSubjects: []*v1.FoundSubject{ 113 {SubjectId: "subject1"}, 114 {SubjectId: "subject4"}, 115 }, 116 }, 117 }, result.FoundSubjectsByResourceId) 118 } 119 120 func TestLookupSubjectsIntersection(t *testing.T) { 121 ctx := context.Background() 122 123 cds := dispatch.NewCollectingDispatchStream[*v1.DispatchLookupSubjectsResponse](ctx) 124 ci, err := newCursorInformation(nil, newLimitTracker(0), 1) 125 require.NoError(t, err) 126 127 reducer := newLookupSubjectsIntersection(cds, ci) 128 129 first := reducer.ForIndex(ctx, 0) 130 second := reducer.ForIndex(ctx, 1) 131 132 err = reducer.RunUntilSpanned(ctx, 0, func(ctx context.Context, current branchRunInformation) error { 133 err = first.Publish(&v1.DispatchLookupSubjectsResponse{ 134 Metadata: emptyMetadata, 135 FoundSubjectsByResourceId: map[string]*v1.FoundSubjects{ 136 "resource1": { 137 FoundSubjects: []*v1.FoundSubject{ 138 {SubjectId: "subject1"}, 139 {SubjectId: "subject2"}, 140 }, 141 }, 142 "resource2": { 143 FoundSubjects: []*v1.FoundSubject{ 144 {SubjectId: "subject42"}, 145 }, 146 }, 147 "resource3": { 148 FoundSubjects: []*v1.FoundSubject{ 149 {SubjectId: "subject1"}, 150 {SubjectId: "subject5"}, 151 }, 152 }, 153 "resource4": { 154 FoundSubjects: []*v1.FoundSubject{ 155 {SubjectId: "subject48"}, 156 }, 157 }, 158 }, 159 AfterResponseCursor: &v1.Cursor{ 160 DispatchVersion: 1, 161 Sections: []string{"subject5"}, 162 }, 163 }) 164 require.NoError(t, err) 165 return nil 166 }) 167 require.NoError(t, err) 168 169 err = reducer.RunUntilSpanned(ctx, 1, func(ctx context.Context, current branchRunInformation) error { 170 err = second.Publish(&v1.DispatchLookupSubjectsResponse{ 171 Metadata: emptyMetadata, 172 FoundSubjectsByResourceId: map[string]*v1.FoundSubjects{ 173 "resource1": { 174 FoundSubjects: []*v1.FoundSubject{ 175 {SubjectId: "subject1"}, 176 {SubjectId: "subject3"}, 177 }, 178 }, 179 "resource2": { 180 FoundSubjects: []*v1.FoundSubject{ 181 {SubjectId: "subject42"}, 182 {SubjectId: "subject43"}, 183 }, 184 }, 185 "resource4": { 186 FoundSubjects: []*v1.FoundSubject{ 187 {SubjectId: "subject1"}, 188 {SubjectId: "subject48"}, 189 {SubjectId: "subject5"}, 190 }, 191 }, 192 }, 193 AfterResponseCursor: &v1.Cursor{ 194 DispatchVersion: 1, 195 Sections: []string{"subject52"}, 196 }, 197 }) 198 require.NoError(t, err) 199 return nil 200 }) 201 require.NoError(t, err) 202 203 firstBranchCount, err := reducer.CompletedDependentChildOperations() 204 require.NoError(t, err) 205 require.Equal(t, 6, firstBranchCount) 206 207 resp := cds.Results() 208 require.Len(t, resp, 1) 209 210 result := resp[0] 211 212 for _, foundSubjects := range result.FoundSubjectsByResourceId { 213 sort.Slice(foundSubjects.FoundSubjects, func(i, j int) bool { 214 return foundSubjects.FoundSubjects[i].SubjectId < foundSubjects.FoundSubjects[j].SubjectId 215 }) 216 } 217 218 require.Equal(t, map[string]*v1.FoundSubjects{ 219 "resource1": { 220 FoundSubjects: []*v1.FoundSubject{ 221 {SubjectId: "subject1"}, 222 }, 223 }, 224 "resource2": { 225 FoundSubjects: []*v1.FoundSubject{ 226 {SubjectId: "subject42"}, 227 }, 228 }, 229 "resource4": { 230 FoundSubjects: []*v1.FoundSubject{ 231 {SubjectId: "subject48"}, 232 }, 233 }, 234 }, result.FoundSubjectsByResourceId) 235 } 236 237 func TestLookupSubjectsExclusion(t *testing.T) { 238 ctx := context.Background() 239 240 cds := dispatch.NewCollectingDispatchStream[*v1.DispatchLookupSubjectsResponse](ctx) 241 ci, err := newCursorInformation(nil, newLimitTracker(0), 1) 242 require.NoError(t, err) 243 244 reducer := newLookupSubjectsExclusion(cds, ci) 245 246 first := reducer.ForIndex(ctx, 0) 247 second := reducer.ForIndex(ctx, 1) 248 249 err = reducer.RunUntilSpanned(ctx, 0, func(ctx context.Context, current branchRunInformation) error { 250 err = first.Publish(&v1.DispatchLookupSubjectsResponse{ 251 Metadata: emptyMetadata, 252 FoundSubjectsByResourceId: map[string]*v1.FoundSubjects{ 253 "resource1": { 254 FoundSubjects: []*v1.FoundSubject{ 255 {SubjectId: "subject1"}, 256 {SubjectId: "subject2"}, 257 }, 258 }, 259 "resource2": { 260 FoundSubjects: []*v1.FoundSubject{ 261 {SubjectId: "subject42"}, 262 }, 263 }, 264 "resource3": { 265 FoundSubjects: []*v1.FoundSubject{ 266 {SubjectId: "subject1"}, 267 {SubjectId: "subject5"}, 268 }, 269 }, 270 "resource4": { 271 FoundSubjects: []*v1.FoundSubject{ 272 {SubjectId: "subject48"}, 273 }, 274 }, 275 }, 276 AfterResponseCursor: &v1.Cursor{ 277 DispatchVersion: 1, 278 Sections: []string{"subject5"}, 279 }, 280 }) 281 require.NoError(t, err) 282 return nil 283 }) 284 require.NoError(t, err) 285 286 err = reducer.RunUntilSpanned(ctx, 1, func(ctx context.Context, current branchRunInformation) error { 287 err = second.Publish(&v1.DispatchLookupSubjectsResponse{ 288 Metadata: emptyMetadata, 289 FoundSubjectsByResourceId: map[string]*v1.FoundSubjects{ 290 "resource1": { 291 FoundSubjects: []*v1.FoundSubject{ 292 {SubjectId: "subject1"}, 293 {SubjectId: "subject3"}, 294 }, 295 }, 296 "resource2": { 297 FoundSubjects: []*v1.FoundSubject{ 298 {SubjectId: "subject42"}, 299 {SubjectId: "subject43"}, 300 }, 301 }, 302 "resource4": { 303 FoundSubjects: []*v1.FoundSubject{ 304 {SubjectId: "subject1"}, 305 {SubjectId: "subject48"}, 306 {SubjectId: "subject5"}, 307 }, 308 }, 309 }, 310 AfterResponseCursor: &v1.Cursor{ 311 DispatchVersion: 1, 312 Sections: []string{"subject52"}, 313 }, 314 }) 315 require.NoError(t, err) 316 return nil 317 }) 318 require.NoError(t, err) 319 320 firstBranchCount, err := reducer.CompletedDependentChildOperations() 321 require.NoError(t, err) 322 require.Equal(t, 6, firstBranchCount) 323 324 resp := cds.Results() 325 require.Len(t, resp, 1) 326 327 result := resp[0] 328 329 for _, foundSubjects := range result.FoundSubjectsByResourceId { 330 sort.Slice(foundSubjects.FoundSubjects, func(i, j int) bool { 331 return foundSubjects.FoundSubjects[i].SubjectId < foundSubjects.FoundSubjects[j].SubjectId 332 }) 333 } 334 335 require.Equal(t, map[string]*v1.FoundSubjects{ 336 "resource1": { 337 FoundSubjects: []*v1.FoundSubject{ 338 {SubjectId: "subject2"}, 339 }, 340 }, 341 "resource3": { 342 FoundSubjects: []*v1.FoundSubject{ 343 {SubjectId: "subject1"}, 344 {SubjectId: "subject5"}, 345 }, 346 }, 347 }, result.FoundSubjectsByResourceId) 348 }