github.com/openfga/openfga@v1.5.4-rc1/cmd/validatemodels/validate_models_test.go (about)

     1  package validatemodels
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"testing"
     7  
     8  	"github.com/oklog/ulid/v2"
     9  	openfgav1 "github.com/openfga/api/proto/openfga/v1"
    10  	parser "github.com/openfga/language/pkg/go/transformer"
    11  	"github.com/spf13/cobra"
    12  	"github.com/spf13/viper"
    13  	"github.com/stretchr/testify/require"
    14  
    15  	"github.com/openfga/openfga/cmd"
    16  	"github.com/openfga/openfga/cmd/util"
    17  	"github.com/openfga/openfga/pkg/typesystem"
    18  )
    19  
    20  func TestValidationResult(t *testing.T) {
    21  	engines := []string{"postgres", "mysql"}
    22  
    23  	totalStores := 200
    24  	totalModelsForOneStore := 200
    25  
    26  	for _, engine := range engines {
    27  		t.Run(engine, func(t *testing.T) {
    28  			_, ds, _ := util.MustBootstrapDatastore(t, engine)
    29  
    30  			ctx := context.Background()
    31  
    32  			// write a bunch of stores (to trigger pagination)
    33  			var storeID string
    34  			for i := 0; i < totalStores; i++ {
    35  				storeID = ulid.Make().String()
    36  				_, err := ds.CreateStore(ctx, &openfgav1.Store{
    37  					Id:   storeID,
    38  					Name: fmt.Sprintf("store-%d", i),
    39  				})
    40  				require.NoError(t, err)
    41  				t.Logf("added store %s\n", storeID)
    42  			}
    43  
    44  			// for the last store, write a bunch of models (to trigger pagination)
    45  			for j := 0; j < totalModelsForOneStore; j++ {
    46  				modelID := ulid.Make().String()
    47  				err := ds.WriteAuthorizationModel(ctx, storeID, &openfgav1.AuthorizationModel{
    48  					Id:            modelID,
    49  					SchemaVersion: typesystem.SchemaVersion1_1,
    50  					// invalid
    51  					TypeDefinitions: parser.MustTransformDSLToProto(`model
    52  	schema 1.1
    53  type document
    54    relations
    55  	define viewer:[user]
    56  `).GetTypeDefinitions(),
    57  				})
    58  				require.NoError(t, err)
    59  				t.Logf("added model %s for store %s\n", modelID, storeID)
    60  			}
    61  
    62  			t.Run("validate returns success", func(t *testing.T) {
    63  				validationResults, err := ValidateAllAuthorizationModels(ctx, ds)
    64  				require.NoError(t, err)
    65  				require.Len(t, validationResults, totalModelsForOneStore)
    66  				require.Contains(t, "the relation type 'user' on 'viewer' in object type 'document' is not valid", validationResults[0].Error)
    67  				require.True(t, validationResults[0].IsLatestModel)
    68  
    69  				require.Contains(t, "the relation type 'user' on 'viewer' in object type 'document' is not valid", validationResults[1].Error)
    70  				require.False(t, validationResults[1].IsLatestModel)
    71  			})
    72  		})
    73  	}
    74  }
    75  
    76  func TestValidateModelsCommandWhenInvalidEngine(t *testing.T) {
    77  	for _, tc := range []struct {
    78  		engine        string
    79  		errorExpected string
    80  	}{
    81  		{
    82  			engine:        "memory",
    83  			errorExpected: "storage engine 'memory' is unsupported",
    84  		},
    85  		{
    86  			engine:        "",
    87  			errorExpected: "missing datastore engine type",
    88  		},
    89  	} {
    90  		t.Run(tc.engine, func(t *testing.T) {
    91  			validateModelsCommand := NewValidateCommand()
    92  			validateModelsCommand.SetArgs([]string{"--datastore-engine", tc.engine, "--datastore-uri", ""})
    93  			err := validateModelsCommand.Execute()
    94  			require.ErrorContains(t, err, tc.errorExpected)
    95  		})
    96  	}
    97  }
    98  
    99  func TestValidateModelsCommandNoConfigDefaultValues(t *testing.T) {
   100  	util.PrepareTempConfigDir(t)
   101  	validateCommand := NewValidateCommand()
   102  	validateCommand.RunE = func(cmd *cobra.Command, _ []string) error {
   103  		require.Equal(t, "", viper.GetString(datastoreEngineFlag))
   104  		require.Equal(t, "", viper.GetString(datastoreURIFlag))
   105  		return nil
   106  	}
   107  
   108  	cmd := cmd.NewRootCommand()
   109  	cmd.AddCommand(validateCommand)
   110  	cmd.SetArgs([]string{"validate-models"})
   111  	require.NoError(t, cmd.Execute())
   112  }
   113  
   114  func TestValidateModelsCommandConfigFileValuesAreParsed(t *testing.T) {
   115  	config := `datastore:
   116      engine: someEngine
   117      uri: postgres://postgres:password@127.0.0.1:5432/postgres
   118  `
   119  	util.PrepareTempConfigFile(t, config)
   120  
   121  	validateCmd := NewValidateCommand()
   122  	validateCmd.RunE = func(cmd *cobra.Command, _ []string) error {
   123  		require.Equal(t, "someEngine", viper.GetString(datastoreEngineFlag))
   124  		require.Equal(t, "postgres://postgres:password@127.0.0.1:5432/postgres", viper.GetString(datastoreURIFlag))
   125  		return nil
   126  	}
   127  
   128  	cmd := cmd.NewRootCommand()
   129  	cmd.AddCommand(validateCmd)
   130  	cmd.SetArgs([]string{"validate-models"})
   131  	require.NoError(t, cmd.Execute())
   132  }
   133  
   134  func TestValidateModelsCommandConfigIsMerged(t *testing.T) {
   135  	config := `datastore:
   136      engine: anotherEngine
   137  `
   138  	util.PrepareTempConfigFile(t, config)
   139  
   140  	t.Setenv("OPENFGA_DATASTORE_URI", "postgres://postgres:PASS2@127.0.0.1:5432/postgres")
   141  
   142  	validateCmd := NewValidateCommand()
   143  	validateCmd.RunE = func(cmd *cobra.Command, _ []string) error {
   144  		require.Equal(t, "anotherEngine", viper.GetString(datastoreEngineFlag))
   145  		require.Equal(t, "postgres://postgres:PASS2@127.0.0.1:5432/postgres", viper.GetString(datastoreURIFlag))
   146  		return nil
   147  	}
   148  
   149  	cmd := cmd.NewRootCommand()
   150  	cmd.AddCommand(validateCmd)
   151  	cmd.SetArgs([]string{"validate-models"})
   152  	require.NoError(t, cmd.Execute())
   153  }