github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/pkg/validationfile/loader_test.go (about)

     1  package validationfile
     2  
     3  import (
     4  	"context"
     5  	"sort"
     6  	"testing"
     7  
     8  	"github.com/authzed/spicedb/internal/datastore/memdb"
     9  	"github.com/authzed/spicedb/internal/datastore/proxy/proxy_test"
    10  	"github.com/authzed/spicedb/pkg/datastore"
    11  	"github.com/authzed/spicedb/pkg/datastore/options"
    12  	"github.com/authzed/spicedb/pkg/tuple"
    13  
    14  	"github.com/stretchr/testify/require"
    15  )
    16  
    17  func TestPopulateFromFiles(t *testing.T) {
    18  	tests := []struct {
    19  		name          string
    20  		filePaths     []string
    21  		want          []string
    22  		expectedError string
    23  	}{
    24  		{
    25  			name:      "no comment",
    26  			filePaths: []string{"testdata/loader_no_comment.yaml"},
    27  			want: []string{
    28  				"example/project:pied_piper#owner@example/user:milburga",
    29  				"example/project:pied_piper#reader@example/user:tarben",
    30  				"example/project:pied_piper#writer@example/user:freyja",
    31  			},
    32  			expectedError: "",
    33  		},
    34  		{
    35  			name:      "with comment",
    36  			filePaths: []string{"testdata/loader_with_comment.yaml"},
    37  			want: []string{
    38  				"example/project:pied_piper#owner@example/user:milburga",
    39  				"example/project:pied_piper#reader@example/user:tarben",
    40  				"example/project:pied_piper#writer@example/user:freyja",
    41  			},
    42  			expectedError: "",
    43  		},
    44  		{
    45  			name:      "multiple files",
    46  			filePaths: []string{"testdata/initial_schema_and_rels.yaml", "testdata/just_rels.yaml"},
    47  			want: []string{
    48  				"example/project:pied_piper#owner@example/user:milburga",
    49  				"example/project:pied_piper#reader@example/user:tarben",
    50  				"example/project:pied_piper#writer@example/user:freyja",
    51  				"example/project:pied_piper#owner@example/user:fred",
    52  				"example/project:pied_piper#reader@example/user:tom",
    53  				"example/project:pied_piper#writer@example/user:sarah",
    54  			},
    55  			expectedError: "",
    56  		},
    57  		{
    58  			name:      "multiple files",
    59  			filePaths: []string{"testdata/initial_schema_and_rels.yaml", "testdata/just_rels.yaml"},
    60  			want: []string{
    61  				"example/project:pied_piper#owner@example/user:milburga",
    62  				"example/project:pied_piper#reader@example/user:tarben",
    63  				"example/project:pied_piper#writer@example/user:freyja",
    64  				"example/project:pied_piper#owner@example/user:fred",
    65  				"example/project:pied_piper#reader@example/user:tom",
    66  				"example/project:pied_piper#writer@example/user:sarah",
    67  			},
    68  			expectedError: "",
    69  		},
    70  		{
    71  			name:          "missing schema",
    72  			filePaths:     []string{"testdata/just_rels.yaml"},
    73  			want:          nil,
    74  			expectedError: "object definition `example/project` not found",
    75  		},
    76  		{
    77  			name:          "legacy file",
    78  			filePaths:     []string{"testdata/legacy.yaml"},
    79  			want:          nil,
    80  			expectedError: "relationships must be specified in `relationships`",
    81  		},
    82  		{
    83  			name:      "basic caveats",
    84  			filePaths: []string{"testdata/basic_caveats.yaml"},
    85  			want: []string{
    86  				"resource:first#reader@user:sarah[some_caveat:{\"somecondition\":42}]",
    87  				"resource:first#reader@user:tom[some_caveat]",
    88  			},
    89  			expectedError: "",
    90  		},
    91  		{
    92  			name:          "invalid caveat",
    93  			filePaths:     []string{"testdata/invalid_caveat.yaml"},
    94  			want:          nil,
    95  			expectedError: "could not lookup caveat `some_caveat` for relation `reader`: caveat with name `some_caveat` not found",
    96  		},
    97  		{
    98  			name:          "invalid caveated relationship",
    99  			filePaths:     []string{"testdata/invalid_caveated_rel.yaml"},
   100  			want:          nil,
   101  			expectedError: "subjects of type `user with some_caveat` are not allowed on relation `resource#reader`",
   102  		},
   103  		{
   104  			name:          "invalid caveated relationship syntax",
   105  			filePaths:     []string{"testdata/invalid_caveated_rel_syntax.yaml"},
   106  			want:          nil,
   107  			expectedError: "error parsing relationship",
   108  		},
   109  		{
   110  			name:          "repeated relationship",
   111  			filePaths:     []string{"testdata/repeated_relationship.yaml"},
   112  			want:          nil,
   113  			expectedError: "found repeated relationship `resource:first#reader@user:tom`",
   114  		},
   115  	}
   116  
   117  	for _, tt := range tests {
   118  		tt := tt
   119  		t.Run(tt.name, func(t *testing.T) {
   120  			require := require.New(t)
   121  			ds, err := memdb.NewMemdbDatastore(0, 0, 0)
   122  			require.NoError(err)
   123  
   124  			parsed, _, err := PopulateFromFiles(context.Background(), ds, tt.filePaths)
   125  			if tt.expectedError == "" {
   126  				require.NoError(err)
   127  
   128  				foundRelationships := make([]string, 0, len(parsed.Tuples))
   129  				for _, tpl := range parsed.Tuples {
   130  					foundRelationships = append(foundRelationships, tuple.MustString(tpl))
   131  				}
   132  
   133  				sort.Strings(tt.want)
   134  				sort.Strings(foundRelationships)
   135  				require.Equal(tt.want, foundRelationships)
   136  			} else {
   137  				require.NotNil(err)
   138  				require.Contains(err.Error(), tt.expectedError)
   139  			}
   140  		})
   141  	}
   142  }
   143  
   144  func TestPopulationChunking(t *testing.T) {
   145  	require := require.New(t)
   146  
   147  	ds, err := memdb.NewMemdbDatastore(0, 0, 0)
   148  	require.NoError(err)
   149  
   150  	cs := txCountingDatastore{delegate: ds}
   151  	_, _, err = PopulateFromFiles(context.Background(), &cs, []string{"testdata/requires_chunking.yaml"})
   152  	require.NoError(err)
   153  	require.Equal(3, cs.count)
   154  }
   155  
   156  type txCountingDatastore struct {
   157  	proxy_test.MockDatastore
   158  	count    int
   159  	delegate datastore.Datastore
   160  }
   161  
   162  func (c *txCountingDatastore) ReadWriteTx(ctx context.Context, userFunc datastore.TxUserFunc, option ...options.RWTOptionsOption) (datastore.Revision, error) {
   163  	c.count++
   164  	return c.delegate.ReadWriteTx(ctx, userFunc, option...)
   165  }