github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/internal/namespace/util_test.go (about)

     1  package namespace_test
     2  
     3  import (
     4  	"context"
     5  	"sort"
     6  	"testing"
     7  
     8  	"github.com/stretchr/testify/require"
     9  
    10  	"github.com/authzed/spicedb/internal/datastore/memdb"
    11  	"github.com/authzed/spicedb/internal/namespace"
    12  	"github.com/authzed/spicedb/internal/testfixtures"
    13  	ns "github.com/authzed/spicedb/pkg/namespace"
    14  	core "github.com/authzed/spicedb/pkg/proto/core/v1"
    15  )
    16  
    17  func TestListReferencedNamespaces(t *testing.T) {
    18  	testCases := []struct {
    19  		name          string
    20  		toCheck       []*core.NamespaceDefinition
    21  		expectedNames []string
    22  	}{
    23  		{
    24  			"basic namespace",
    25  			[]*core.NamespaceDefinition{
    26  				ns.Namespace(
    27  					"document",
    28  					ns.MustRelation("owner", nil),
    29  				),
    30  			},
    31  			[]string{"document"},
    32  		},
    33  		{
    34  			"basic namespaces",
    35  			[]*core.NamespaceDefinition{
    36  				ns.Namespace(
    37  					"document",
    38  					ns.MustRelation("viewer", nil, ns.AllowedRelation("user", "...")),
    39  				),
    40  				ns.Namespace("user"),
    41  			},
    42  			[]string{"document", "user"},
    43  		},
    44  		{
    45  			"basic namespaces with references",
    46  			[]*core.NamespaceDefinition{
    47  				ns.Namespace(
    48  					"document",
    49  					ns.MustRelation("viewer", nil,
    50  						ns.AllowedRelation("group", "member"),
    51  						ns.AllowedRelation("group", "manager"),
    52  						ns.AllowedRelation("team", "member")),
    53  				),
    54  				ns.Namespace("user"),
    55  			},
    56  			[]string{"document", "user", "group", "team"},
    57  		},
    58  	}
    59  
    60  	for _, tc := range testCases {
    61  		tc := tc
    62  		t.Run(tc.name, func(t *testing.T) {
    63  			require := require.New(t)
    64  
    65  			found := namespace.ListReferencedNamespaces(tc.toCheck)
    66  			sort.Strings(found)
    67  			sort.Strings(tc.expectedNames)
    68  
    69  			require.Equal(tc.expectedNames, found)
    70  		})
    71  	}
    72  }
    73  
    74  func TestCheckNamespaceAndRelations(t *testing.T) {
    75  	tcs := []struct {
    76  		name          string
    77  		schema        string
    78  		checks        []namespace.TypeAndRelationToCheck
    79  		expectedError string
    80  	}{
    81  		{
    82  			"missing namespace",
    83  			`definition user {}`,
    84  			[]namespace.TypeAndRelationToCheck{
    85  				{
    86  					NamespaceName: "user",
    87  					RelationName:  "...",
    88  					AllowEllipsis: true,
    89  				},
    90  				{
    91  					NamespaceName: "resource",
    92  					RelationName:  "viewer",
    93  					AllowEllipsis: false,
    94  				},
    95  			},
    96  			"object definition `resource` not found",
    97  		},
    98  		{
    99  			"missing relation",
   100  			`definition resource {}`,
   101  			[]namespace.TypeAndRelationToCheck{
   102  				{
   103  					NamespaceName: "resource",
   104  					RelationName:  "viewer",
   105  					AllowEllipsis: false,
   106  				},
   107  			},
   108  			"relation/permission `viewer` not found",
   109  		},
   110  		{
   111  			"valid",
   112  			`definition user {}
   113  			
   114  			definition resource {
   115  				relation viewer: user
   116  			}
   117  			`,
   118  			[]namespace.TypeAndRelationToCheck{
   119  				{
   120  					NamespaceName: "user",
   121  					RelationName:  "...",
   122  					AllowEllipsis: true,
   123  				},
   124  				{
   125  					NamespaceName: "resource",
   126  					RelationName:  "viewer",
   127  					AllowEllipsis: false,
   128  				},
   129  			},
   130  			"",
   131  		},
   132  		{
   133  			"ellipsis not allowed",
   134  			`definition user {}`,
   135  			[]namespace.TypeAndRelationToCheck{
   136  				{
   137  					NamespaceName: "user",
   138  					RelationName:  "...",
   139  					AllowEllipsis: false,
   140  				},
   141  			},
   142  			"relation/permission `...` not found",
   143  		},
   144  		{
   145  			"another missing namespace",
   146  			`definition user {}`,
   147  			[]namespace.TypeAndRelationToCheck{
   148  				{
   149  					NamespaceName: "resource",
   150  					RelationName:  "viewer",
   151  					AllowEllipsis: false,
   152  				},
   153  				{
   154  					NamespaceName: "user",
   155  					RelationName:  "...",
   156  					AllowEllipsis: true,
   157  				},
   158  			},
   159  			"object definition `resource` not found",
   160  		},
   161  	}
   162  	for _, tc := range tcs {
   163  		t.Run(tc.name, func(t *testing.T) {
   164  			req := require.New(t)
   165  			rawDS, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC)
   166  			req.NoError(err)
   167  
   168  			ds, _ := testfixtures.DatastoreFromSchemaAndTestRelationships(rawDS, tc.schema, nil, req)
   169  
   170  			rev, err := ds.HeadRevision(context.Background())
   171  			require.NoError(t, err)
   172  
   173  			reader := ds.SnapshotReader(rev)
   174  
   175  			err = namespace.CheckNamespaceAndRelations(context.Background(), tc.checks, reader)
   176  			if tc.expectedError == "" {
   177  				require.Nil(t, err)
   178  			} else {
   179  				require.ErrorContains(t, err, tc.expectedError)
   180  			}
   181  		})
   182  	}
   183  }