github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/pkg/sys/it/impl_bootstrap_test.go (about)

     1  /*
     2   * Copyright (c) 2024-present unTill Software Development Group B.V.
     3   * @author Denis Gribanov
     4   */
     5  
     6  package sys_it
     7  
     8  import (
     9  	"fmt"
    10  	"testing"
    11  	"testing/fstest"
    12  
    13  	"github.com/stretchr/testify/require"
    14  	"github.com/voedger/voedger/pkg/appparts"
    15  	"github.com/voedger/voedger/pkg/apps"
    16  	"github.com/voedger/voedger/pkg/apps/sys/clusterapp"
    17  	"github.com/voedger/voedger/pkg/btstrp"
    18  	"github.com/voedger/voedger/pkg/cluster"
    19  	"github.com/voedger/voedger/pkg/extensionpoints"
    20  	"github.com/voedger/voedger/pkg/iblobstoragestg"
    21  	"github.com/voedger/voedger/pkg/istorage"
    22  	"github.com/voedger/voedger/pkg/istorage/mem"
    23  	"github.com/voedger/voedger/pkg/istructs"
    24  	"github.com/voedger/voedger/pkg/istructsmem"
    25  	payloads "github.com/voedger/voedger/pkg/itokens-payloads"
    26  	"github.com/voedger/voedger/pkg/parser"
    27  	"github.com/voedger/voedger/pkg/sys"
    28  	"github.com/voedger/voedger/pkg/sys/authnz"
    29  	"github.com/voedger/voedger/pkg/sys/smtp"
    30  	coreutils "github.com/voedger/voedger/pkg/utils"
    31  	it "github.com/voedger/voedger/pkg/vit"
    32  	"github.com/voedger/voedger/pkg/vvm"
    33  	dbcertcache "github.com/voedger/voedger/pkg/vvm/db_cert_cache"
    34  )
    35  
    36  func TestBoostrap_BasicUsage(t *testing.T) {
    37  	require := require.New(t)
    38  	memStorage := mem.Provide()
    39  	keyspacePrefix := t.Name()
    40  
    41  	// launch the VVM with an app with a certain NumParts and NumAppWorkspaces
    42  	numParts := istructs.NumAppPartitions(42)
    43  	numAppWS := istructs.NumAppWorkspaces(43)
    44  	cfg := getTestCfg(numParts, numAppWS, memStorage, keyspacePrefix)
    45  	vit := it.NewVIT(t, &cfg)
    46  
    47  	var clusterApp btstrp.ClusterBuiltInApp
    48  	otherApps := []appparts.BuiltInApp{}
    49  	for _, app := range vit.BuiltInAppsPackages {
    50  		if app.Name == istructs.AppQName_sys_cluster {
    51  			clusterApp = btstrp.ClusterBuiltInApp(app.BuiltInApp)
    52  		} else {
    53  			otherApps = append(otherApps, app.BuiltInApp)
    54  		}
    55  	}
    56  
    57  	t.Run("basic usage", func(t *testing.T) {
    58  		appParts, cleanup, err := appparts.New(vit.IAppStructsProvider)
    59  		require.NoError(err)
    60  		defer cleanup()
    61  		blobStorage := iblobstoragestg.BlobAppStoragePtr(new(istorage.IAppStorage))
    62  		routerStorage := dbcertcache.RouterAppStoragePtr(new(istorage.IAppStorage))
    63  		err = btstrp.Bootstrap(vit.IFederation, vit.IAppStructsProvider, vit.TimeFunc, appParts, clusterApp, otherApps, vit.ITokens, vit.IAppStorageProvider,
    64  			blobStorage, routerStorage)
    65  		require.NoError(err)
    66  		require.NotNil(*blobStorage)
    67  		require.NotNil(*routerStorage)
    68  	})
    69  
    70  	t.Run("panic on NumPartitions change", func(t *testing.T) {
    71  		appParts, cleanup, err := appparts.New(vit.IAppStructsProvider)
    72  		require.NoError(err)
    73  		defer cleanup()
    74  		otherApps[0].AppDeploymentDescriptor.NumParts++
    75  		defer func() {
    76  			otherApps[0].AppDeploymentDescriptor.NumParts--
    77  		}()
    78  		blobStorage := iblobstoragestg.BlobAppStoragePtr(new(istorage.IAppStorage))
    79  		routerStorage := dbcertcache.RouterAppStoragePtr(new(istorage.IAppStorage))
    80  		require.PanicsWithValue(fmt.Sprintf("failed to deploy app %[1]s: status 409: num partitions changed: app %[1]s declaring NumPartitions=%d but was previously deployed with NumPartitions=%d",
    81  			otherApps[0].Name, otherApps[0].AppDeploymentDescriptor.NumParts, otherApps[0].AppDeploymentDescriptor.NumParts-1), func() {
    82  			btstrp.Bootstrap(vit.IFederation, vit.IAppStructsProvider, vit.TimeFunc, appParts, clusterApp, otherApps, vit.ITokens, vit.IAppStorageProvider,
    83  				blobStorage, routerStorage)
    84  		})
    85  	})
    86  
    87  	t.Run("panic on NumAppPartitions change", func(t *testing.T) {
    88  		appParts, cleanup, err := appparts.New(vit.IAppStructsProvider)
    89  		require.NoError(err)
    90  		defer cleanup()
    91  		otherApps[0].AppDeploymentDescriptor.NumAppWorkspaces++
    92  		defer func() {
    93  			otherApps[0].AppDeploymentDescriptor.NumAppWorkspaces--
    94  		}()
    95  
    96  		require.PanicsWithValue(fmt.Sprintf("failed to deploy app %[1]s: status 409: num application workspaces changed: app %[1]s declaring NumAppWorkspaces=%d but was previously deployed with NumAppWorksaces=%d",
    97  			otherApps[0].Name, otherApps[0].AppDeploymentDescriptor.NumAppWorkspaces, otherApps[0].AppDeploymentDescriptor.NumAppWorkspaces-1), func() {
    98  			blobStorage := iblobstoragestg.BlobAppStoragePtr(new(istorage.IAppStorage))
    99  			routerStorage := dbcertcache.RouterAppStoragePtr(new(istorage.IAppStorage))
   100  			btstrp.Bootstrap(vit.IFederation, vit.IAppStructsProvider, vit.TimeFunc, appParts, clusterApp, otherApps, vit.ITokens,
   101  				vit.IAppStorageProvider, blobStorage, routerStorage)
   102  		})
   103  	})
   104  }
   105  
   106  func getTestCfg(numParts istructs.NumAppPartitions, numAppWS istructs.NumAppWorkspaces, storage istorage.IAppStorageFactory, testName string) it.VITConfig {
   107  	fs := fstest.MapFS{
   108  		"app.vsql": &fstest.MapFile{
   109  			Data: []byte(`APPLICATION app1();`),
   110  		},
   111  	}
   112  	app1PackageFS := parser.PackageFS{
   113  		Path: it.App1PkgPath,
   114  		FS:   fs,
   115  	}
   116  	return it.NewOwnVITConfig(
   117  		it.WithApp(istructs.AppQName_test1_app1, func(apis apps.APIs, cfg *istructsmem.AppConfigType, ep extensionpoints.IExtensionPoint) apps.BuiltInAppDef {
   118  			sysPkg := sys.Provide(cfg, smtp.Cfg{}, ep, nil, apis.TimeFunc, apis.ITokens, apis.IFederation, apis.IAppStructsProvider, apis.IAppTokensFactory,
   119  				nil, apis.IAppStorageProvider)
   120  			return apps.BuiltInAppDef{
   121  				AppDeploymentDescriptor: appparts.AppDeploymentDescriptor{
   122  					NumParts:         numParts,
   123  					EnginePoolSize:   it.DefaultTestAppEnginesPool,
   124  					NumAppWorkspaces: numAppWS,
   125  				},
   126  				AppQName: istructs.AppQName_test1_app1,
   127  				Packages: []parser.PackageFS{sysPkg, app1PackageFS},
   128  			}
   129  		}),
   130  		it.WithVVMConfig(func(cfg *vvm.VVMConfig) {
   131  			// use predefined storage
   132  			cfg.StorageFactory = func() (provider istorage.IAppStorageFactory, err error) {
   133  				return storage, nil
   134  			}
   135  			cfg.KeyspaceNameSuffix = testName
   136  		}),
   137  	)
   138  }
   139  
   140  func TestDeployAppErrors(t *testing.T) {
   141  	require := require.New(t)
   142  	vit := it.NewVIT(t, &it.SharedConfig_App1)
   143  	defer vit.TearDown()
   144  
   145  	sysToken, err := payloads.GetSystemPrincipalToken(vit.ITokens, istructs.AppQName_sys_cluster)
   146  	require.NoError(err)
   147  
   148  	t.Run("sys/cluster can not be deployed by c.cluster.DeployApp", func(t *testing.T) {
   149  		body := fmt.Sprintf(`{"args":{"AppQName":"%s","NumPartitions":1,"NumAppWorkspaces":1}}`, istructs.AppQName_sys_cluster)
   150  		vit.PostApp(istructs.AppQName_sys_cluster, clusterapp.ClusterAppPseudoWSID, "c.cluster.DeployApp", body,
   151  			coreutils.WithAuthorizeBy(sysToken), coreutils.Expect400()).Println()
   152  	})
   153  
   154  	var test1App1DeploymentDescriptor appparts.AppDeploymentDescriptor
   155  	for _, app := range vit.BuiltInAppsPackages {
   156  		if app.Name == istructs.AppQName_test1_app1 {
   157  			test1App1DeploymentDescriptor = app.AppDeploymentDescriptor
   158  			break
   159  		}
   160  	}
   161  
   162  	t.Run("409 conflict on try to deploy with different NumPartitions", func(t *testing.T) {
   163  		body := fmt.Sprintf(`{"args":{"AppQName":"%s","NumPartitions":%d,"NumAppWorkspaces":%d}}`,
   164  			istructs.AppQName_test1_app1,
   165  			test1App1DeploymentDescriptor.NumParts+1, test1App1DeploymentDescriptor.NumAppWorkspaces)
   166  		resp := vit.PostApp(istructs.AppQName_sys_cluster, clusterapp.ClusterAppPseudoWSID, "c.cluster.DeployApp", body,
   167  			coreutils.WithAuthorizeBy(sysToken),
   168  			coreutils.Expect409(),
   169  		)
   170  		resp.Println()
   171  		require.Empty(resp.NewIDs)
   172  	})
   173  
   174  	t.Run("409 conflict on try to deploy with different NumAppPartitions", func(t *testing.T) {
   175  		body := fmt.Sprintf(`{"args":{"AppQName":"%s","NumPartitions":%d,"NumAppWorkspaces":%d}}`,
   176  			istructs.AppQName_test1_app1,
   177  			test1App1DeploymentDescriptor.NumParts, test1App1DeploymentDescriptor.NumAppWorkspaces+1)
   178  		resp := vit.PostApp(istructs.AppQName_sys_cluster, clusterapp.ClusterAppPseudoWSID, "c.cluster.DeployApp", body,
   179  			coreutils.WithAuthorizeBy(sysToken),
   180  			coreutils.Expect409(),
   181  		)
   182  		resp.Println()
   183  		require.Empty(resp.NewIDs)
   184  	})
   185  
   186  	t.Run("400 bad request on wrong appQName", func(t *testing.T) {
   187  		body := `{"args":{"AppQName":"wrong","NumPartitions":1,"NumAppWorkspaces":1}}`
   188  		vit.PostApp(istructs.AppQName_sys_cluster, clusterapp.ClusterAppPseudoWSID, "c.cluster.DeployApp", body,
   189  			coreutils.WithAuthorizeBy(sysToken),
   190  			coreutils.Expect400(),
   191  		).Println()
   192  	})
   193  }
   194  
   195  func TestAppWSInitIndempotency(t *testing.T) {
   196  	require := require.New(t)
   197  	vit := it.NewVIT(t, &it.SharedConfig_App1)
   198  	defer vit.TearDown()
   199  
   200  	checkCDocsWSDesc(vit.VVMConfig, vit.VVM, require)
   201  
   202  	// init app ws again (first is done on NewVIT()) -> expect no errors + assume next tests will work as well
   203  	for _, app := range vit.BuiltInAppsPackages {
   204  		as, err := vit.AppStructs(app.Name)
   205  		require.NoError(err)
   206  		initedWSIDs, err := cluster.InitAppWSes(as, as.NumAppWorkspaces(), app.NumParts, istructs.UnixMilli(vit.TimeFunc().UnixMilli()))
   207  		require.NoError(err)
   208  		require.Empty(initedWSIDs)
   209  	}
   210  }
   211  
   212  func checkCDocsWSDesc(vvmCfg *vvm.VVMConfig, vvm *vvm.VVM, require *require.Assertions) {
   213  	for appQName := range vvmCfg.VVMAppsBuilder {
   214  		as, err := vvm.AppStructs(appQName)
   215  		require.NoError(err)
   216  		for wsNum := 0; istructs.NumAppWorkspaces(wsNum) < as.NumAppWorkspaces(); wsNum++ {
   217  			appWSID := istructs.NewWSID(istructs.MainClusterID, istructs.WSID(wsNum+int(istructs.FirstBaseAppWSID)))
   218  			existingCDocWSDesc, err := as.Records().GetSingleton(appWSID, authnz.QNameCDocWorkspaceDescriptor)
   219  			require.NoError(err)
   220  			require.Equal(authnz.QNameCDocWorkspaceDescriptor, existingCDocWSDesc.QName())
   221  		}
   222  	}
   223  }