github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/pkg/diff/diff_test.go (about) 1 package diff 2 3 import ( 4 "fmt" 5 "testing" 6 7 "github.com/stretchr/testify/require" 8 9 "github.com/authzed/spicedb/pkg/diff/caveats" 10 "github.com/authzed/spicedb/pkg/diff/namespace" 11 "github.com/authzed/spicedb/pkg/schemadsl/compiler" 12 "github.com/authzed/spicedb/pkg/schemadsl/input" 13 ) 14 15 func TestDiffSchemas(t *testing.T) { 16 tcs := []struct { 17 name string 18 existingSchema string 19 comparisonSchema string 20 expectedDiff SchemaDiff 21 }{ 22 { 23 name: "no changes", 24 existingSchema: `definition user {}`, 25 comparisonSchema: ` definition user {} `, 26 expectedDiff: SchemaDiff{}, 27 }, 28 { 29 name: "added namespace", 30 existingSchema: ``, 31 comparisonSchema: `definition user {}`, 32 expectedDiff: SchemaDiff{ 33 AddedNamespaces: []string{"user"}, 34 }, 35 }, 36 { 37 name: "removed namespace", 38 existingSchema: `definition user {}`, 39 comparisonSchema: ``, 40 expectedDiff: SchemaDiff{ 41 RemovedNamespaces: []string{"user"}, 42 }, 43 }, 44 { 45 name: "added caveat", 46 existingSchema: ``, 47 comparisonSchema: `caveat someCaveat(someparam int) { someparam < 42 }`, 48 expectedDiff: SchemaDiff{ 49 AddedCaveats: []string{"someCaveat"}, 50 }, 51 }, 52 { 53 name: "removed caveat", 54 existingSchema: `caveat someCaveat(someparam int) { someparam < 42 }`, 55 comparisonSchema: ``, 56 expectedDiff: SchemaDiff{ 57 RemovedCaveats: []string{"someCaveat"}, 58 }, 59 }, 60 { 61 name: "add and remove namespace and caveat", 62 existingSchema: `definition user {}`, 63 comparisonSchema: `caveat someCaveat(someparam int) { someparam < 42 }`, 64 expectedDiff: SchemaDiff{ 65 AddedCaveats: []string{"someCaveat"}, 66 RemovedNamespaces: []string{"user"}, 67 }, 68 }, 69 { 70 name: "add and remove namespaces", 71 existingSchema: `definition user {}`, 72 comparisonSchema: `definition user2 {}`, 73 expectedDiff: SchemaDiff{ 74 AddedNamespaces: []string{"user2"}, 75 RemovedNamespaces: []string{"user"}, 76 }, 77 }, 78 { 79 name: "add and remove caveats", 80 existingSchema: `caveat someCaveat(someparam int) { someparam < 42 }`, 81 comparisonSchema: `caveat someCaveat2(someparam int) { someparam < 42 }`, 82 expectedDiff: SchemaDiff{ 83 AddedCaveats: []string{"someCaveat2"}, 84 RemovedCaveats: []string{"someCaveat"}, 85 }, 86 }, 87 } 88 89 for _, tc := range tcs { 90 t.Run(tc.name, func(t *testing.T) { 91 existingSchema, err := compiler.Compile(compiler.InputSchema{ 92 Source: input.Source("schema"), 93 SchemaString: tc.existingSchema, 94 }, compiler.AllowUnprefixedObjectType()) 95 require.NoError(t, err) 96 97 comparisonSchema, err := compiler.Compile(compiler.InputSchema{ 98 Source: input.Source("schema"), 99 SchemaString: tc.comparisonSchema, 100 }, compiler.AllowUnprefixedObjectType()) 101 require.NoError(t, err) 102 103 diff, err := DiffSchemas(NewDiffableSchemaFromCompiledSchema(existingSchema), NewDiffableSchemaFromCompiledSchema(comparisonSchema)) 104 require.NoError(t, err) 105 require.Equal(t, tc.expectedDiff, *diff) 106 }) 107 } 108 } 109 110 func TestDiffSchemasWithChangedNamespace(t *testing.T) { 111 existingSchema, err := compiler.Compile(compiler.InputSchema{ 112 Source: input.Source("schema"), 113 SchemaString: `definition user {}`, 114 }, compiler.AllowUnprefixedObjectType()) 115 require.NoError(t, err) 116 117 comparisonSchema, err := compiler.Compile(compiler.InputSchema{ 118 Source: input.Source("schema"), 119 SchemaString: `definition user { relation somerel: user; }`, 120 }, compiler.AllowUnprefixedObjectType()) 121 require.NoError(t, err) 122 123 diff, err := DiffSchemas(NewDiffableSchemaFromCompiledSchema(existingSchema), NewDiffableSchemaFromCompiledSchema(comparisonSchema)) 124 require.NoError(t, err) 125 126 require.Len(t, diff.ChangedNamespaces, 1) 127 require.Contains(t, diff.ChangedNamespaces, "user") 128 require.Len(t, diff.ChangedNamespaces["user"].Deltas(), 1) 129 require.Equal(t, namespace.AddedRelation, diff.ChangedNamespaces["user"].Deltas()[0].Type) 130 require.Equal(t, "somerel", diff.ChangedNamespaces["user"].Deltas()[0].RelationName) 131 } 132 133 func TestDiffSchemasWithChangedCaveat(t *testing.T) { 134 existingSchema, err := compiler.Compile(compiler.InputSchema{ 135 Source: input.Source("schema"), 136 SchemaString: `caveat someCaveat(someparam int) { someparam < 42 }`, 137 }, compiler.AllowUnprefixedObjectType()) 138 require.NoError(t, err) 139 140 comparisonSchema, err := compiler.Compile(compiler.InputSchema{ 141 Source: input.Source("schema"), 142 SchemaString: `caveat someCaveat(someparam int) { someparam <= 42 }`, 143 }, compiler.AllowUnprefixedObjectType()) 144 require.NoError(t, err) 145 146 diff, err := DiffSchemas(NewDiffableSchemaFromCompiledSchema(existingSchema), NewDiffableSchemaFromCompiledSchema(comparisonSchema)) 147 require.NoError(t, err) 148 149 require.Len(t, diff.ChangedCaveats, 1) 150 require.Contains(t, diff.ChangedCaveats, "someCaveat") 151 require.Len(t, diff.ChangedCaveats["someCaveat"].Deltas(), 1) 152 require.Equal(t, caveats.CaveatExpressionChanged, diff.ChangedCaveats["someCaveat"].Deltas()[0].Type) 153 } 154 155 func TestDiffSchemasWithChangedCaveatComment(t *testing.T) { 156 existingSchema, err := compiler.Compile(compiler.InputSchema{ 157 Source: input.Source("schema"), 158 SchemaString: `// hi there 159 caveat someCaveat(someparam int) { someparam < 42 }`, 160 }, compiler.AllowUnprefixedObjectType()) 161 require.NoError(t, err) 162 163 comparisonSchema, err := compiler.Compile(compiler.InputSchema{ 164 Source: input.Source("schema"), 165 SchemaString: `// hello there 166 caveat someCaveat(someparam int) { someparam < 42 }`, 167 }, compiler.AllowUnprefixedObjectType()) 168 require.NoError(t, err) 169 170 diff, err := DiffSchemas(NewDiffableSchemaFromCompiledSchema(existingSchema), NewDiffableSchemaFromCompiledSchema(comparisonSchema)) 171 require.NoError(t, err) 172 173 require.Len(t, diff.ChangedCaveats, 1) 174 require.Contains(t, diff.ChangedCaveats, "someCaveat") 175 require.Len(t, diff.ChangedCaveats["someCaveat"].Deltas(), 1) 176 require.Equal(t, caveats.CaveatCommentsChanged, diff.ChangedCaveats["someCaveat"].Deltas()[0].Type) 177 } 178 179 type checker func(*testing.T, *DiffableSchema) 180 181 func TestDiffableSchema(t *testing.T) { 182 tcs := []struct { 183 name string 184 schema string 185 checkers []checker 186 }{ 187 { 188 name: "basic schema", 189 schema: ` 190 definition user {} 191 192 caveat someCaveat(someparam int) { someparam < 42 } 193 194 definition resource { 195 relation owner: user 196 relation viewer: user 197 permission view = owner + viewer 198 } 199 `, 200 checkers: []checker{ 201 func(t *testing.T, ds *DiffableSchema) { 202 ns, ok := ds.GetNamespace("user") 203 require.True(t, ok) 204 require.Equal(t, "user", ns.Name) 205 }, 206 func(t *testing.T, ds *DiffableSchema) { 207 ns, ok := ds.GetNamespace("resource") 208 require.True(t, ok) 209 require.Equal(t, "resource", ns.Name) 210 }, 211 func(t *testing.T, ds *DiffableSchema) { 212 caveat, ok := ds.GetCaveat("someCaveat") 213 require.True(t, ok) 214 require.Equal(t, "someCaveat", caveat.Name) 215 }, 216 func(t *testing.T, ds *DiffableSchema) { 217 _, ok := ds.GetRelation("user", "owner") 218 require.False(t, ok) 219 }, 220 func(t *testing.T, ds *DiffableSchema) { 221 _, ok := ds.GetRelation("resource", "owner") 222 require.True(t, ok) 223 }, 224 func(t *testing.T, ds *DiffableSchema) { 225 _, ok := ds.GetRelation("resource", "viewer") 226 require.True(t, ok) 227 }, 228 func(t *testing.T, ds *DiffableSchema) { 229 _, ok := ds.GetNamespace("nonexistent") 230 require.False(t, ok) 231 }, 232 func(t *testing.T, ds *DiffableSchema) { 233 _, ok := ds.GetCaveat("nonexistent") 234 require.False(t, ok) 235 }, 236 }, 237 }, 238 } 239 240 for _, tc := range tcs { 241 tc := tc 242 t.Run(tc.name, func(t *testing.T) { 243 schema, err := compiler.Compile(compiler.InputSchema{ 244 Source: input.Source("schema"), 245 SchemaString: tc.schema, 246 }, compiler.AllowUnprefixedObjectType()) 247 require.NoError(t, err) 248 249 diffableSchema := NewDiffableSchemaFromCompiledSchema(schema) 250 for index, check := range tc.checkers { 251 check := check 252 t.Run(fmt.Sprintf("check-%d", index), func(t *testing.T) { 253 check(t, &diffableSchema) 254 }) 255 } 256 }) 257 } 258 }