github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/pkg/diff/namespace/diff_test.go (about)

     1  package namespace
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/stretchr/testify/require"
     7  
     8  	core "github.com/authzed/spicedb/pkg/proto/core/v1"
     9  
    10  	ns "github.com/authzed/spicedb/pkg/namespace"
    11  )
    12  
    13  func TestNamespaceDiff(t *testing.T) {
    14  	testCases := []struct {
    15  		name           string
    16  		existing       *core.NamespaceDefinition
    17  		updated        *core.NamespaceDefinition
    18  		expectedDeltas []Delta
    19  	}{
    20  		{
    21  			"added namespace",
    22  			nil,
    23  			ns.Namespace(
    24  				"document",
    25  			),
    26  			[]Delta{
    27  				{Type: NamespaceAdded},
    28  			},
    29  		},
    30  		{
    31  			"removed namespace",
    32  			ns.Namespace(
    33  				"document",
    34  			),
    35  			nil,
    36  			[]Delta{
    37  				{Type: NamespaceRemoved},
    38  			},
    39  		},
    40  		{
    41  			"added namespace comments",
    42  			ns.Namespace(
    43  				"document",
    44  			),
    45  			ns.WithComment(
    46  				"document",
    47  				"some cool comment",
    48  			),
    49  			[]Delta{
    50  				{Type: NamespaceCommentsChanged},
    51  			},
    52  		},
    53  		{
    54  			"unchanged namespace comments",
    55  			ns.WithComment(
    56  				"document",
    57  				"some cool comment",
    58  			),
    59  			ns.WithComment(
    60  				"document",
    61  				"some cool comment",
    62  			),
    63  			[]Delta{},
    64  		},
    65  		{
    66  			"changed namespace comments",
    67  			ns.WithComment(
    68  				"document",
    69  				"some cool comment!",
    70  			),
    71  			ns.WithComment(
    72  				"document",
    73  				"some cool comment",
    74  			),
    75  			[]Delta{
    76  				{Type: NamespaceCommentsChanged},
    77  			},
    78  		},
    79  		{
    80  			"added relation",
    81  			ns.Namespace(
    82  				"document",
    83  			),
    84  			ns.Namespace(
    85  				"document",
    86  				ns.MustRelation("somerel", nil),
    87  			),
    88  			[]Delta{
    89  				{Type: AddedRelation, RelationName: "somerel"},
    90  			},
    91  		},
    92  		{
    93  			"remove relation",
    94  			ns.Namespace(
    95  				"document",
    96  				ns.MustRelation("somerel", nil),
    97  			),
    98  			ns.Namespace(
    99  				"document",
   100  			),
   101  			[]Delta{
   102  				{Type: RemovedRelation, RelationName: "somerel"},
   103  			},
   104  		},
   105  		{
   106  			"renamed relation",
   107  			ns.Namespace(
   108  				"document",
   109  				ns.MustRelation("somerel", nil),
   110  			),
   111  			ns.Namespace(
   112  				"document",
   113  				ns.MustRelation("somerel2", nil),
   114  			),
   115  			[]Delta{
   116  				{Type: RemovedRelation, RelationName: "somerel"},
   117  				{Type: AddedRelation, RelationName: "somerel2"},
   118  			},
   119  		},
   120  		{
   121  			"added permission",
   122  			ns.Namespace(
   123  				"document",
   124  			),
   125  			ns.Namespace(
   126  				"document",
   127  				ns.MustRelation("someperm", ns.Union(ns.ComputedUserset("hiya"))),
   128  			),
   129  			[]Delta{
   130  				{Type: AddedPermission, RelationName: "someperm"},
   131  			},
   132  		},
   133  		{
   134  			"remove permission",
   135  			ns.Namespace(
   136  				"document",
   137  				ns.MustRelation("someperm", ns.Union(ns.ComputedUserset("hiya"))),
   138  			),
   139  			ns.Namespace(
   140  				"document",
   141  			),
   142  			[]Delta{
   143  				{Type: RemovedPermission, RelationName: "someperm"},
   144  			},
   145  		},
   146  		{
   147  			"renamed permission",
   148  			ns.Namespace(
   149  				"document",
   150  				ns.MustRelation("someperm", ns.Union(ns.ComputedUserset("hiya"))),
   151  			),
   152  			ns.Namespace(
   153  				"document",
   154  				ns.MustRelation("someperm2", ns.Union(ns.ComputedUserset("hiya"))),
   155  			),
   156  			[]Delta{
   157  				{Type: RemovedPermission, RelationName: "someperm"},
   158  				{Type: AddedPermission, RelationName: "someperm2"},
   159  			},
   160  		},
   161  		{
   162  			"legacy changed relation impl",
   163  			ns.Namespace(
   164  				"document",
   165  				ns.MustRelation(
   166  					"somerel",
   167  					nil,
   168  					ns.AllowedRelation("someothernamespace", "somerel"),
   169  				),
   170  			),
   171  			ns.Namespace(
   172  				"document",
   173  				ns.MustRelation("somerel",
   174  					ns.Union(
   175  						ns.ComputedUserset("owner"),
   176  					),
   177  					ns.AllowedRelation("someothernamespace", "somerel"),
   178  				),
   179  			),
   180  			[]Delta{
   181  				{Type: LegacyChangedRelationImpl, RelationName: "somerel"},
   182  			},
   183  		},
   184  		{
   185  			"changed permission impl",
   186  			ns.Namespace(
   187  				"document",
   188  				ns.MustRelation("somerel", ns.Union(
   189  					ns.ComputedUserset("editor"),
   190  				)),
   191  			),
   192  			ns.Namespace(
   193  				"document",
   194  				ns.MustRelation("somerel", ns.Union(
   195  					ns.ComputedUserset("owner"),
   196  				)),
   197  			),
   198  			[]Delta{
   199  				{Type: ChangedPermissionImpl, RelationName: "somerel"},
   200  			},
   201  		},
   202  		{
   203  			"changed permission comment",
   204  			ns.Namespace(
   205  				"document",
   206  				ns.MustRelationWithComment("somerel", "some comment", ns.Union(
   207  					ns.ComputedUserset("editor"),
   208  				)),
   209  			),
   210  			ns.Namespace(
   211  				"document",
   212  				ns.MustRelationWithComment("somerel", "some other comment", ns.Union(
   213  					ns.ComputedUserset("editor"),
   214  				)),
   215  			),
   216  			[]Delta{
   217  				{Type: ChangedPermissionComment, RelationName: "somerel"},
   218  			},
   219  		},
   220  		{
   221  			"changed permission impl and comment",
   222  			ns.Namespace(
   223  				"document",
   224  				ns.MustRelationWithComment("somerel", "some comment", ns.Union(
   225  					ns.ComputedUserset("editor"),
   226  				)),
   227  			),
   228  			ns.Namespace(
   229  				"document",
   230  				ns.MustRelationWithComment("somerel", "some other comment", ns.Union(
   231  					ns.ComputedUserset("editor2"),
   232  				)),
   233  			),
   234  			[]Delta{
   235  				{Type: ChangedPermissionImpl, RelationName: "somerel"},
   236  				{Type: ChangedPermissionComment, RelationName: "somerel"},
   237  			},
   238  		},
   239  		{
   240  			"no changes",
   241  			ns.Namespace(
   242  				"document",
   243  				ns.MustRelationWithComment("somerel", "some comment", ns.Union(
   244  					ns.ComputedUserset("editor"),
   245  				)),
   246  			),
   247  			ns.Namespace(
   248  				"document",
   249  				ns.MustRelationWithComment("somerel", "some comment", ns.Union(
   250  					ns.ComputedUserset("editor"),
   251  				)),
   252  			),
   253  			[]Delta{},
   254  		},
   255  		{
   256  			"added direct type",
   257  			ns.Namespace(
   258  				"document",
   259  				ns.MustRelation("somerel", nil),
   260  			),
   261  			ns.Namespace(
   262  				"document",
   263  				ns.MustRelation("somerel", nil, ns.AllowedRelation("foo", "bar")),
   264  			),
   265  			[]Delta{
   266  				{
   267  					Type:         RelationAllowedTypeAdded,
   268  					RelationName: "somerel",
   269  					AllowedType:  ns.AllowedRelation("foo", "bar"),
   270  				},
   271  			},
   272  		},
   273  		{
   274  			"removed direct type",
   275  			ns.Namespace(
   276  				"document",
   277  				ns.MustRelation("somerel", nil, ns.AllowedRelation("foo", "bar")),
   278  			),
   279  			ns.Namespace(
   280  				"document",
   281  				ns.MustRelation("somerel", nil),
   282  			),
   283  			[]Delta{
   284  				{
   285  					Type:         RelationAllowedTypeRemoved,
   286  					RelationName: "somerel",
   287  					AllowedType:  ns.AllowedRelation("foo", "bar"),
   288  				},
   289  			},
   290  		},
   291  		{
   292  			"no changes with types",
   293  			ns.Namespace(
   294  				"document",
   295  				ns.MustRelation("somerel", ns.Union(
   296  					ns.ComputedUserset("owner"),
   297  				), ns.AllowedRelation("foo", "bar")),
   298  			),
   299  			ns.Namespace(
   300  				"document",
   301  				ns.MustRelation("somerel", ns.Union(
   302  					ns.ComputedUserset("owner"),
   303  				), ns.AllowedRelation("foo", "bar")),
   304  			),
   305  			[]Delta{},
   306  		},
   307  		{
   308  			"changed relation comment",
   309  			ns.Namespace(
   310  				"document",
   311  				ns.MustRelationWithComment("somerel", "some comment", ns.Union(
   312  					ns.ComputedUserset("owner"),
   313  				), ns.AllowedRelation("foo", "bar")),
   314  			),
   315  			ns.Namespace(
   316  				"document",
   317  				ns.MustRelationWithComment("somerel", "changed comment", ns.Union(
   318  					ns.ComputedUserset("owner"),
   319  				), ns.AllowedRelation("foo", "bar")),
   320  			),
   321  			[]Delta{
   322  				{Type: ChangedRelationComment, RelationName: "somerel"},
   323  			},
   324  		},
   325  		{
   326  			"type added and removed",
   327  			ns.Namespace(
   328  				"document",
   329  				ns.MustRelation("somerel", ns.Union(
   330  					ns.ComputedUserset("owner"),
   331  				), ns.AllowedRelation("foo", "bar")),
   332  			),
   333  			ns.Namespace(
   334  				"document",
   335  				ns.MustRelation("somerel", ns.Union(
   336  					ns.ComputedUserset("owner"),
   337  				), ns.AllowedRelation("foo2", "bar")),
   338  			),
   339  			[]Delta{
   340  				{
   341  					Type:         RelationAllowedTypeRemoved,
   342  					RelationName: "somerel",
   343  					AllowedType:  ns.AllowedRelation("foo", "bar"),
   344  				},
   345  				{
   346  					Type:         RelationAllowedTypeAdded,
   347  					RelationName: "somerel",
   348  					AllowedType:  ns.AllowedRelation("foo2", "bar"),
   349  				},
   350  			},
   351  		},
   352  		{
   353  			"wildcard type added and removed",
   354  			ns.Namespace(
   355  				"document",
   356  				ns.MustRelation("somerel", ns.Union(
   357  					ns.ComputedUserset("owner"),
   358  				), ns.AllowedPublicNamespace("foo")),
   359  			),
   360  			ns.Namespace(
   361  				"document",
   362  				ns.MustRelation("somerel", ns.Union(
   363  					ns.ComputedUserset("owner"),
   364  				), ns.AllowedPublicNamespace("foo2")),
   365  			),
   366  			[]Delta{
   367  				{
   368  					Type:         RelationAllowedTypeRemoved,
   369  					RelationName: "somerel",
   370  					AllowedType:  ns.AllowedPublicNamespace("foo"),
   371  				},
   372  				{
   373  					Type:         RelationAllowedTypeAdded,
   374  					RelationName: "somerel",
   375  					AllowedType:  ns.AllowedPublicNamespace("foo2"),
   376  				},
   377  			},
   378  		},
   379  		{
   380  			"wildcard type changed",
   381  			ns.Namespace(
   382  				"document",
   383  				ns.MustRelation("somerel", ns.Union(
   384  					ns.ComputedUserset("owner"),
   385  				), ns.AllowedPublicNamespace("foo")),
   386  			),
   387  			ns.Namespace(
   388  				"document",
   389  				ns.MustRelation("somerel", ns.Union(
   390  					ns.ComputedUserset("owner"),
   391  				), ns.AllowedRelation("foo", "something")),
   392  			),
   393  			[]Delta{
   394  				{
   395  					Type:         RelationAllowedTypeRemoved,
   396  					RelationName: "somerel",
   397  					AllowedType:  ns.AllowedPublicNamespace("foo"),
   398  				},
   399  				{
   400  					Type:         RelationAllowedTypeAdded,
   401  					RelationName: "somerel",
   402  					AllowedType:  ns.AllowedRelation("foo", "something"),
   403  				},
   404  			},
   405  		},
   406  		{
   407  			"wildcard type changed no rewrite",
   408  			ns.Namespace(
   409  				"document",
   410  				ns.MustRelation("somerel", nil, ns.AllowedPublicNamespace("user")),
   411  			),
   412  			ns.Namespace(
   413  				"document",
   414  				ns.MustRelation("somerel", nil, ns.AllowedRelation("organization", "user")),
   415  			),
   416  			[]Delta{
   417  				{
   418  					Type:         RelationAllowedTypeRemoved,
   419  					RelationName: "somerel",
   420  					AllowedType:  ns.AllowedPublicNamespace("user"),
   421  				},
   422  				{
   423  					Type:         RelationAllowedTypeAdded,
   424  					RelationName: "somerel",
   425  					AllowedType:  ns.AllowedRelation("organization", "user"),
   426  				},
   427  			},
   428  		},
   429  		{
   430  			"added relation and removed permission with same name",
   431  			ns.Namespace(
   432  				"document",
   433  				ns.MustRelation("somerel", ns.Union(ns.ComputedUserset("someotherrel"))),
   434  			),
   435  			ns.Namespace(
   436  				"document",
   437  				ns.MustRelation("somerel", nil),
   438  			),
   439  			[]Delta{
   440  				{Type: AddedRelation, RelationName: "somerel"},
   441  				{Type: RemovedPermission, RelationName: "somerel"},
   442  			},
   443  		},
   444  		{
   445  			"added permission and removed relation with same name",
   446  			ns.Namespace(
   447  				"document",
   448  				ns.MustRelation("somerel", nil),
   449  			),
   450  			ns.Namespace(
   451  				"document",
   452  				ns.MustRelation("somerel", ns.Union(ns.ComputedUserset("someotherrel"))),
   453  			),
   454  			[]Delta{
   455  				{Type: RemovedRelation, RelationName: "somerel"},
   456  				{Type: AddedPermission, RelationName: "somerel"},
   457  			},
   458  		},
   459  		{
   460  			"added required caveat type",
   461  			ns.Namespace(
   462  				"document",
   463  				ns.MustRelation("somerel", nil, ns.AllowedRelation("user", "...")),
   464  			),
   465  			ns.Namespace(
   466  				"document",
   467  				ns.MustRelation("somerel", nil, ns.AllowedRelationWithCaveat("user", "...", ns.AllowedCaveat("somecaveat"))),
   468  			),
   469  			[]Delta{
   470  				{
   471  					Type:         RelationAllowedTypeRemoved,
   472  					RelationName: "somerel",
   473  					AllowedType:  ns.AllowedRelation("user", "..."),
   474  				},
   475  				{
   476  					Type:         RelationAllowedTypeAdded,
   477  					RelationName: "somerel",
   478  					AllowedType:  ns.AllowedRelationWithCaveat("user", "...", ns.AllowedCaveat("somecaveat")),
   479  				},
   480  			},
   481  		},
   482  		{
   483  			"added optional caveat type",
   484  			ns.Namespace(
   485  				"document",
   486  				ns.MustRelation("somerel", nil, ns.AllowedRelation("user", "...")),
   487  			),
   488  			ns.Namespace(
   489  				"document",
   490  				ns.MustRelation("somerel", nil, ns.AllowedRelation("user", "..."), ns.AllowedRelationWithCaveat("user", "...", ns.AllowedCaveat("somecaveat"))),
   491  			),
   492  			[]Delta{
   493  				{
   494  					Type:         RelationAllowedTypeAdded,
   495  					RelationName: "somerel",
   496  					AllowedType:  ns.AllowedRelationWithCaveat("user", "...", ns.AllowedCaveat("somecaveat")),
   497  				},
   498  			},
   499  		},
   500  		{
   501  			"changed required caveat type",
   502  			ns.Namespace(
   503  				"document",
   504  				ns.MustRelation("somerel", nil, ns.AllowedRelationWithCaveat("user", "...", ns.AllowedCaveat("somecaveat"))),
   505  			),
   506  			ns.Namespace(
   507  				"document",
   508  				ns.MustRelation("somerel", nil, ns.AllowedRelationWithCaveat("user", "...", ns.AllowedCaveat("anothercaveat"))),
   509  			),
   510  			[]Delta{
   511  				{
   512  					Type:         RelationAllowedTypeRemoved,
   513  					RelationName: "somerel",
   514  					AllowedType:  ns.AllowedRelationWithCaveat("user", "...", ns.AllowedCaveat("somecaveat")),
   515  				},
   516  				{
   517  					Type:         RelationAllowedTypeAdded,
   518  					RelationName: "somerel",
   519  					AllowedType:  ns.AllowedRelationWithCaveat("user", "...", ns.AllowedCaveat("anothercaveat")),
   520  				},
   521  			},
   522  		},
   523  		{
   524  			"removed required caveat type",
   525  			ns.Namespace(
   526  				"document",
   527  				ns.MustRelation("somerel", nil, ns.AllowedRelationWithCaveat("user", "...", ns.AllowedCaveat("somecaveat"))),
   528  			),
   529  			ns.Namespace(
   530  				"document",
   531  				ns.MustRelation("somerel", nil),
   532  			),
   533  			[]Delta{
   534  				{
   535  					Type:         RelationAllowedTypeRemoved,
   536  					RelationName: "somerel",
   537  					AllowedType:  ns.AllowedRelationWithCaveat("user", "...", ns.AllowedCaveat("somecaveat")),
   538  				},
   539  			},
   540  		},
   541  		{
   542  			"change required caveat type to optional",
   543  			ns.Namespace(
   544  				"document",
   545  				ns.MustRelation("somerel", nil, ns.AllowedRelationWithCaveat("user", "...", ns.AllowedCaveat("somecaveat"))),
   546  			),
   547  			ns.Namespace(
   548  				"document",
   549  				ns.MustRelation("somerel", nil, ns.AllowedRelation("user", "..."), ns.AllowedRelationWithCaveat("user", "...", ns.AllowedCaveat("somecaveat"))),
   550  			),
   551  			[]Delta{
   552  				{
   553  					Type:         RelationAllowedTypeAdded,
   554  					RelationName: "somerel",
   555  					AllowedType:  ns.AllowedRelation("user", "..."),
   556  				},
   557  			},
   558  		},
   559  		{
   560  			"location change does not cause expression change",
   561  			ns.Namespace(
   562  				"document",
   563  				ns.MustRelation("somerel", ns.Union(
   564  					ns.MustComputesUsersetWithSourcePosition("editor", 1),
   565  				)),
   566  			),
   567  			ns.Namespace(
   568  				"document",
   569  				ns.MustRelation("somerel", ns.Union(
   570  					ns.MustComputesUsersetWithSourcePosition("editor", 2),
   571  				)),
   572  			),
   573  			[]Delta{},
   574  		},
   575  	}
   576  
   577  	for _, tc := range testCases {
   578  		tc := tc
   579  		t.Run(tc.name, func(t *testing.T) {
   580  			require := require.New(t)
   581  			diff, err := DiffNamespaces(tc.existing, tc.updated)
   582  			require.Nil(err)
   583  			require.Equal(tc.expectedDeltas, diff.Deltas())
   584  		})
   585  	}
   586  }