github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/pkg/istructsmem/internal/singletons/provide_test.go (about)

     1  /*
     2   * Copyright (c) 2021-present Sigma-Soft, Ltd.
     3   * @author: Nikolay Nikitin
     4   */
     5  
     6  package singletons
     7  
     8  import (
     9  	"fmt"
    10  	"testing"
    11  
    12  	"github.com/stretchr/testify/require"
    13  
    14  	"github.com/voedger/voedger/pkg/appdef"
    15  	"github.com/voedger/voedger/pkg/istorage"
    16  	"github.com/voedger/voedger/pkg/istorage/mem"
    17  	istorageimpl "github.com/voedger/voedger/pkg/istorage/provider"
    18  	"github.com/voedger/voedger/pkg/istructs"
    19  	"github.com/voedger/voedger/pkg/istructsmem/internal/consts"
    20  	"github.com/voedger/voedger/pkg/istructsmem/internal/teststore"
    21  	"github.com/voedger/voedger/pkg/istructsmem/internal/utils"
    22  	"github.com/voedger/voedger/pkg/istructsmem/internal/vers"
    23  )
    24  
    25  func Test_BasicUsage(t *testing.T) {
    26  	sp := istorageimpl.Provide(mem.Provide())
    27  	storage, err := sp.AppStorage(istructs.AppQName_test1_app1)
    28  	require.NoError(t, err)
    29  
    30  	versions := vers.New()
    31  	if err := versions.Prepare(storage); err != nil {
    32  		panic(err)
    33  	}
    34  
    35  	testName := appdef.NewQName("test", "doc")
    36  
    37  	testAppDef := func() appdef.IAppDef {
    38  		adb := appdef.New()
    39  		adb.AddPackage("test", "test.com/test")
    40  		doc := adb.AddCDoc(testName)
    41  		doc.SetSingleton()
    42  		appDef, err := adb.Build()
    43  		if err != nil {
    44  			panic(err)
    45  		}
    46  		return appDef
    47  	}
    48  
    49  	appDef := testAppDef()
    50  
    51  	stones := New()
    52  	if err := stones.Prepare(storage, versions, appDef); err != nil {
    53  		panic(err)
    54  	}
    55  
    56  	require := require.New(t)
    57  	t.Run("basic Singletons methods", func(t *testing.T) {
    58  		id, err := stones.ID(testName)
    59  		require.NoError(err)
    60  		require.NotEqual(istructs.NullRecordID, id)
    61  
    62  		t.Run("must be able to load early stored singletons", func(t *testing.T) {
    63  			versions1 := vers.New()
    64  			if err := versions1.Prepare(storage); err != nil {
    65  				panic(err)
    66  			}
    67  
    68  			appDef1 := testAppDef()
    69  
    70  			stones1 := New()
    71  			if err := stones1.Prepare(storage, versions1, appDef1); err != nil {
    72  				panic(err)
    73  			}
    74  
    75  			id1, err := stones1.ID(testName)
    76  			require.NoError(err)
    77  			require.Equal(id, id1)
    78  		})
    79  	})
    80  }
    81  
    82  func test_AppDefSingletons(t *testing.T, appDef appdef.IAppDef, st *Singletons) {
    83  	require := require.New(t)
    84  	appDef.Types(
    85  		func(t appdef.IType) {
    86  			if singleton, ok := t.(appdef.ISingleton); ok {
    87  				if singleton.Singleton() {
    88  					id, err := st.ID(singleton.QName())
    89  					require.NoError(err)
    90  					require.NotEqual(istructs.NullRecordID, id)
    91  				}
    92  			}
    93  		})
    94  }
    95  
    96  func Test_SingletonsGetID(t *testing.T) {
    97  
    98  	require := require.New(t)
    99  	cDocName := appdef.NewQName("test", "SingletonCDoc")
   100  	wDocName := appdef.NewQName("test", "SingletonWDoc")
   101  
   102  	st := New()
   103  
   104  	t.Run("must be ok to construct Singletons", func(t *testing.T) {
   105  		storage, versions, appDef := func() (istorage.IAppStorage, *vers.Versions, appdef.IAppDef) {
   106  			storage, err := istorageimpl.Provide(mem.Provide()).AppStorage(istructs.AppQName_test1_app1)
   107  			require.NoError(err)
   108  
   109  			versions := vers.New()
   110  			err = versions.Prepare(storage)
   111  			require.NoError(err)
   112  
   113  			adb := appdef.New()
   114  			adb.AddPackage("test", "test.com/test")
   115  
   116  			{
   117  				doc := adb.AddCDoc(cDocName)
   118  				doc.SetSingleton()
   119  				doc.AddField("f1", appdef.DataKind_QName, true)
   120  			}
   121  
   122  			{
   123  				doc := adb.AddWDoc(wDocName)
   124  				doc.SetSingleton()
   125  				doc.AddField("f1", appdef.DataKind_QName, true)
   126  			}
   127  
   128  			appDef, err := adb.Build()
   129  			require.NoError(err)
   130  
   131  			return storage, versions, appDef
   132  		}()
   133  
   134  		err := st.Prepare(storage, versions, appDef)
   135  		require.NoError(err)
   136  
   137  		test_AppDefSingletons(t, appDef, st)
   138  	})
   139  
   140  	testID := func(id istructs.RecordID, known bool, qname appdef.QName) {
   141  		t.Run(fmt.Sprintf("test Singletons QName(%v) founded", id), func(t *testing.T) {
   142  			n, ok := st.ids[id]
   143  			if known {
   144  				require.True(ok)
   145  				require.Equal(qname, n)
   146  			} else {
   147  				require.False(ok)
   148  			}
   149  		})
   150  	}
   151  
   152  	testQName := func(qname appdef.QName, known bool) {
   153  		t.Run(fmt.Sprintf("test Stones.GetID(%v)", qname), func(t *testing.T) {
   154  			var id istructs.RecordID
   155  			var err error
   156  
   157  			id, err = st.ID(qname)
   158  			if known {
   159  				require.NoError(err)
   160  				require.NotNil(id)
   161  
   162  				testID(id, true, qname)
   163  			} else {
   164  				require.ErrorIs(err, ErrNameNotFound)
   165  			}
   166  		})
   167  	}
   168  
   169  	t.Run("check NullQName", func(t *testing.T) {
   170  		testQName(appdef.NullQName, false)
   171  	})
   172  
   173  	t.Run("check known QName", func(t *testing.T) {
   174  		testQName(cDocName, true)
   175  		testQName(wDocName, true)
   176  	})
   177  
   178  	t.Run("check unknown QName", func(t *testing.T) {
   179  		testQName(appdef.NewQName("unknown", "CDoc"), false)
   180  		testQName(appdef.NewQName("unknown", "WDoc"), false)
   181  	})
   182  
   183  	t.Run("check unknown id", func(t *testing.T) {
   184  		testID(istructs.MaxSingletonID-1, false, appdef.NullQName)
   185  	})
   186  }
   187  
   188  func Test_Singletons_Errors(t *testing.T) {
   189  
   190  	require := require.New(t)
   191  	cDocName := appdef.NewQName("test", "SingletonCDoc")
   192  	testError := fmt.Errorf("test error")
   193  
   194  	t.Run("must error if unknown version of Singletons system view", func(t *testing.T) {
   195  		storage, err := istorageimpl.Provide(mem.Provide()).AppStorage(istructs.AppQName_test1_app1)
   196  		require.NoError(err)
   197  
   198  		versions := vers.New()
   199  		err = versions.Prepare(storage)
   200  		require.NoError(err)
   201  
   202  		err = versions.Put(vers.SysSingletonsVersion, 0xFF)
   203  		require.NoError(err)
   204  
   205  		stone := New()
   206  		err = stone.Prepare(storage, versions, nil)
   207  		require.ErrorIs(err, vers.ErrorInvalidVersion)
   208  	})
   209  
   210  	t.Run("must error if unable store version of Singletons system  view", func(t *testing.T) {
   211  		storage := teststore.NewStorage()
   212  		storage.SchedulePutError(testError, utils.ToBytes(consts.SysView_Versions), utils.ToBytes(vers.SysSingletonsVersion))
   213  
   214  		versions := vers.New()
   215  		err := versions.Prepare(storage)
   216  		require.NoError(err)
   217  
   218  		adb := appdef.New()
   219  		adb.AddPackage("test", "test.com/test")
   220  
   221  		doc := adb.AddCDoc(cDocName)
   222  		doc.SetSingleton()
   223  		doc.AddField("f1", appdef.DataKind_QName, true)
   224  		appDef, err := adb.Build()
   225  		require.NoError(err)
   226  
   227  		stone := New()
   228  		err = stone.Prepare(storage, versions, appDef)
   229  
   230  		require.ErrorIs(err, testError)
   231  	})
   232  
   233  	t.Run("must error if maximum singletons is exceeded", func(t *testing.T) {
   234  		storage := teststore.NewStorage()
   235  
   236  		versions := vers.New()
   237  		err := versions.Prepare(storage)
   238  		require.NoError(err)
   239  
   240  		adb := appdef.New()
   241  		adb.AddPackage("test", "test.com/test")
   242  
   243  		for id := istructs.FirstSingletonID; id <= istructs.MaxSingletonID; id++ {
   244  			doc := adb.AddCDoc(appdef.NewQName("test", fmt.Sprintf("doc_%v", id)))
   245  			doc.SetSingleton()
   246  		}
   247  		appDef, err := adb.Build()
   248  		require.NoError(err)
   249  
   250  		st := New()
   251  		err = st.Prepare(storage, versions, appDef)
   252  
   253  		require.ErrorIs(err, ErrSingletonIDsExceeds)
   254  	})
   255  
   256  	t.Run("must error if store ID for some singleton doc to storage is failed", func(t *testing.T) {
   257  		defName := appdef.NewQName("test", "ErrorDef")
   258  
   259  		storage := teststore.NewStorage()
   260  		storage.SchedulePutError(testError, utils.ToBytes(consts.SysView_SingletonIDs, latestVersion), []byte(defName.String()))
   261  
   262  		versions := vers.New()
   263  		err := versions.Prepare(storage)
   264  		require.NoError(err)
   265  
   266  		app := appdef.New()
   267  		doc := app.AddCDoc(defName)
   268  		doc.SetSingleton()
   269  		appDef, err := app.Build()
   270  		require.NoError(err)
   271  
   272  		st := New()
   273  		err = st.Prepare(storage, versions, appDef)
   274  		require.ErrorIs(err, testError)
   275  	})
   276  
   277  	t.Run("must error if retrieve ID for some singleton doc from storage is failed", func(t *testing.T) {
   278  		defName := appdef.NewQName("test", "ErrorDef")
   279  
   280  		storage := teststore.NewStorage()
   281  
   282  		versions := vers.New()
   283  		err := versions.Prepare(storage)
   284  		require.NoError(err)
   285  
   286  		app := appdef.New()
   287  		doc := app.AddCDoc(defName)
   288  		doc.SetSingleton()
   289  		appDef, err := app.Build()
   290  		require.NoError(err)
   291  
   292  		st := New()
   293  		err = st.Prepare(storage, versions, appDef)
   294  		require.NoError(err)
   295  
   296  		storage.ScheduleGetError(testError, nil, []byte(defName.String()))
   297  		st1 := New()
   298  		err = st1.Prepare(storage, versions, appDef)
   299  		require.ErrorIs(err, testError)
   300  	})
   301  
   302  	t.Run("must error if some some singleton QName from storage is not well formed", func(t *testing.T) {
   303  		storage := teststore.NewStorage()
   304  
   305  		versions := vers.New()
   306  		err := versions.Prepare(storage)
   307  		require.NoError(err)
   308  		versions.Put(vers.SysSingletonsVersion, latestVersion)
   309  
   310  		t.Run("crack storage by put invalid QName string into Singletons system view", func(t *testing.T) {
   311  			err = storage.Put(
   312  				utils.ToBytes(consts.SysView_SingletonIDs, latestVersion),
   313  				[]byte("error.CDoc.be-e-e"),
   314  				utils.ToBytes(istructs.MaxSingletonID),
   315  			)
   316  			require.NoError(err)
   317  		})
   318  
   319  		st := New()
   320  		err = st.Prepare(storage, versions, nil)
   321  
   322  		require.ErrorIs(err, appdef.ErrConvertError)
   323  	})
   324  }