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  }