code.vegaprotocol.io/vega@v0.79.0/datanode/sqlstore/assets_test.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package sqlstore_test 17 18 import ( 19 "context" 20 "fmt" 21 "testing" 22 23 "code.vegaprotocol.io/vega/datanode/entities" 24 "code.vegaprotocol.io/vega/datanode/sqlstore" 25 vegapb "code.vegaprotocol.io/vega/protos/vega" 26 27 "github.com/shopspring/decimal" 28 "github.com/stretchr/testify/assert" 29 "github.com/stretchr/testify/require" 30 ) 31 32 var testAssetCount int 33 34 func addTestAsset(t *testing.T, ctx context.Context, as *sqlstore.Assets, block entities.Block, idPrefix ...string) entities.Asset { 35 t.Helper() 36 asset := getTestAsst(t, block, idPrefix...) 37 38 // Add it to the database 39 err := as.Add(ctx, asset) 40 require.NoError(t, err) 41 return asset 42 } 43 44 func getTestAsst(t *testing.T, block entities.Block, idPrefix ...string) entities.Asset { 45 t.Helper() 46 // Make an asset 47 testAssetCount++ 48 quantum, _ := decimal.NewFromString("10") 49 assetID := GenerateID() 50 51 if len(idPrefix) > 0 && idPrefix[0] != "" { 52 assetID = fmt.Sprintf("%s%02d", idPrefix[0], testAssetCount) 53 } 54 55 return entities.Asset{ 56 ID: entities.AssetID(assetID), 57 Name: fmt.Sprint("my test asset", testAssetCount), 58 Symbol: fmt.Sprint("TEST", testAssetCount), 59 Decimals: 5, 60 Quantum: quantum, 61 ChainID: "1789", 62 ERC20Contract: "0xdeadbeef", 63 VegaTime: block.VegaTime, 64 LifetimeLimit: decimal.New(42, 0), 65 WithdrawThreshold: decimal.New(81, 0), 66 Status: entities.AssetStatusEnabled, 67 TxHash: generateTxHash(), 68 } 69 } 70 71 func assetsEqual(t *testing.T, expected, actual entities.Asset) { 72 t.Helper() 73 74 assert.Equal(t, expected.ID, actual.ID) 75 assert.Equal(t, expected.Name, actual.Name) 76 assert.Equal(t, expected.Symbol, actual.Symbol) 77 assert.Equal(t, expected.Decimals, actual.Decimals) 78 assert.Equal(t, expected.Quantum, actual.Quantum) 79 assert.Equal(t, expected.ERC20Contract, actual.ERC20Contract) 80 assert.Equal(t, expected.VegaTime, actual.VegaTime) 81 assert.Equal(t, expected.ChainID, actual.ChainID) 82 assert.True(t, expected.LifetimeLimit.Equal(actual.LifetimeLimit)) 83 assert.True(t, expected.WithdrawThreshold.Equal(actual.WithdrawThreshold)) 84 } 85 86 // TestAssetCache tests for a bug which was discovered whereby fetching an asset by ID after 87 // it had been updated but before the transaction was committed led to a poisoned cache that 88 // returned stale values. 89 func TestAssetCache(t *testing.T) { 90 ctx := tempTransaction(t) 91 92 bs := sqlstore.NewBlocks(connectionSource) 93 as := sqlstore.NewAssets(connectionSource) 94 block := addTestBlock(t, ctx, bs) 95 96 // A make a lovely asset 97 asset := addTestAsset(t, ctx, as, block, "") 98 99 // Try updating the asset to have a new symbol in the top level transaction 100 asset2 := asset 101 asset2.Symbol = "TEST2" 102 err := as.Add(ctx, asset2) 103 require.NoError(t, err) 104 105 // Should get new asset symbol immediately 106 fetched, err := as.GetByID(ctx, string(asset.ID)) 107 require.NoError(t, err) 108 require.Equal(t, asset2, fetched) 109 110 // Now in a sub-transaction, update the asset to have another different symbol 111 txCtx, err := connectionSource.WithTransaction(ctx) 112 require.NoError(t, err) 113 asset3 := asset 114 asset3.Symbol = "TEST3" 115 err = as.Add(txCtx, asset3) 116 require.NoError(t, err) 117 118 // Transaction hasn't committed yet, we should still get the old symbol when fetching that asset 119 fetched, err = as.GetByID(ctx, string(asset.ID)) 120 require.NoError(t, err) 121 assert.Equal(t, asset2, fetched) 122 123 // after commit, the new asset should be there already 124 err = connectionSource.Commit(txCtx) 125 require.NoError(t, err) 126 fetched, err = as.GetByID(ctx, string(asset.ID)) 127 require.NoError(t, err) 128 assert.Equal(t, asset3, fetched) 129 } 130 131 func TestAsset(t *testing.T) { 132 ctx := tempTransaction(t) 133 134 bs := sqlstore.NewBlocks(connectionSource) 135 block := addTestBlock(t, ctx, bs) 136 137 as := sqlstore.NewAssets(connectionSource) 138 139 // Get all assets, there shouldn't be any yet 140 assets, err := as.GetAll(ctx) 141 require.NoError(t, err) 142 require.Empty(t, assets) 143 144 asset := addTestAsset(t, ctx, as, block) 145 asset2 := addTestAsset(t, ctx, as, block) 146 147 // Query and check we've got back an asset the same as the one we put in 148 fetchedAsset, err := as.GetByID(ctx, asset.ID.String()) 149 assert.NoError(t, err) 150 assetsEqual(t, asset, fetchedAsset) 151 152 // Get all assets and make sure there's one more than there was to begin with 153 assets, err = as.GetAll(ctx) 154 assert.NoError(t, err) 155 assert.Len(t, assets, 2) 156 157 fetchedAssets, err := as.GetByTxHash(ctx, asset.TxHash) 158 assert.NoError(t, err) 159 assetsEqual(t, asset, fetchedAssets[0]) 160 161 fetchedAssets, err = as.GetByTxHash(ctx, asset2.TxHash) 162 assert.NoError(t, err) 163 assetsEqual(t, asset2, fetchedAssets[0]) 164 } 165 166 func setupAssetPaginationTest(t *testing.T, ctx context.Context) (*sqlstore.Assets, []entities.Asset) { 167 t.Helper() 168 bs := sqlstore.NewBlocks(connectionSource) 169 block := addTestBlock(t, ctx, bs) 170 171 as := sqlstore.NewAssets(connectionSource) 172 173 assets := make([]entities.Asset, 0, 10) 174 175 testAssetCount = 0 176 177 for i := 0; i < 10; i++ { 178 asset := addTestAsset(t, ctx, as, block, "deadbeef") 179 assets = append(assets, asset) 180 } 181 182 return as, assets 183 } 184 185 func TestAssets_GetAllWithCursorPagination(t *testing.T) { 186 t.Run("should return all deposits if no pagination is specified", testAssetsPaginationNoPagination) 187 t.Run("should return the first page of results if first is provided", testAssetPaginationFirst) 188 t.Run("should return the last page of results if last is provided", testAssetPaginationLast) 189 t.Run("should return the specified page of results if first and after is provided", testAssetPaginationFirstAndAfter) 190 t.Run("should return the specified page of results if last and before is provided", testAssetPaginationLastAndBefore) 191 192 t.Run("should return all deposits if no pagination is specified - newest first", testAssetsPaginationNoPaginationNewestFirst) 193 t.Run("should return the first page of results if first is provided - newest first", testAssetPaginationFirstNewestFirst) 194 t.Run("should return the last page of results if last is provided - newest first", testAssetPaginationLastNewestFirst) 195 t.Run("should return the specified page of results if first and after is provided - newest first", testAssetPaginationFirstAndAfterNewestFirst) 196 t.Run("should return the specified page of results if last and before is provided - newest first", testAssetPaginationLastAndBeforeNewestFirst) 197 } 198 199 func testAssetsPaginationNoPagination(t *testing.T) { 200 ctx := tempTransaction(t) 201 202 as, assets := setupAssetPaginationTest(t, ctx) 203 204 pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, false) 205 assert.NoError(t, err) 206 207 got, pageInfo, err := as.GetAllWithCursorPagination(ctx, pagination) 208 assert.NoError(t, err) 209 assert.Equal(t, assets, got) 210 assert.Equal(t, entities.PageInfo{ 211 HasNextPage: false, 212 HasPreviousPage: false, 213 StartCursor: assets[0].Cursor().Encode(), 214 EndCursor: assets[9].Cursor().Encode(), 215 }, pageInfo) 216 } 217 218 func testAssetPaginationFirst(t *testing.T) { 219 ctx := tempTransaction(t) 220 221 as, assets := setupAssetPaginationTest(t, ctx) 222 223 first := int32(3) 224 pagination, err := entities.NewCursorPagination(&first, nil, nil, nil, false) 225 assert.NoError(t, err) 226 227 got, pageInfo, err := as.GetAllWithCursorPagination(ctx, pagination) 228 assert.NoError(t, err) 229 assert.Equal(t, assets[:3], got) 230 assert.Equal(t, entities.PageInfo{ 231 HasNextPage: true, 232 HasPreviousPage: false, 233 StartCursor: assets[0].Cursor().Encode(), 234 EndCursor: assets[2].Cursor().Encode(), 235 }, pageInfo) 236 } 237 238 func testAssetPaginationLast(t *testing.T) { 239 ctx := tempTransaction(t) 240 241 as, assets := setupAssetPaginationTest(t, ctx) 242 243 last := int32(3) 244 pagination, err := entities.NewCursorPagination(nil, nil, &last, nil, false) 245 assert.NoError(t, err) 246 247 got, pageInfo, err := as.GetAllWithCursorPagination(ctx, pagination) 248 assert.NoError(t, err) 249 assert.Equal(t, assets[7:], got) 250 assert.Equal(t, entities.PageInfo{ 251 HasNextPage: false, 252 HasPreviousPage: true, 253 StartCursor: assets[7].Cursor().Encode(), 254 EndCursor: assets[9].Cursor().Encode(), 255 }, pageInfo) 256 } 257 258 func testAssetPaginationFirstAndAfter(t *testing.T) { 259 ctx := tempTransaction(t) 260 261 as, assets := setupAssetPaginationTest(t, ctx) 262 263 first := int32(3) 264 after := assets[2].Cursor().Encode() 265 266 pagination, err := entities.NewCursorPagination(&first, &after, nil, nil, false) 267 assert.NoError(t, err) 268 269 got, pageInfo, err := as.GetAllWithCursorPagination(ctx, pagination) 270 assert.NoError(t, err) 271 assert.Equal(t, assets[3:6], got) 272 assert.Equal(t, entities.PageInfo{ 273 HasNextPage: true, 274 HasPreviousPage: true, 275 StartCursor: assets[3].Cursor().Encode(), 276 EndCursor: assets[5].Cursor().Encode(), 277 }, pageInfo) 278 } 279 280 func testAssetPaginationLastAndBefore(t *testing.T) { 281 ctx := tempTransaction(t) 282 283 as, assets := setupAssetPaginationTest(t, ctx) 284 285 last := int32(3) 286 before := assets[7].Cursor().Encode() 287 288 pagination, err := entities.NewCursorPagination(nil, nil, &last, &before, false) 289 assert.NoError(t, err) 290 291 got, pageInfo, err := as.GetAllWithCursorPagination(ctx, pagination) 292 assert.NoError(t, err) 293 assert.Equal(t, assets[4:7], got) 294 assert.Equal(t, entities.PageInfo{ 295 HasNextPage: true, 296 HasPreviousPage: true, 297 StartCursor: assets[4].Cursor().Encode(), 298 EndCursor: assets[6].Cursor().Encode(), 299 }, pageInfo) 300 } 301 302 func testAssetsPaginationNoPaginationNewestFirst(t *testing.T) { 303 ctx := tempTransaction(t) 304 305 as, assets := setupAssetPaginationTest(t, ctx) 306 assets = entities.ReverseSlice(assets) 307 308 pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, true) 309 assert.NoError(t, err) 310 311 got, pageInfo, err := as.GetAllWithCursorPagination(ctx, pagination) 312 assert.NoError(t, err) 313 assert.Equal(t, assets, got) 314 assert.Equal(t, entities.PageInfo{ 315 HasNextPage: false, 316 HasPreviousPage: false, 317 StartCursor: assets[0].Cursor().Encode(), 318 EndCursor: assets[9].Cursor().Encode(), 319 }, pageInfo) 320 } 321 322 func testAssetPaginationFirstNewestFirst(t *testing.T) { 323 ctx := tempTransaction(t) 324 325 as, assets := setupAssetPaginationTest(t, ctx) 326 assets = entities.ReverseSlice(assets) 327 328 first := int32(3) 329 pagination, err := entities.NewCursorPagination(&first, nil, nil, nil, true) 330 assert.NoError(t, err) 331 332 got, pageInfo, err := as.GetAllWithCursorPagination(ctx, pagination) 333 assert.NoError(t, err) 334 assert.Equal(t, assets[:3], got) 335 assert.Equal(t, entities.PageInfo{ 336 HasNextPage: true, 337 HasPreviousPage: false, 338 StartCursor: assets[0].Cursor().Encode(), 339 EndCursor: assets[2].Cursor().Encode(), 340 }, pageInfo) 341 } 342 343 func testAssetPaginationLastNewestFirst(t *testing.T) { 344 ctx := tempTransaction(t) 345 346 as, assets := setupAssetPaginationTest(t, ctx) 347 assets = entities.ReverseSlice(assets) 348 349 last := int32(3) 350 pagination, err := entities.NewCursorPagination(nil, nil, &last, nil, true) 351 assert.NoError(t, err) 352 353 got, pageInfo, err := as.GetAllWithCursorPagination(ctx, pagination) 354 assert.NoError(t, err) 355 assert.Equal(t, assets[7:], got) 356 assert.Equal(t, entities.PageInfo{ 357 HasNextPage: false, 358 HasPreviousPage: true, 359 StartCursor: assets[7].Cursor().Encode(), 360 EndCursor: assets[9].Cursor().Encode(), 361 }, pageInfo) 362 } 363 364 func testAssetPaginationFirstAndAfterNewestFirst(t *testing.T) { 365 ctx := tempTransaction(t) 366 367 as, assets := setupAssetPaginationTest(t, ctx) 368 assets = entities.ReverseSlice(assets) 369 370 first := int32(3) 371 after := assets[2].Cursor().Encode() 372 373 pagination, err := entities.NewCursorPagination(&first, &after, nil, nil, true) 374 assert.NoError(t, err) 375 376 got, pageInfo, err := as.GetAllWithCursorPagination(ctx, pagination) 377 assert.NoError(t, err) 378 assert.Equal(t, assets[3:6], got) 379 assert.Equal(t, entities.PageInfo{ 380 HasNextPage: true, 381 HasPreviousPage: true, 382 StartCursor: assets[3].Cursor().Encode(), 383 EndCursor: assets[5].Cursor().Encode(), 384 }, pageInfo) 385 } 386 387 func testAssetPaginationLastAndBeforeNewestFirst(t *testing.T) { 388 ctx := tempTransaction(t) 389 390 as, assets := setupAssetPaginationTest(t, ctx) 391 assets = entities.ReverseSlice(assets) 392 393 last := int32(3) 394 before := assets[7].Cursor().Encode() 395 396 pagination, err := entities.NewCursorPagination(nil, nil, &last, &before, true) 397 assert.NoError(t, err) 398 399 got, pageInfo, err := as.GetAllWithCursorPagination(ctx, pagination) 400 assert.NoError(t, err) 401 assert.Equal(t, assets[4:7], got) 402 assert.Equal(t, entities.PageInfo{ 403 HasNextPage: true, 404 HasPreviousPage: true, 405 StartCursor: assets[4].Cursor().Encode(), 406 EndCursor: assets[6].Cursor().Encode(), 407 }, pageInfo) 408 } 409 410 func TestAssets_AssetStatusEnum(t *testing.T) { 411 ctx := tempTransaction(t) 412 413 bs := sqlstore.NewBlocks(connectionSource) 414 var assetStatus vegapb.Asset_Status 415 416 states := getEnums(t, assetStatus) 417 assert.Len(t, states, 5) 418 419 for e, state := range states { 420 t.Run(state, func(tt *testing.T) { 421 block := addTestBlock(t, ctx, bs) 422 423 as := sqlstore.NewAssets(connectionSource) 424 425 asset := getTestAsst(t, block) 426 asset.Status = entities.AssetStatus(e) 427 err := as.Add(ctx, asset) 428 require.NoError(tt, err, "failed to add asset with state %s", state) 429 430 fetchedAsset, err := as.GetByID(ctx, asset.ID.String()) 431 assert.NoError(tt, err) 432 assert.Equal(tt, entities.AssetStatus(e), fetchedAsset.Status) 433 }) 434 } 435 }