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

     1  /*
     2   * Copyright (c) 2021-present Sigma-Soft, Ltd.
     3   * @author: Nikolay Nikitin
     4   */
     5  
     6  package qnames
     7  
     8  import (
     9  	"errors"
    10  	"fmt"
    11  	"testing"
    12  
    13  	"github.com/stretchr/testify/mock"
    14  	"github.com/stretchr/testify/require"
    15  
    16  	"github.com/voedger/voedger/pkg/appdef"
    17  	"github.com/voedger/voedger/pkg/istorage/mem"
    18  	istorageimpl "github.com/voedger/voedger/pkg/istorage/provider"
    19  	"github.com/voedger/voedger/pkg/istructs"
    20  	"github.com/voedger/voedger/pkg/istructsmem/internal/consts"
    21  	"github.com/voedger/voedger/pkg/istructsmem/internal/teststore"
    22  	"github.com/voedger/voedger/pkg/istructsmem/internal/utils"
    23  	"github.com/voedger/voedger/pkg/istructsmem/internal/vers"
    24  )
    25  
    26  func TestQNames(t *testing.T) {
    27  	require := require.New(t)
    28  
    29  	sp := istorageimpl.Provide(mem.Provide())
    30  	storage, err := sp.AppStorage(istructs.AppQName_test1_app1)
    31  	require.NoError(err)
    32  
    33  	versions := vers.New()
    34  	if err := versions.Prepare(storage); err != nil {
    35  		panic(err)
    36  	}
    37  
    38  	defName := appdef.NewQName("test", "doc")
    39  
    40  	resourceName := appdef.NewQName("test", "resource")
    41  	r := mockResources{}
    42  	r.On("Resources", mock.AnythingOfType("func(appdef.QName)")).
    43  		Run(func(args mock.Arguments) {
    44  			cb := args.Get(0).(func(appdef.QName))
    45  			cb(resourceName)
    46  		})
    47  
    48  	names := New()
    49  	if err := names.Prepare(storage, versions,
    50  		func() appdef.IAppDef {
    51  			adb := appdef.New()
    52  			adb.AddPackage("test", "test.com/test")
    53  			adb.AddCDoc(defName)
    54  			appDef, err := adb.Build()
    55  			require.NoError(err)
    56  			return appDef
    57  		}(),
    58  		&r); err != nil {
    59  		panic(err)
    60  	}
    61  
    62  	t.Run("basic QNames methods", func(t *testing.T) {
    63  
    64  		check := func(names *QNames, name appdef.QName) QNameID {
    65  			id, err := names.ID(name)
    66  			require.NoError(err)
    67  			require.NotEqual(NullQNameID, id)
    68  
    69  			n, err := names.QName(id)
    70  			require.NoError(err)
    71  			require.Equal(name, n)
    72  
    73  			return id
    74  		}
    75  
    76  		sID := check(names, defName)
    77  		rID := check(names, resourceName)
    78  
    79  		t.Run("must be ok to load early stored names", func(t *testing.T) {
    80  			versions1 := vers.New()
    81  			if err := versions1.Prepare(storage); err != nil {
    82  				panic(err)
    83  			}
    84  
    85  			names1 := New()
    86  			if err := names1.Prepare(storage, versions, nil, nil); err != nil {
    87  				panic(err)
    88  			}
    89  
    90  			require.Equal(sID, check(names1, defName))
    91  			require.Equal(rID, check(names1, resourceName))
    92  		})
    93  
    94  		t.Run("must be ok to redeclare names", func(t *testing.T) {
    95  			versions2 := vers.New()
    96  			if err := versions2.Prepare(storage); err != nil {
    97  				panic(err)
    98  			}
    99  
   100  			names2 := New()
   101  			if err := names2.Prepare(storage, versions,
   102  				func() appdef.IAppDef {
   103  					adb := appdef.New()
   104  					adb.AddPackage("test", "test.com/test")
   105  					adb.AddCDoc(defName)
   106  					appDef, err := adb.Build()
   107  					require.NoError(err)
   108  					return appDef
   109  				}(),
   110  				nil); err != nil {
   111  				panic(err)
   112  			}
   113  
   114  			require.Equal(sID, check(names2, defName))
   115  			require.Equal(rID, check(names2, resourceName))
   116  		})
   117  	})
   118  
   119  	t.Run("must be error if unknown name", func(t *testing.T) {
   120  		id, err := names.ID(appdef.NewQName("test", "unknown"))
   121  		require.Equal(NullQNameID, id)
   122  		require.ErrorIs(err, ErrNameNotFound)
   123  	})
   124  
   125  	t.Run("must be error if unknown id", func(t *testing.T) {
   126  		n, err := names.QName(QNameID(MaxAvailableQNameID))
   127  		require.Equal(appdef.NullQName, n)
   128  		require.ErrorIs(err, ErrIDNotFound)
   129  	})
   130  }
   131  
   132  func TestQNamesPrepareErrors(t *testing.T) {
   133  	require := require.New(t)
   134  
   135  	t.Run("must be error if unknown system view version", func(t *testing.T) {
   136  		sp := istorageimpl.Provide(mem.Provide())
   137  		storage, _ := sp.AppStorage(istructs.AppQName_test1_app1)
   138  
   139  		versions := vers.New()
   140  		if err := versions.Prepare(storage); err != nil {
   141  			panic(err)
   142  		}
   143  
   144  		versions.Put(vers.SysQNamesVersion, latestVersion+1)
   145  
   146  		names := New()
   147  		err := names.Prepare(storage, versions, nil, nil)
   148  		require.ErrorIs(err, vers.ErrorInvalidVersion)
   149  	})
   150  
   151  	t.Run("must be error if invalid QName loaded from system view ", func(t *testing.T) {
   152  		sp := istorageimpl.Provide(mem.Provide())
   153  		storage, _ := sp.AppStorage(istructs.AppQName_test1_app1)
   154  
   155  		versions := vers.New()
   156  		if err := versions.Prepare(storage); err != nil {
   157  			panic(err)
   158  		}
   159  
   160  		versions.Put(vers.SysQNamesVersion, latestVersion)
   161  		const badName = "-test.error.qname-"
   162  		storage.Put(utils.ToBytes(consts.SysView_QNames, ver01), []byte(badName), utils.ToBytes(QNameID(512)))
   163  
   164  		names := New()
   165  		err := names.Prepare(storage, versions, nil, nil)
   166  		require.ErrorIs(err, appdef.ErrConvertError)
   167  		require.ErrorContains(err, badName)
   168  	})
   169  
   170  	t.Run("must be ok if deleted QName loaded from system view ", func(t *testing.T) {
   171  		sp := istorageimpl.Provide(mem.Provide())
   172  		storage, _ := sp.AppStorage(istructs.AppQName_test1_app1)
   173  
   174  		versions := vers.New()
   175  		if err := versions.Prepare(storage); err != nil {
   176  			panic(err)
   177  		}
   178  
   179  		versions.Put(vers.SysQNamesVersion, latestVersion)
   180  		storage.Put(utils.ToBytes(consts.SysView_QNames, ver01), []byte("test.deleted"), utils.ToBytes(NullQNameID))
   181  
   182  		names := New()
   183  		err := names.Prepare(storage, versions, nil, nil)
   184  		require.NoError(err)
   185  	})
   186  
   187  	t.Run("must be error if invalid (small) QNameID loaded from system view ", func(t *testing.T) {
   188  		sp := istorageimpl.Provide(mem.Provide())
   189  		storage, _ := sp.AppStorage(istructs.AppQName_test1_app1)
   190  
   191  		versions := vers.New()
   192  		if err := versions.Prepare(storage); err != nil {
   193  			panic(err)
   194  		}
   195  
   196  		versions.Put(vers.SysQNamesVersion, latestVersion)
   197  		storage.Put(utils.ToBytes(consts.SysView_QNames, ver01), []byte(istructs.QNameForError.String()), utils.ToBytes(QNameIDForError))
   198  
   199  		names := New()
   200  		err := names.Prepare(storage, versions, nil, nil)
   201  		require.ErrorIs(err, ErrWrongQNameID)
   202  		require.ErrorContains(err, fmt.Sprintf("unexpected ID (%v)", QNameIDForError))
   203  	})
   204  
   205  	t.Run("must be error if too many QNames", func(t *testing.T) {
   206  		sp := istorageimpl.Provide(mem.Provide())
   207  		storage, _ := sp.AppStorage(istructs.AppQName_test1_app1)
   208  
   209  		versions := vers.New()
   210  		if err := versions.Prepare(storage); err != nil {
   211  			panic(err)
   212  		}
   213  
   214  		names := New()
   215  		err := names.Prepare(storage, versions,
   216  			func() appdef.IAppDef {
   217  				adb := appdef.New()
   218  				adb.AddPackage("test", "test.com/test")
   219  				for i := 0; i <= MaxAvailableQNameID; i++ {
   220  					adb.AddObject(appdef.NewQName("test", fmt.Sprintf("name_%d", i)))
   221  				}
   222  				appDef, err := adb.Build()
   223  				require.NoError(err)
   224  				return appDef
   225  			}(),
   226  			nil)
   227  		require.ErrorIs(err, ErrQNameIDsExceeds)
   228  	})
   229  
   230  	t.Run("must be error if write to storage failed", func(t *testing.T) {
   231  		qName := appdef.NewQName("test", "test")
   232  		writeError := errors.New("storage write error")
   233  
   234  		t.Run("must be error if write some name failed", func(t *testing.T) {
   235  			storage := teststore.NewStorage()
   236  
   237  			versions := vers.New()
   238  			if err := versions.Prepare(storage); err != nil {
   239  				panic(err)
   240  			}
   241  
   242  			storage.SchedulePutError(writeError, utils.ToBytes(consts.SysView_QNames, ver01), []byte(qName.String()))
   243  
   244  			names := New()
   245  			err := names.Prepare(storage, versions,
   246  				func() appdef.IAppDef {
   247  					adb := appdef.New()
   248  					adb.AddPackage("test", "test.com/test")
   249  					adb.AddObject(qName)
   250  					appDef, err := adb.Build()
   251  					require.NoError(err)
   252  					return appDef
   253  				}(),
   254  				nil)
   255  			require.ErrorIs(err, writeError)
   256  		})
   257  
   258  		t.Run("must be error if write system view version failed", func(t *testing.T) {
   259  			storage := teststore.NewStorage()
   260  
   261  			versions := vers.New()
   262  			if err := versions.Prepare(storage); err != nil {
   263  				panic(err)
   264  			}
   265  
   266  			storage.SchedulePutError(writeError, utils.ToBytes(consts.SysView_Versions), utils.ToBytes(vers.SysQNamesVersion))
   267  
   268  			names := New()
   269  			err := names.Prepare(storage, versions,
   270  				func() appdef.IAppDef {
   271  					adb := appdef.New()
   272  					adb.AddPackage("test", "test.com/test")
   273  					adb.AddObject(qName)
   274  					appDef, err := adb.Build()
   275  					require.NoError(err)
   276  					return appDef
   277  				}(),
   278  				nil)
   279  			require.ErrorIs(err, writeError)
   280  		})
   281  	})
   282  }
   283  
   284  type mockResources struct {
   285  	mock.Mock
   286  }
   287  
   288  func (r *mockResources) QueryResource(resource appdef.QName) istructs.IResource {
   289  	return r.Called(resource).Get(0).(istructs.IResource)
   290  }
   291  
   292  func (r *mockResources) Resources(cb func(appdef.QName)) {
   293  	r.Called(cb)
   294  }