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 }