github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/internal/services/v1/schema_test.go (about)

     1  package v1_test
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"google.golang.org/protobuf/types/known/structpb"
     8  
     9  	v1 "github.com/authzed/authzed-go/proto/authzed/api/v1"
    10  	"github.com/authzed/grpcutil"
    11  	"github.com/stretchr/testify/require"
    12  	"google.golang.org/grpc/codes"
    13  
    14  	"github.com/authzed/spicedb/internal/datastore/memdb"
    15  	tf "github.com/authzed/spicedb/internal/testfixtures"
    16  	"github.com/authzed/spicedb/internal/testserver"
    17  	core "github.com/authzed/spicedb/pkg/proto/core/v1"
    18  	"github.com/authzed/spicedb/pkg/spiceerrors"
    19  	"github.com/authzed/spicedb/pkg/tuple"
    20  )
    21  
    22  func TestSchemaWriteNoPrefix(t *testing.T) {
    23  	conn, cleanup, _, _ := testserver.NewTestServer(require.New(t), 0, memdb.DisableGC, true, tf.EmptyDatastore)
    24  	t.Cleanup(cleanup)
    25  	client := v1.NewSchemaServiceClient(conn)
    26  	resp, err := client.WriteSchema(context.Background(), &v1.WriteSchemaRequest{
    27  		Schema: `definition user {}`,
    28  	})
    29  	require.NoError(t, err)
    30  	require.NotNil(t, resp.WrittenAt)
    31  	require.NotEmpty(t, resp.WrittenAt.Token)
    32  }
    33  
    34  func TestSchemaWriteInvalidSchema(t *testing.T) {
    35  	conn, cleanup, _, _ := testserver.NewTestServer(require.New(t), 0, memdb.DisableGC, true, tf.EmptyDatastore)
    36  	t.Cleanup(cleanup)
    37  	client := v1.NewSchemaServiceClient(conn)
    38  
    39  	_, err := client.WriteSchema(context.Background(), &v1.WriteSchemaRequest{
    40  		Schema: `invalid example/user {}`,
    41  	})
    42  	grpcutil.RequireStatus(t, codes.InvalidArgument, err)
    43  
    44  	_, err = client.ReadSchema(context.Background(), &v1.ReadSchemaRequest{})
    45  	grpcutil.RequireStatus(t, codes.NotFound, err)
    46  }
    47  
    48  func TestSchemaWriteInvalidNamespace(t *testing.T) {
    49  	conn, cleanup, _, _ := testserver.NewTestServer(require.New(t), 0, memdb.DisableGC, true, tf.EmptyDatastore)
    50  	t.Cleanup(cleanup)
    51  	client := v1.NewSchemaServiceClient(conn)
    52  
    53  	_, err := client.WriteSchema(context.Background(), &v1.WriteSchemaRequest{
    54  		Schema: `definition user {}
    55  		
    56  		definition document {
    57  			relation viewer: user | somemissingdef
    58  		}
    59  	`,
    60  	})
    61  	grpcutil.RequireStatus(t, codes.FailedPrecondition, err)
    62  }
    63  
    64  func TestSchemaWriteAndReadBack(t *testing.T) {
    65  	conn, cleanup, _, _ := testserver.NewTestServer(require.New(t), 0, memdb.DisableGC, true, tf.EmptyDatastore)
    66  	t.Cleanup(cleanup)
    67  	client := v1.NewSchemaServiceClient(conn)
    68  
    69  	_, err := client.ReadSchema(context.Background(), &v1.ReadSchemaRequest{})
    70  	grpcutil.RequireStatus(t, codes.NotFound, err)
    71  
    72  	userSchema := "caveat someCaveat(somecondition int) {\n\tsomecondition == 42\n}\n\ndefinition example/document {\n\trelation viewer: example/user | example/user with someCaveat\n}\n\ndefinition example/user {}"
    73  
    74  	writeResp, err := client.WriteSchema(context.Background(), &v1.WriteSchemaRequest{
    75  		Schema: userSchema,
    76  	})
    77  	require.NoError(t, err)
    78  	require.NotNil(t, writeResp.WrittenAt)
    79  	require.NotEmpty(t, writeResp.WrittenAt.Token)
    80  
    81  	readback, err := client.ReadSchema(context.Background(), &v1.ReadSchemaRequest{})
    82  	require.NoError(t, err)
    83  	require.Equal(t, userSchema, readback.SchemaText)
    84  	require.NotNil(t, readback.ReadAt)
    85  	require.NotEmpty(t, readback.ReadAt.Token)
    86  }
    87  
    88  func TestSchemaDeleteRelation(t *testing.T) {
    89  	conn, cleanup, _, _ := testserver.NewTestServer(require.New(t), 0, memdb.DisableGC, true, tf.EmptyDatastore)
    90  	t.Cleanup(cleanup)
    91  	client := v1.NewSchemaServiceClient(conn)
    92  	v1client := v1.NewPermissionsServiceClient(conn)
    93  
    94  	// Write a basic schema.
    95  	writeResp, err := client.WriteSchema(context.Background(), &v1.WriteSchemaRequest{
    96  		Schema: `definition example/user {}
    97  	
    98  		definition example/document {
    99  			relation somerelation: example/user
   100  			relation anotherrelation: example/user
   101  		}`,
   102  	})
   103  	require.NoError(t, err)
   104  	require.NotNil(t, writeResp.WrittenAt)
   105  	require.NotEmpty(t, writeResp.WrittenAt.Token)
   106  
   107  	// Write a relationship for one of the relations.
   108  	_, err = v1client.WriteRelationships(context.Background(), &v1.WriteRelationshipsRequest{
   109  		Updates: []*v1.RelationshipUpdate{tuple.UpdateToRelationshipUpdate(tuple.Create(
   110  			tuple.MustParse("example/document:somedoc#somerelation@example/user:someuser#..."),
   111  		))},
   112  	})
   113  	require.Nil(t, err)
   114  
   115  	// Attempt to delete the `somerelation` relation, which should fail.
   116  	_, err = client.WriteSchema(context.Background(), &v1.WriteSchemaRequest{
   117  		Schema: `definition example/user {}
   118  	
   119  		definition example/document {
   120  			relation anotherrelation: example/user
   121  		}`,
   122  	})
   123  	grpcutil.RequireStatus(t, codes.InvalidArgument, err)
   124  
   125  	// Attempt to delete the `anotherrelation` relation, which should succeed.
   126  	updateResp, err := client.WriteSchema(context.Background(), &v1.WriteSchemaRequest{
   127  		Schema: `definition example/user {}
   128  	
   129  		definition example/document {
   130  			relation somerelation: example/user
   131  		}`,
   132  	})
   133  	require.Nil(t, err)
   134  	require.NotNil(t, updateResp.WrittenAt)
   135  	require.NotEmpty(t, updateResp.WrittenAt.Token)
   136  
   137  	// Delete the relationship.
   138  	_, err = v1client.WriteRelationships(context.Background(), &v1.WriteRelationshipsRequest{
   139  		Updates: []*v1.RelationshipUpdate{tuple.UpdateToRelationshipUpdate(tuple.Delete(
   140  			tuple.MustParse("example/document:somedoc#somerelation@example/user:someuser#..."),
   141  		))},
   142  	})
   143  	require.Nil(t, err)
   144  
   145  	// Attempt to delete the `somerelation` relation, which should succeed.
   146  	deleteRelResp, err := client.WriteSchema(context.Background(), &v1.WriteSchemaRequest{
   147  		Schema: `definition example/user {}
   148  		
   149  			definition example/document {}`,
   150  	})
   151  	require.Nil(t, err)
   152  	require.NotNil(t, deleteRelResp.WrittenAt)
   153  	require.NotEmpty(t, deleteRelResp.WrittenAt.Token)
   154  }
   155  
   156  func TestSchemaDeletePermission(t *testing.T) {
   157  	conn, cleanup, _, _ := testserver.NewTestServer(require.New(t), 0, memdb.DisableGC, true, tf.EmptyDatastore)
   158  	t.Cleanup(cleanup)
   159  	client := v1.NewSchemaServiceClient(conn)
   160  	v1client := v1.NewPermissionsServiceClient(conn)
   161  
   162  	// Write a basic schema.
   163  	_, err := client.WriteSchema(context.Background(), &v1.WriteSchemaRequest{
   164  		Schema: `definition example/user {}
   165  	
   166  		definition example/document {
   167  			relation somerelation: example/user
   168  			relation anotherrelation: example/user
   169  			permission someperm = somerelation + anotherrelation
   170  		}`,
   171  	})
   172  	require.NoError(t, err)
   173  
   174  	// Write a relationship for one of the relations.
   175  	_, err = v1client.WriteRelationships(context.Background(), &v1.WriteRelationshipsRequest{
   176  		Updates: []*v1.RelationshipUpdate{tuple.UpdateToRelationshipUpdate(tuple.Create(
   177  			tuple.MustParse("example/document:somedoc#somerelation@example/user:someuser#..."),
   178  		))},
   179  	})
   180  	require.Nil(t, err)
   181  
   182  	// Attempt to delete the `someperm` relation, which should succeed.
   183  	_, err = client.WriteSchema(context.Background(), &v1.WriteSchemaRequest{
   184  		Schema: `definition example/user {}
   185  	
   186  		definition example/document {
   187  			relation somerelation: example/user
   188  			relation anotherrelation: example/user
   189  		}`,
   190  	})
   191  	require.Nil(t, err)
   192  }
   193  
   194  func TestSchemaChangeRelationToPermission(t *testing.T) {
   195  	conn, cleanup, _, _ := testserver.NewTestServer(require.New(t), 0, memdb.DisableGC, true, tf.EmptyDatastore)
   196  	t.Cleanup(cleanup)
   197  	client := v1.NewSchemaServiceClient(conn)
   198  	v1client := v1.NewPermissionsServiceClient(conn)
   199  
   200  	// Write a basic schema.
   201  	_, err := client.WriteSchema(context.Background(), &v1.WriteSchemaRequest{
   202  		Schema: `definition example/user {}
   203  	
   204  		definition example/document {
   205  			relation somerelation: example/user
   206  			relation anotherrelation: example/user
   207  			permission someperm = somerelation + anotherrelation
   208  		}`,
   209  	})
   210  	require.NoError(t, err)
   211  
   212  	// Write a relationship for one of the relations.
   213  	_, err = v1client.WriteRelationships(context.Background(), &v1.WriteRelationshipsRequest{
   214  		Updates: []*v1.RelationshipUpdate{tuple.UpdateToRelationshipUpdate(tuple.Create(
   215  			tuple.MustParse("example/document:somedoc#anotherrelation@example/user:someuser#..."),
   216  		))},
   217  	})
   218  	require.Nil(t, err)
   219  
   220  	// Attempt to change `anotherrelation` into a permission, which should fail since it has data.
   221  	_, err = client.WriteSchema(context.Background(), &v1.WriteSchemaRequest{
   222  		Schema: `definition example/user {}
   223  	
   224  		definition example/document {
   225  			relation somerelation: example/user
   226  			permission anotherrelation = nil
   227  			permission someperm = somerelation + anotherrelation
   228  		}`,
   229  	})
   230  	grpcutil.RequireStatus(t, codes.InvalidArgument, err)
   231  
   232  	// Delete the relationship.
   233  	_, err = v1client.WriteRelationships(context.Background(), &v1.WriteRelationshipsRequest{
   234  		Updates: []*v1.RelationshipUpdate{tuple.UpdateToRelationshipUpdate(tuple.Delete(
   235  			tuple.MustParse("example/document:somedoc#anotherrelation@example/user:someuser#..."),
   236  		))},
   237  	})
   238  	require.Nil(t, err)
   239  
   240  	// Attempt to change `anotherrelation` into a permission, which should now succeed.
   241  	_, err = client.WriteSchema(context.Background(), &v1.WriteSchemaRequest{
   242  		Schema: `definition example/user {}
   243  	
   244  		definition example/document {
   245  			relation somerelation: example/user
   246  			permission anotherrelation = nil
   247  			permission someperm = somerelation + anotherrelation
   248  		}`,
   249  	})
   250  	require.Nil(t, err)
   251  }
   252  
   253  func TestSchemaDeleteDefinition(t *testing.T) {
   254  	conn, cleanup, _, _ := testserver.NewTestServer(require.New(t), 0, memdb.DisableGC, true, tf.EmptyDatastore)
   255  	t.Cleanup(cleanup)
   256  	client := v1.NewSchemaServiceClient(conn)
   257  	v1client := v1.NewPermissionsServiceClient(conn)
   258  
   259  	// Write a basic schema.
   260  	_, err := client.WriteSchema(context.Background(), &v1.WriteSchemaRequest{
   261  		Schema: `definition example/user {}
   262  	
   263  		definition example/document {
   264  			relation somerelation: example/user
   265  			relation anotherrelation: example/user
   266  		}`,
   267  	})
   268  	require.NoError(t, err)
   269  
   270  	// Write a relationship for one of the relations.
   271  	_, err = v1client.WriteRelationships(context.Background(), &v1.WriteRelationshipsRequest{
   272  		Updates: []*v1.RelationshipUpdate{tuple.UpdateToRelationshipUpdate(tuple.Create(
   273  			tuple.MustParse("example/document:somedoc#somerelation@example/user:someuser#..."),
   274  		))},
   275  	})
   276  	require.Nil(t, err)
   277  
   278  	// Attempt to delete the `document` type, which should fail.
   279  	_, err = client.WriteSchema(context.Background(), &v1.WriteSchemaRequest{
   280  		Schema: `definition example/user {}`,
   281  	})
   282  	grpcutil.RequireStatus(t, codes.InvalidArgument, err)
   283  
   284  	// Delete the relationship.
   285  	_, err = v1client.WriteRelationships(context.Background(), &v1.WriteRelationshipsRequest{
   286  		Updates: []*v1.RelationshipUpdate{tuple.UpdateToRelationshipUpdate(tuple.Delete(
   287  			tuple.MustParse("example/document:somedoc#somerelation@example/user:someuser#..."),
   288  		))},
   289  	})
   290  	require.Nil(t, err)
   291  
   292  	// Attempt to  delete the `document` type, which should succeed.
   293  	_, err = client.WriteSchema(context.Background(), &v1.WriteSchemaRequest{
   294  		Schema: `definition example/user {}`,
   295  	})
   296  	require.Nil(t, err)
   297  
   298  	// Ensure it was deleted.
   299  	readback, err := client.ReadSchema(context.Background(), &v1.ReadSchemaRequest{})
   300  	require.NoError(t, err)
   301  	require.Equal(t, `definition example/user {}`, readback.SchemaText)
   302  }
   303  
   304  func TestSchemaRemoveWildcard(t *testing.T) {
   305  	conn, cleanup, _, _ := testserver.NewTestServer(require.New(t), 0, memdb.DisableGC, true, tf.EmptyDatastore)
   306  	t.Cleanup(cleanup)
   307  	client := v1.NewSchemaServiceClient(conn)
   308  	v1client := v1.NewPermissionsServiceClient(conn)
   309  
   310  	// Write a basic schema.
   311  	_, err := client.WriteSchema(context.Background(), &v1.WriteSchemaRequest{
   312  		Schema: `definition example/user {}
   313  	
   314  		definition example/document {
   315  			relation somerelation: example/user:*
   316  		}`,
   317  	})
   318  	require.NoError(t, err)
   319  
   320  	// Write the wildcard relationship.
   321  	_, err = v1client.WriteRelationships(context.Background(), &v1.WriteRelationshipsRequest{
   322  		Updates: []*v1.RelationshipUpdate{tuple.UpdateToRelationshipUpdate(tuple.Create(
   323  			tuple.MustParse("example/document:somedoc#somerelation@example/user:*"),
   324  		))},
   325  	})
   326  	require.Nil(t, err)
   327  
   328  	newSchema := `definition example/document {
   329  	relation somerelation: example/organization#user
   330  }
   331  
   332  definition example/organization {
   333  	relation user: example/user
   334  }
   335  
   336  definition example/user {}`
   337  
   338  	// Attempt to change the wildcard type, which should fail.
   339  	_, err = client.WriteSchema(context.Background(), &v1.WriteSchemaRequest{
   340  		Schema: newSchema,
   341  	})
   342  	grpcutil.RequireStatus(t, codes.InvalidArgument, err)
   343  	require.Equal(t, "rpc error: code = InvalidArgument desc = cannot remove allowed type `example/user:*` from relation `somerelation` in object definition `example/document`, as a relationship exists with it", err.Error())
   344  
   345  	// Delete the relationship.
   346  	_, err = v1client.WriteRelationships(context.Background(), &v1.WriteRelationshipsRequest{
   347  		Updates: []*v1.RelationshipUpdate{tuple.UpdateToRelationshipUpdate(tuple.Delete(
   348  			tuple.MustParse("example/document:somedoc#somerelation@example/user:*"),
   349  		))},
   350  	})
   351  	require.Nil(t, err)
   352  
   353  	// Attempt to delete the wildcard type, which should work now.
   354  	_, err = client.WriteSchema(context.Background(), &v1.WriteSchemaRequest{
   355  		Schema: newSchema,
   356  	})
   357  	require.Nil(t, err)
   358  
   359  	// Ensure it was deleted.
   360  	readback, err := client.ReadSchema(context.Background(), &v1.ReadSchemaRequest{})
   361  	require.NoError(t, err)
   362  	require.Equal(t, newSchema, readback.SchemaText)
   363  }
   364  
   365  func TestSchemaEmpty(t *testing.T) {
   366  	conn, cleanup, _, _ := testserver.NewTestServer(require.New(t), 0, memdb.DisableGC, true, tf.EmptyDatastore)
   367  	t.Cleanup(cleanup)
   368  	client := v1.NewSchemaServiceClient(conn)
   369  	v1client := v1.NewPermissionsServiceClient(conn)
   370  
   371  	// Write a basic schema.
   372  	_, err := client.WriteSchema(context.Background(), &v1.WriteSchemaRequest{
   373  		Schema: `definition example/user {}
   374  	
   375  		definition example/document {
   376  			relation somerelation: example/user
   377  			relation anotherrelation: example/user
   378  		}`,
   379  	})
   380  	require.NoError(t, err)
   381  
   382  	// Write a relationship for one of the relations.
   383  	_, err = v1client.WriteRelationships(context.Background(), &v1.WriteRelationshipsRequest{
   384  		Updates: []*v1.RelationshipUpdate{tuple.UpdateToRelationshipUpdate(tuple.Create(
   385  			tuple.MustParse("example/document:somedoc#somerelation@example/user:someuser#..."),
   386  		))},
   387  	})
   388  	require.Nil(t, err)
   389  
   390  	// Attempt to empty the schema, which should fail.
   391  	_, err = client.WriteSchema(context.Background(), &v1.WriteSchemaRequest{
   392  		Schema: ``,
   393  	})
   394  	grpcutil.RequireStatus(t, codes.InvalidArgument, err)
   395  
   396  	// Delete the relationship.
   397  	_, err = v1client.WriteRelationships(context.Background(), &v1.WriteRelationshipsRequest{
   398  		Updates: []*v1.RelationshipUpdate{tuple.UpdateToRelationshipUpdate(tuple.Delete(
   399  			tuple.MustParse("example/document:somedoc#somerelation@example/user:someuser#..."),
   400  		))},
   401  	})
   402  	require.Nil(t, err)
   403  
   404  	// Attempt to empty the schema, which should succeed.
   405  	emptyResp, err := client.WriteSchema(context.Background(), &v1.WriteSchemaRequest{
   406  		Schema: ``,
   407  	})
   408  	require.Nil(t, err)
   409  	require.NotNil(t, emptyResp.WrittenAt)
   410  	require.NotEmpty(t, emptyResp.WrittenAt.Token)
   411  
   412  	// Ensure it was deleted.
   413  	_, err = client.ReadSchema(context.Background(), &v1.ReadSchemaRequest{})
   414  	grpcutil.RequireStatus(t, codes.NotFound, err)
   415  }
   416  
   417  func TestSchemaTypeRedefined(t *testing.T) {
   418  	conn, cleanup, _, _ := testserver.NewTestServer(require.New(t), 0, memdb.DisableGC, true, tf.EmptyDatastore)
   419  	t.Cleanup(cleanup)
   420  	client := v1.NewSchemaServiceClient(conn)
   421  
   422  	// Write a schema that redefines the same type.
   423  	_, err := client.WriteSchema(context.Background(), &v1.WriteSchemaRequest{
   424  		Schema: `definition example/user {}
   425  	
   426  		definition example/user {}`,
   427  	})
   428  	grpcutil.RequireStatus(t, codes.InvalidArgument, err)
   429  	spiceerrors.RequireReason(t, v1.ErrorReason_ERROR_REASON_SCHEMA_PARSE_ERROR, err,
   430  		"source_code",
   431  		"start_line_number",
   432  		"start_column_position",
   433  		"end_line_number",
   434  		"end_column_position",
   435  	)
   436  }
   437  
   438  func TestSchemaTypeInvalid(t *testing.T) {
   439  	conn, cleanup, _, _ := testserver.NewTestServer(require.New(t), 0, memdb.DisableGC, false, tf.EmptyDatastore)
   440  	t.Cleanup(cleanup)
   441  	client := v1.NewSchemaServiceClient(conn)
   442  
   443  	// Write a schema that references an invalid type.
   444  	_, err := client.WriteSchema(context.Background(), &v1.WriteSchemaRequest{
   445  		Schema: `definition example/user {}
   446  	
   447  		definition example/document {
   448  			relation viewer: hiya
   449  		}`,
   450  	})
   451  	grpcutil.RequireStatus(t, codes.FailedPrecondition, err)
   452  	spiceerrors.RequireReason(t, v1.ErrorReason_ERROR_REASON_SCHEMA_TYPE_ERROR, err, "definition_name")
   453  }
   454  
   455  func TestSchemaRemoveCaveat(t *testing.T) {
   456  	conn, cleanup, _, _ := testserver.NewTestServer(require.New(t), 0, memdb.DisableGC, true, tf.EmptyDatastore)
   457  	t.Cleanup(cleanup)
   458  	client := v1.NewSchemaServiceClient(conn)
   459  	v1client := v1.NewPermissionsServiceClient(conn)
   460  
   461  	// Write a basic schema.
   462  	_, err := client.WriteSchema(context.Background(), &v1.WriteSchemaRequest{
   463  		Schema: `definition user {}
   464  
   465  		caveat somecaveat(a int, b int) {
   466  			a + b == 42
   467  		}
   468  
   469  		definition document {
   470  			relation somerelation: user with somecaveat
   471  		}`,
   472  	})
   473  	require.NoError(t, err)
   474  
   475  	// Write the relationship referencing the caveat.
   476  	caveatCtx, err := structpb.NewStruct(map[string]any{"a": 1, "b": 2})
   477  	require.NoError(t, err)
   478  
   479  	toWrite := tuple.MustParse("document:somedoc#somerelation@user:tom")
   480  	toWrite.Caveat = &core.ContextualizedCaveat{
   481  		CaveatName: "somecaveat",
   482  		Context:    caveatCtx,
   483  	}
   484  
   485  	_, err = v1client.WriteRelationships(context.Background(), &v1.WriteRelationshipsRequest{
   486  		Updates: []*v1.RelationshipUpdate{tuple.UpdateToRelationshipUpdate(tuple.Create(
   487  			toWrite,
   488  		))},
   489  	})
   490  	require.Nil(t, err)
   491  
   492  	newSchema := `definition document {
   493  	relation somerelation: user
   494  }
   495  
   496  definition user {}`
   497  
   498  	// Attempt to change the relation type, which should fail.
   499  	_, err = client.WriteSchema(context.Background(), &v1.WriteSchemaRequest{
   500  		Schema: newSchema,
   501  	})
   502  	grpcutil.RequireStatus(t, codes.InvalidArgument, err)
   503  	require.Equal(t, "rpc error: code = InvalidArgument desc = cannot remove allowed type `user with somecaveat` from relation `somerelation` in object definition `document`, as a relationship exists with it", err.Error())
   504  
   505  	// Delete the relationship.
   506  	_, err = v1client.WriteRelationships(context.Background(), &v1.WriteRelationshipsRequest{
   507  		Updates: []*v1.RelationshipUpdate{tuple.UpdateToRelationshipUpdate(tuple.Delete(
   508  			toWrite,
   509  		))},
   510  	})
   511  	require.Nil(t, err)
   512  
   513  	// Attempt to delete the caveated type, which should work now.
   514  	_, err = client.WriteSchema(context.Background(), &v1.WriteSchemaRequest{
   515  		Schema: newSchema,
   516  	})
   517  	require.Nil(t, err)
   518  
   519  	// Ensure it was deleted.
   520  	readback, err := client.ReadSchema(context.Background(), &v1.ReadSchemaRequest{})
   521  	require.NoError(t, err)
   522  	require.Equal(t, newSchema, readback.SchemaText)
   523  }
   524  
   525  func TestSchemaUnchangedNamespaces(t *testing.T) {
   526  	conn, cleanup, ds, _ := testserver.NewTestServer(require.New(t), 0, memdb.DisableGC, true, tf.EmptyDatastore)
   527  	t.Cleanup(cleanup)
   528  
   529  	client := v1.NewSchemaServiceClient(conn)
   530  
   531  	// Write a schema.
   532  	_, err := client.WriteSchema(context.Background(), &v1.WriteSchemaRequest{
   533  		Schema: `definition user {}
   534  	
   535  		definition document {
   536  			relation editor: user
   537  			relation viewer: user
   538  		}`,
   539  	})
   540  	require.NoError(t, err)
   541  
   542  	// Update the schema.
   543  	_, err = client.WriteSchema(context.Background(), &v1.WriteSchemaRequest{
   544  		Schema: `definition user {}
   545  	
   546  		definition document {
   547  			relation viewer: user
   548  		}`,
   549  	})
   550  	require.NoError(t, err)
   551  
   552  	// Ensure the `user` definition was not modified.
   553  	rev, err := ds.HeadRevision(context.Background())
   554  	require.NoError(t, err)
   555  
   556  	reader := ds.SnapshotReader(rev)
   557  
   558  	_, userRevision, err := reader.ReadNamespaceByName(context.Background(), "user")
   559  	require.NoError(t, err)
   560  
   561  	_, docRevision, err := reader.ReadNamespaceByName(context.Background(), "document")
   562  	require.NoError(t, err)
   563  
   564  	require.True(t, docRevision.GreaterThan(userRevision))
   565  }
   566  
   567  func TestSchemaInvalid(t *testing.T) {
   568  	conn, cleanup, _, _ := testserver.NewTestServer(require.New(t), 0, memdb.DisableGC, false, tf.EmptyDatastore)
   569  	t.Cleanup(cleanup)
   570  	client := v1.NewSchemaServiceClient(conn)
   571  
   572  	// Write a schema that references an invalid type.
   573  	_, err := client.WriteSchema(context.Background(), &v1.WriteSchemaRequest{
   574  		Schema: `definition org {
   575  			relation admin: user
   576  			relation member: user
   577  		
   578  			permission read = admin + member
   579  			permission create = admin
   580  			permission update = admin
   581  			permission delete = admin
   582  			permission * = read + create + update + delete // <= crash case
   583  		}`,
   584  	})
   585  	grpcutil.RequireStatus(t, codes.InvalidArgument, err)
   586  	require.ErrorContains(t, err, "found token TokenTypeStar")
   587  }