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

     1  //go:build !skipintegrationtests
     2  // +build !skipintegrationtests
     3  
     4  package integrationtesting_test
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"testing"
    10  
    11  	v1 "github.com/authzed/authzed-go/proto/authzed/api/v1"
    12  	"github.com/stretchr/testify/require"
    13  	"google.golang.org/grpc"
    14  	"google.golang.org/protobuf/types/known/structpb"
    15  
    16  	"github.com/authzed/spicedb/internal/datastore/memdb"
    17  	tf "github.com/authzed/spicedb/internal/testfixtures"
    18  	"github.com/authzed/spicedb/internal/testserver"
    19  	core "github.com/authzed/spicedb/pkg/proto/core/v1"
    20  	"github.com/authzed/spicedb/pkg/tuple"
    21  )
    22  
    23  type stcOp interface {
    24  	Execute(tester opsTester) error
    25  }
    26  
    27  type stcStep struct {
    28  	op            stcOp
    29  	expectedError string
    30  }
    31  
    32  type schemaTestCase struct {
    33  	name  string
    34  	steps []stcStep
    35  }
    36  
    37  type writeSchema struct{ schemaText string }
    38  
    39  func (ws writeSchema) Execute(tester opsTester) error {
    40  	return tester.WriteSchema(context.Background(), ws.schemaText)
    41  }
    42  
    43  type readSchema struct{ expectedSchemaText string }
    44  
    45  func (rs readSchema) Execute(tester opsTester) error {
    46  	schemaText, err := tester.ReadSchema(context.Background())
    47  	if err != nil {
    48  		return err
    49  	}
    50  	if schemaText != rs.expectedSchemaText {
    51  		return fmt.Errorf("unexpected schema: %#v", schemaText)
    52  	}
    53  	return nil
    54  }
    55  
    56  type createCaveatedRelationship struct {
    57  	relString  string
    58  	caveatName string
    59  	context    map[string]any
    60  }
    61  
    62  func (wr createCaveatedRelationship) Execute(tester opsTester) error {
    63  	ctx, err := structpb.NewStruct(wr.context)
    64  	if err != nil {
    65  		return err
    66  	}
    67  
    68  	rel := tuple.MustParse(wr.relString)
    69  	rel.Caveat = &core.ContextualizedCaveat{
    70  		CaveatName: wr.caveatName,
    71  		Context:    ctx,
    72  	}
    73  	return tester.CreateRelationship(context.Background(), rel)
    74  }
    75  
    76  type touchCaveatedRelationship struct {
    77  	relString  string
    78  	caveatName string
    79  	context    map[string]any
    80  }
    81  
    82  func (wr touchCaveatedRelationship) Execute(tester opsTester) error {
    83  	ctx, err := structpb.NewStruct(wr.context)
    84  	if err != nil {
    85  		return err
    86  	}
    87  
    88  	rel := tuple.MustParse(wr.relString)
    89  	rel.Caveat = &core.ContextualizedCaveat{
    90  		CaveatName: wr.caveatName,
    91  		Context:    ctx,
    92  	}
    93  	return tester.TouchRelationship(context.Background(), rel)
    94  }
    95  
    96  type createRelationship struct{ relString string }
    97  
    98  func (wr createRelationship) Execute(tester opsTester) error {
    99  	return tester.CreateRelationship(context.Background(), tuple.MustParse(wr.relString))
   100  }
   101  
   102  type deleteRelationship struct{ relString string }
   103  
   104  func (dr deleteRelationship) Execute(tester opsTester) error {
   105  	return tester.DeleteRelationship(context.Background(), tuple.MustParse(dr.relString))
   106  }
   107  
   108  type deleteCaveatedRelationship struct {
   109  	relString  string
   110  	caveatName string
   111  }
   112  
   113  func (dr deleteCaveatedRelationship) Execute(tester opsTester) error {
   114  	rel := tuple.MustParse(dr.relString)
   115  	rel.Caveat = &core.ContextualizedCaveat{
   116  		CaveatName: dr.caveatName,
   117  	}
   118  
   119  	return tester.DeleteRelationship(context.Background(), rel)
   120  }
   121  
   122  func TestSchemaAndRelationshipsOperations(t *testing.T) {
   123  	tcs := []schemaTestCase{
   124  		// Test: write a basic, valid schema.
   125  		{
   126  			"basic valid schema write",
   127  			[]stcStep{
   128  				{writeSchema{`definition user {}`}, ""},
   129  			},
   130  		},
   131  
   132  		// Test: write a basic, invalid schema.
   133  		{
   134  			"basic invalid schema write",
   135  			[]stcStep{
   136  				{writeSchema{`definitin user {}`}, "Unexpected token at root level"},
   137  			},
   138  		},
   139  
   140  		// Test: try to remove a relation that has at least one relationship.
   141  		{
   142  			"try to remove relation with data",
   143  			[]stcStep{
   144  				// Write the schema.
   145  				{
   146  					writeSchema{`
   147  						definition user {}
   148  					
   149  						definition document {
   150  							relation viewer: user
   151  						}
   152  					`}, "",
   153  				},
   154  				// Write a relationship using the relation.
   155  				{
   156  					createRelationship{"document:foo#viewer@user:tom"}, "",
   157  				},
   158  				// Remove the relation, which should fail.
   159  				{
   160  					writeSchema{`
   161  						definition user {}
   162  					
   163  						definition document {
   164  						}
   165  					`}, "cannot delete relation",
   166  				},
   167  				// Delete the relationship.
   168  				{
   169  					deleteRelationship{"document:foo#viewer@user:tom"}, "",
   170  				},
   171  				// Remove the relation, which should now succeed.
   172  				{
   173  					writeSchema{`
   174  							definition user {}
   175  						
   176  							definition document {
   177  							}
   178  					`}, "",
   179  				},
   180  			},
   181  		},
   182  
   183  		// Test: try to change the types allowed on a relation when existing relationships
   184  		// use the former type.
   185  		{
   186  			"try to modify relation type with data",
   187  			[]stcStep{
   188  				// Write the schema.
   189  				{
   190  					writeSchema{`
   191  						definition user {}
   192  						definition anotheruser {}
   193  					
   194  						definition document {
   195  							relation viewer: user | anotheruser
   196  						}
   197  					`}, "",
   198  				},
   199  				// Write a relationship using the relation.
   200  				{
   201  					createRelationship{"document:foo#viewer@user:tom"}, "",
   202  				},
   203  				// Remove the relation's type, which should fail.
   204  				{
   205  					writeSchema{`
   206  						definition user {}
   207  						definition anotheruser {}
   208  					
   209  						definition document {
   210  							relation viewer: anotheruser
   211  						}
   212  					`}, "cannot remove allowed type `user`",
   213  				},
   214  				// Delete the relationship.
   215  				{
   216  					deleteRelationship{"document:foo#viewer@user:tom"}, "",
   217  				},
   218  				// Remove the relation, which should now succeed.
   219  				{
   220  					writeSchema{`
   221  						definition user {}
   222  						definition anotheruser {}
   223  
   224  						definition document {
   225  							relation viewer: anotheruser
   226  						}
   227  					`}, "",
   228  				},
   229  			},
   230  		},
   231  
   232  		// Test: try to modify the allowed caveat type when it is being used by relationships.
   233  		{
   234  			"try to modify relation caveat with data",
   235  			[]stcStep{
   236  				// Write the schema.
   237  				{
   238  					writeSchema{`
   239  						caveat somecaveat(someParam int) {
   240  							someParam == 42
   241  						}
   242  
   243  						definition user {}
   244  					
   245  						definition document {
   246  							relation viewer: user with somecaveat | user
   247  						}
   248  					`}, "",
   249  				},
   250  				// Write some relationships using the relation.
   251  				{
   252  					createRelationship{"document:foo#viewer@user:tom"}, "",
   253  				},
   254  				{
   255  					createCaveatedRelationship{"document:foo#viewer@user:sarah", "somecaveat", nil}, "",
   256  				},
   257  				// Remove the caveat from the relation, which should fail.
   258  				{
   259  					writeSchema{`
   260  						caveat somecaveat(someParam int) {
   261  							someParam == 42
   262  						}
   263  
   264  						definition user {}
   265  					
   266  						definition document {
   267  							relation viewer: user
   268  						}
   269  					`}, "cannot remove allowed type `user with somecaveat`",
   270  				},
   271  				// Delete the relationship.
   272  				{
   273  					deleteCaveatedRelationship{"document:foo#viewer@user:sarah", "somecaveat"}, "",
   274  				},
   275  				// Remove the caveat from the relation, which should now succeed.
   276  				{
   277  					writeSchema{`
   278  						caveat somecaveat(someParam int) {
   279  							someParam == 42
   280  						}
   281  
   282  						definition user {}
   283  						definition anotheruser {}
   284  
   285  						definition document {
   286  							relation viewer: user
   287  						}
   288  					`}, "",
   289  				},
   290  			},
   291  		},
   292  
   293  		// Test: try to rename a relation that has relationships.
   294  		{
   295  			"try to rename relation with data",
   296  			[]stcStep{
   297  				// Write the schema.
   298  				{
   299  					writeSchema{`
   300  						definition user {}			
   301  
   302  						definition document {
   303  							relation viewer: user
   304  						}
   305  					`}, "",
   306  				},
   307  				// Write a relationship using the relation.
   308  				{
   309  					createRelationship{"document:foo#viewer@user:tom"}, "",
   310  				},
   311  				// Rename the relation, which should fail.
   312  				{
   313  					writeSchema{`
   314  						definition user {}
   315  					
   316  						definition document {
   317  							relation viewuser: user
   318  						}
   319  					`}, "cannot delete relation `viewer`",
   320  				},
   321  				// Delete the relationship.
   322  				{
   323  					deleteRelationship{"document:foo#viewer@user:tom"}, "",
   324  				},
   325  				// Rename the relation, which should now succeed.
   326  				{
   327  					writeSchema{`
   328  						definition user {}
   329  
   330  						definition document {
   331  							relation viewuser: user
   332  						}
   333  					`}, "",
   334  				},
   335  			},
   336  		},
   337  
   338  		// Test: attempt to write a relationship with an unknown resource type.
   339  		{
   340  			"write relationship with unknown resource type",
   341  			[]stcStep{
   342  				// Write the schema.
   343  				{
   344  					writeSchema{`
   345  						definition user {}
   346  
   347  						definition document {
   348  							relation viewer: user
   349  						}
   350  					`}, "",
   351  				},
   352  				// Write a relationship using the relation, but with an undefined type, which should fail.
   353  				{
   354  					createRelationship{"doc:foo#viewer@user:tom"},
   355  					"object definition `doc` not found",
   356  				},
   357  			},
   358  		},
   359  
   360  		// Test: attempt to write a relationship with an unknown relation.
   361  		{
   362  			"write relationship with unknown relation",
   363  			[]stcStep{
   364  				// Write the schema.
   365  				{
   366  					writeSchema{`
   367  						definition user {}
   368  
   369  						definition document {
   370  							relation viewer: user
   371  						}
   372  					`}, "",
   373  				},
   374  				// Write a relationship using an unknown relation, which should fail.
   375  				{
   376  					createRelationship{"document:foo#viewguy@user:tom"},
   377  					"relation/permission `viewguy` not found",
   378  				},
   379  			},
   380  		},
   381  
   382  		// Test: attempt to write a relationship with an unknown subject type.
   383  		{
   384  			"write relationship with unknown subject type",
   385  			[]stcStep{
   386  				// Write the schema.
   387  				{
   388  					writeSchema{`
   389  						definition user {}
   390  
   391  						definition document {
   392  							relation viewer: user
   393  						}
   394  					`}, "",
   395  				},
   396  				// Write a relationship using the relation, but with an undefined type, which should fail.
   397  				{
   398  					createRelationship{"document:foo#viewer@anothersubject:tom"},
   399  					"object definition `anothersubject` not found",
   400  				},
   401  			},
   402  		},
   403  
   404  		// Test: attempt to write a relationship with a disallowed subject type.
   405  		{
   406  			"try to write relationship subject type that is not allowed",
   407  			[]stcStep{
   408  				// Write the schema.
   409  				{
   410  					writeSchema{`
   411  						definition user {}
   412  						definition anothersubject {}
   413  
   414  						definition document {
   415  							relation viewer: user
   416  						}
   417  					`}, "",
   418  				},
   419  				// Write a relationship using the relation, but with the wrong type, which should fail.
   420  				{
   421  					createRelationship{"document:foo#viewer@anothersubject:tom"},
   422  					"subjects of type `anothersubject` are not allowed",
   423  				},
   424  				// Update the schema to add.
   425  				{
   426  					writeSchema{`
   427  						definition user {}
   428  						definition anothersubject {}
   429  
   430  						definition document {
   431  							relation viewer: user | anothersubject
   432  						}
   433  					`}, "",
   434  				},
   435  				// Write a relationship, which should succeed now.
   436  				{
   437  					createRelationship{"document:foo#viewer@anothersubject:tom"}, "",
   438  				},
   439  			},
   440  		},
   441  
   442  		// Test: attempt to write a relationship with a disallowed caveat type.
   443  		{
   444  			"try to write relationship subject caveat type that is not allowed",
   445  			[]stcStep{
   446  				// Write the schema.
   447  				{
   448  					writeSchema{`
   449  						definition user {}
   450  
   451  						definition document {
   452  							relation viewer: user
   453  						}
   454  					`}, "",
   455  				},
   456  				// Write a relationship using the relation, but with the wrong caveat, which should fail.
   457  				{
   458  					createCaveatedRelationship{"document:foo#viewer@user:tom", "somecaveat", nil},
   459  					"subjects of type `user with somecaveat` are not allowed",
   460  				},
   461  			},
   462  		},
   463  
   464  		// Test: attempt to write a relationship with the wrong context parameter type.
   465  		{
   466  			"try to write relationship subject caveat with wrong context parameter type",
   467  			[]stcStep{
   468  				// Write the schema.
   469  				{
   470  					writeSchema{`
   471  						caveat somecaveat(someParam int) {
   472  							someParam == 42
   473  						}
   474  
   475  						definition user {}
   476  
   477  						definition document {
   478  							relation viewer: user with somecaveat
   479  						}
   480  					`}, "",
   481  				},
   482  				// Write a relationship using the caveat, but with the wrong context value.
   483  				{
   484  					createCaveatedRelationship{
   485  						"document:foo#viewer@user:tom",
   486  						"somecaveat",
   487  						map[string]any{
   488  							"someParam": "42e",
   489  						},
   490  					},
   491  					"a int64 value is required, but found invalid string value `42e`",
   492  				},
   493  				// Write a relationship using the caveat, but with the correct context value.
   494  				{
   495  					createCaveatedRelationship{
   496  						"document:foo#viewer@user:tom",
   497  						"somecaveat",
   498  						map[string]any{
   499  							"someParam": "42",
   500  						},
   501  					},
   502  					"",
   503  				},
   504  			},
   505  		},
   506  
   507  		// Test: attempt to write a relationship with an unknown context parameter type.
   508  		{
   509  			"try to write relationship subject caveat with an unknown context parameter type",
   510  			[]stcStep{
   511  				// Write the schema.
   512  				{
   513  					writeSchema{`
   514  						caveat somecaveat(someParam int) {
   515  							someParam == 42
   516  						}
   517  
   518  						definition user {}
   519  
   520  						definition document {
   521  							relation viewer: user with somecaveat
   522  						}
   523  					`}, "",
   524  				},
   525  				// Write a relationship using the caveat, but with an unknown context value.
   526  				{
   527  					createCaveatedRelationship{
   528  						"document:foo#viewer@user:tom",
   529  						"somecaveat",
   530  						map[string]any{
   531  							"someUnknownParam": "",
   532  						},
   533  					},
   534  					"unknown parameter `someUnknownParam`",
   535  				},
   536  			},
   537  		},
   538  
   539  		// Test: attempt to change the parameters on a caveat.
   540  		{
   541  			"caveat parameter changes",
   542  			[]stcStep{
   543  				// Write the initial schema.
   544  				{
   545  					writeSchema{`
   546  						caveat somecaveat(someParam int, anotherParam int) {
   547  							someParam == 42 && anotherParam == 43
   548  						}
   549  
   550  						definition user {}
   551  
   552  						definition document {
   553  							relation viewer: user with somecaveat
   554  						}
   555  					`}, "",
   556  				},
   557  				// Try to add a parameter to the caveat, which should succeed.
   558  				{
   559  					writeSchema{`
   560  							caveat somecaveat(someParam int, anotherParam int, newParam int) {
   561  								someParam == 42 && anotherParam == 43 && newParam == 44
   562  							}
   563  	
   564  							definition user {}
   565  	
   566  							definition document {
   567  								relation viewer: user with somecaveat
   568  							}
   569  						`}, "",
   570  				},
   571  				// Try to remove a parameter from the caveat, which should fail.
   572  				{
   573  					writeSchema{`
   574  							caveat somecaveat(someParam int, anotherParam int) {
   575  								someParam == 42 && anotherParam == 43
   576  							}
   577  	
   578  							definition user {}
   579  	
   580  							definition document {
   581  								relation viewer: user with somecaveat
   582  							}
   583  						`}, "cannot remove parameter `newParam` on caveat `somecaveat`",
   584  				},
   585  				// Try to change the type of a parameter on the caveat, which should fail.
   586  				{
   587  					writeSchema{`
   588  							caveat somecaveat(someParam int, anotherParam int, newParam bool) {
   589  								someParam == 42 && anotherParam == 43 && newParam
   590  							}
   591  	
   592  							definition user {}
   593  	
   594  							definition document {
   595  								relation viewer: user with somecaveat
   596  							}
   597  						`}, "cannot change the type of parameter `newParam` on caveat `somecaveat`",
   598  				},
   599  			},
   600  		},
   601  
   602  		// Test: write relationships differing only by caveat
   603  		{
   604  			"attempt to write two relationships on the same relation, one without caveat and one with",
   605  			[]stcStep{
   606  				// Write the schema.
   607  				{
   608  					writeSchema{`
   609  							caveat somecaveat(someParam int) {
   610  								someParam == 42
   611  							}
   612  
   613  							definition user {}
   614  
   615  							definition document {
   616  								relation viewer: user with somecaveat | user
   617  							}
   618  						`}, "",
   619  				},
   620  				// Write a relationship without the caveat.
   621  				{
   622  					createRelationship{
   623  						"document:foo#viewer@user:tom",
   624  					}, "",
   625  				},
   626  				// Write a relationship using the caveat, which should fail since the relationship already exists.
   627  				{
   628  					createCaveatedRelationship{
   629  						"document:foo#viewer@user:tom",
   630  						"somecaveat",
   631  						nil,
   632  					},
   633  					"as it already existed",
   634  				},
   635  			},
   636  		},
   637  
   638  		// Test: touch relationships differing only by caveat
   639  		{
   640  			"touch a relationship, changing its caveat",
   641  			[]stcStep{
   642  				// Write the schema.
   643  				{
   644  					writeSchema{`
   645  							caveat somecaveat(someParam int) {
   646  								someParam == 42
   647  							}
   648  
   649  							definition user {}
   650  
   651  							definition document {
   652  								relation viewer: user with somecaveat | user
   653  							}
   654  						`}, "",
   655  				},
   656  				// Create a relationship without the caveat.
   657  				{
   658  					createRelationship{
   659  						"document:foo#viewer@user:tom",
   660  					}, "",
   661  				},
   662  				// Touch a relationship using the caveat, which should update the caveat reference.
   663  				{
   664  					touchCaveatedRelationship{
   665  						"document:foo#viewer@user:tom",
   666  						"somecaveat",
   667  						nil,
   668  					},
   669  					"",
   670  				},
   671  				// Attempt to remove the caveat, which should fail since there is a relationship using it.
   672  				{
   673  					writeSchema{`
   674  							caveat somecaveat(someParam int) {
   675  								someParam == 42
   676  							}
   677  
   678  							definition user {}
   679  
   680  							definition document {
   681  								relation viewer: user
   682  							}
   683  						`}, "cannot remove allowed type `user with somecaveat`",
   684  				},
   685  			},
   686  		},
   687  
   688  		// Test: write a schema, add a caveat definition, read back, then remove, and continue.
   689  		{
   690  			"add and remove caveat in schema",
   691  			[]stcStep{
   692  				// Write the initial schema.
   693  				{
   694  					writeSchema{`
   695  							definition user {}
   696  
   697  							definition document {
   698  								relation viewer: user
   699  							}
   700  						`}, "",
   701  				},
   702  				// Read back.
   703  				{
   704  					readSchema{"definition document {\n\trelation viewer: user\n}\n\ndefinition user {}"}, "",
   705  				},
   706  				// Add a caveat definition.
   707  				{
   708  					writeSchema{`
   709  							definition user {}
   710  
   711  							caveat someCaveat(somecondition int) {
   712  								somecondition == 42
   713  							}
   714  
   715  							definition document {
   716  								relation viewer: user
   717  							}
   718  						`}, "",
   719  				},
   720  				// Read back.
   721  				{
   722  					readSchema{"caveat someCaveat(somecondition int) {\n\tsomecondition == 42\n}\n\ndefinition document {\n\trelation viewer: user\n}\n\ndefinition user {}"}, "",
   723  				},
   724  				// Remove the caveat definition.
   725  				{
   726  					writeSchema{`
   727  							definition user {}
   728  
   729  							definition document {
   730  								relation viewer: user
   731  							}
   732  						`}, "",
   733  				},
   734  				// Read back.
   735  				{
   736  					readSchema{"definition document {\n\trelation viewer: user\n}\n\ndefinition user {}"}, "",
   737  				},
   738  			},
   739  		},
   740  	}
   741  
   742  	testers := map[string]func(conn grpc.ClientConnInterface) opsTester{
   743  		"v1": func(conn grpc.ClientConnInterface) opsTester {
   744  			return v1OpsTester{
   745  				schemaClient:  v1.NewSchemaServiceClient(conn),
   746  				baseOpsTester: baseOpsTester{permClient: v1.NewPermissionsServiceClient(conn)},
   747  			}
   748  		},
   749  	}
   750  
   751  	for _, tc := range tcs {
   752  		tc := tc
   753  		t.Run(tc.name, func(t *testing.T) {
   754  			for _, testerName := range []string{"v1"} {
   755  				testerName := testerName
   756  				t.Run(testerName, func(t *testing.T) {
   757  					conn, cleanup, _, _ := testserver.NewTestServer(require.New(t), 0, memdb.DisableGC, false, tf.EmptyDatastore)
   758  					t.Cleanup(cleanup)
   759  
   760  					tester := testers[testerName](conn)
   761  
   762  					for _, step := range tc.steps {
   763  						err := step.op.Execute(tester)
   764  						if step.expectedError != "" {
   765  							require.NotNil(t, err)
   766  							require.Contains(t, err.Error(), step.expectedError)
   767  						} else {
   768  							require.NoError(t, err)
   769  						}
   770  					}
   771  				})
   772  			}
   773  		})
   774  	}
   775  }
   776  
   777  type opsTester interface {
   778  	Name() string
   779  	ReadSchema(ctx context.Context) (string, error)
   780  	WriteSchema(ctx context.Context, schemaString string) error
   781  	CreateRelationship(ctx context.Context, relationship *core.RelationTuple) error
   782  	TouchRelationship(ctx context.Context, relationship *core.RelationTuple) error
   783  	DeleteRelationship(ctx context.Context, relationship *core.RelationTuple) error
   784  }
   785  
   786  type baseOpsTester struct {
   787  	permClient v1.PermissionsServiceClient
   788  }
   789  
   790  func (st baseOpsTester) CreateRelationship(ctx context.Context, relationship *core.RelationTuple) error {
   791  	_, err := st.permClient.WriteRelationships(ctx, &v1.WriteRelationshipsRequest{
   792  		Updates: []*v1.RelationshipUpdate{tuple.UpdateToRelationshipUpdate(tuple.Create(relationship))},
   793  	})
   794  	return err
   795  }
   796  
   797  func (st baseOpsTester) TouchRelationship(ctx context.Context, relationship *core.RelationTuple) error {
   798  	_, err := st.permClient.WriteRelationships(ctx, &v1.WriteRelationshipsRequest{
   799  		Updates: []*v1.RelationshipUpdate{tuple.UpdateToRelationshipUpdate(tuple.Touch(relationship))},
   800  	})
   801  	return err
   802  }
   803  
   804  func (st baseOpsTester) DeleteRelationship(ctx context.Context, relationship *core.RelationTuple) error {
   805  	_, err := st.permClient.WriteRelationships(ctx, &v1.WriteRelationshipsRequest{
   806  		Updates: []*v1.RelationshipUpdate{tuple.UpdateToRelationshipUpdate(tuple.Delete(relationship))},
   807  	})
   808  	return err
   809  }
   810  
   811  type v1OpsTester struct {
   812  	baseOpsTester
   813  	schemaClient v1.SchemaServiceClient
   814  }
   815  
   816  func (st v1OpsTester) Name() string {
   817  	return "v1"
   818  }
   819  
   820  func (st v1OpsTester) WriteSchema(ctx context.Context, schemaString string) error {
   821  	_, err := st.schemaClient.WriteSchema(ctx, &v1.WriteSchemaRequest{
   822  		Schema: schemaString,
   823  	})
   824  	return err
   825  }
   826  
   827  func (st v1OpsTester) ReadSchema(ctx context.Context) (string, error) {
   828  	resp, err := st.schemaClient.ReadSchema(ctx, &v1.ReadSchemaRequest{})
   829  	if err != nil {
   830  		return "", err
   831  	}
   832  	return resp.SchemaText, nil
   833  }