github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/pkg/schemadsl/generator/generator_test.go (about) 1 package generator 2 3 import ( 4 "strings" 5 "testing" 6 7 "github.com/stretchr/testify/require" 8 9 core "github.com/authzed/spicedb/pkg/proto/core/v1" 10 11 "github.com/authzed/spicedb/pkg/caveats" 12 caveattypes "github.com/authzed/spicedb/pkg/caveats/types" 13 "github.com/authzed/spicedb/pkg/namespace" 14 "github.com/authzed/spicedb/pkg/schemadsl/compiler" 15 "github.com/authzed/spicedb/pkg/schemadsl/input" 16 ) 17 18 func TestGenerateCaveat(t *testing.T) { 19 type generatorTest struct { 20 name string 21 input *core.CaveatDefinition 22 expected string 23 okay bool 24 } 25 26 tests := []generatorTest{ 27 { 28 "basic", 29 namespace.MustCaveatDefinition(caveats.MustEnvForVariables( 30 map[string]caveattypes.VariableType{ 31 "someParam": caveattypes.IntType, 32 }, 33 ), "somecaveat", "someParam == 42"), 34 ` 35 caveat somecaveat(someParam int) { 36 someParam == 42 37 }`, 38 true, 39 }, 40 { 41 "multiparameter", 42 namespace.MustCaveatDefinition(caveats.MustEnvForVariables( 43 map[string]caveattypes.VariableType{ 44 "someParam": caveattypes.IntType, 45 "anotherParam": caveattypes.MustMapType(caveattypes.UIntType), 46 }, 47 ), "somecaveat", "someParam == 42"), 48 ` 49 caveat somecaveat(anotherParam map<uint>, someParam int) { 50 someParam == 42 51 }`, 52 true, 53 }, 54 { 55 "long", 56 namespace.MustCaveatDefinition(caveats.MustEnvForVariables( 57 map[string]caveattypes.VariableType{ 58 "someParam": caveattypes.IntType, 59 }, 60 ), "somecaveat", "someParam == 42 && someParam == 43 && someParam == 44 && someParam == 45"), 61 ` 62 caveat somecaveat(someParam int) { 63 someParam == 42 && someParam == 43 && someParam == 44 && someParam == 45 64 }`, 65 true, 66 }, 67 } 68 69 for _, test := range tests { 70 test := test 71 t.Run(test.name, func(t *testing.T) { 72 require := require.New(t) 73 source, ok, err := GenerateCaveatSource(test.input) 74 require.NoError(err) 75 require.Equal(strings.TrimSpace(test.expected), source) 76 require.Equal(test.okay, ok) 77 }) 78 } 79 } 80 81 func TestGenerateNamespace(t *testing.T) { 82 type generatorTest struct { 83 name string 84 input *core.NamespaceDefinition 85 expected string 86 okay bool 87 } 88 89 tests := []generatorTest{ 90 { 91 "empty", 92 namespace.Namespace("foos/test"), 93 "definition foos/test {}", 94 true, 95 }, 96 { 97 "simple relation", 98 namespace.Namespace("foos/test", 99 namespace.MustRelation("somerel", nil, namespace.AllowedRelation("foos/bars", "hiya")), 100 ), 101 `definition foos/test { 102 relation somerel: foos/bars#hiya 103 }`, 104 true, 105 }, 106 { 107 "simple permission", 108 namespace.Namespace("foos/test", 109 namespace.MustRelation("someperm", namespace.Union( 110 namespace.ComputedUserset("anotherrel"), 111 )), 112 ), 113 `definition foos/test { 114 permission someperm = anotherrel 115 }`, 116 true, 117 }, 118 { 119 "complex permission", 120 namespace.Namespace("foos/test", 121 namespace.MustRelation("someperm", namespace.Union( 122 namespace.Rewrite( 123 namespace.Exclusion( 124 namespace.ComputedUserset("rela"), 125 namespace.ComputedUserset("relb"), 126 namespace.TupleToUserset("rely", "relz"), 127 ), 128 ), 129 namespace.ComputedUserset("relc"), 130 )), 131 ), 132 `definition foos/test { 133 permission someperm = (rela - relb - rely->relz) + relc 134 }`, 135 true, 136 }, 137 { 138 "complex permission with nil", 139 namespace.Namespace("foos/test", 140 namespace.MustRelation("someperm", namespace.Union( 141 namespace.Rewrite( 142 namespace.Exclusion( 143 namespace.ComputedUserset("rela"), 144 namespace.ComputedUserset("relb"), 145 namespace.TupleToUserset("rely", "relz"), 146 namespace.Nil(), 147 ), 148 ), 149 namespace.ComputedUserset("relc"), 150 )), 151 ), 152 `definition foos/test { 153 permission someperm = (rela - relb - rely->relz - nil) + relc 154 }`, 155 true, 156 }, 157 { 158 "legacy relation", 159 namespace.Namespace("foos/test", 160 namespace.MustRelation("somerel", namespace.Union( 161 &core.SetOperation_Child{ 162 ChildType: &core.SetOperation_Child_XThis{}, 163 }, 164 namespace.ComputedUserset("anotherrel"), 165 ), namespace.AllowedRelation("foos/bars", "hiya")), 166 ), 167 `definition foos/test { 168 relation somerel: foos/bars#hiya = /* _this unsupported here. Please rewrite into a relation and permission */ + anotherrel 169 }`, 170 false, 171 }, 172 { 173 "missing type information", 174 namespace.Namespace("foos/test", 175 namespace.MustRelation("somerel", nil), 176 ), 177 `definition foos/test { 178 relation somerel: /* missing allowed types */ 179 }`, 180 false, 181 }, 182 183 { 184 "full example", 185 namespace.WithComment("foos/document", `/** 186 * Some comment goes here 187 */`, 188 namespace.MustRelation("owner", nil, 189 namespace.AllowedRelation("foos/user", "..."), 190 ), 191 namespace.MustRelationWithComment("reader", "//foobar", nil, 192 namespace.AllowedRelation("foos/user", "..."), 193 namespace.AllowedPublicNamespace("foos/user"), 194 namespace.AllowedRelation("foos/group", "member"), 195 namespace.AllowedRelationWithCaveat("foos/user", "...", namespace.AllowedCaveat("somecaveat")), 196 namespace.AllowedRelationWithCaveat("foos/group", "member", namespace.AllowedCaveat("somecaveat")), 197 namespace.AllowedPublicNamespaceWithCaveat("foos/user", namespace.AllowedCaveat("somecaveat")), 198 ), 199 namespace.MustRelation("read", namespace.Union( 200 namespace.ComputedUserset("reader"), 201 namespace.ComputedUserset("owner"), 202 )), 203 ), 204 `/** Some comment goes here */ 205 definition foos/document { 206 relation owner: foos/user 207 208 // foobar 209 relation reader: foos/user | foos/user:* | foos/group#member | foos/user with somecaveat | foos/group#member with somecaveat | foos/user:* with somecaveat 210 permission read = reader + owner 211 }`, 212 true, 213 }, 214 } 215 216 for _, test := range tests { 217 test := test 218 t.Run(test.name, func(t *testing.T) { 219 require := require.New(t) 220 source, ok, err := GenerateSource(test.input) 221 require.NoError(err) 222 require.Equal(test.expected, source) 223 require.Equal(test.okay, ok) 224 }) 225 } 226 } 227 228 func TestFormatting(t *testing.T) { 229 type formattingTest struct { 230 name string 231 input string 232 expected string 233 } 234 235 tests := []formattingTest{ 236 { 237 "empty", 238 "definition foos/test {}", 239 "definition foos/test {}", 240 }, 241 { 242 "with comment", 243 `/** some def */definition foos/test {}`, 244 `/** some def */ 245 definition foos/test {}`, 246 }, 247 { 248 "with rel comment", 249 `/** some def */definition foos/test { 250 251 // some rel 252 relation somerel: foos/bars; 253 }`, 254 `/** some def */ 255 definition foos/test { 256 // some rel 257 relation somerel: foos/bars 258 }`, 259 }, 260 { 261 "with multiple rel comment", 262 `/** some def */definition foos/test { 263 264 // some rel 265 /* another comment */ 266 relation somerel: foos/bars; 267 }`, 268 `/** some def */ 269 definition foos/test { 270 // some rel 271 /* another comment */ 272 relation somerel: foos/bars 273 }`, 274 }, 275 { 276 "with multiple rels with comment", 277 `/** some def */definition foos/test { 278 279 // some rel 280 relation somerel: foos/bars; 281 // another perm 282 permission someperm = somerel 283 }`, 284 `/** some def */ 285 definition foos/test { 286 // some rel 287 relation somerel: foos/bars 288 289 // another perm 290 permission someperm = somerel 291 }`, 292 }, 293 294 { 295 "becomes single line comment", 296 `definition foos/test { 297 /** 298 * hi there 299 */ 300 relation somerel: foos/bars; 301 }`, 302 `definition foos/test { 303 /** hi there */ 304 relation somerel: foos/bars 305 }`, 306 }, 307 308 { 309 "full example", 310 ` 311 /** some cool caveat */ 312 caveat foos/somecaveat(someParam int, anotherParam bool) { 313 someParam == 42 && 314 anotherParam 315 } 316 317 /** the document */ 318 definition foos/document { 319 /** some super long comment goes here and therefore should be made into a multiline comment */ 320 relation reader: foos/user | foos/user:* | foos/user with foos/somecaveat 321 322 /** multiline 323 comment */ 324 relation writer: foos/user 325 326 // writers are also readers 327 permission read = reader + writer + another 328 permission write = writer 329 permission minus = rela - relb - relc 330 } 331 `, 332 `/** some cool caveat */ 333 caveat foos/somecaveat(anotherParam bool, someParam int) { 334 someParam == 42 && anotherParam 335 } 336 337 /** the document */ 338 definition foos/document { 339 /** 340 * some super long comment goes here and therefore should be made into a multiline comment 341 */ 342 relation reader: foos/user | foos/user:* | foos/user with foos/somecaveat 343 344 /** 345 * multiline 346 * comment 347 */ 348 relation writer: foos/user 349 350 // writers are also readers 351 permission read = reader + writer + another 352 permission write = writer 353 permission minus = (rela - relb) - relc 354 }`, 355 }, 356 } 357 358 for _, test := range tests { 359 test := test 360 t.Run(test.name, func(t *testing.T) { 361 require := require.New(t) 362 compiled, err := compiler.Compile(compiler.InputSchema{ 363 Source: input.Source(test.name), 364 SchemaString: test.input, 365 }, compiler.AllowUnprefixedObjectType()) 366 require.NoError(err) 367 368 source, _, err := GenerateSchema(compiled.OrderedDefinitions) 369 require.NoError(err) 370 require.Equal(test.expected, source) 371 }) 372 } 373 }