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 }