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

     1  package blocks
     2  
     3  import (
     4  	"sort"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/require"
     8  	yamlv3 "gopkg.in/yaml.v3"
     9  
    10  	"github.com/authzed/spicedb/pkg/tuple"
    11  )
    12  
    13  func TestValidationString(t *testing.T) {
    14  	type testCase struct {
    15  		name               string
    16  		input              string
    17  		expectedSubject    string
    18  		isSubjectCaveated  bool
    19  		expectedONRs       []string
    20  		expectedExceptions []string
    21  	}
    22  
    23  	tests := []testCase{
    24  		{
    25  			"empty",
    26  			"",
    27  			"",
    28  			false,
    29  			[]string{},
    30  			nil,
    31  		},
    32  		{
    33  			"basic",
    34  			"[tenant/user:someuser#...] is <tenant/document:example#viewer>",
    35  			"tenant/user:someuser",
    36  			false,
    37  			[]string{"tenant/document:example#viewer"},
    38  			nil,
    39  		},
    40  		{
    41  			"missing onrs",
    42  			"[tenant/user:someuser#...]",
    43  			"tenant/user:someuser",
    44  			false,
    45  			[]string{},
    46  			nil,
    47  		},
    48  		{
    49  			"missing subject",
    50  			"is <tenant/document:example#viewer>",
    51  			"",
    52  			false,
    53  			[]string{"tenant/document:example#viewer"},
    54  			nil,
    55  		},
    56  		{
    57  			"multiple onrs",
    58  			"[tenant/user:someuser#...] is <tenant/document:example#viewer>/<tenant/document:example#builder>",
    59  			"tenant/user:someuser",
    60  			false,
    61  			[]string{"tenant/document:example#viewer", "tenant/document:example#builder"},
    62  			nil,
    63  		},
    64  		{
    65  			"ellided ellipsis",
    66  			"[tenant/user:someuser] is <tenant/document:example#viewer>/<tenant/document:example#builder>",
    67  			"tenant/user:someuser",
    68  			false,
    69  			[]string{"tenant/document:example#viewer", "tenant/document:example#builder"},
    70  			nil,
    71  		},
    72  		{
    73  			"bad subject",
    74  			"[tenant/user:someuser#... is <tenant/document:example#viewer>/<tenant/document:example#builder>",
    75  			"",
    76  			false,
    77  			[]string{"tenant/document:example#viewer", "tenant/document:example#builder"},
    78  			nil,
    79  		},
    80  		{
    81  			"bad parse",
    82  			"[tenant/user:someuser:asdsad] is <tenant/document:example#viewer>/<tenant/document:example#builder>",
    83  			"",
    84  			false,
    85  			[]string{"tenant/document:example#viewer", "tenant/document:example#builder"},
    86  			nil,
    87  		},
    88  		{
    89  			"subject with exclusions",
    90  			"[tenant/user:someuser#... - {test/user:1,test/user:2}] is <tenant/document:example#viewer>/<tenant/document:example#builder>",
    91  			"tenant/user:someuser",
    92  			false,
    93  			[]string{"tenant/document:example#viewer", "tenant/document:example#builder"},
    94  			[]string{"test/user:1", "test/user:2"},
    95  		},
    96  		{
    97  			"subject with bad exclusions",
    98  			"[tenant/user:someuser#... - {te1,test/user:2}] is <tenant/document:example#viewer>/<tenant/document:example#builder>",
    99  			"",
   100  			false,
   101  			[]string{"tenant/document:example#viewer", "tenant/document:example#builder"},
   102  			nil,
   103  		},
   104  		{
   105  			"caveated subject with exclusions",
   106  			"[tenant/user:someuser[...] - {test/user:1[...],test/user:2}] is <tenant/document:example#viewer>/<tenant/document:example#builder>",
   107  			"tenant/user:someuser",
   108  			true,
   109  			[]string{"tenant/document:example#viewer", "tenant/document:example#builder"},
   110  			[]string{"test/user:1[...]", "test/user:2"},
   111  		},
   112  	}
   113  
   114  	for _, tc := range tests {
   115  		tc := tc
   116  		t.Run(tc.name, func(t *testing.T) {
   117  			require := require.New(t)
   118  			vs := ValidationString(tc.input)
   119  
   120  			subject, err := vs.Subject()
   121  
   122  			if tc.expectedSubject == "" {
   123  				require.Nil(subject)
   124  			} else {
   125  				require.Nil(err)
   126  				require.Equal(tc.expectedSubject, tuple.StringONR(subject.Subject.Subject))
   127  				require.Equal(tc.isSubjectCaveated, subject.Subject.IsCaveated)
   128  				require.Equal(len(tc.expectedExceptions), len(subject.Exceptions))
   129  
   130  				if len(tc.expectedExceptions) > 0 {
   131  					sort.Strings(tc.expectedExceptions)
   132  
   133  					exceptionStrings := make([]string, 0, len(subject.Exceptions))
   134  					for _, exception := range subject.Exceptions {
   135  						exceptionString := tuple.StringONR(exception.Subject)
   136  						if exception.IsCaveated {
   137  							exceptionString += "[...]"
   138  						}
   139  						exceptionStrings = append(exceptionStrings, exceptionString)
   140  					}
   141  
   142  					require.Equal(tc.expectedExceptions, exceptionStrings)
   143  				}
   144  			}
   145  
   146  			foundONRStrings := []string{}
   147  			onrs, _ := vs.ONRS()
   148  			for _, onr := range onrs {
   149  				foundONRStrings = append(foundONRStrings, tuple.StringONR(onr))
   150  			}
   151  
   152  			require.Equal(tc.expectedONRs, foundONRStrings)
   153  		})
   154  	}
   155  }
   156  
   157  func TestParseExpectedRelations(t *testing.T) {
   158  	type testCase struct {
   159  		name          string
   160  		contents      string
   161  		expectedError string
   162  		expectedKeys  int
   163  	}
   164  
   165  	tests := []testCase{
   166  		{
   167  			"empty",
   168  			"",
   169  			"",
   170  			0,
   171  		},
   172  		{
   173  			"not a map",
   174  			`- hi
   175  - hello `,
   176  			`cannot unmarshal !!seq into blocks.ValidationMap`,
   177  			0,
   178  		},
   179  		{
   180  			"valid",
   181  			`document:firstdoc#view:
   182  - "[user:tom] is <document:firstdoc#writer>"
   183  - "[user:fred] is <document:firstdoc#reader>/<document:firstdoc#writer>"
   184  document:seconddoc#view:
   185  - "[user:tom] is <document:seconddoc#reader>"`,
   186  			"",
   187  			2,
   188  		},
   189  		{
   190  			"invalid key",
   191  			`document:firstdocview:
   192  - "[user:tom] is <document:firstdoc#writer>"`,
   193  			"could not parse document:firstdocview",
   194  			0,
   195  		},
   196  		{
   197  			"invalid subject",
   198  			`document:firstdoc#view:
   199  - "[usertom] is <document:firstdoc#writer>"`,
   200  			"invalid subject: `usertom`",
   201  			0,
   202  		},
   203  		{
   204  			"invalid caveated subject",
   205  			`document:firstdoc#view:
   206  - "[user:tom[df]] is <document:firstdoc#writer>"`,
   207  			"invalid subject: `user:tom[df]`",
   208  			0,
   209  		},
   210  		{
   211  			"invalid exception subject",
   212  			`document:firstdoc#view:
   213  - "[user:*-{}] is <document:firstdoc#writer>"`,
   214  			"invalid subject: `user:*-{}`",
   215  			0,
   216  		},
   217  		{
   218  			"invalid resource",
   219  			`document:firstdoc#view:
   220  - "[user:tom] is <document:firstdocwriter>"`,
   221  			"invalid resource and relation: `document:firstdocwriter`",
   222  			0,
   223  		},
   224  		{
   225  			"invalid multi-resource",
   226  			`document:firstdoc#view:
   227  - "[user:tom] is <document:firstdoc#writer/document:firstdoc#reader>"`,
   228  			"invalid resource and relation: `document:firstdoc#writer/document:firstdoc#reader`",
   229  			0,
   230  		},
   231  		{
   232  			"valid with caveats and exceptions",
   233  			`document:firstdoc#view:
   234  - "[user:tom[...]] is <document:firstdoc#writer>"
   235  - "[user:fred - {user:a, user:b}] is <document:firstdoc#reader>/<document:firstdoc#writer>"
   236  document:seconddoc#view:
   237  - "[user:*[...] - {user:a, user:b}] is <document:seconddoc#reader>"`,
   238  			"",
   239  			2,
   240  		},
   241  		{
   242  			"valid with caveats and caveated exceptions",
   243  			`document:firstdoc#view:
   244  - "[user:tom[...]] is <document:firstdoc#writer>"
   245  - "[user:fred - {user:a[...], user:b}] is <document:firstdoc#reader>/<document:firstdoc#writer>"
   246  document:seconddoc#view:
   247  - "[user:*[...] - {user:a, user:b[...]}] is <document:seconddoc#reader>"`,
   248  			"",
   249  			2,
   250  		},
   251  	}
   252  
   253  	for _, tc := range tests {
   254  		tc := tc
   255  		t.Run(tc.name, func(t *testing.T) {
   256  			require := require.New(t)
   257  			per := ParsedExpectedRelations{}
   258  			err := yamlv3.Unmarshal([]byte(tc.contents), &per)
   259  			if tc.expectedError != "" {
   260  				require.Contains(err.Error(), tc.expectedError)
   261  			} else {
   262  				require.NoError(err)
   263  				require.Equal(tc.expectedKeys, len(per.ValidationMap))
   264  			}
   265  		})
   266  	}
   267  }