github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/pkg/appdefcompat/impl_test.go (about) 1 /* 2 * Copyright (c) 2023-present unTill Pro, Ltd. 3 * @author Alisher Nurmanov 4 */ 5 package appdefcompat 6 7 import ( 8 "embed" 9 "fmt" 10 "strings" 11 "testing" 12 13 "github.com/stretchr/testify/require" 14 "golang.org/x/exp/slices" 15 16 "github.com/voedger/voedger/pkg/appdef" 17 "github.com/voedger/voedger/pkg/parser" 18 ) 19 20 //go:embed sql/sys.old.vsql 21 var oldSysSchemaFS embed.FS 22 23 //go:embed sql/pkg1.old.vsql 24 var oldPkg1SchemaFS embed.FS 25 26 //go:embed sql/pkg2.vsql 27 var pkg2SchemaFS embed.FS 28 29 //go:embed sql/sys.new.vsql 30 var newSysSchemaFS embed.FS 31 32 //go:embed sql/pkg1.new.vsql 33 var newPkg1SchemaFS embed.FS 34 35 func Test_Basic(t *testing.T) { 36 oldSysPkgAST, err := parser.ParsePackageDir(appdef.SysPackage, oldSysSchemaFS, "sql") 37 require.NoError(t, err) 38 39 oldPkg1AST, err := parser.ParsePackageDir("pkg1", oldPkg1SchemaFS, "sql") 40 require.NoError(t, err) 41 42 oldpkg2AST, err := parser.ParsePackageDir("pkg2", pkg2SchemaFS, "sql") 43 require.NoError(t, err) 44 45 newpkg2AST, err := parser.ParsePackageDir("pkg2", pkg2SchemaFS, "sql") 46 require.NoError(t, err) 47 48 oldPackages, err := parser.BuildAppSchema([]*parser.PackageSchemaAST{oldSysPkgAST, oldPkg1AST, oldpkg2AST}) 49 require.NoError(t, err) 50 51 newSysPkgAST, err := parser.ParsePackageDir(appdef.SysPackage, newSysSchemaFS, "sql") 52 require.NoError(t, err) 53 54 newPkg1AST, err := parser.ParsePackageDir("pkg1", newPkg1SchemaFS, "sql") 55 require.NoError(t, err) 56 57 newPackages, err := parser.BuildAppSchema([]*parser.PackageSchemaAST{newSysPkgAST, newPkg1AST, newpkg2AST}) 58 require.NoError(t, err) 59 60 oldBuilder := appdef.New() 61 require.NoError(t, parser.BuildAppDefs(oldPackages, oldBuilder)) 62 63 newBuilder := appdef.New() 64 require.NoError(t, parser.BuildAppDefs(newPackages, newBuilder)) 65 66 oldAppDef, err := oldBuilder.Build() 67 require.NoError(t, err) 68 69 newAppDef, err := newBuilder.Build() 70 require.NoError(t, err) 71 72 t.Run("CheckBackwardCompatibility", func(t *testing.T) { 73 expectedErrors := []CompatibilityError{ 74 {OldTreePath: []string{"AppDef", "Types", "sys.Profile", "Types", "sys.ProfileTable"}, ErrorType: ErrorTypeNodeRemoved}, 75 {OldTreePath: []string{"AppDef", "Types", "sys.ProfileTable"}, ErrorType: ErrorTypeNodeRemoved}, 76 {OldTreePath: []string{"AppDef", "Types", "sys.SomeTable"}, ErrorType: ErrorTypeNodeRemoved}, 77 {OldTreePath: []string{"AppDef", "Types", "sys.CreateLoginUnloggedParams", "Fields", "Password"}, ErrorType: ErrorTypeOrderChanged}, 78 {OldTreePath: []string{"AppDef", "Types", "sys.CreateLoginUnloggedParams", "Fields", "Email"}, ErrorType: ErrorTypeOrderChanged}, 79 {OldTreePath: []string{"AppDef", "Types", "sys.CreateLoginParams", "Fields", "Login"}, ErrorType: ErrorTypeNodeRemoved}, 80 {OldTreePath: []string{"AppDef", "Types", "sys.CreateLoginParams", "Fields", "ProfileCluster"}, ErrorType: ErrorTypeValueChanged}, 81 {OldTreePath: []string{"AppDef", "Types", "sys.CreateLoginParams", "Fields", "ProfileToken"}, ErrorType: ErrorTypeValueChanged}, 82 {OldTreePath: []string{"AppDef", "Types", "sys.AnotherOneTable", "Fields"}, ErrorType: ErrorTypeNodeInserted}, 83 {OldTreePath: []string{"AppDef", "Types", "sys.AnotherOneTable", "Fields", "C"}, ErrorType: ErrorTypeValueChanged}, 84 {OldTreePath: []string{"AppDef", "Types", "sys.AnotherOneTable", "Fields", "C"}, ErrorType: ErrorTypeOrderChanged}, 85 {OldTreePath: []string{"AppDef", "Types", "sys.SomeWorkspace", "Types", "sys.SomeTable"}, ErrorType: ErrorTypeNodeRemoved}, 86 {OldTreePath: []string{"AppDef", "Types", "sys.SomeCommand", "CommandArgs"}, ErrorType: ErrorTypeValueChanged}, 87 {OldTreePath: []string{"AppDef", "Types", "sys.SomeCommand", "UnloggedArgs"}, ErrorType: ErrorTypeValueChanged}, 88 {OldTreePath: []string{"AppDef", "Types", "sys.SomeCommand", "CommandResult"}, ErrorType: ErrorTypeValueChanged}, 89 {OldTreePath: []string{"AppDef", "Types", "sys.Workspace", "Abstract"}, ErrorType: ErrorTypeValueChanged}, 90 {OldTreePath: []string{"AppDef", "Types", "sys.SomeView", "PartKeyFields"}, ErrorType: ErrorTypeNodeModified}, 91 {OldTreePath: []string{"AppDef", "Types", "sys.SomeView", "Fields", "E"}, ErrorType: ErrorTypeValueChanged}, 92 {OldTreePath: []string{"AppDef", "Types", "sys.SomeView", "ClustColsFields", "B"}, ErrorType: ErrorTypeValueChanged}, 93 {OldTreePath: []string{"AppDef", "Types", "sys.AnotherOneTable", "Uniques", "sys.AnotherOneTable$uniques$01", "UniqueFields"}, ErrorType: ErrorTypeNodeModified}, 94 {OldTreePath: []string{"AppDef", "Packages", "pkg1"}, ErrorType: ErrorTypeValueChanged}, 95 {OldTreePath: []string{"AppDef", "Packages", "pkg2"}, ErrorType: ErrorTypeValueChanged}, 96 } 97 compatErrors := CheckBackwardCompatibility(oldAppDef, newAppDef) 98 fmt.Println(compatErrors.Error()) 99 validateCompatibilityErrors2(t, expectedErrors, compatErrors) 100 }) 101 102 t.Run("IgnoreCompatibilityErrors", func(t *testing.T) { 103 // testing ignoring some compatibility errors 104 pathsToIgnore := [][]string{ 105 {"AppDef", "Types", "sys.CreateLoginParams", "Fields", "ProfileToken"}, 106 } 107 compatErrors := CheckBackwardCompatibility(oldAppDef, newAppDef) 108 fmt.Println(compatErrors.Error()) 109 filteredCompatErrors := IgnoreCompatibilityErrors(compatErrors, pathsToIgnore) 110 checkPathsToIgnore(t, pathsToIgnore, compatErrors, filteredCompatErrors) 111 }) 112 } 113 114 func validateCompatibilityErrors2(t *testing.T, expectedErrors []CompatibilityError, actualErrors *CompatibilityErrors) { 115 for _, actualErr := range actualErrors.Errors { 116 require.True(t, slices.ContainsFunc(expectedErrors, func(expectedErr CompatibilityError) bool { 117 if expectedErr.Path() == actualErr.Path() && expectedErr.ErrorType == actualErr.ErrorType { 118 return true 119 } 120 return false 121 }), actualErr.Error()) 122 } 123 for _, expectedErr := range expectedErrors { 124 require.True(t, slices.ContainsFunc(actualErrors.Errors, func(actualErr CompatibilityError) bool { 125 if expectedErr.Path() == actualErr.Path() && expectedErr.ErrorType == actualErr.ErrorType { 126 return true 127 } 128 return false 129 }), "error is expected but not occurred: "+expectedErr.Error()) 130 } 131 } 132 133 func checkPathsToIgnore(t *testing.T, pathsToIgnore [][]string, compatErrors, filteredCompatErrors *CompatibilityErrors) { 134 for _, pathToIgnore := range pathsToIgnore { 135 found := false 136 for _, cerr := range compatErrors.Errors { 137 if cerr.Path() == strings.Join(pathToIgnore, pathDelimiter) { 138 found = true 139 break 140 } 141 } 142 require.True(t, found, fmt.Sprintf("there is no path %s in compat errors", pathToIgnore)) 143 } 144 for _, pathToIgnore := range pathsToIgnore { 145 found := false 146 for _, cerr := range filteredCompatErrors.Errors { 147 if cerr.Path() == strings.Join(pathToIgnore, pathDelimiter) { 148 found = true 149 break 150 } 151 } 152 require.False(t, found, fmt.Sprintf("path %s should be ignored", pathToIgnore)) 153 } 154 }