github.com/openfga/openfga@v1.5.4-rc1/pkg/server/server_test.go (about)

     1  package server
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"math"
     8  	"os"
     9  	"path"
    10  	"runtime"
    11  	"strconv"
    12  	"sync"
    13  	"testing"
    14  	"time"
    15  
    16  	"github.com/oklog/ulid/v2"
    17  	openfgav1 "github.com/openfga/api/proto/openfga/v1"
    18  	language "github.com/openfga/language/pkg/go/transformer"
    19  	"github.com/stretchr/testify/require"
    20  	"go.uber.org/goleak"
    21  	"go.uber.org/mock/gomock"
    22  	"google.golang.org/grpc"
    23  	"google.golang.org/grpc/codes"
    24  	"google.golang.org/grpc/status"
    25  
    26  	"github.com/openfga/openfga/cmd/migrate"
    27  	"github.com/openfga/openfga/cmd/util"
    28  	"github.com/openfga/openfga/internal/build"
    29  	"github.com/openfga/openfga/internal/graph"
    30  	mockstorage "github.com/openfga/openfga/internal/mocks"
    31  	serverconfig "github.com/openfga/openfga/internal/server/config"
    32  	"github.com/openfga/openfga/pkg/server/commands"
    33  	serverErrors "github.com/openfga/openfga/pkg/server/errors"
    34  	"github.com/openfga/openfga/pkg/server/test"
    35  	"github.com/openfga/openfga/pkg/storage"
    36  	"github.com/openfga/openfga/pkg/storage/memory"
    37  	"github.com/openfga/openfga/pkg/storage/mysql"
    38  	"github.com/openfga/openfga/pkg/storage/postgres"
    39  	"github.com/openfga/openfga/pkg/storage/sqlcommon"
    40  	"github.com/openfga/openfga/pkg/storage/storagewrappers"
    41  	storagefixtures "github.com/openfga/openfga/pkg/testfixtures/storage"
    42  	"github.com/openfga/openfga/pkg/testutils"
    43  	"github.com/openfga/openfga/pkg/tuple"
    44  	"github.com/openfga/openfga/pkg/typesystem"
    45  )
    46  
    47  func init() {
    48  	_, filename, _, _ := runtime.Caller(0)
    49  	dir := path.Join(path.Dir(filename), "..", "..")
    50  	err := os.Chdir(dir)
    51  	if err != nil {
    52  		panic(err)
    53  	}
    54  }
    55  
    56  func ExampleNewServerWithOpts() {
    57  	datastore := memory.New() // other supported datastores include Postgres and MySQL
    58  	defer datastore.Close()
    59  
    60  	openfga, err := NewServerWithOpts(WithDatastore(datastore),
    61  		WithCheckQueryCacheEnabled(true),
    62  		// more options available
    63  	)
    64  	if err != nil {
    65  		panic(err)
    66  	}
    67  	defer openfga.Close()
    68  
    69  	// create store
    70  	store, err := openfga.CreateStore(context.Background(),
    71  		&openfgav1.CreateStoreRequest{Name: "demo"})
    72  	if err != nil {
    73  		panic(err)
    74  	}
    75  
    76  	model := language.MustTransformDSLToProto(`
    77  	model
    78  		schema 1.1
    79  	type user
    80  
    81  	type document
    82  		relations
    83  			define reader: [user]`)
    84  
    85  	// write the model to the store
    86  	authorizationModel, err := openfga.WriteAuthorizationModel(context.Background(), &openfgav1.WriteAuthorizationModelRequest{
    87  		StoreId:         store.GetId(),
    88  		TypeDefinitions: model.GetTypeDefinitions(),
    89  		Conditions:      model.GetConditions(),
    90  		SchemaVersion:   model.GetSchemaVersion(),
    91  	})
    92  	if err != nil {
    93  		panic(err)
    94  	}
    95  
    96  	// write tuples to the store
    97  	_, err = openfga.Write(context.Background(), &openfgav1.WriteRequest{
    98  		StoreId: store.GetId(),
    99  		Writes: &openfgav1.WriteRequestWrites{
   100  			TupleKeys: []*openfgav1.TupleKey{
   101  				{Object: "document:budget", Relation: "reader", User: "user:anne"},
   102  			},
   103  		},
   104  		Deletes: nil,
   105  	})
   106  	if err != nil {
   107  		panic(err)
   108  	}
   109  
   110  	// make an authorization check
   111  	checkResponse, err := openfga.Check(context.Background(), &openfgav1.CheckRequest{
   112  		StoreId:              store.GetId(),
   113  		AuthorizationModelId: authorizationModel.GetAuthorizationModelId(), // optional, but recommended for speed
   114  		TupleKey: &openfgav1.CheckRequestTupleKey{
   115  			User:     "user:anne",
   116  			Relation: "reader",
   117  			Object:   "document:budget",
   118  		},
   119  	})
   120  	if err != nil {
   121  		panic(err)
   122  	}
   123  	fmt.Println(checkResponse.GetAllowed())
   124  	// Output: true
   125  }
   126  
   127  func TestServerPanicIfNoDatastore(t *testing.T) {
   128  	require.PanicsWithError(t, "failed to construct the OpenFGA server: a datastore option must be provided", func() {
   129  		_ = MustNewServerWithOpts()
   130  	})
   131  }
   132  
   133  func TestServerNotReadyDueToDatastoreRevision(t *testing.T) {
   134  	engines := []string{"postgres", "mysql"}
   135  
   136  	for _, engine := range engines {
   137  		t.Run(engine, func(t *testing.T) {
   138  			_, ds, uri := util.MustBootstrapDatastore(t, engine)
   139  
   140  			targetVersion := build.MinimumSupportedDatastoreSchemaRevision - 1
   141  
   142  			migrateCommand := migrate.NewMigrateCommand()
   143  
   144  			migrateCommand.SetArgs([]string{"--datastore-engine", engine, "--datastore-uri", uri, "--version", strconv.Itoa(int(targetVersion))})
   145  
   146  			err := migrateCommand.Execute()
   147  			require.NoError(t, err)
   148  
   149  			status, _ := ds.IsReady(context.Background())
   150  			require.Contains(t, status.Message, fmt.Sprintf("datastore requires migrations: at revision '%d', but requires '%d'.", targetVersion, build.MinimumSupportedDatastoreSchemaRevision))
   151  			require.False(t, status.IsReady)
   152  		})
   153  	}
   154  }
   155  
   156  func TestServerPanicIfEmptyRequestDurationDatastoreCountBuckets(t *testing.T) {
   157  	require.PanicsWithError(t, "failed to construct the OpenFGA server: request duration datastore count buckets must not be empty", func() {
   158  		mockController := gomock.NewController(t)
   159  		defer mockController.Finish()
   160  		mockDatastore := mockstorage.NewMockOpenFGADatastore(mockController)
   161  		_ = MustNewServerWithOpts(
   162  			WithDatastore(mockDatastore),
   163  			WithRequestDurationByQueryHistogramBuckets([]uint{}),
   164  		)
   165  	})
   166  }
   167  
   168  func TestServerPanicIfEmptyRequestDurationDispatchCountBuckets(t *testing.T) {
   169  	require.PanicsWithError(t, "failed to construct the OpenFGA server: request duration by dispatch count buckets must not be empty", func() {
   170  		mockController := gomock.NewController(t)
   171  		defer mockController.Finish()
   172  		mockDatastore := mockstorage.NewMockOpenFGADatastore(mockController)
   173  		_ = MustNewServerWithOpts(
   174  			WithDatastore(mockDatastore),
   175  			WithRequestDurationByDispatchCountHistogramBuckets([]uint{}),
   176  		)
   177  	})
   178  }
   179  
   180  func TestServerPanicIfDefaultDispatchThresholdGreaterThanMaxDispatchThreshold(t *testing.T) {
   181  	require.PanicsWithError(t, "failed to construct the OpenFGA server: default dispatch throttling threshold must be equal or smaller than max dispatch threshold", func() {
   182  		mockController := gomock.NewController(t)
   183  		defer mockController.Finish()
   184  		mockDatastore := mockstorage.NewMockOpenFGADatastore(mockController)
   185  		_ = MustNewServerWithOpts(
   186  			WithDatastore(mockDatastore),
   187  			WithDispatchThrottlingCheckResolverEnabled(true),
   188  			WithDispatchThrottlingCheckResolverThreshold(100),
   189  			WithDispatchThrottlingCheckResolverMaxThreshold(80),
   190  		)
   191  	})
   192  }
   193  
   194  func TestServerWithPostgresDatastore(t *testing.T) {
   195  	t.Cleanup(func() {
   196  		goleak.VerifyNone(t)
   197  	})
   198  	_, ds, _ := util.MustBootstrapDatastore(t, "postgres")
   199  
   200  	test.RunAllTests(t, ds)
   201  }
   202  
   203  func TestServerWithPostgresDatastoreAndExplicitCredentials(t *testing.T) {
   204  	t.Cleanup(func() {
   205  		goleak.VerifyNone(t)
   206  	})
   207  	testDatastore := storagefixtures.RunDatastoreTestContainer(t, "postgres")
   208  
   209  	uri := testDatastore.GetConnectionURI(false)
   210  	ds, err := postgres.New(
   211  		uri,
   212  		sqlcommon.NewConfig(
   213  			sqlcommon.WithUsername(testDatastore.GetUsername()),
   214  			sqlcommon.WithPassword(testDatastore.GetPassword()),
   215  		),
   216  	)
   217  	require.NoError(t, err)
   218  	defer ds.Close()
   219  
   220  	test.RunAllTests(t, ds)
   221  }
   222  
   223  func TestServerWithMemoryDatastore(t *testing.T) {
   224  	t.Cleanup(func() {
   225  		goleak.VerifyNone(t)
   226  	})
   227  	_, ds, _ := util.MustBootstrapDatastore(t, "memory")
   228  
   229  	test.RunAllTests(t, ds)
   230  }
   231  
   232  func TestServerWithMySQLDatastore(t *testing.T) {
   233  	t.Cleanup(func() {
   234  		goleak.VerifyNone(t)
   235  	})
   236  	_, ds, _ := util.MustBootstrapDatastore(t, "mysql")
   237  
   238  	test.RunAllTests(t, ds)
   239  }
   240  
   241  func TestServerWithMySQLDatastoreAndExplicitCredentials(t *testing.T) {
   242  	t.Cleanup(func() {
   243  		goleak.VerifyNone(t)
   244  	})
   245  	testDatastore := storagefixtures.RunDatastoreTestContainer(t, "mysql")
   246  
   247  	uri := testDatastore.GetConnectionURI(false)
   248  	ds, err := mysql.New(
   249  		uri,
   250  		sqlcommon.NewConfig(
   251  			sqlcommon.WithUsername(testDatastore.GetUsername()),
   252  			sqlcommon.WithPassword(testDatastore.GetPassword()),
   253  		),
   254  	)
   255  	require.NoError(t, err)
   256  	defer ds.Close()
   257  
   258  	test.RunAllTests(t, ds)
   259  }
   260  
   261  func TestCheckResolverOuterLayerDefault(t *testing.T) {
   262  	t.Cleanup(func() {
   263  		goleak.VerifyNone(t)
   264  	})
   265  
   266  	_, ds, _ := util.MustBootstrapDatastore(t, "memory")
   267  
   268  	s := MustNewServerWithOpts(
   269  		WithDatastore(ds),
   270  	)
   271  	t.Cleanup(s.Close)
   272  
   273  	// the default (outer most layer) of the CheckResolver
   274  	// composition should always be CycleDetectionCheckResolver.
   275  	_, ok := s.checkResolver.(*graph.CycleDetectionCheckResolver)
   276  	require.True(t, ok)
   277  }
   278  
   279  func TestAvoidDeadlockAcrossCheckRequests(t *testing.T) {
   280  	t.Cleanup(func() {
   281  		goleak.VerifyNone(t)
   282  	})
   283  
   284  	_, ds, _ := util.MustBootstrapDatastore(t, "memory")
   285  
   286  	s := MustNewServerWithOpts(
   287  		WithDatastore(ds),
   288  	)
   289  	t.Cleanup(s.Close)
   290  
   291  	createStoreResp, err := s.CreateStore(context.Background(), &openfgav1.CreateStoreRequest{
   292  		Name: "openfga-test",
   293  	})
   294  	require.NoError(t, err)
   295  
   296  	storeID := createStoreResp.GetId()
   297  
   298  	model := testutils.MustTransformDSLToProtoWithID(`model
   299  	schema 1.1
   300  
   301  	type user
   302  
   303  	type document
   304  	  relations
   305  			define viewer: [user, document#viewer] or editor
   306  			define editor: [user, document#viewer]`)
   307  
   308  	writeAuthModelResp, err := s.WriteAuthorizationModel(context.Background(), &openfgav1.WriteAuthorizationModelRequest{
   309  		StoreId:         storeID,
   310  		SchemaVersion:   model.GetSchemaVersion(),
   311  		TypeDefinitions: model.GetTypeDefinitions(),
   312  	})
   313  	require.NoError(t, err)
   314  
   315  	modelID := writeAuthModelResp.GetAuthorizationModelId()
   316  
   317  	_, err = s.Write(context.Background(), &openfgav1.WriteRequest{
   318  		StoreId: storeID,
   319  		Writes: &openfgav1.WriteRequestWrites{
   320  			TupleKeys: []*openfgav1.TupleKey{
   321  				tuple.NewTupleKey("document:1", "editor", "document:1#viewer"),
   322  				tuple.NewTupleKey("document:1", "editor", "user:andres"),
   323  			},
   324  		},
   325  	})
   326  	require.NoError(t, err)
   327  
   328  	var wg sync.WaitGroup
   329  
   330  	wg.Add(3)
   331  
   332  	var resp1 *openfgav1.CheckResponse
   333  	var err1 error
   334  	go func() {
   335  		defer wg.Done()
   336  
   337  		resp1, err1 = s.Check(context.Background(), &openfgav1.CheckRequest{
   338  			StoreId:              storeID,
   339  			AuthorizationModelId: modelID,
   340  			TupleKey:             tuple.NewCheckRequestTupleKey("document:1", "editor", "user:jon"),
   341  		})
   342  	}()
   343  
   344  	var resp2 *openfgav1.CheckResponse
   345  	var err2 error
   346  	go func() {
   347  		defer wg.Done()
   348  
   349  		resp2, err2 = s.Check(context.Background(), &openfgav1.CheckRequest{
   350  			StoreId:              storeID,
   351  			AuthorizationModelId: modelID,
   352  			TupleKey:             tuple.NewCheckRequestTupleKey("document:1", "viewer", "user:jon"),
   353  		})
   354  	}()
   355  
   356  	var resp3 *openfgav1.CheckResponse
   357  	var err3 error
   358  	go func() {
   359  		defer wg.Done()
   360  
   361  		resp3, err3 = s.Check(context.Background(), &openfgav1.CheckRequest{
   362  			StoreId:              storeID,
   363  			AuthorizationModelId: modelID,
   364  			TupleKey:             tuple.NewCheckRequestTupleKey("document:1", "viewer", "user:andres"),
   365  		})
   366  	}()
   367  
   368  	wg.Wait()
   369  
   370  	require.NoError(t, err1)
   371  	require.NotNil(t, resp1)
   372  	require.False(t, resp1.GetAllowed())
   373  
   374  	require.NoError(t, err2)
   375  	require.NotNil(t, resp2)
   376  
   377  	require.NoError(t, err3)
   378  	require.NotNil(t, resp3)
   379  	require.True(t, resp3.GetAllowed())
   380  }
   381  
   382  func TestAvoidDeadlockWithinSingleCheckRequest(t *testing.T) {
   383  	t.Cleanup(func() {
   384  		goleak.VerifyNone(t)
   385  	})
   386  
   387  	_, ds, _ := util.MustBootstrapDatastore(t, "memory")
   388  
   389  	s := MustNewServerWithOpts(
   390  		WithDatastore(ds),
   391  	)
   392  	t.Cleanup(s.Close)
   393  
   394  	createStoreResp, err := s.CreateStore(context.Background(), &openfgav1.CreateStoreRequest{
   395  		Name: "openfga-test",
   396  	})
   397  	require.NoError(t, err)
   398  
   399  	storeID := createStoreResp.GetId()
   400  
   401  	model := testutils.MustTransformDSLToProtoWithID(`model
   402  			schema 1.1
   403  
   404  		  type user
   405  
   406  		  type document
   407  			relations
   408  			  define editor1: [user, document#viewer1]
   409  
   410  			  define viewer2: [document#viewer1] or editor1
   411  			  define viewer1: [user] or viewer2
   412  			  define can_view: viewer1 or editor1`)
   413  
   414  	writeAuthModelResp, err := s.WriteAuthorizationModel(context.Background(), &openfgav1.WriteAuthorizationModelRequest{
   415  		StoreId:         storeID,
   416  		SchemaVersion:   model.GetSchemaVersion(),
   417  		TypeDefinitions: model.GetTypeDefinitions(),
   418  	})
   419  	require.NoError(t, err)
   420  
   421  	modelID := writeAuthModelResp.GetAuthorizationModelId()
   422  
   423  	_, err = s.Write(context.Background(), &openfgav1.WriteRequest{
   424  		StoreId: storeID,
   425  		Writes: &openfgav1.WriteRequestWrites{
   426  			TupleKeys: []*openfgav1.TupleKey{
   427  				tuple.NewTupleKey("document:1", "editor1", "document:1#viewer1"),
   428  			},
   429  		},
   430  	})
   431  	require.NoError(t, err)
   432  
   433  	resp, err := s.Check(context.Background(), &openfgav1.CheckRequest{
   434  		StoreId:              storeID,
   435  		AuthorizationModelId: modelID,
   436  		TupleKey:             tuple.NewCheckRequestTupleKey("document:1", "can_view", "user:jon"),
   437  	})
   438  
   439  	require.NoError(t, err)
   440  	require.NotNil(t, resp)
   441  	require.False(t, resp.GetAllowed())
   442  }
   443  
   444  func TestThreeProngThroughVariousLayers(t *testing.T) {
   445  	t.Cleanup(func() {
   446  		goleak.VerifyNone(t)
   447  	})
   448  
   449  	_, ds, _ := util.MustBootstrapDatastore(t, "memory")
   450  
   451  	s := MustNewServerWithOpts(
   452  		WithDatastore(ds),
   453  	)
   454  	t.Cleanup(func() {
   455  		s.Close()
   456  	})
   457  
   458  	createStoreResp, err := s.CreateStore(context.Background(), &openfgav1.CreateStoreRequest{
   459  		Name: "openfga-test",
   460  	})
   461  	require.NoError(t, err)
   462  
   463  	storeID := createStoreResp.GetId()
   464  
   465  	model := testutils.MustTransformDSLToProtoWithID(`model
   466  	schema 1.1
   467  
   468    type user
   469    type module
   470    relations
   471  	  define owner: [user] or owner from parent
   472  	  define parent: [document, module]
   473  	  define viewer: [user] or owner or viewer from parent
   474    type folder
   475    relations
   476  	  define owner: [user] or owner from parent
   477  	  define parent: [module, folder]
   478  	  define viewer: [user] or owner or viewer from parent
   479    type document
   480    relations
   481  	  define owner: [user] or owner from parent
   482  	  define parent: [folder, document]
   483  	  define viewer: [user] or owner or viewer from parent`)
   484  
   485  	writeAuthModelResp, err := s.WriteAuthorizationModel(context.Background(), &openfgav1.WriteAuthorizationModelRequest{
   486  		StoreId:         storeID,
   487  		SchemaVersion:   model.GetSchemaVersion(),
   488  		TypeDefinitions: model.GetTypeDefinitions(),
   489  	})
   490  	require.NoError(t, err)
   491  
   492  	modelID := writeAuthModelResp.GetAuthorizationModelId()
   493  
   494  	_, err = s.Write(context.Background(), &openfgav1.WriteRequest{
   495  		StoreId: storeID,
   496  		Writes: &openfgav1.WriteRequestWrites{
   497  			TupleKeys: []*openfgav1.TupleKey{
   498  				tuple.NewTupleKey("module:a", "owner", "user:anne"),
   499  				tuple.NewTupleKey("folder:a", "parent", "module:a"),
   500  				tuple.NewTupleKey("document:a", "parent", "folder:a"),
   501  				tuple.NewTupleKey("module:b", "parent", "document:a"),
   502  				tuple.NewTupleKey("folder:b", "parent", "module:b"),
   503  				tuple.NewTupleKey("document:b", "parent", "folder:b"),
   504  				tuple.NewTupleKey("module:a", "parent", "document:b"),
   505  			},
   506  		},
   507  	})
   508  	require.NoError(t, err)
   509  
   510  	for i := 0; i < 100; i++ {
   511  		t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
   512  			tupleKeys := []*openfgav1.CheckRequestTupleKey{
   513  				tuple.NewCheckRequestTupleKey("module:a", "viewer", "user:anne"),
   514  				tuple.NewCheckRequestTupleKey("module:b", "viewer", "user:anne"),
   515  				tuple.NewCheckRequestTupleKey("folder:a", "viewer", "user:anne"),
   516  				tuple.NewCheckRequestTupleKey("folder:b", "viewer", "user:anne"),
   517  				tuple.NewCheckRequestTupleKey("document:a", "viewer", "user:anne"),
   518  				tuple.NewCheckRequestTupleKey("document:b", "viewer", "user:anne"),
   519  			}
   520  
   521  			for _, tupleKey := range tupleKeys {
   522  				resp, err := s.Check(context.Background(), &openfgav1.CheckRequest{
   523  					StoreId:              storeID,
   524  					AuthorizationModelId: modelID,
   525  					TupleKey:             tupleKey,
   526  				})
   527  				require.NoError(t, err)
   528  				require.True(t, resp.GetAllowed())
   529  			}
   530  		})
   531  	}
   532  }
   533  
   534  func TestCheckDispatchThrottledTimeout(t *testing.T) {
   535  	t.Cleanup(func() {
   536  		goleak.VerifyNone(t)
   537  	})
   538  
   539  	const dispatchFrequency = 5 * time.Millisecond
   540  	const dispatchThreshold = 5
   541  
   542  	_, ds, _ := util.MustBootstrapDatastore(t, "memory")
   543  	s := MustNewServerWithOpts(
   544  		WithDatastore(ds),
   545  		WithDispatchThrottlingCheckResolverFrequency(dispatchFrequency),
   546  		WithDispatchThrottlingCheckResolverEnabled(true),
   547  		WithDispatchThrottlingCheckResolverThreshold(dispatchThreshold),
   548  	)
   549  	t.Cleanup(s.Close)
   550  
   551  	createStoreResp, err := s.CreateStore(context.Background(), &openfgav1.CreateStoreRequest{
   552  		Name: "openfga-test",
   553  	})
   554  	require.NoError(t, err)
   555  
   556  	storeID := createStoreResp.GetId()
   557  
   558  	model := testutils.MustTransformDSLToProtoWithID(`model
   559  		schema 1.1
   560  
   561    type user
   562  
   563    type group
   564      relations
   565        define member: [user, group#member]
   566  `)
   567  
   568  	writeAuthModelResp, err := s.WriteAuthorizationModel(context.Background(), &openfgav1.WriteAuthorizationModelRequest{
   569  		StoreId:         storeID,
   570  		SchemaVersion:   model.GetSchemaVersion(),
   571  		TypeDefinitions: model.GetTypeDefinitions(),
   572  	})
   573  	require.NoError(t, err)
   574  
   575  	modelID := writeAuthModelResp.GetAuthorizationModelId()
   576  
   577  	_, err = s.Write(context.Background(), &openfgav1.WriteRequest{
   578  		StoreId: storeID,
   579  		Writes: &openfgav1.WriteRequestWrites{
   580  			TupleKeys: []*openfgav1.TupleKey{
   581  				tuple.NewTupleKey("group:x", "member", "group:1#member"),
   582  				tuple.NewTupleKey("group:x", "member", "group:2#member"),
   583  				tuple.NewTupleKey("group:x", "member", "group:3#member"),
   584  				tuple.NewTupleKey("group:x", "member", "group:4#member"),
   585  				tuple.NewTupleKey("group:x", "member", "group:5#member"),
   586  				tuple.NewTupleKey("group:x", "member", "group:6#member"),
   587  				tuple.NewTupleKey("group:x", "member", "group:7#member"),
   588  				tuple.NewTupleKey("group:x", "member", "group:8#member"),
   589  				tuple.NewTupleKey("group:x", "member", "group:9#member"),
   590  				tuple.NewTupleKey("group:x", "member", "group:10#member"),
   591  			},
   592  		},
   593  	})
   594  	require.NoError(t, err)
   595  
   596  	// we know that the above query will take 10 dispatches
   597  	// Since the threshold level is 5 and each dispatch will be throttled by 5ms
   598  	// The request will take at least 25 ms and will be timeout since timeout is 20ms.
   599  	ctx, cancel := context.WithTimeout(context.Background(), 20*time.Millisecond)
   600  	defer cancel()
   601  
   602  	_, err = s.Check(ctx, &openfgav1.CheckRequest{
   603  		StoreId:              storeID,
   604  		AuthorizationModelId: modelID,
   605  		TupleKey:             tuple.NewCheckRequestTupleKey("group:x", "member", "user:anne"),
   606  	})
   607  	require.ErrorIs(t, err, serverErrors.ThrottledTimeout)
   608  }
   609  
   610  func BenchmarkOpenFGAServer(b *testing.B) {
   611  	b.Cleanup(func() {
   612  		goleak.VerifyNone(b,
   613  			// https://github.com/uber-go/goleak/discussions/89
   614  			goleak.IgnoreTopFunction("testing.(*B).run1"),
   615  			goleak.IgnoreTopFunction("testing.(*B).doBench"),
   616  		)
   617  	})
   618  	b.Run("BenchmarkPostgresDatastore", func(b *testing.B) {
   619  		testDatastore := storagefixtures.RunDatastoreTestContainer(b, "postgres")
   620  
   621  		uri := testDatastore.GetConnectionURI(true)
   622  		ds, err := postgres.New(uri, sqlcommon.NewConfig())
   623  		require.NoError(b, err)
   624  		b.Cleanup(ds.Close)
   625  		test.RunAllBenchmarks(b, ds)
   626  	})
   627  
   628  	b.Run("BenchmarkMemoryDatastore", func(b *testing.B) {
   629  		ds := memory.New()
   630  		b.Cleanup(ds.Close)
   631  		test.RunAllBenchmarks(b, ds)
   632  	})
   633  
   634  	b.Run("BenchmarkMySQLDatastore", func(b *testing.B) {
   635  		testDatastore := storagefixtures.RunDatastoreTestContainer(b, "mysql")
   636  
   637  		uri := testDatastore.GetConnectionURI(true)
   638  		ds, err := mysql.New(uri, sqlcommon.NewConfig())
   639  		require.NoError(b, err)
   640  		b.Cleanup(ds.Close)
   641  		test.RunAllBenchmarks(b, ds)
   642  	})
   643  }
   644  
   645  func TestCheckDoesNotThrowBecauseDirectTupleWasFound(t *testing.T) {
   646  	t.Cleanup(func() {
   647  		goleak.VerifyNone(t)
   648  	})
   649  	ctx := context.Background()
   650  	storeID := ulid.Make().String()
   651  	modelID := ulid.Make().String()
   652  
   653  	typedefs := language.MustTransformDSLToProto(`model
   654  	schema 1.1
   655  type user
   656  
   657  type repo
   658    relations
   659  	define reader: [user]
   660  `).GetTypeDefinitions()
   661  
   662  	tk := tuple.NewCheckRequestTupleKey("repo:openfga", "reader", "user:anne")
   663  	returnedTuple := &openfgav1.Tuple{Key: tuple.ConvertCheckRequestTupleKeyToTupleKey(tk)}
   664  
   665  	mockController := gomock.NewController(t)
   666  	defer mockController.Finish()
   667  
   668  	mockDatastore := mockstorage.NewMockOpenFGADatastore(mockController)
   669  
   670  	mockDatastore.EXPECT().
   671  		ReadAuthorizationModel(gomock.Any(), storeID, modelID).
   672  		AnyTimes().
   673  		Return(&openfgav1.AuthorizationModel{
   674  			SchemaVersion:   typesystem.SchemaVersion1_1,
   675  			TypeDefinitions: typedefs,
   676  		}, nil)
   677  
   678  	// it could happen that one of the following two mocks won't be necessary because the goroutine will be short-circuited
   679  	mockDatastore.EXPECT().
   680  		ReadUserTuple(gomock.Any(), storeID, gomock.Any()).
   681  		AnyTimes().
   682  		Return(returnedTuple, nil)
   683  
   684  	mockDatastore.EXPECT().
   685  		ReadUsersetTuples(gomock.Any(), storeID, gomock.Any()).
   686  		AnyTimes().
   687  		DoAndReturn(
   688  			func(_ context.Context, _ string, _ storage.ReadUsersetTuplesFilter) (storage.TupleIterator, error) {
   689  				time.Sleep(50 * time.Millisecond)
   690  				return nil, errors.New("some error")
   691  			})
   692  
   693  	s := MustNewServerWithOpts(
   694  		WithDatastore(mockDatastore),
   695  	)
   696  	t.Cleanup(s.Close)
   697  
   698  	checkResponse, err := s.Check(ctx, &openfgav1.CheckRequest{
   699  		StoreId:              storeID,
   700  		TupleKey:             tk,
   701  		AuthorizationModelId: modelID,
   702  	})
   703  	require.NoError(t, err)
   704  	require.True(t, checkResponse.GetAllowed())
   705  }
   706  
   707  func TestReleasesConnections(t *testing.T) {
   708  	t.Cleanup(func() {
   709  		goleak.VerifyNone(t)
   710  	})
   711  
   712  	testDatastore := storagefixtures.RunDatastoreTestContainer(t, "postgres")
   713  
   714  	uri := testDatastore.GetConnectionURI(true)
   715  	ds, err := postgres.New(uri, sqlcommon.NewConfig(
   716  		sqlcommon.WithMaxOpenConns(1),
   717  		sqlcommon.WithMaxTuplesPerWrite(2000),
   718  	))
   719  	require.NoError(t, err)
   720  	defer ds.Close()
   721  
   722  	s := MustNewServerWithOpts(
   723  		WithDatastore(storagewrappers.NewContextWrapper(ds)),
   724  		WithExperimentals(ExperimentalEnableListUsers),
   725  	)
   726  	t.Cleanup(s.Close)
   727  
   728  	storeID := ulid.Make().String()
   729  
   730  	writeAuthzModelResp, err := s.WriteAuthorizationModel(context.Background(), &openfgav1.WriteAuthorizationModelRequest{
   731  		StoreId: storeID,
   732  		TypeDefinitions: language.MustTransformDSLToProto(`model
   733  	schema 1.1
   734  type user
   735  
   736  type document
   737    relations
   738  	define editor: [user]`).GetTypeDefinitions(),
   739  		SchemaVersion: typesystem.SchemaVersion1_1,
   740  	})
   741  	require.NoError(t, err)
   742  
   743  	modelID := writeAuthzModelResp.GetAuthorizationModelId()
   744  
   745  	numTuples := 2000
   746  
   747  	t.Run("list_objects", func(t *testing.T) {
   748  		tuples := make([]*openfgav1.TupleKey, 0, numTuples)
   749  		for i := 0; i < numTuples; i++ {
   750  			tk := tuple.NewTupleKey(fmt.Sprintf("document:%d", i), "editor", "user:jon")
   751  
   752  			tuples = append(tuples, tk)
   753  		}
   754  
   755  		_, err = s.Write(context.Background(), &openfgav1.WriteRequest{
   756  			StoreId:              storeID,
   757  			AuthorizationModelId: modelID,
   758  			Writes: &openfgav1.WriteRequestWrites{
   759  				TupleKeys: tuples,
   760  			},
   761  		})
   762  		require.NoError(t, err)
   763  
   764  		_, err = s.ListObjects(context.Background(), &openfgav1.ListObjectsRequest{
   765  			StoreId:              storeID,
   766  			AuthorizationModelId: modelID,
   767  			Type:                 "document",
   768  			Relation:             "editor",
   769  			User:                 "user:jon",
   770  		})
   771  		require.NoError(t, err)
   772  
   773  		timeoutCtx, timeoutCancel := context.WithTimeout(context.Background(), 3*time.Second)
   774  		defer timeoutCancel()
   775  
   776  		// If ListObjects is still hogging the database connection pool even after responding, then this fails.
   777  		// If ListObjects is closing up its connections effectively then this will not fail.
   778  		status, err := ds.IsReady(timeoutCtx)
   779  		require.NoError(t, err)
   780  		require.True(t, status.IsReady)
   781  	})
   782  
   783  	t.Run("list_users", func(t *testing.T) {
   784  		tuples := make([]*openfgav1.TupleKey, 0, numTuples)
   785  		for i := 0; i < numTuples; i++ {
   786  			tk := tuple.NewTupleKey("document:1", "editor", fmt.Sprintf("user:%d", i))
   787  
   788  			tuples = append(tuples, tk)
   789  		}
   790  
   791  		_, err = s.Write(context.Background(), &openfgav1.WriteRequest{
   792  			StoreId:              storeID,
   793  			AuthorizationModelId: modelID,
   794  			Writes: &openfgav1.WriteRequestWrites{
   795  				TupleKeys: tuples,
   796  			},
   797  		})
   798  		require.NoError(t, err)
   799  
   800  		_, err = s.ListUsers(context.Background(), &openfgav1.ListUsersRequest{
   801  			StoreId:              storeID,
   802  			AuthorizationModelId: modelID,
   803  			Relation:             "editor",
   804  			Object: &openfgav1.Object{
   805  				Type: "document",
   806  				Id:   "1",
   807  			},
   808  			UserFilters: []*openfgav1.UserTypeFilter{{Type: "user"}},
   809  		})
   810  		require.NoError(t, err)
   811  
   812  		timeoutCtx, timeoutCancel := context.WithTimeout(context.Background(), 3*time.Second)
   813  		defer timeoutCancel()
   814  
   815  		// If ListUsers is still hogging the database connection pool even after responding, then this fails.
   816  		// If ListUsers is closing up its connections effectively then this will not fail.
   817  		status, err := ds.IsReady(timeoutCtx)
   818  		require.NoError(t, err)
   819  		require.True(t, status.IsReady)
   820  	})
   821  }
   822  
   823  func TestOperationsWithInvalidModel(t *testing.T) {
   824  	t.Cleanup(func() {
   825  		goleak.VerifyNone(t)
   826  	})
   827  
   828  	ctx := context.Background()
   829  	storeID := ulid.Make().String()
   830  	modelID := ulid.Make().String()
   831  
   832  	// The model is invalid
   833  	typedefs := language.MustTransformDSLToProto(`model
   834  	schema 1.1
   835  type user
   836  
   837  type repo
   838    relations
   839  	define admin: [user]
   840  	define r1: [user] and r2 and r3
   841  	define r2: [user] and r1 and r3
   842  	define r3: [user] and r1 and r2`).GetTypeDefinitions()
   843  
   844  	tk := tuple.NewCheckRequestTupleKey("repo:openfga", "r1", "user:anne")
   845  	mockController := gomock.NewController(t)
   846  	defer mockController.Finish()
   847  
   848  	mockDatastore := mockstorage.NewMockOpenFGADatastore(mockController)
   849  
   850  	mockDatastore.EXPECT().
   851  		ReadAuthorizationModel(gomock.Any(), storeID, modelID).
   852  		AnyTimes().
   853  		Return(&openfgav1.AuthorizationModel{
   854  			Id:              modelID,
   855  			SchemaVersion:   typesystem.SchemaVersion1_1,
   856  			TypeDefinitions: typedefs,
   857  		}, nil)
   858  
   859  	// the model is error and err should return
   860  
   861  	s := MustNewServerWithOpts(
   862  		WithDatastore(mockDatastore),
   863  		WithExperimentals(ExperimentalEnableListUsers),
   864  	)
   865  	t.Cleanup(s.Close)
   866  
   867  	_, err := s.Check(ctx, &openfgav1.CheckRequest{
   868  		StoreId:              storeID,
   869  		TupleKey:             tk,
   870  		AuthorizationModelId: modelID,
   871  	})
   872  	require.Error(t, err)
   873  	e, ok := status.FromError(err)
   874  	require.True(t, ok)
   875  	require.Equal(t, codes.Code(openfgav1.ErrorCode_validation_error), e.Code())
   876  
   877  	_, err = s.ListObjects(ctx, &openfgav1.ListObjectsRequest{
   878  		StoreId:              storeID,
   879  		AuthorizationModelId: modelID,
   880  		Type:                 "repo",
   881  		Relation:             "r1",
   882  		User:                 "user:anne",
   883  	})
   884  	require.Error(t, err)
   885  	e, ok = status.FromError(err)
   886  	require.True(t, ok)
   887  	require.Equal(t, codes.Code(openfgav1.ErrorCode_validation_error), e.Code())
   888  
   889  	err = s.StreamedListObjects(&openfgav1.StreamedListObjectsRequest{
   890  		StoreId:              storeID,
   891  		AuthorizationModelId: modelID,
   892  		Type:                 "repo",
   893  		Relation:             "r1",
   894  		User:                 "user:anne",
   895  	}, NewMockStreamServer())
   896  	require.Error(t, err)
   897  	e, ok = status.FromError(err)
   898  	require.True(t, ok)
   899  	require.Equal(t, codes.Code(openfgav1.ErrorCode_validation_error), e.Code())
   900  
   901  	_, err = s.ListUsers(ctx, &openfgav1.ListUsersRequest{
   902  		StoreId:              storeID,
   903  		AuthorizationModelId: modelID,
   904  		Relation:             tk.GetRelation(),
   905  		Object:               &openfgav1.Object{Type: "repo", Id: "openfga"},
   906  		UserFilters:          []*openfgav1.UserTypeFilter{{Type: "user"}},
   907  	})
   908  	require.Error(t, err)
   909  	e, ok = status.FromError(err)
   910  	require.True(t, ok)
   911  	require.Equal(t, codes.Code(openfgav1.ErrorCode_validation_error), e.Code())
   912  
   913  	_, err = s.Expand(ctx, &openfgav1.ExpandRequest{
   914  		StoreId:              storeID,
   915  		AuthorizationModelId: modelID,
   916  		TupleKey:             tuple.NewExpandRequestTupleKey(tk.GetObject(), tk.GetRelation()),
   917  	})
   918  	require.Error(t, err)
   919  	e, ok = status.FromError(err)
   920  	require.True(t, ok)
   921  	require.Equal(t, codes.Code(openfgav1.ErrorCode_validation_error), e.Code())
   922  }
   923  
   924  func TestShortestPathToSolutionWins(t *testing.T) {
   925  	t.Cleanup(func() {
   926  		goleak.VerifyNone(t)
   927  	})
   928  
   929  	ctx := context.Background()
   930  
   931  	storeID := ulid.Make().String()
   932  	modelID := ulid.Make().String()
   933  
   934  	typedefs := language.MustTransformDSLToProto(`model
   935    schema 1.1
   936  type user
   937  
   938  type repo
   939    relations
   940  	define reader: [user:*]`).GetTypeDefinitions()
   941  
   942  	tk := tuple.NewCheckRequestTupleKey("repo:openfga", "reader", "user:*")
   943  	returnedTuple := &openfgav1.Tuple{Key: tuple.ConvertCheckRequestTupleKeyToTupleKey(tk)}
   944  
   945  	mockController := gomock.NewController(t)
   946  	defer mockController.Finish()
   947  
   948  	mockDatastore := mockstorage.NewMockOpenFGADatastore(mockController)
   949  
   950  	mockDatastore.EXPECT().
   951  		ReadAuthorizationModel(gomock.Any(), storeID, modelID).
   952  		AnyTimes().
   953  		Return(&openfgav1.AuthorizationModel{
   954  			SchemaVersion:   typesystem.SchemaVersion1_1,
   955  			TypeDefinitions: typedefs,
   956  		}, nil)
   957  
   958  	// it could happen that one of the following two mocks won't be necessary because the goroutine will be short-circuited
   959  	mockDatastore.EXPECT().
   960  		ReadUserTuple(gomock.Any(), storeID, gomock.Any()).
   961  		AnyTimes().
   962  		DoAndReturn(
   963  			func(ctx context.Context, _ string, _ *openfgav1.TupleKey) (storage.TupleIterator, error) {
   964  				select {
   965  				case <-ctx.Done():
   966  					return nil, ctx.Err()
   967  				case <-time.After(500 * time.Millisecond):
   968  					return nil, storage.ErrNotFound
   969  				}
   970  			})
   971  
   972  	mockDatastore.EXPECT().
   973  		ReadUsersetTuples(gomock.Any(), storeID, gomock.Any()).
   974  		AnyTimes().
   975  		DoAndReturn(
   976  			func(_ context.Context, _ string, _ storage.ReadUsersetTuplesFilter) (storage.TupleIterator, error) {
   977  				time.Sleep(100 * time.Millisecond)
   978  				return storage.NewStaticTupleIterator([]*openfgav1.Tuple{returnedTuple}), nil
   979  			})
   980  
   981  	s := MustNewServerWithOpts(
   982  		WithDatastore(mockDatastore),
   983  	)
   984  	t.Cleanup(s.Close)
   985  
   986  	start := time.Now()
   987  	checkResponse, err := s.Check(ctx, &openfgav1.CheckRequest{
   988  		StoreId:              storeID,
   989  		TupleKey:             tk,
   990  		AuthorizationModelId: modelID,
   991  	})
   992  	end := time.Since(start)
   993  
   994  	// we expect the Check call to be short-circuited after ReadUsersetTuples runs
   995  	require.Lessf(t, end, 200*time.Millisecond, fmt.Sprintf("end was %s", end))
   996  	require.NoError(t, err)
   997  	require.True(t, checkResponse.GetAllowed())
   998  }
   999  
  1000  func TestCheckWithCachedResolution(t *testing.T) {
  1001  	t.Cleanup(func() {
  1002  		goleak.VerifyNone(t)
  1003  	})
  1004  
  1005  	ctx := context.Background()
  1006  
  1007  	storeID := ulid.Make().String()
  1008  	modelID := ulid.Make().String()
  1009  
  1010  	typedefs := language.MustTransformDSLToProto(`model
  1011    schema 1.1
  1012  type user
  1013  
  1014  type repo
  1015    relations
  1016  	define reader: [user]`).GetTypeDefinitions()
  1017  
  1018  	tk := tuple.NewCheckRequestTupleKey("repo:openfga", "reader", "user:mike")
  1019  	returnedTuple := &openfgav1.Tuple{Key: tuple.ConvertCheckRequestTupleKeyToTupleKey(tk)}
  1020  
  1021  	mockController := gomock.NewController(t)
  1022  	defer mockController.Finish()
  1023  
  1024  	mockDatastore := mockstorage.NewMockOpenFGADatastore(mockController)
  1025  
  1026  	mockDatastore.EXPECT().
  1027  		ReadAuthorizationModel(gomock.Any(), storeID, modelID).
  1028  		AnyTimes().
  1029  		Return(&openfgav1.AuthorizationModel{
  1030  			SchemaVersion:   typesystem.SchemaVersion1_1,
  1031  			TypeDefinitions: typedefs,
  1032  		}, nil)
  1033  
  1034  	mockDatastore.EXPECT().
  1035  		ReadUserTuple(gomock.Any(), storeID, gomock.Any()).
  1036  		Times(1).
  1037  		Return(returnedTuple, nil)
  1038  
  1039  	s := MustNewServerWithOpts(
  1040  		WithDatastore(mockDatastore),
  1041  		WithCheckQueryCacheEnabled(true),
  1042  		WithCheckQueryCacheLimit(10),
  1043  		WithCheckQueryCacheTTL(1*time.Minute),
  1044  	)
  1045  	t.Cleanup(s.Close)
  1046  
  1047  	checkResponse, err := s.Check(ctx, &openfgav1.CheckRequest{
  1048  		StoreId:              storeID,
  1049  		TupleKey:             tk,
  1050  		AuthorizationModelId: modelID,
  1051  	})
  1052  
  1053  	require.NoError(t, err)
  1054  	require.True(t, checkResponse.GetAllowed())
  1055  
  1056  	// If we check for the same request, data should come from cache and number of ReadUserTuple should still be 1
  1057  	checkResponse, err = s.Check(ctx, &openfgav1.CheckRequest{
  1058  		StoreId:              storeID,
  1059  		TupleKey:             tk,
  1060  		AuthorizationModelId: modelID,
  1061  	})
  1062  
  1063  	require.NoError(t, err)
  1064  	require.True(t, checkResponse.GetAllowed())
  1065  }
  1066  
  1067  func TestWriteAssertionModelDSError(t *testing.T) {
  1068  	t.Cleanup(func() {
  1069  		goleak.VerifyNone(t)
  1070  	})
  1071  
  1072  	ctx := context.Background()
  1073  
  1074  	storeID := ulid.Make().String()
  1075  	modelID := ulid.Make().String()
  1076  
  1077  	typedefs := language.MustTransformDSLToProto(`model
  1078  	schema 1.1
  1079  type user
  1080  
  1081  type repo
  1082    relations
  1083  	define reader: [user]`).GetTypeDefinitions()
  1084  
  1085  	mockController := gomock.NewController(t)
  1086  	defer mockController.Finish()
  1087  
  1088  	mockDSOldSchema := mockstorage.NewMockOpenFGADatastore(mockController)
  1089  
  1090  	mockDSOldSchema.EXPECT().
  1091  		ReadAuthorizationModel(gomock.Any(), storeID, modelID).
  1092  		AnyTimes().
  1093  		Return(&openfgav1.AuthorizationModel{
  1094  			SchemaVersion:   typesystem.SchemaVersion1_0,
  1095  			TypeDefinitions: typedefs,
  1096  		}, nil)
  1097  
  1098  	mockDSBadReadAuthModel := mockstorage.NewMockOpenFGADatastore(mockController)
  1099  
  1100  	mockDSBadReadAuthModel.EXPECT().
  1101  		ReadAuthorizationModel(gomock.Any(), storeID, modelID).
  1102  		AnyTimes().
  1103  		Return(nil, fmt.Errorf("unable to read"))
  1104  
  1105  	mockDSBadWriteAssertions := mockstorage.NewMockOpenFGADatastore(mockController)
  1106  	mockDSBadWriteAssertions.EXPECT().
  1107  		ReadAuthorizationModel(gomock.Any(), storeID, modelID).
  1108  		AnyTimes().
  1109  		Return(&openfgav1.AuthorizationModel{
  1110  			SchemaVersion:   typesystem.SchemaVersion1_1,
  1111  			TypeDefinitions: typedefs,
  1112  		}, nil)
  1113  	mockDSBadWriteAssertions.EXPECT().
  1114  		WriteAssertions(gomock.Any(), storeID, modelID, gomock.Any()).
  1115  		AnyTimes().
  1116  		Return(fmt.Errorf("unable to write"))
  1117  
  1118  	tests := []struct {
  1119  		name          string
  1120  		assertions    []*openfgav1.Assertion
  1121  		mockDatastore *mockstorage.MockOpenFGADatastore
  1122  		expectedError error
  1123  	}{
  1124  		{
  1125  			name:          "unsupported_schema",
  1126  			assertions:    []*openfgav1.Assertion{},
  1127  			mockDatastore: mockDSOldSchema,
  1128  			expectedError: serverErrors.ValidationError(
  1129  				fmt.Errorf("invalid schema version"),
  1130  			),
  1131  		},
  1132  		{
  1133  			name:          "failed_to_read",
  1134  			assertions:    []*openfgav1.Assertion{},
  1135  			mockDatastore: mockDSBadReadAuthModel,
  1136  			expectedError: serverErrors.NewInternalError(
  1137  				"", fmt.Errorf("unable to read"),
  1138  			),
  1139  		},
  1140  		{
  1141  			name:          "failed_to_write",
  1142  			assertions:    []*openfgav1.Assertion{},
  1143  			mockDatastore: mockDSBadWriteAssertions,
  1144  			expectedError: serverErrors.NewInternalError(
  1145  				"", fmt.Errorf("unable to write"),
  1146  			),
  1147  		},
  1148  	}
  1149  
  1150  	for _, curTest := range tests {
  1151  		t.Run(curTest.name, func(t *testing.T) {
  1152  			request := &openfgav1.WriteAssertionsRequest{
  1153  				StoreId:              storeID,
  1154  				Assertions:           curTest.assertions,
  1155  				AuthorizationModelId: modelID,
  1156  			}
  1157  
  1158  			writeAssertionCmd := commands.NewWriteAssertionsCommand(curTest.mockDatastore)
  1159  			_, err := writeAssertionCmd.Execute(ctx, request)
  1160  			require.ErrorIs(t, curTest.expectedError, err)
  1161  		})
  1162  	}
  1163  }
  1164  
  1165  func TestReadAssertionModelDSError(t *testing.T) {
  1166  	t.Cleanup(func() {
  1167  		goleak.VerifyNone(t)
  1168  	})
  1169  
  1170  	ctx := context.Background()
  1171  
  1172  	storeID := ulid.Make().String()
  1173  	modelID := ulid.Make().String()
  1174  
  1175  	mockController := gomock.NewController(t)
  1176  	defer mockController.Finish()
  1177  
  1178  	mockDSBadReadAssertions := mockstorage.NewMockOpenFGADatastore(mockController)
  1179  	mockDSBadReadAssertions.EXPECT().
  1180  		ReadAssertions(gomock.Any(), storeID, modelID).
  1181  		AnyTimes().
  1182  		Return(nil, fmt.Errorf("unable to read"))
  1183  
  1184  	readAssertionQuery := commands.NewReadAssertionsQuery(mockDSBadReadAssertions)
  1185  	_, err := readAssertionQuery.Execute(ctx, storeID, modelID)
  1186  	expectedError := serverErrors.NewInternalError(
  1187  		"", fmt.Errorf("unable to read"),
  1188  	)
  1189  	require.ErrorIs(t, expectedError, err)
  1190  }
  1191  
  1192  func TestResolveAuthorizationModel(t *testing.T) {
  1193  	t.Cleanup(func() {
  1194  		goleak.VerifyNone(t)
  1195  	})
  1196  
  1197  	ctx := context.Background()
  1198  
  1199  	t.Run("no_latest_authorization_model_id_found", func(t *testing.T) {
  1200  		store := ulid.Make().String()
  1201  
  1202  		mockController := gomock.NewController(t)
  1203  		defer mockController.Finish()
  1204  
  1205  		mockDatastore := mockstorage.NewMockOpenFGADatastore(mockController)
  1206  		mockDatastore.EXPECT().FindLatestAuthorizationModel(gomock.Any(), store).Return(nil, storage.ErrNotFound)
  1207  
  1208  		s := MustNewServerWithOpts(
  1209  			WithDatastore(mockDatastore),
  1210  		)
  1211  		t.Cleanup(s.Close)
  1212  
  1213  		expectedError := serverErrors.LatestAuthorizationModelNotFound(store)
  1214  
  1215  		_, err := s.resolveTypesystem(ctx, store, "")
  1216  		require.ErrorIs(t, err, expectedError)
  1217  	})
  1218  
  1219  	t.Run("read_existing_authorization_model", func(t *testing.T) {
  1220  		store := ulid.Make().String()
  1221  		modelID := ulid.Make().String()
  1222  
  1223  		mockController := gomock.NewController(t)
  1224  		defer mockController.Finish()
  1225  
  1226  		mockDatastore := mockstorage.NewMockOpenFGADatastore(mockController)
  1227  		mockDatastore.EXPECT().FindLatestAuthorizationModel(gomock.Any(), store).Return(
  1228  			&openfgav1.AuthorizationModel{
  1229  				Id:            modelID,
  1230  				SchemaVersion: typesystem.SchemaVersion1_1,
  1231  			},
  1232  			nil,
  1233  		)
  1234  
  1235  		s := MustNewServerWithOpts(
  1236  			WithDatastore(mockDatastore),
  1237  		)
  1238  		t.Cleanup(s.Close)
  1239  
  1240  		typesys, err := s.resolveTypesystem(ctx, store, "")
  1241  		require.NoError(t, err)
  1242  		require.Equal(t, modelID, typesys.GetAuthorizationModelID())
  1243  	})
  1244  
  1245  	t.Run("non-valid_modelID_returns_error", func(t *testing.T) {
  1246  		store := ulid.Make().String()
  1247  		modelID := "foo"
  1248  		want := serverErrors.AuthorizationModelNotFound(modelID)
  1249  
  1250  		mockController := gomock.NewController(t)
  1251  		defer mockController.Finish()
  1252  
  1253  		mockDatastore := mockstorage.NewMockOpenFGADatastore(mockController)
  1254  
  1255  		s := MustNewServerWithOpts(
  1256  			WithDatastore(mockDatastore),
  1257  		)
  1258  		t.Cleanup(s.Close)
  1259  
  1260  		_, err := s.resolveTypesystem(ctx, store, modelID)
  1261  		require.Equal(t, want, err)
  1262  	})
  1263  }
  1264  
  1265  type mockStreamServer struct {
  1266  	grpc.ServerStream
  1267  }
  1268  
  1269  func NewMockStreamServer() *mockStreamServer {
  1270  	return &mockStreamServer{}
  1271  }
  1272  
  1273  func (m *mockStreamServer) Context() context.Context {
  1274  	return context.Background()
  1275  }
  1276  
  1277  func (m *mockStreamServer) Send(*openfgav1.StreamedListObjectsResponse) error {
  1278  	return nil
  1279  }
  1280  
  1281  // This runs ListObjects and StreamedListObjects many times over to ensure no race conditions (see https://github.com/openfga/openfga/pull/762)
  1282  func BenchmarkListObjectsNoRaceCondition(b *testing.B) {
  1283  	b.Cleanup(func() {
  1284  		goleak.VerifyNone(b,
  1285  			// https://github.com/uber-go/goleak/discussions/89
  1286  			goleak.IgnoreTopFunction("testing.(*B).run1"),
  1287  			goleak.IgnoreTopFunction("testing.(*B).doBench"),
  1288  		)
  1289  	})
  1290  	ctx := context.Background()
  1291  	store := ulid.Make().String()
  1292  	modelID := ulid.Make().String()
  1293  
  1294  	mockController := gomock.NewController(b)
  1295  	defer mockController.Finish()
  1296  
  1297  	typedefs := language.MustTransformDSLToProto(`model
  1298    schema 1.1
  1299  type user
  1300  
  1301  type repo
  1302    relations
  1303  	define allowed: [user]
  1304  	define viewer: [user] and allowed`).GetTypeDefinitions()
  1305  
  1306  	mockDatastore := mockstorage.NewMockOpenFGADatastore(mockController)
  1307  
  1308  	mockDatastore.EXPECT().ReadAuthorizationModel(gomock.Any(), store, modelID).AnyTimes().Return(&openfgav1.AuthorizationModel{
  1309  		SchemaVersion:   typesystem.SchemaVersion1_1,
  1310  		TypeDefinitions: typedefs,
  1311  	}, nil)
  1312  	mockDatastore.EXPECT().ReadStartingWithUser(gomock.Any(), store, gomock.Any()).AnyTimes().Return(nil, errors.New("error reading from storage"))
  1313  
  1314  	s := MustNewServerWithOpts(
  1315  		WithDatastore(mockDatastore),
  1316  	)
  1317  	b.Cleanup(func() {
  1318  		s.Close()
  1319  	})
  1320  
  1321  	b.ResetTimer()
  1322  	for i := 0; i < b.N; i++ {
  1323  		_, err := s.ListObjects(ctx, &openfgav1.ListObjectsRequest{
  1324  			StoreId:              store,
  1325  			AuthorizationModelId: modelID,
  1326  			Type:                 "repo",
  1327  			Relation:             "viewer",
  1328  			User:                 "user:bob",
  1329  		})
  1330  
  1331  		require.ErrorIs(b, err, serverErrors.NewInternalError("", errors.New("error reading from storage")))
  1332  
  1333  		err = s.StreamedListObjects(&openfgav1.StreamedListObjectsRequest{
  1334  			StoreId:              store,
  1335  			AuthorizationModelId: modelID,
  1336  			Type:                 "repo",
  1337  			Relation:             "viewer",
  1338  			User:                 "user:bob",
  1339  		}, NewMockStreamServer())
  1340  
  1341  		require.ErrorIs(b, err, serverErrors.NewInternalError("", errors.New("error reading from storage")))
  1342  	}
  1343  }
  1344  
  1345  func TestListObjects_ErrorCases(t *testing.T) {
  1346  	t.Cleanup(func() {
  1347  		goleak.VerifyNone(t)
  1348  	})
  1349  
  1350  	ctx := context.Background()
  1351  	store := ulid.Make().String()
  1352  
  1353  	mockController := gomock.NewController(t)
  1354  	defer mockController.Finish()
  1355  
  1356  	t.Run("database_errors", func(t *testing.T) {
  1357  		mockDatastore := mockstorage.NewMockOpenFGADatastore(mockController)
  1358  
  1359  		s := MustNewServerWithOpts(
  1360  			WithDatastore(mockDatastore),
  1361  		)
  1362  		t.Cleanup(s.Close)
  1363  
  1364  		modelID := ulid.Make().String()
  1365  
  1366  		mockDatastore.EXPECT().ReadAuthorizationModel(gomock.Any(), store, modelID).AnyTimes().Return(&openfgav1.AuthorizationModel{
  1367  			SchemaVersion: typesystem.SchemaVersion1_1,
  1368  			TypeDefinitions: language.MustTransformDSLToProto(`model
  1369    schema 1.1
  1370  type user
  1371  
  1372  type document
  1373    relations
  1374  	define viewer: [user, user:*]`).GetTypeDefinitions(),
  1375  		}, nil)
  1376  
  1377  		mockDatastore.EXPECT().ReadStartingWithUser(gomock.Any(), store, storage.ReadStartingWithUserFilter{
  1378  			ObjectType: "document",
  1379  			Relation:   "viewer",
  1380  			UserFilter: []*openfgav1.ObjectRelation{
  1381  				{Object: "user:*"},
  1382  				{Object: "user:bob"},
  1383  			}}).AnyTimes().Return(nil, errors.New("error reading from storage"))
  1384  
  1385  		t.Run("error_listing_objects_from_storage_in_non-streaming_version", func(t *testing.T) {
  1386  			res, err := s.ListObjects(ctx, &openfgav1.ListObjectsRequest{
  1387  				StoreId:              store,
  1388  				AuthorizationModelId: modelID,
  1389  				Type:                 "document",
  1390  				Relation:             "viewer",
  1391  				User:                 "user:bob",
  1392  			})
  1393  
  1394  			require.Nil(t, res)
  1395  			require.ErrorIs(t, err, serverErrors.NewInternalError("", errors.New("error reading from storage")))
  1396  		})
  1397  
  1398  		t.Run("error_listing_objects_from_storage_in_streaming_version", func(t *testing.T) {
  1399  			err := s.StreamedListObjects(&openfgav1.StreamedListObjectsRequest{
  1400  				StoreId:              store,
  1401  				AuthorizationModelId: modelID,
  1402  				Type:                 "document",
  1403  				Relation:             "viewer",
  1404  				User:                 "user:bob",
  1405  			}, NewMockStreamServer())
  1406  
  1407  			require.ErrorIs(t, err, serverErrors.NewInternalError("", errors.New("error reading from storage")))
  1408  		})
  1409  	})
  1410  
  1411  	t.Run("graph_resolution_errors", func(t *testing.T) {
  1412  		s := MustNewServerWithOpts(
  1413  			WithDatastore(memory.New()),
  1414  			WithResolveNodeLimit(2),
  1415  		)
  1416  		t.Cleanup(s.Close)
  1417  
  1418  		writeModelResp, err := s.WriteAuthorizationModel(ctx, &openfgav1.WriteAuthorizationModelRequest{
  1419  			StoreId:       store,
  1420  			SchemaVersion: typesystem.SchemaVersion1_1,
  1421  			TypeDefinitions: language.MustTransformDSLToProto(`model
  1422    schema 1.1
  1423  type user
  1424  
  1425  type group
  1426    relations
  1427  	define member: [user, group#member]
  1428  
  1429  type document
  1430    relations
  1431  	define viewer: [group#member]`).GetTypeDefinitions(),
  1432  		})
  1433  		require.NoError(t, err)
  1434  
  1435  		_, err = s.Write(ctx, &openfgav1.WriteRequest{
  1436  			StoreId: store,
  1437  			Writes: &openfgav1.WriteRequestWrites{
  1438  				TupleKeys: []*openfgav1.TupleKey{
  1439  					tuple.NewTupleKey("document:1", "viewer", "group:1#member"),
  1440  					tuple.NewTupleKey("group:1", "member", "group:2#member"),
  1441  					tuple.NewTupleKey("group:2", "member", "group:3#member"),
  1442  					tuple.NewTupleKey("group:3", "member", "user:jon"),
  1443  				},
  1444  			},
  1445  		})
  1446  		require.NoError(t, err)
  1447  
  1448  		t.Run("resolution_depth_exceeded_error_unary", func(t *testing.T) {
  1449  			res, err := s.ListObjects(ctx, &openfgav1.ListObjectsRequest{
  1450  				StoreId:              store,
  1451  				AuthorizationModelId: writeModelResp.GetAuthorizationModelId(),
  1452  				Type:                 "document",
  1453  				Relation:             "viewer",
  1454  				User:                 "user:jon",
  1455  			})
  1456  
  1457  			require.Nil(t, res)
  1458  			require.ErrorIs(t, err, serverErrors.AuthorizationModelResolutionTooComplex)
  1459  		})
  1460  
  1461  		t.Run("resolution_depth_exceeded_error_streaming", func(t *testing.T) {
  1462  			err := s.StreamedListObjects(&openfgav1.StreamedListObjectsRequest{
  1463  				StoreId:              store,
  1464  				AuthorizationModelId: writeModelResp.GetAuthorizationModelId(),
  1465  				Type:                 "document",
  1466  				Relation:             "viewer",
  1467  				User:                 "user:jon",
  1468  			}, NewMockStreamServer())
  1469  
  1470  			require.ErrorIs(t, err, serverErrors.AuthorizationModelResolutionTooComplex)
  1471  		})
  1472  	})
  1473  }
  1474  
  1475  func TestAuthorizationModelInvalidSchemaVersion(t *testing.T) {
  1476  	t.Cleanup(func() {
  1477  		goleak.VerifyNone(t)
  1478  	})
  1479  
  1480  	ctx := context.Background()
  1481  	store := ulid.Make().String()
  1482  	modelID := ulid.Make().String()
  1483  
  1484  	mockController := gomock.NewController(t)
  1485  	defer mockController.Finish()
  1486  
  1487  	mockDatastore := mockstorage.NewMockOpenFGADatastore(mockController)
  1488  
  1489  	mockDatastore.EXPECT().ReadAuthorizationModel(gomock.Any(), store, modelID).AnyTimes().Return(&openfgav1.AuthorizationModel{
  1490  		SchemaVersion: typesystem.SchemaVersion1_0,
  1491  		TypeDefinitions: []*openfgav1.TypeDefinition{
  1492  			{
  1493  				Type: "user",
  1494  			},
  1495  			{
  1496  				Type: "team",
  1497  				Relations: map[string]*openfgav1.Userset{
  1498  					"member": typesystem.This(),
  1499  				},
  1500  			},
  1501  		},
  1502  	}, nil)
  1503  
  1504  	s := MustNewServerWithOpts(
  1505  		WithDatastore(mockDatastore),
  1506  	)
  1507  	t.Cleanup(s.Close)
  1508  
  1509  	t.Run("invalid_schema_error_in_check", func(t *testing.T) {
  1510  		_, err := s.Check(ctx, &openfgav1.CheckRequest{
  1511  			StoreId:              store,
  1512  			AuthorizationModelId: modelID,
  1513  			TupleKey: tuple.NewCheckRequestTupleKey(
  1514  				"team:abc",
  1515  				"member",
  1516  				"user:anne"),
  1517  		})
  1518  		require.Error(t, err)
  1519  		e, ok := status.FromError(err)
  1520  		require.True(t, ok)
  1521  		require.Equal(t, codes.Code(openfgav1.ErrorCode_validation_error), e.Code())
  1522  	})
  1523  
  1524  	t.Run("invalid_schema_error_in_list_objects", func(t *testing.T) {
  1525  		_, err := s.ListObjects(ctx, &openfgav1.ListObjectsRequest{
  1526  			StoreId:              store,
  1527  			AuthorizationModelId: modelID,
  1528  			Type:                 "team",
  1529  			Relation:             "member",
  1530  			User:                 "user:anne",
  1531  		})
  1532  		require.Error(t, err)
  1533  		e, ok := status.FromError(err)
  1534  		require.True(t, ok)
  1535  		require.Equal(t, codes.Code(openfgav1.ErrorCode_validation_error), e.Code())
  1536  	})
  1537  
  1538  	t.Run("invalid_schema_error_in_streamed_list_objects", func(t *testing.T) {
  1539  		err := s.StreamedListObjects(&openfgav1.StreamedListObjectsRequest{
  1540  			StoreId:              store,
  1541  			AuthorizationModelId: modelID,
  1542  			Type:                 "team",
  1543  			Relation:             "member",
  1544  			User:                 "user:anne",
  1545  		}, NewMockStreamServer())
  1546  		require.Error(t, err)
  1547  		e, ok := status.FromError(err)
  1548  		require.True(t, ok)
  1549  		require.Equal(t, codes.Code(openfgav1.ErrorCode_validation_error), e.Code())
  1550  	})
  1551  
  1552  	t.Run("invalid_schema_error_in_write", func(t *testing.T) {
  1553  		_, err := s.Write(ctx, &openfgav1.WriteRequest{
  1554  			StoreId:              store,
  1555  			AuthorizationModelId: modelID,
  1556  			Writes: &openfgav1.WriteRequestWrites{
  1557  				TupleKeys: []*openfgav1.TupleKey{
  1558  					{
  1559  						Object:   "repo:openfga/openfga",
  1560  						Relation: "reader",
  1561  						User:     "user:anne",
  1562  					},
  1563  				},
  1564  			},
  1565  		})
  1566  		require.Error(t, err)
  1567  		e, ok := status.FromError(err)
  1568  		require.True(t, ok)
  1569  		require.Equal(t, codes.Code(openfgav1.ErrorCode_validation_error), e.Code())
  1570  	})
  1571  
  1572  	t.Run("invalid_schema_error_in_write_model", func(t *testing.T) {
  1573  		mockDatastore.EXPECT().MaxTypesPerAuthorizationModel().Return(100)
  1574  
  1575  		_, err := s.WriteAuthorizationModel(ctx, &openfgav1.WriteAuthorizationModelRequest{
  1576  			StoreId:       store,
  1577  			SchemaVersion: typesystem.SchemaVersion1_0,
  1578  			TypeDefinitions: language.MustTransformDSLToProto(`model
  1579  	schema 1.1
  1580  type repo
  1581  `).GetTypeDefinitions(),
  1582  		})
  1583  		require.Error(t, err)
  1584  		e, ok := status.FromError(err)
  1585  		require.True(t, ok)
  1586  		require.Equal(t, codes.Code(openfgav1.ErrorCode_invalid_authorization_model), e.Code(), err)
  1587  	})
  1588  
  1589  	t.Run("invalid_schema_error_in_write_assertion", func(t *testing.T) {
  1590  		_, err := s.WriteAssertions(ctx, &openfgav1.WriteAssertionsRequest{
  1591  			StoreId:              store,
  1592  			AuthorizationModelId: modelID,
  1593  			Assertions: []*openfgav1.Assertion{{
  1594  				TupleKey:    tuple.NewAssertionTupleKey("repo:test", "reader", "user:elbuo"),
  1595  				Expectation: false,
  1596  			}},
  1597  		})
  1598  		require.Error(t, err)
  1599  		e, ok := status.FromError(err)
  1600  		require.True(t, ok)
  1601  		require.Equal(t, codes.Code(openfgav1.ErrorCode_validation_error), e.Code())
  1602  	})
  1603  }
  1604  
  1605  func TestDefaultMaxConcurrentReadSettings(t *testing.T) {
  1606  	t.Cleanup(func() {
  1607  		goleak.VerifyNone(t)
  1608  	})
  1609  
  1610  	cfg := serverconfig.DefaultConfig()
  1611  	require.EqualValues(t, math.MaxUint32, cfg.MaxConcurrentReadsForCheck)
  1612  	require.EqualValues(t, math.MaxUint32, cfg.MaxConcurrentReadsForListObjects)
  1613  	require.EqualValues(t, math.MaxUint32, cfg.MaxConcurrentReadsForListUsers)
  1614  
  1615  	s := MustNewServerWithOpts(
  1616  		WithDatastore(memory.New()),
  1617  	)
  1618  	t.Cleanup(s.Close)
  1619  	require.EqualValues(t, math.MaxUint32, s.maxConcurrentReadsForCheck)
  1620  	require.EqualValues(t, math.MaxUint32, s.maxConcurrentReadsForListObjects)
  1621  	require.EqualValues(t, math.MaxUint32, s.maxConcurrentReadsForListUsers)
  1622  }
  1623  
  1624  func TestDelegateCheckResolver(t *testing.T) {
  1625  	t.Cleanup(func() {
  1626  		goleak.VerifyNone(t)
  1627  	})
  1628  	t.Run("default_check_resolver_alone", func(t *testing.T) {
  1629  		cfg := serverconfig.DefaultConfig()
  1630  		require.False(t, cfg.DispatchThrottling.Enabled)
  1631  		require.False(t, cfg.CheckQueryCache.Enabled)
  1632  
  1633  		ds := memory.New()
  1634  		t.Cleanup(ds.Close)
  1635  		s := MustNewServerWithOpts(
  1636  			WithDatastore(ds),
  1637  		)
  1638  		t.Cleanup(s.Close)
  1639  		require.Nil(t, s.dispatchThrottlingCheckResolver)
  1640  		require.False(t, s.dispatchThrottlingCheckResolverEnabled)
  1641  
  1642  		require.False(t, s.checkQueryCacheEnabled)
  1643  		require.Nil(t, s.cachedCheckResolver)
  1644  
  1645  		require.NotNil(t, s.checkResolver)
  1646  		cycleDetectionCheckResolver, ok := s.checkResolver.(*graph.CycleDetectionCheckResolver)
  1647  		require.True(t, ok)
  1648  
  1649  		localCheckResolver, ok := cycleDetectionCheckResolver.GetDelegate().(*graph.LocalChecker)
  1650  		require.True(t, ok)
  1651  
  1652  		_, ok = localCheckResolver.GetDelegate().(*graph.CycleDetectionCheckResolver)
  1653  		require.True(t, ok)
  1654  	})
  1655  
  1656  	t.Run("dispatch_throttling_check_resolver_enabled", func(t *testing.T) {
  1657  		ds := memory.New()
  1658  		t.Cleanup(ds.Close)
  1659  		const dispatchThreshold = 50
  1660  		s := MustNewServerWithOpts(
  1661  			WithDatastore(ds),
  1662  			WithDispatchThrottlingCheckResolverEnabled(true),
  1663  			WithDispatchThrottlingCheckResolverThreshold(dispatchThreshold),
  1664  		)
  1665  		t.Cleanup(s.Close)
  1666  
  1667  		require.False(t, s.checkQueryCacheEnabled)
  1668  		require.Nil(t, s.cachedCheckResolver)
  1669  
  1670  		require.True(t, s.dispatchThrottlingCheckResolverEnabled)
  1671  		require.EqualValues(t, dispatchThreshold, s.dispatchThrottlingDefaultThreshold)
  1672  		require.EqualValues(t, 0, s.dispatchThrottlingMaxThreshold)
  1673  		require.NotNil(t, s.dispatchThrottlingCheckResolver)
  1674  		require.NotNil(t, s.checkResolver)
  1675  		cycleDetectionCheckResolver, ok := s.checkResolver.(*graph.CycleDetectionCheckResolver)
  1676  		require.True(t, ok)
  1677  
  1678  		dispatchThrottlingResolver, ok := cycleDetectionCheckResolver.GetDelegate().(*graph.DispatchThrottlingCheckResolver)
  1679  		require.True(t, ok)
  1680  
  1681  		localChecker, ok := dispatchThrottlingResolver.GetDelegate().(*graph.LocalChecker)
  1682  		require.True(t, ok)
  1683  
  1684  		_, ok = localChecker.GetDelegate().(*graph.CycleDetectionCheckResolver)
  1685  		require.True(t, ok)
  1686  	})
  1687  
  1688  	t.Run("dispatch_throttling_check_resolver_enabled_zero_max_threshold", func(t *testing.T) {
  1689  		ds := memory.New()
  1690  		t.Cleanup(ds.Close)
  1691  		const dispatchThreshold = 50
  1692  		s := MustNewServerWithOpts(
  1693  			WithDatastore(ds),
  1694  			WithDispatchThrottlingCheckResolverEnabled(true),
  1695  			WithDispatchThrottlingCheckResolverThreshold(dispatchThreshold),
  1696  			WithDispatchThrottlingCheckResolverMaxThreshold(0),
  1697  		)
  1698  		t.Cleanup(s.Close)
  1699  
  1700  		require.False(t, s.checkQueryCacheEnabled)
  1701  		require.Nil(t, s.cachedCheckResolver)
  1702  
  1703  		require.True(t, s.dispatchThrottlingCheckResolverEnabled)
  1704  		require.EqualValues(t, dispatchThreshold, s.dispatchThrottlingDefaultThreshold)
  1705  		require.EqualValues(t, 0, s.dispatchThrottlingMaxThreshold)
  1706  		require.NotNil(t, s.dispatchThrottlingCheckResolver)
  1707  		require.NotNil(t, s.checkResolver)
  1708  		cycleDetectionCheckResolver, ok := s.checkResolver.(*graph.CycleDetectionCheckResolver)
  1709  		require.True(t, ok)
  1710  
  1711  		dispatchThrottlingResolver, ok := cycleDetectionCheckResolver.GetDelegate().(*graph.DispatchThrottlingCheckResolver)
  1712  		require.True(t, ok)
  1713  
  1714  		localChecker, ok := dispatchThrottlingResolver.GetDelegate().(*graph.LocalChecker)
  1715  		require.True(t, ok)
  1716  
  1717  		_, ok = localChecker.GetDelegate().(*graph.CycleDetectionCheckResolver)
  1718  		require.True(t, ok)
  1719  	})
  1720  
  1721  	t.Run("dispatch_throttling_check_resolver_enabled_non_zero_max_threshold", func(t *testing.T) {
  1722  		ds := memory.New()
  1723  		t.Cleanup(ds.Close)
  1724  		const dispatchThreshold = 50
  1725  		const maxDispatchThreshold = 60
  1726  
  1727  		s := MustNewServerWithOpts(
  1728  			WithDatastore(ds),
  1729  			WithDispatchThrottlingCheckResolverEnabled(true),
  1730  			WithDispatchThrottlingCheckResolverThreshold(dispatchThreshold),
  1731  			WithDispatchThrottlingCheckResolverMaxThreshold(maxDispatchThreshold),
  1732  		)
  1733  		t.Cleanup(s.Close)
  1734  
  1735  		require.False(t, s.checkQueryCacheEnabled)
  1736  		require.Nil(t, s.cachedCheckResolver)
  1737  
  1738  		require.True(t, s.dispatchThrottlingCheckResolverEnabled)
  1739  		require.EqualValues(t, dispatchThreshold, s.dispatchThrottlingDefaultThreshold)
  1740  		require.EqualValues(t, maxDispatchThreshold, s.dispatchThrottlingMaxThreshold)
  1741  		require.NotNil(t, s.dispatchThrottlingCheckResolver)
  1742  		require.NotNil(t, s.checkResolver)
  1743  		cycleDetectionCheckResolver, ok := s.checkResolver.(*graph.CycleDetectionCheckResolver)
  1744  		require.True(t, ok)
  1745  
  1746  		dispatchThrottlingResolver, ok := cycleDetectionCheckResolver.GetDelegate().(*graph.DispatchThrottlingCheckResolver)
  1747  		require.True(t, ok)
  1748  
  1749  		localChecker, ok := dispatchThrottlingResolver.GetDelegate().(*graph.LocalChecker)
  1750  		require.True(t, ok)
  1751  
  1752  		_, ok = localChecker.GetDelegate().(*graph.CycleDetectionCheckResolver)
  1753  		require.True(t, ok)
  1754  	})
  1755  
  1756  	t.Run("cache_check_resolver_enabled", func(t *testing.T) {
  1757  		ds := memory.New()
  1758  		t.Cleanup(ds.Close)
  1759  		s := MustNewServerWithOpts(
  1760  			WithDatastore(ds),
  1761  			WithCheckQueryCacheEnabled(true),
  1762  		)
  1763  		t.Cleanup(s.Close)
  1764  
  1765  		require.False(t, s.dispatchThrottlingCheckResolverEnabled)
  1766  		require.Nil(t, s.dispatchThrottlingCheckResolver)
  1767  
  1768  		require.True(t, s.checkQueryCacheEnabled)
  1769  		require.NotNil(t, s.cachedCheckResolver)
  1770  		require.NotNil(t, s.checkResolver)
  1771  		cycleDetectionCheckResolver, ok := s.checkResolver.(*graph.CycleDetectionCheckResolver)
  1772  		require.True(t, ok)
  1773  
  1774  		cachedCheckResolver, ok := cycleDetectionCheckResolver.GetDelegate().(*graph.CachedCheckResolver)
  1775  		require.True(t, ok)
  1776  
  1777  		localChecker, ok := cachedCheckResolver.GetDelegate().(*graph.LocalChecker)
  1778  		require.True(t, ok)
  1779  
  1780  		_, ok = localChecker.GetDelegate().(*graph.CycleDetectionCheckResolver)
  1781  		require.True(t, ok)
  1782  	})
  1783  
  1784  	t.Run("both_dispatch_throttling_and_cache_check_resolver_enabled", func(t *testing.T) {
  1785  		ds := memory.New()
  1786  		t.Cleanup(ds.Close)
  1787  		s := MustNewServerWithOpts(
  1788  			WithDatastore(ds),
  1789  			WithCheckQueryCacheEnabled(true),
  1790  			WithDispatchThrottlingCheckResolverEnabled(true),
  1791  			WithDispatchThrottlingCheckResolverThreshold(50),
  1792  			WithDispatchThrottlingCheckResolverMaxThreshold(100),
  1793  		)
  1794  		t.Cleanup(s.Close)
  1795  
  1796  		require.True(t, s.dispatchThrottlingCheckResolverEnabled)
  1797  		require.EqualValues(t, 50, s.dispatchThrottlingDefaultThreshold)
  1798  		require.EqualValues(t, 100, s.dispatchThrottlingMaxThreshold)
  1799  		require.NotNil(t, s.dispatchThrottlingCheckResolver)
  1800  		require.NotNil(t, s.checkResolver)
  1801  		cycleDetectionCheckResolver, ok := s.checkResolver.(*graph.CycleDetectionCheckResolver)
  1802  		require.True(t, ok)
  1803  
  1804  		dispatchThrottlingResolver, ok := cycleDetectionCheckResolver.GetDelegate().(*graph.DispatchThrottlingCheckResolver)
  1805  		require.True(t, ok)
  1806  
  1807  		require.True(t, s.checkQueryCacheEnabled)
  1808  		require.NotNil(t, s.cachedCheckResolver)
  1809  
  1810  		cachedCheckResolver, ok := dispatchThrottlingResolver.GetDelegate().(*graph.CachedCheckResolver)
  1811  		require.True(t, ok)
  1812  
  1813  		localChecker, ok := cachedCheckResolver.GetDelegate().(*graph.LocalChecker)
  1814  		require.True(t, ok)
  1815  
  1816  		_, ok = localChecker.GetDelegate().(*graph.CycleDetectionCheckResolver)
  1817  		require.True(t, ok)
  1818  	})
  1819  }
  1820  
  1821  func TestWriteAuthorizationModelWithSchema12(t *testing.T) {
  1822  	t.Cleanup(func() {
  1823  		goleak.VerifyNone(t)
  1824  	})
  1825  	ctx := context.Background()
  1826  	storeID := ulid.Make().String()
  1827  
  1828  	mockController := gomock.NewController(t)
  1829  	defer mockController.Finish()
  1830  
  1831  	mockDatastore := mockstorage.NewMockOpenFGADatastore(mockController)
  1832  
  1833  	t.Run("accepts_request_with_schema_version_1.2", func(t *testing.T) {
  1834  		s := MustNewServerWithOpts(
  1835  			WithDatastore(mockDatastore),
  1836  		)
  1837  		defer s.Close()
  1838  
  1839  		mockDatastore.EXPECT().MaxTypesPerAuthorizationModel().Return(100)
  1840  		mockDatastore.EXPECT().WriteAuthorizationModel(gomock.Any(), storeID, gomock.Any()).Return(nil)
  1841  
  1842  		_, err := s.WriteAuthorizationModel(ctx, &openfgav1.WriteAuthorizationModelRequest{
  1843  			StoreId:       storeID,
  1844  			SchemaVersion: typesystem.SchemaVersion1_2,
  1845  			TypeDefinitions: []*openfgav1.TypeDefinition{
  1846  				{
  1847  					Type: "user",
  1848  					Metadata: &openfgav1.Metadata{
  1849  						Relations:  nil,
  1850  						Module:     "usermanagement",
  1851  						SourceInfo: nil,
  1852  					},
  1853  				},
  1854  			},
  1855  		})
  1856  
  1857  		require.NoError(t, err)
  1858  	})
  1859  }
  1860  
  1861  func TestIsExperimentallyEnabled(t *testing.T) {
  1862  	someExperimentalFlag := ExperimentalFeatureFlag("some-experimental-feature-to-enable")
  1863  
  1864  	server := Server{}
  1865  
  1866  	t.Run("returns_false_if_experimentals_is_empty", func(t *testing.T) {
  1867  		require.False(t, server.IsExperimentallyEnabled(someExperimentalFlag))
  1868  	})
  1869  
  1870  	t.Run("returns_true_if_experimentals_has_matching_element", func(t *testing.T) {
  1871  		server.experimentals = []ExperimentalFeatureFlag{someExperimentalFlag}
  1872  
  1873  		require.True(t, server.IsExperimentallyEnabled(someExperimentalFlag))
  1874  	})
  1875  
  1876  	t.Run("returns_true_if_experimentals_has_matching_element_and_other_matching_element", func(t *testing.T) {
  1877  		server.experimentals = []ExperimentalFeatureFlag{someExperimentalFlag, ExperimentalFeatureFlag("some-other-feature")}
  1878  
  1879  		require.True(t, server.IsExperimentallyEnabled(someExperimentalFlag))
  1880  	})
  1881  
  1882  	t.Run("returns_false_if_experimentals_has_no_matching_element", func(t *testing.T) {
  1883  		server.experimentals = []ExperimentalFeatureFlag{ExperimentalFeatureFlag("some-other-feature")}
  1884  
  1885  		require.False(t, server.IsExperimentallyEnabled(someExperimentalFlag))
  1886  	})
  1887  }