code.vegaprotocol.io/vega@v0.79.0/datanode/sqlstore/markets_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  	"testing"
    21  	"time"
    22  
    23  	dstypes "code.vegaprotocol.io/vega/core/datasource/common"
    24  	"code.vegaprotocol.io/vega/datanode/entities"
    25  	"code.vegaprotocol.io/vega/datanode/sqlstore"
    26  	"code.vegaprotocol.io/vega/libs/num"
    27  	"code.vegaprotocol.io/vega/protos/vega"
    28  	v1 "code.vegaprotocol.io/vega/protos/vega/data/v1"
    29  
    30  	"github.com/georgysavva/scany/pgxscan"
    31  	"github.com/stretchr/testify/assert"
    32  	"github.com/stretchr/testify/require"
    33  	"google.golang.org/protobuf/proto"
    34  )
    35  
    36  func TestMarkets_Add(t *testing.T) {
    37  	t.Run("Add should insert a valid market record", shouldInsertAValidMarketRecord)
    38  	t.Run("Add should update a valid market record if the block number already exists", shouldUpdateAValidMarketRecord)
    39  	t.Run("Add should insert a valid spot market record", shouldInsertAValidSpotMarketRecord)
    40  	t.Run("Add should insert a valid perpetual market record", shouldInsertAValidPerpetualMarketRecord)
    41  }
    42  
    43  func TestMarkets_Get(t *testing.T) {
    44  	t.Run("GetByID should return the request market if it exists", getByIDShouldReturnTheRequestedMarketIfItExists)
    45  	t.Run("GetByID should return error if the market does not exist", getByIDShouldReturnErrorIfTheMarketDoesNotExist)
    46  	t.Run("GetAllPaged should not include rejected markets", getAllPagedShouldNotIncludeRejectedMarkets)
    47  	t.Run("GetByTxHash", getByTxHashReturnsMatchingMarkets)
    48  	t.Run("GetByID should return a spot market if it exists", getByIDShouldReturnASpotMarketIfItExists)
    49  	t.Run("GetByID should return a perpetual market if it exists", getByIDShouldReturnAPerpetualMarketIfItExists)
    50  }
    51  
    52  func TestGetAllFees(t *testing.T) {
    53  	bs, md := setupMarketsTest(t)
    54  
    55  	ctx := tempTransaction(t)
    56  
    57  	block := addTestBlock(t, ctx, bs)
    58  
    59  	market := entities.Market{
    60  		ID:       "deadbeef",
    61  		TxHash:   generateTxHash(),
    62  		VegaTime: block.VegaTime,
    63  		State:    entities.MarketStateActive,
    64  		Fees: entities.Fees{
    65  			Factors: &entities.FeeFactors{
    66  				MakerFee:          "0.1",
    67  				InfrastructureFee: "0.2",
    68  				LiquidityFee:      "0.3",
    69  				BuyBackFee:        "0.4",
    70  				TreasuryFee:       "0.5",
    71  			},
    72  		},
    73  	}
    74  	err := md.Upsert(ctx, &market)
    75  	require.NoError(t, err)
    76  
    77  	market2 := entities.Market{
    78  		ID:       "beefdead",
    79  		TxHash:   generateTxHash(),
    80  		VegaTime: block.VegaTime,
    81  		State:    entities.MarketStateActive,
    82  		Fees: entities.Fees{
    83  			Factors: &entities.FeeFactors{
    84  				MakerFee:          "0.5",
    85  				InfrastructureFee: "0.4",
    86  				LiquidityFee:      "0.3",
    87  				BuyBackFee:        "0.2",
    88  				TreasuryFee:       "0.1",
    89  			},
    90  		},
    91  	}
    92  	err = md.Upsert(ctx, &market2)
    93  	require.NoError(t, err, "Saving market entity to database")
    94  
    95  	mkts, err := md.GetAllFees(ctx)
    96  	require.NoError(t, err)
    97  	require.Equal(t, 2, len(mkts))
    98  
    99  	require.Equal(t, "beefdead", mkts[0].ID.String())
   100  	require.Equal(t, "0.5", mkts[0].Fees.Factors.MakerFee)
   101  	require.Equal(t, "0.4", mkts[0].Fees.Factors.InfrastructureFee)
   102  	require.Equal(t, "0.3", mkts[0].Fees.Factors.LiquidityFee)
   103  	require.Equal(t, "0.2", mkts[0].Fees.Factors.BuyBackFee)
   104  	require.Equal(t, "0.1", mkts[0].Fees.Factors.TreasuryFee)
   105  	require.Equal(t, "deadbeef", mkts[1].ID.String())
   106  	require.Equal(t, "0.1", mkts[1].Fees.Factors.MakerFee)
   107  	require.Equal(t, "0.2", mkts[1].Fees.Factors.InfrastructureFee)
   108  	require.Equal(t, "0.3", mkts[1].Fees.Factors.LiquidityFee)
   109  	require.Equal(t, "0.4", mkts[1].Fees.Factors.BuyBackFee)
   110  	require.Equal(t, "0.5", mkts[1].Fees.Factors.TreasuryFee)
   111  }
   112  
   113  func getByIDShouldReturnTheRequestedMarketIfItExists(t *testing.T) {
   114  	bs, md := setupMarketsTest(t)
   115  
   116  	ctx := tempTransaction(t)
   117  
   118  	block := addTestBlock(t, ctx, bs)
   119  
   120  	market := entities.Market{
   121  		ID:       "deadbeef",
   122  		TxHash:   generateTxHash(),
   123  		VegaTime: block.VegaTime,
   124  		State:    entities.MarketStateActive,
   125  	}
   126  	err := md.Upsert(ctx, &market)
   127  	require.NoError(t, err, "Saving market entity to database")
   128  
   129  	marketFromDB, err := md.GetByID(ctx, market.ID.String())
   130  	require.NoError(t, err)
   131  	assert.Equal(t, market.ID, marketFromDB.ID)
   132  	assert.Equal(t, market.TxHash, marketFromDB.TxHash)
   133  	assert.Equal(t, market.VegaTime, marketFromDB.VegaTime)
   134  	assert.Equal(t, market.State, marketFromDB.State)
   135  }
   136  
   137  func getByIDShouldReturnASpotMarketIfItExists(t *testing.T) {
   138  	bs, md := setupMarketsTest(t)
   139  
   140  	ctx := tempTransaction(t)
   141  
   142  	block := addTestBlock(t, ctx, bs)
   143  
   144  	marketProto := getTestSpotMarket()
   145  
   146  	market, err := entities.NewMarketFromProto(marketProto, generateTxHash(), block.VegaTime)
   147  	require.NoError(t, err, "Converting market proto to database entity")
   148  	err = md.Upsert(ctx, market)
   149  	require.NoError(t, err, "Saving market entity to database")
   150  
   151  	marketFromDB, err := md.GetByID(ctx, market.ID.String())
   152  	require.NoError(t, err)
   153  	marketToProto := marketFromDB.ToProto()
   154  	assert.IsType(t, &vega.Spot{}, marketToProto.TradableInstrument.GetInstrument().GetSpot())
   155  }
   156  
   157  func getByIDShouldReturnAPerpetualMarketIfItExists(t *testing.T) {
   158  	bs, md := setupMarketsTest(t)
   159  
   160  	ctx := tempTransaction(t)
   161  
   162  	block := addTestBlock(t, ctx, bs)
   163  
   164  	marketProto := getTestSpotMarket()
   165  
   166  	market, err := entities.NewMarketFromProto(marketProto, generateTxHash(), block.VegaTime)
   167  	require.NoError(t, err, "Converting market proto to database entity")
   168  	err = md.Upsert(ctx, market)
   169  	require.NoError(t, err, "Saving market entity to database")
   170  
   171  	marketFromDB, err := md.GetByID(ctx, market.ID.String())
   172  	require.NoError(t, err)
   173  	marketToProto := marketFromDB.ToProto()
   174  	assert.IsType(t, &vega.Perpetual{}, marketToProto.TradableInstrument.GetInstrument().GetPerpetual())
   175  }
   176  
   177  func getByTxHashReturnsMatchingMarkets(t *testing.T) {
   178  	bs, md := setupMarketsTest(t)
   179  
   180  	ctx := tempTransaction(t)
   181  
   182  	block := addTestBlock(t, ctx, bs)
   183  
   184  	market := entities.Market{
   185  		ID:       "deadbeef",
   186  		TxHash:   generateTxHash(),
   187  		VegaTime: block.VegaTime,
   188  		State:    entities.MarketStateActive,
   189  	}
   190  	err := md.Upsert(ctx, &market)
   191  	require.NoError(t, err, "Saving market entity to database")
   192  
   193  	foundMarkets, err := md.GetByTxHash(ctx, market.TxHash)
   194  	require.NoError(t, err)
   195  	require.Len(t, foundMarkets, 1)
   196  	assert.Equal(t, market.ID, foundMarkets[0].ID)
   197  	assert.Equal(t, market.TxHash, foundMarkets[0].TxHash)
   198  	assert.Equal(t, market.VegaTime, foundMarkets[0].VegaTime)
   199  	assert.Equal(t, market.State, foundMarkets[0].State)
   200  }
   201  
   202  func getByIDShouldReturnErrorIfTheMarketDoesNotExist(t *testing.T) {
   203  	bs, md := setupMarketsTest(t)
   204  
   205  	ctx := tempTransaction(t)
   206  
   207  	block := addTestBlock(t, ctx, bs)
   208  
   209  	market := entities.Market{
   210  		ID:       "deadbeef",
   211  		TxHash:   generateTxHash(),
   212  		VegaTime: block.VegaTime,
   213  		State:    entities.MarketStateActive,
   214  	}
   215  	err := md.Upsert(ctx, &market)
   216  	require.NoError(t, err, "Saving market entity to database")
   217  
   218  	_, err = md.GetByID(ctx, "not-a-market")
   219  	require.Error(t, err)
   220  }
   221  
   222  func getAllPagedShouldNotIncludeRejectedMarkets(t *testing.T) {
   223  	bs, md := setupMarketsTest(t)
   224  
   225  	ctx := tempTransaction(t)
   226  
   227  	block := addTestBlock(t, ctx, bs)
   228  
   229  	market := entities.Market{
   230  		ID:       "deadbeef",
   231  		TxHash:   generateTxHash(),
   232  		VegaTime: block.VegaTime,
   233  		State:    entities.MarketStateActive,
   234  	}
   235  	err := md.Upsert(ctx, &market)
   236  	require.NoError(t, err, "Saving market entity to database")
   237  
   238  	rejected := entities.Market{
   239  		ID:       "DEADBAAD",
   240  		TxHash:   generateTxHash(),
   241  		VegaTime: block.VegaTime,
   242  		State:    entities.MarketStateRejected,
   243  	}
   244  	err = md.Upsert(ctx, &rejected)
   245  	require.NoError(t, err, "Saving market entity to database")
   246  
   247  	markets, pageInfo, err := md.GetAllPaged(ctx, "", entities.CursorPagination{}, true)
   248  	require.NoError(t, err)
   249  	assert.Len(t, markets, 1)
   250  	assert.Equal(t, market.ID, markets[0].ID)
   251  	assert.Equal(t, market.TxHash, markets[0].TxHash)
   252  	assert.Equal(t, market.VegaTime, markets[0].VegaTime)
   253  	assert.Equal(t, market.State, markets[0].State)
   254  	assert.Equal(t, entities.PageInfo{
   255  		HasNextPage:     false,
   256  		HasPreviousPage: false,
   257  		StartCursor:     market.Cursor().Encode(),
   258  		EndCursor:       market.Cursor().Encode(),
   259  	}, pageInfo)
   260  }
   261  
   262  func shouldInsertAValidMarketRecord(t *testing.T) {
   263  	bs, md := setupMarketsTest(t)
   264  
   265  	ctx := tempTransaction(t)
   266  
   267  	var rowCount int
   268  
   269  	err := connectionSource.QueryRow(ctx, `select count(*) from markets`).Scan(&rowCount)
   270  	require.NoError(t, err)
   271  	assert.Equal(t, 0, rowCount)
   272  
   273  	block := addTestBlock(t, ctx, bs)
   274  
   275  	marketProto := getTestFutureMarket(true)
   276  
   277  	market, err := entities.NewMarketFromProto(marketProto, generateTxHash(), block.VegaTime)
   278  	require.NoError(t, err, "Converting market proto to database entity")
   279  
   280  	err = md.Upsert(ctx, market)
   281  	require.NoError(t, err, "Saving market entity to database")
   282  	err = connectionSource.QueryRow(ctx, `select count(*) from markets`).Scan(&rowCount)
   283  	assert.NoError(t, err)
   284  	assert.Equal(t, 1, rowCount)
   285  }
   286  
   287  func setupMarketsTest(t *testing.T) (*sqlstore.Blocks, *sqlstore.Markets) {
   288  	t.Helper()
   289  	bs := sqlstore.NewBlocks(connectionSource)
   290  	md := sqlstore.NewMarkets(connectionSource)
   291  	return bs, md
   292  }
   293  
   294  func shouldUpdateAValidMarketRecord(t *testing.T) {
   295  	bs, md := setupMarketsTest(t)
   296  	ctx := tempTransaction(t)
   297  
   298  	var rowCount int
   299  
   300  	t.Run("should have no markets in the database", func(t *testing.T) {
   301  		err := connectionSource.QueryRow(ctx, `select count(*) from markets`).Scan(&rowCount)
   302  		require.NoError(t, err)
   303  		assert.Equal(t, 0, rowCount)
   304  	})
   305  
   306  	var block entities.Block
   307  	var marketProto *vega.Market
   308  
   309  	t.Run("should insert a valid market record to the database with liquidation strategy", func(t *testing.T) {
   310  		block = addTestBlock(t, ctx, bs)
   311  		marketProto = getTestFutureMarketWithLiquidationStrategy(false)
   312  
   313  		market, err := entities.NewMarketFromProto(marketProto, generateTxHash(), block.VegaTime)
   314  		require.NoError(t, err, "Converting market proto to database entity")
   315  
   316  		err = md.Upsert(ctx, market)
   317  		require.NoError(t, err, "Saving market entity to database")
   318  
   319  		var got entities.Market
   320  		err = pgxscan.Get(ctx, connectionSource, &got, `select * from markets where id = $1 and vega_time = $2`, market.ID, market.VegaTime)
   321  		assert.NoError(t, err)
   322  		assert.Equal(t, "TEST_INSTRUMENT", market.InstrumentID)
   323  		assert.NotNil(t, got.LiquidationStrategy)
   324  
   325  		assert.Equal(t, marketProto.TradableInstrument, got.TradableInstrument.ToProto())
   326  		assert.Equal(t, marketProto.LiquidationStrategy, got.LiquidationStrategy.IntoProto())
   327  	})
   328  
   329  	t.Run("should insert a valid market record to the database", func(t *testing.T) {
   330  		block = addTestBlock(t, ctx, bs)
   331  		marketProto = getTestFutureMarket(false)
   332  
   333  		market, err := entities.NewMarketFromProto(marketProto, generateTxHash(), block.VegaTime)
   334  		require.NoError(t, err, "Converting market proto to database entity")
   335  
   336  		err = md.Upsert(ctx, market)
   337  		require.NoError(t, err, "Saving market entity to database")
   338  
   339  		var got entities.Market
   340  		err = pgxscan.Get(ctx, connectionSource, &got, `select * from markets where id = $1 and vega_time = $2`, market.ID, market.VegaTime)
   341  		assert.NoError(t, err)
   342  		assert.Equal(t, "TEST_INSTRUMENT", market.InstrumentID)
   343  
   344  		assert.Equal(t, marketProto.TradableInstrument, got.TradableInstrument.ToProto())
   345  	})
   346  
   347  	marketProto.TradableInstrument.Instrument.Name = "Updated Test Instrument"
   348  	marketProto.TradableInstrument.Instrument.Metadata.Tags = append(marketProto.TradableInstrument.Instrument.Metadata.Tags, "CCC")
   349  
   350  	t.Run("should update a valid market record to the database if the block number already exists", func(t *testing.T) {
   351  		market, err := entities.NewMarketFromProto(marketProto, generateTxHash(), block.VegaTime)
   352  
   353  		require.NoError(t, err, "Converting market proto to database entity")
   354  
   355  		err = md.Upsert(ctx, market)
   356  		require.NoError(t, err, "Saving market entity to database")
   357  
   358  		var got entities.Market
   359  		err = pgxscan.Get(ctx, connectionSource, &got, `select * from markets where id = $1 and vega_time = $2`, market.ID, market.VegaTime)
   360  		assert.NoError(t, err)
   361  		assert.Equal(t, "TEST_INSTRUMENT", market.InstrumentID)
   362  
   363  		assert.Equal(t, marketProto.TradableInstrument, got.TradableInstrument.ToProto())
   364  	})
   365  
   366  	t.Run("should add the updated market record to the database if the block number has changed", func(t *testing.T) {
   367  		newMarketProto := proto.Clone(marketProto).(*vega.Market)
   368  		newMarketProto.TradableInstrument.Instrument.Metadata.Tags = append(newMarketProto.TradableInstrument.Instrument.Metadata.Tags, "DDD")
   369  		newBlock := addTestBlockForTime(t, ctx, bs, time.Now().Add(time.Second))
   370  
   371  		market, err := entities.NewMarketFromProto(newMarketProto, generateTxHash(), newBlock.VegaTime)
   372  		require.NoError(t, err, "Converting market proto to database entity")
   373  
   374  		err = md.Upsert(ctx, market)
   375  		require.NoError(t, err, "Saving market entity to database")
   376  
   377  		err = connectionSource.QueryRow(ctx, `select count(*) from markets`).Scan(&rowCount)
   378  		require.NoError(t, err)
   379  		assert.Equal(t, 3, rowCount)
   380  
   381  		var gotFirstBlock, gotSecondBlock entities.Market
   382  
   383  		err = pgxscan.Get(ctx, connectionSource, &gotFirstBlock, `select * from markets where id = $1 and vega_time = $2`, market.ID, block.VegaTime)
   384  		assert.NoError(t, err)
   385  		assert.Equal(t, "TEST_INSTRUMENT", market.InstrumentID)
   386  
   387  		assert.Equal(t, marketProto.TradableInstrument, gotFirstBlock.TradableInstrument.ToProto())
   388  
   389  		err = pgxscan.Get(ctx, connectionSource, &gotSecondBlock, `select * from markets where id = $1 and vega_time = $2`, market.ID, newBlock.VegaTime)
   390  		assert.NoError(t, err)
   391  		assert.Equal(t, "TEST_INSTRUMENT", market.InstrumentID)
   392  
   393  		assert.Equal(t, newMarketProto.TradableInstrument, gotSecondBlock.TradableInstrument.ToProto())
   394  	})
   395  }
   396  
   397  func shouldInsertAValidSpotMarketRecord(t *testing.T) {
   398  	bs, md := setupMarketsTest(t)
   399  
   400  	ctx := tempTransaction(t)
   401  
   402  	var rowCount int
   403  
   404  	err := connectionSource.QueryRow(ctx, `select count(*) from markets`).Scan(&rowCount)
   405  	require.NoError(t, err)
   406  	assert.Equal(t, 0, rowCount)
   407  
   408  	block := addTestBlock(t, ctx, bs)
   409  
   410  	marketProto := getTestSpotMarket()
   411  
   412  	market, err := entities.NewMarketFromProto(marketProto, generateTxHash(), block.VegaTime)
   413  	require.NoError(t, err, "Converting market proto to database entity")
   414  
   415  	err = md.Upsert(ctx, market)
   416  	require.NoError(t, err, "Saving market entity to database")
   417  	err = connectionSource.QueryRow(ctx, `select count(*) from markets`).Scan(&rowCount)
   418  	assert.NoError(t, err)
   419  	assert.Equal(t, 1, rowCount)
   420  }
   421  
   422  func shouldInsertAValidPerpetualMarketRecord(t *testing.T) {
   423  	bs, md := setupMarketsTest(t)
   424  
   425  	ctx := tempTransaction(t)
   426  
   427  	var rowCount int
   428  
   429  	err := connectionSource.QueryRow(ctx, `select count(*) from markets`).Scan(&rowCount)
   430  	require.NoError(t, err)
   431  	assert.Equal(t, 0, rowCount)
   432  
   433  	block := addTestBlock(t, ctx, bs)
   434  
   435  	marketProto := getTestPerpetualMarket()
   436  
   437  	market, err := entities.NewMarketFromProto(marketProto, generateTxHash(), block.VegaTime)
   438  	require.NoError(t, err, "Converting market proto to database entity")
   439  
   440  	err = md.Upsert(ctx, market)
   441  	require.NoError(t, err, "Saving market entity to database")
   442  	err = connectionSource.QueryRow(ctx, `select count(*) from markets`).Scan(&rowCount)
   443  	assert.NoError(t, err)
   444  	assert.Equal(t, 1, rowCount)
   445  }
   446  
   447  func getTestSpotMarket() *vega.Market {
   448  	mkt := getTestMarket()
   449  
   450  	mkt.TradableInstrument.Instrument.Product = &vega.Instrument_Spot{
   451  		Spot: &vega.Spot{
   452  			BaseAsset:  "Ethereum",
   453  			QuoteAsset: "USD",
   454  		},
   455  	}
   456  
   457  	return mkt
   458  }
   459  
   460  func getTestPerpetualMarket() *vega.Market {
   461  	pk := dstypes.CreateSignerFromString("0xDEADBEEF", dstypes.SignerTypePubKey)
   462  	mkt := getTestMarket()
   463  	mkt.TradableInstrument.Instrument.Product = &vega.Instrument_Perpetual{
   464  		Perpetual: &vega.Perpetual{
   465  			SettlementAsset:     "Ethereum/Ether",
   466  			QuoteName:           "ETH-230929",
   467  			MarginFundingFactor: "0.5",
   468  			InterestRate:        "0.012",
   469  			ClampLowerBound:     "0.2",
   470  			ClampUpperBound:     "0.8",
   471  			DataSourceSpecForSettlementSchedule: &vega.DataSourceSpec{
   472  				Id:        "test-settlement-schedule",
   473  				CreatedAt: time.Now().UnixNano(),
   474  				UpdatedAt: time.Now().UnixNano(),
   475  				Data: vega.NewDataSourceDefinition(
   476  					vega.DataSourceContentTypeOracle,
   477  				).SetOracleConfig(
   478  					&vega.DataSourceDefinitionExternal_Oracle{
   479  						Oracle: &vega.DataSourceSpecConfiguration{
   480  							Signers: []*v1.Signer{pk.IntoProto()},
   481  							Filters: []*v1.Filter{
   482  								{
   483  									Key: &v1.PropertyKey{
   484  										Name: "prices.ETH.value",
   485  										Type: v1.PropertyKey_TYPE_INTEGER,
   486  									},
   487  									Conditions: []*v1.Condition{},
   488  								},
   489  							},
   490  						},
   491  					},
   492  				),
   493  				Status: vega.DataSourceSpec_STATUS_ACTIVE,
   494  			},
   495  			DataSourceSpecForSettlementData: &vega.DataSourceSpec{
   496  				Id:        "test-settlement-data",
   497  				CreatedAt: time.Now().UnixNano(),
   498  				UpdatedAt: time.Now().UnixNano(),
   499  				Data: vega.NewDataSourceDefinition(
   500  					vega.DataSourceContentTypeOracle,
   501  				).SetOracleConfig(
   502  					&vega.DataSourceDefinitionExternal_Oracle{
   503  						Oracle: &vega.DataSourceSpecConfiguration{
   504  							Signers: []*v1.Signer{pk.IntoProto()},
   505  							Filters: []*v1.Filter{
   506  								{
   507  									Key: &v1.PropertyKey{
   508  										Name: "prices.ETH.value",
   509  										Type: v1.PropertyKey_TYPE_INTEGER,
   510  									},
   511  									Conditions: []*v1.Condition{},
   512  								},
   513  							},
   514  						},
   515  					},
   516  				),
   517  				Status: vega.DataSourceSpec_STATUS_ACTIVE,
   518  			},
   519  			DataSourceSpecBinding: &vega.DataSourceSpecToPerpetualBinding{
   520  				SettlementDataProperty:     "prices.ETH.value",
   521  				SettlementScheduleProperty: "2023-09-29T00:00:00.000000000Z",
   522  			},
   523  		},
   524  	}
   525  	return mkt
   526  }
   527  
   528  func getTestMarket() *vega.Market {
   529  	return &vega.Market{
   530  		Id: GenerateID(),
   531  		TradableInstrument: &vega.TradableInstrument{
   532  			Instrument: &vega.Instrument{
   533  				Id:   "Crypto/BTCUSD/Futures/Dec19",
   534  				Code: "FX:BTCUSD/DEC19",
   535  				Name: "December 2019 BTC vs USD future",
   536  				Metadata: &vega.InstrumentMetadata{
   537  					Tags: []string{
   538  						"asset_class:fx/crypto",
   539  						"product:futures",
   540  					},
   541  				},
   542  			},
   543  			MarginCalculator: &vega.MarginCalculator{
   544  				ScalingFactors: &vega.ScalingFactors{
   545  					SearchLevel:       1.1,
   546  					InitialMargin:     1.2,
   547  					CollateralRelease: 1.4,
   548  				},
   549  			},
   550  			RiskModel: &vega.TradableInstrument_LogNormalRiskModel{
   551  				LogNormalRiskModel: &vega.LogNormalRiskModel{
   552  					RiskAversionParameter: 0.01,
   553  					Tau:                   1.0 / 365.25 / 24,
   554  					Params: &vega.LogNormalModelParams{
   555  						Mu:    0,
   556  						R:     0.016,
   557  						Sigma: 0.09,
   558  					},
   559  				},
   560  			},
   561  		},
   562  		Fees: &vega.Fees{
   563  			Factors: &vega.FeeFactors{
   564  				MakerFee:          "",
   565  				InfrastructureFee: "",
   566  				LiquidityFee:      "",
   567  			},
   568  			LiquidityFeeSettings: &vega.LiquidityFeeSettings{
   569  				Method: vega.LiquidityFeeSettings_METHOD_MARGINAL_COST,
   570  			},
   571  		},
   572  		OpeningAuction: &vega.AuctionDuration{
   573  			Duration: 0,
   574  			Volume:   0,
   575  		},
   576  		PriceMonitoringSettings: &vega.PriceMonitoringSettings{
   577  			Parameters: &vega.PriceMonitoringParameters{
   578  				Triggers: []*vega.PriceMonitoringTrigger{
   579  					{
   580  						Horizon:          0,
   581  						Probability:      "",
   582  						AuctionExtension: 0,
   583  					},
   584  				},
   585  			},
   586  		},
   587  		LiquidityMonitoringParameters: &vega.LiquidityMonitoringParameters{
   588  			TargetStakeParameters: &vega.TargetStakeParameters{
   589  				TimeWindow:    0,
   590  				ScalingFactor: 0,
   591  			},
   592  		},
   593  		TradingMode: vega.Market_TRADING_MODE_CONTINUOUS,
   594  		State:       vega.Market_STATE_ACTIVE,
   595  		MarketTimestamps: &vega.MarketTimestamps{
   596  			Proposed: 0,
   597  			Pending:  0,
   598  			Open:     0,
   599  			Close:    0,
   600  		},
   601  		PositionDecimalPlaces:   8,
   602  		LpPriceRange:            "0.95",
   603  		LinearSlippageFactor:    "1.23",
   604  		QuadraticSlippageFactor: "5.67",
   605  	}
   606  }
   607  
   608  func getTestFutureMarketWithLiquidationStrategy(termInt bool) *vega.Market {
   609  	mkt := getTestFutureMarket(termInt)
   610  	mkt.LiquidationStrategy = &vega.LiquidationStrategy{
   611  		DisposalTimeStep:      10,
   612  		DisposalFraction:      "0.1",
   613  		FullDisposalSize:      20,
   614  		MaxFractionConsumed:   "0.01",
   615  		DisposalSlippageRange: "0.1",
   616  	}
   617  	return mkt
   618  }
   619  
   620  func getTestFutureMarket(termInt bool) *vega.Market {
   621  	term := &vega.DataSourceSpec{
   622  		Id:        "",
   623  		CreatedAt: 0,
   624  		UpdatedAt: 0,
   625  		Data: vega.NewDataSourceDefinition(
   626  			vega.DataSourceContentTypeOracle,
   627  		).SetOracleConfig(
   628  			&vega.DataSourceDefinitionExternal_Oracle{
   629  				Oracle: &vega.DataSourceSpecConfiguration{
   630  					Signers: nil,
   631  					Filters: nil,
   632  				},
   633  			},
   634  		),
   635  		Status: 0,
   636  	}
   637  
   638  	if termInt {
   639  		term = &vega.DataSourceSpec{
   640  			Id:        "",
   641  			CreatedAt: 0,
   642  			UpdatedAt: 0,
   643  			Data: vega.NewDataSourceDefinition(
   644  				vega.DataSourceContentTypeInternalTimeTermination,
   645  			).SetTimeTriggerConditionConfig(
   646  				[]*v1.Condition{
   647  					{
   648  						Operator: v1.Condition_OPERATOR_GREATER_THAN,
   649  						Value:    "test-value",
   650  					},
   651  				},
   652  			),
   653  			Status: 0,
   654  		}
   655  	}
   656  
   657  	return &vega.Market{
   658  		Id: "DEADBEEF",
   659  		TradableInstrument: &vega.TradableInstrument{
   660  			Instrument: &vega.Instrument{
   661  				Id:   "TEST_INSTRUMENT",
   662  				Code: "TEST",
   663  				Name: "Test Instrument",
   664  				Metadata: &vega.InstrumentMetadata{
   665  					Tags: []string{"AAA", "BBB"},
   666  				},
   667  				Product: &vega.Instrument_Future{
   668  					Future: &vega.Future{
   669  						SettlementAsset: "Test Asset",
   670  						QuoteName:       "Test Quote",
   671  						DataSourceSpecForSettlementData: &vega.DataSourceSpec{
   672  							Id:        "",
   673  							CreatedAt: 0,
   674  							UpdatedAt: 0,
   675  							Data: vega.NewDataSourceDefinition(
   676  								vega.DataSourceContentTypeOracle,
   677  							).SetOracleConfig(
   678  								&vega.DataSourceDefinitionExternal_Oracle{
   679  									Oracle: &vega.DataSourceSpecConfiguration{
   680  										Signers: nil,
   681  										Filters: nil,
   682  									},
   683  								},
   684  							),
   685  							Status: 0,
   686  						},
   687  						DataSourceSpecForTradingTermination: term,
   688  						DataSourceSpecBinding: &vega.DataSourceSpecToFutureBinding{
   689  							SettlementDataProperty:     "",
   690  							TradingTerminationProperty: "",
   691  						},
   692  					},
   693  				},
   694  			},
   695  			MarginCalculator: &vega.MarginCalculator{
   696  				ScalingFactors: &vega.ScalingFactors{
   697  					SearchLevel:       0,
   698  					InitialMargin:     0,
   699  					CollateralRelease: 0,
   700  				},
   701  			},
   702  			RiskModel: &vega.TradableInstrument_SimpleRiskModel{
   703  				SimpleRiskModel: &vega.SimpleRiskModel{
   704  					Params: &vega.SimpleModelParams{
   705  						FactorLong:           0,
   706  						FactorShort:          0,
   707  						MaxMoveUp:            0,
   708  						MinMoveDown:          0,
   709  						ProbabilityOfTrading: 0,
   710  					},
   711  				},
   712  			},
   713  		},
   714  		DecimalPlaces: 16,
   715  		Fees: &vega.Fees{
   716  			Factors: &vega.FeeFactors{
   717  				MakerFee:          "",
   718  				InfrastructureFee: "",
   719  				LiquidityFee:      "",
   720  			},
   721  			LiquidityFeeSettings: &vega.LiquidityFeeSettings{
   722  				Method: vega.LiquidityFeeSettings_METHOD_MARGINAL_COST,
   723  			},
   724  		},
   725  		OpeningAuction: &vega.AuctionDuration{
   726  			Duration: 0,
   727  			Volume:   0,
   728  		},
   729  		PriceMonitoringSettings: &vega.PriceMonitoringSettings{
   730  			Parameters: &vega.PriceMonitoringParameters{
   731  				Triggers: []*vega.PriceMonitoringTrigger{
   732  					{
   733  						Horizon:          0,
   734  						Probability:      "",
   735  						AuctionExtension: 0,
   736  					},
   737  				},
   738  			},
   739  		},
   740  		LiquidityMonitoringParameters: &vega.LiquidityMonitoringParameters{
   741  			TargetStakeParameters: &vega.TargetStakeParameters{
   742  				TimeWindow:    0,
   743  				ScalingFactor: 0,
   744  			},
   745  		},
   746  		TradingMode: vega.Market_TRADING_MODE_CONTINUOUS,
   747  		State:       vega.Market_STATE_ACTIVE,
   748  		MarketTimestamps: &vega.MarketTimestamps{
   749  			Proposed: 0,
   750  			Pending:  0,
   751  			Open:     0,
   752  			Close:    0,
   753  		},
   754  		PositionDecimalPlaces:   8,
   755  		LpPriceRange:            "0.95",
   756  		LinearSlippageFactor:    "1.23",
   757  		QuadraticSlippageFactor: "5.67",
   758  		LiquiditySlaParams: &vega.LiquiditySLAParameters{
   759  			PriceRange:                  "0.75",
   760  			CommitmentMinTimeFraction:   "0.5",
   761  			PerformanceHysteresisEpochs: 0,
   762  			SlaCompetitionFactor:        "1.0",
   763  		},
   764  	}
   765  }
   766  
   767  func populateTestMarkets(ctx context.Context, t *testing.T, bs *sqlstore.Blocks, md *sqlstore.Markets, blockTimes map[string]time.Time) {
   768  	t.Helper()
   769  
   770  	markets := []entities.Market{
   771  		{
   772  			ID:           entities.MarketID("02a16077"),
   773  			InstrumentID: "AAA",
   774  		},
   775  		{
   776  			ID:           entities.MarketID("44eea1bc"),
   777  			InstrumentID: "BBB",
   778  		},
   779  		{
   780  			ID:           entities.MarketID("65be62cd"),
   781  			InstrumentID: "CCC",
   782  		},
   783  		{
   784  			ID:           entities.MarketID("7a797e0e"),
   785  			InstrumentID: "DDD",
   786  		},
   787  		{
   788  			ID:           entities.MarketID("7bb2356e"),
   789  			InstrumentID: "EEE",
   790  		},
   791  		{
   792  			ID:           entities.MarketID("b7c84b8e"),
   793  			InstrumentID: "FFF",
   794  		},
   795  		{
   796  			ID:           entities.MarketID("c612300d"),
   797  			InstrumentID: "GGG",
   798  		},
   799  		{
   800  			ID:           entities.MarketID("c8744329"),
   801  			InstrumentID: "HHH",
   802  		},
   803  		{
   804  			ID:           entities.MarketID("da8d1803"),
   805  			InstrumentID: "III",
   806  		},
   807  		{
   808  			ID:           entities.MarketID("fb1528a5"),
   809  			InstrumentID: "JJJ",
   810  		},
   811  	}
   812  
   813  	source := &testBlockSource{bs, time.Now()}
   814  	for _, market := range markets {
   815  		block := source.getNextBlock(t, ctx)
   816  		market.VegaTime = block.VegaTime
   817  		blockTimes[market.ID.String()] = block.VegaTime
   818  		err := md.Upsert(ctx, &market)
   819  		require.NoError(t, err)
   820  	}
   821  }
   822  
   823  func TestMarketsCursorPagination(t *testing.T) {
   824  	t.Run("Should return the market if Market ID is provided", testCursorPaginationReturnsTheSpecifiedMarket)
   825  	t.Run("Should return all markets if no market ID and no cursor is provided", testCursorPaginationReturnsAllMarkets)
   826  	t.Run("Should return the first page when first limit is provided with no after cursor", testCursorPaginationReturnsFirstPage)
   827  	t.Run("Should return the last page when last limit is provided with first before cursor", testCursorPaginationReturnsLastPage)
   828  	t.Run("Should return the page specified by the first limit and after cursor", testCursorPaginationReturnsPageTraversingForward)
   829  	t.Run("Should return the page specified by the last limit and before cursor", testCursorPaginationReturnsPageTraversingBackward)
   830  
   831  	t.Run("Should return the market if Market ID is provided - newest first", testCursorPaginationReturnsTheSpecifiedMarketNewestFirst)
   832  	t.Run("Should return all markets if no market ID and no cursor is provided - newest first", testCursorPaginationReturnsAllMarketsNewestFirst)
   833  	t.Run("Should return the first page when first limit is provided with no after cursor - newest first", testCursorPaginationReturnsFirstPageNewestFirst)
   834  	t.Run("Should return the last page when last limit is provided with first before cursor - newest first", testCursorPaginationReturnsLastPageNewestFirst)
   835  	t.Run("Should return the page specified by the first limit and after cursor - newest first", testCursorPaginationReturnsPageTraversingForwardNewestFirst)
   836  	t.Run("Should return the page specified by the last limit and before cursor - newest first", testCursorPaginationReturnsPageTraversingBackwardNewestFirst)
   837  }
   838  
   839  func testCursorPaginationReturnsTheSpecifiedMarket(t *testing.T) {
   840  	ctx := tempTransaction(t)
   841  
   842  	bs, md := setupMarketsTest(t)
   843  
   844  	blockTimes := make(map[string]time.Time)
   845  	populateTestMarkets(ctx, t, bs, md, blockTimes)
   846  	pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, false)
   847  	require.NoError(t, err)
   848  
   849  	got, pageInfo, err := md.GetAllPaged(ctx, "c612300d", pagination, true)
   850  	require.NoError(t, err)
   851  	assert.Equal(t, 1, len(got))
   852  	assert.Equal(t, "c612300d", got[0].ID.String())
   853  	assert.Equal(t, "GGG", got[0].InstrumentID)
   854  
   855  	mc := entities.MarketCursor{
   856  		VegaTime: blockTimes["c612300d"],
   857  		ID:       "c612300d",
   858  	}
   859  
   860  	wantStartCursor := entities.NewCursor(mc.String()).Encode()
   861  	wantEndCursor := entities.NewCursor(mc.String()).Encode()
   862  
   863  	assert.Equal(t, entities.PageInfo{
   864  		HasNextPage:     false,
   865  		HasPreviousPage: false,
   866  		StartCursor:     wantStartCursor,
   867  		EndCursor:       wantEndCursor,
   868  	}, pageInfo)
   869  }
   870  
   871  func testCursorPaginationReturnsAllMarkets(t *testing.T) {
   872  	bs, md := setupMarketsTest(t)
   873  	ctx := tempTransaction(t)
   874  
   875  	blockTimes := make(map[string]time.Time)
   876  	populateTestMarkets(ctx, t, bs, md, blockTimes)
   877  
   878  	pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, false)
   879  	require.NoError(t, err)
   880  
   881  	got, pageInfo, err := md.GetAllPaged(ctx, "", pagination, true)
   882  	require.NoError(t, err)
   883  	assert.Equal(t, 10, len(got))
   884  	assert.Equal(t, "02a16077", got[0].ID.String())
   885  	assert.Equal(t, "fb1528a5", got[9].ID.String())
   886  	assert.Equal(t, "AAA", got[0].InstrumentID)
   887  	assert.Equal(t, "JJJ", got[9].InstrumentID)
   888  
   889  	wantStartCursor := entities.NewCursor(
   890  		entities.MarketCursor{
   891  			VegaTime: blockTimes["02a16077"],
   892  			ID:       "02a16077",
   893  		}.String(),
   894  	).Encode()
   895  	wantEndCursor := entities.NewCursor(
   896  		entities.MarketCursor{
   897  			VegaTime: blockTimes["fb1528a5"],
   898  			ID:       "fb1528a5",
   899  		}.String(),
   900  	).Encode()
   901  	assert.Equal(t, entities.PageInfo{
   902  		HasNextPage:     false,
   903  		HasPreviousPage: false,
   904  		StartCursor:     wantStartCursor,
   905  		EndCursor:       wantEndCursor,
   906  	}, pageInfo)
   907  }
   908  
   909  func testCursorPaginationReturnsFirstPage(t *testing.T) {
   910  	bs, md := setupMarketsTest(t)
   911  	ctx := tempTransaction(t)
   912  
   913  	blockTimes := make(map[string]time.Time)
   914  	populateTestMarkets(ctx, t, bs, md, blockTimes)
   915  	first := int32(3)
   916  	pagination, err := entities.NewCursorPagination(&first, nil, nil, nil, false)
   917  	require.NoError(t, err)
   918  
   919  	got, pageInfo, err := md.GetAllPaged(ctx, "", pagination, true)
   920  	require.NoError(t, err)
   921  
   922  	assert.Equal(t, 3, len(got))
   923  	assert.Equal(t, "02a16077", got[0].ID.String())
   924  	assert.Equal(t, "65be62cd", got[2].ID.String())
   925  	assert.Equal(t, "AAA", got[0].InstrumentID)
   926  	assert.Equal(t, "CCC", got[2].InstrumentID)
   927  
   928  	wantStartCursor := entities.NewCursor(
   929  		entities.MarketCursor{
   930  			VegaTime: blockTimes["02a16077"],
   931  			ID:       "02a16077",
   932  		}.String(),
   933  	).Encode()
   934  	wantEndCursor := entities.NewCursor(
   935  		entities.MarketCursor{
   936  			VegaTime: blockTimes["65be62cd"],
   937  			ID:       "65be62cd",
   938  		}.String(),
   939  	).Encode()
   940  	assert.Equal(t, entities.PageInfo{
   941  		HasNextPage:     true,
   942  		HasPreviousPage: false,
   943  		StartCursor:     wantStartCursor,
   944  		EndCursor:       wantEndCursor,
   945  	}, pageInfo)
   946  }
   947  
   948  func testCursorPaginationReturnsLastPage(t *testing.T) {
   949  	bs, md := setupMarketsTest(t)
   950  	ctx := tempTransaction(t)
   951  
   952  	blockTimes := make(map[string]time.Time)
   953  	populateTestMarkets(ctx, t, bs, md, blockTimes)
   954  	last := int32(3)
   955  	pagination, err := entities.NewCursorPagination(nil, nil, &last, nil, false)
   956  	require.NoError(t, err)
   957  
   958  	got, pageInfo, err := md.GetAllPaged(ctx, "", pagination, true)
   959  	require.NoError(t, err)
   960  
   961  	assert.Equal(t, 3, len(got))
   962  	assert.Equal(t, "c8744329", got[0].ID.String())
   963  	assert.Equal(t, "fb1528a5", got[2].ID.String())
   964  	assert.Equal(t, "HHH", got[0].InstrumentID)
   965  	assert.Equal(t, "JJJ", got[2].InstrumentID)
   966  
   967  	wantStartCursor := entities.NewCursor(
   968  		entities.MarketCursor{
   969  			VegaTime: blockTimes["c8744329"],
   970  			ID:       "c8744329",
   971  		}.String(),
   972  	).Encode()
   973  	wantEndCursor := entities.NewCursor(
   974  		entities.MarketCursor{
   975  			VegaTime: blockTimes["fb1528a5"],
   976  			ID:       "fb1528a5",
   977  		}.String(),
   978  	).Encode()
   979  	assert.Equal(t, entities.PageInfo{
   980  		HasNextPage:     false,
   981  		HasPreviousPage: true,
   982  		StartCursor:     wantStartCursor,
   983  		EndCursor:       wantEndCursor,
   984  	}, pageInfo)
   985  }
   986  
   987  func testCursorPaginationReturnsPageTraversingForward(t *testing.T) {
   988  	bs, md := setupMarketsTest(t)
   989  	ctx := tempTransaction(t)
   990  
   991  	blockTimes := make(map[string]time.Time)
   992  	populateTestMarkets(ctx, t, bs, md, blockTimes)
   993  	first := int32(3)
   994  	after := entities.NewCursor(
   995  		entities.MarketCursor{
   996  			VegaTime: blockTimes["65be62cd"],
   997  			ID:       "65be62cd",
   998  		}.String(),
   999  	).Encode()
  1000  	pagination, err := entities.NewCursorPagination(&first, &after, nil, nil, false)
  1001  	require.NoError(t, err)
  1002  
  1003  	got, pageInfo, err := md.GetAllPaged(ctx, "", pagination, true)
  1004  	require.NoError(t, err)
  1005  
  1006  	assert.Equal(t, 3, len(got))
  1007  	assert.Equal(t, "7a797e0e", got[0].ID.String())
  1008  	assert.Equal(t, "b7c84b8e", got[2].ID.String())
  1009  	assert.Equal(t, "DDD", got[0].InstrumentID)
  1010  	assert.Equal(t, "FFF", got[2].InstrumentID)
  1011  
  1012  	wantStartCursor := entities.NewCursor(
  1013  		entities.MarketCursor{
  1014  			VegaTime: blockTimes["7a797e0e"],
  1015  			ID:       "7a797e0e",
  1016  		}.String(),
  1017  	).Encode()
  1018  	wantEndCursor := entities.NewCursor(
  1019  		entities.MarketCursor{
  1020  			VegaTime: blockTimes["b7c84b8e"],
  1021  			ID:       "b7c84b8e",
  1022  		}.String(),
  1023  	).Encode()
  1024  	assert.Equal(t, entities.PageInfo{
  1025  		HasNextPage:     true,
  1026  		HasPreviousPage: true,
  1027  		StartCursor:     wantStartCursor,
  1028  		EndCursor:       wantEndCursor,
  1029  	}, pageInfo)
  1030  }
  1031  
  1032  func testCursorPaginationReturnsPageTraversingBackward(t *testing.T) {
  1033  	bs, md := setupMarketsTest(t)
  1034  	ctx := tempTransaction(t)
  1035  
  1036  	blockTimes := make(map[string]time.Time)
  1037  	populateTestMarkets(ctx, t, bs, md, blockTimes)
  1038  	last := int32(3)
  1039  	before := entities.NewCursor(
  1040  		entities.MarketCursor{
  1041  			VegaTime: blockTimes["c8744329"],
  1042  			ID:       "c8744329",
  1043  		}.String(),
  1044  	).Encode()
  1045  	pagination, err := entities.NewCursorPagination(nil, nil, &last, &before, false)
  1046  	require.NoError(t, err)
  1047  
  1048  	got, pageInfo, err := md.GetAllPaged(ctx, "", pagination, true)
  1049  	require.NoError(t, err)
  1050  
  1051  	assert.Equal(t, 3, len(got))
  1052  	assert.Equal(t, "7bb2356e", got[0].ID.String())
  1053  	assert.Equal(t, "c612300d", got[2].ID.String())
  1054  	assert.Equal(t, "EEE", got[0].InstrumentID)
  1055  	assert.Equal(t, "GGG", got[2].InstrumentID)
  1056  
  1057  	wantStartCursor := entities.NewCursor(
  1058  		entities.MarketCursor{
  1059  			VegaTime: blockTimes["7bb2356e"],
  1060  			ID:       "7bb2356e",
  1061  		}.String(),
  1062  	).Encode()
  1063  	wantEndCursor := entities.NewCursor(
  1064  		entities.MarketCursor{
  1065  			VegaTime: blockTimes["c612300d"],
  1066  			ID:       "c612300d",
  1067  		}.String(),
  1068  	).Encode()
  1069  	assert.Equal(t, entities.PageInfo{
  1070  		HasNextPage:     true,
  1071  		HasPreviousPage: true,
  1072  		StartCursor:     wantStartCursor,
  1073  		EndCursor:       wantEndCursor,
  1074  	}, pageInfo)
  1075  }
  1076  
  1077  func testCursorPaginationReturnsTheSpecifiedMarketNewestFirst(t *testing.T) {
  1078  	bs, md := setupMarketsTest(t)
  1079  	ctx := tempTransaction(t)
  1080  
  1081  	blockTimes := make(map[string]time.Time)
  1082  	populateTestMarkets(ctx, t, bs, md, blockTimes)
  1083  	pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, true)
  1084  	require.NoError(t, err)
  1085  
  1086  	got, pageInfo, err := md.GetAllPaged(ctx, "c612300d", pagination, true)
  1087  	require.NoError(t, err)
  1088  	assert.Equal(t, 1, len(got))
  1089  	assert.Equal(t, "c612300d", got[0].ID.String())
  1090  	assert.Equal(t, "GGG", got[0].InstrumentID)
  1091  
  1092  	wantStartCursor := entities.NewCursor(
  1093  		entities.MarketCursor{
  1094  			VegaTime: blockTimes["c612300d"],
  1095  			ID:       "c612300d",
  1096  		}.String(),
  1097  	).Encode()
  1098  	wantEndCursor := entities.NewCursor(
  1099  		entities.MarketCursor{
  1100  			VegaTime: blockTimes["c612300d"],
  1101  			ID:       "c612300d",
  1102  		}.String(),
  1103  	).Encode()
  1104  
  1105  	assert.Equal(t, entities.PageInfo{
  1106  		HasNextPage:     false,
  1107  		HasPreviousPage: false,
  1108  		StartCursor:     wantStartCursor,
  1109  		EndCursor:       wantEndCursor,
  1110  	}, pageInfo)
  1111  }
  1112  
  1113  func testCursorPaginationReturnsAllMarketsNewestFirst(t *testing.T) {
  1114  	bs, md := setupMarketsTest(t)
  1115  	ctx := tempTransaction(t)
  1116  
  1117  	blockTimes := make(map[string]time.Time)
  1118  	populateTestMarkets(ctx, t, bs, md, blockTimes)
  1119  
  1120  	pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, true)
  1121  	require.NoError(t, err)
  1122  
  1123  	got, pageInfo, err := md.GetAllPaged(ctx, "", pagination, true)
  1124  	require.NoError(t, err)
  1125  	assert.Equal(t, 10, len(got))
  1126  	assert.Equal(t, "fb1528a5", got[0].ID.String())
  1127  	assert.Equal(t, "02a16077", got[9].ID.String())
  1128  	assert.Equal(t, "JJJ", got[0].InstrumentID)
  1129  	assert.Equal(t, "AAA", got[9].InstrumentID)
  1130  
  1131  	wantStartCursor := entities.NewCursor(
  1132  		entities.MarketCursor{
  1133  			VegaTime: blockTimes["fb1528a5"],
  1134  			ID:       "fb1528a5",
  1135  		}.String(),
  1136  	).Encode()
  1137  	wantEndCursor := entities.NewCursor(
  1138  		entities.MarketCursor{
  1139  			VegaTime: blockTimes["02a16077"],
  1140  			ID:       "02a16077",
  1141  		}.String(),
  1142  	).Encode()
  1143  	assert.Equal(t, entities.PageInfo{
  1144  		HasNextPage:     false,
  1145  		HasPreviousPage: false,
  1146  		StartCursor:     wantStartCursor,
  1147  		EndCursor:       wantEndCursor,
  1148  	}, pageInfo)
  1149  }
  1150  
  1151  func testCursorPaginationReturnsFirstPageNewestFirst(t *testing.T) {
  1152  	bs, md := setupMarketsTest(t)
  1153  	ctx := tempTransaction(t)
  1154  
  1155  	blockTimes := make(map[string]time.Time)
  1156  	populateTestMarkets(ctx, t, bs, md, blockTimes)
  1157  	first := int32(3)
  1158  	pagination, err := entities.NewCursorPagination(&first, nil, nil, nil, true)
  1159  	require.NoError(t, err)
  1160  
  1161  	got, pageInfo, err := md.GetAllPaged(ctx, "", pagination, true)
  1162  	require.NoError(t, err)
  1163  
  1164  	assert.Equal(t, 3, len(got))
  1165  	assert.Equal(t, "fb1528a5", got[0].ID.String())
  1166  	assert.Equal(t, "c8744329", got[2].ID.String())
  1167  	assert.Equal(t, "JJJ", got[0].InstrumentID)
  1168  	assert.Equal(t, "HHH", got[2].InstrumentID)
  1169  
  1170  	wantStartCursor := entities.NewCursor(
  1171  		entities.MarketCursor{
  1172  			VegaTime: blockTimes["fb1528a5"],
  1173  			ID:       "fb1528a5",
  1174  		}.String(),
  1175  	).Encode()
  1176  	wantEndCursor := entities.NewCursor(
  1177  		entities.MarketCursor{
  1178  			VegaTime: blockTimes["c8744329"],
  1179  			ID:       "c8744329",
  1180  		}.String(),
  1181  	).Encode()
  1182  	assert.Equal(t, entities.PageInfo{
  1183  		HasNextPage:     true,
  1184  		HasPreviousPage: false,
  1185  		StartCursor:     wantStartCursor,
  1186  		EndCursor:       wantEndCursor,
  1187  	}, pageInfo)
  1188  }
  1189  
  1190  func testCursorPaginationReturnsLastPageNewestFirst(t *testing.T) {
  1191  	bs, md := setupMarketsTest(t)
  1192  	ctx := tempTransaction(t)
  1193  
  1194  	blockTimes := make(map[string]time.Time)
  1195  	populateTestMarkets(ctx, t, bs, md, blockTimes)
  1196  	last := int32(3)
  1197  	pagination, err := entities.NewCursorPagination(nil, nil, &last, nil, true)
  1198  	require.NoError(t, err)
  1199  
  1200  	got, pageInfo, err := md.GetAllPaged(ctx, "", pagination, true)
  1201  	require.NoError(t, err)
  1202  
  1203  	assert.Equal(t, 3, len(got))
  1204  	assert.Equal(t, "65be62cd", got[0].ID.String())
  1205  	assert.Equal(t, "02a16077", got[2].ID.String())
  1206  	assert.Equal(t, "CCC", got[0].InstrumentID)
  1207  	assert.Equal(t, "AAA", got[2].InstrumentID)
  1208  
  1209  	wantStartCursor := entities.NewCursor(
  1210  		entities.MarketCursor{
  1211  			VegaTime: blockTimes["65be62cd"],
  1212  			ID:       "65be62cd",
  1213  		}.String(),
  1214  	).Encode()
  1215  	wantEndCursor := entities.NewCursor(
  1216  		entities.MarketCursor{
  1217  			VegaTime: blockTimes["02a16077"],
  1218  			ID:       "02a16077",
  1219  		}.String(),
  1220  	).Encode()
  1221  	assert.Equal(t, entities.PageInfo{
  1222  		HasNextPage:     false,
  1223  		HasPreviousPage: true,
  1224  		StartCursor:     wantStartCursor,
  1225  		EndCursor:       wantEndCursor,
  1226  	}, pageInfo)
  1227  }
  1228  
  1229  func testCursorPaginationReturnsPageTraversingForwardNewestFirst(t *testing.T) {
  1230  	bs, md := setupMarketsTest(t)
  1231  	ctx := tempTransaction(t)
  1232  
  1233  	blockTimes := make(map[string]time.Time)
  1234  	populateTestMarkets(ctx, t, bs, md, blockTimes)
  1235  	first := int32(3)
  1236  	after := entities.NewCursor(
  1237  		entities.MarketCursor{
  1238  			VegaTime: blockTimes["c8744329"],
  1239  			ID:       "c8744329",
  1240  		}.String(),
  1241  	).Encode()
  1242  	pagination, err := entities.NewCursorPagination(&first, &after, nil, nil, true)
  1243  	require.NoError(t, err)
  1244  
  1245  	got, pageInfo, err := md.GetAllPaged(ctx, "", pagination, true)
  1246  	require.NoError(t, err)
  1247  
  1248  	assert.Equal(t, 3, len(got))
  1249  	assert.Equal(t, "c612300d", got[0].ID.String())
  1250  	assert.Equal(t, "7bb2356e", got[2].ID.String())
  1251  	assert.Equal(t, "GGG", got[0].InstrumentID)
  1252  	assert.Equal(t, "EEE", got[2].InstrumentID)
  1253  
  1254  	wantStartCursor := entities.NewCursor(
  1255  		entities.MarketCursor{
  1256  			VegaTime: blockTimes["c612300d"],
  1257  			ID:       "c612300d",
  1258  		}.String(),
  1259  	).Encode()
  1260  	wantEndCursor := entities.NewCursor(
  1261  		entities.MarketCursor{
  1262  			VegaTime: blockTimes["7bb2356e"],
  1263  			ID:       "7bb2356e",
  1264  		}.String(),
  1265  	).Encode()
  1266  	assert.Equal(t, entities.PageInfo{
  1267  		HasNextPage:     true,
  1268  		HasPreviousPage: true,
  1269  		StartCursor:     wantStartCursor,
  1270  		EndCursor:       wantEndCursor,
  1271  	}, pageInfo)
  1272  }
  1273  
  1274  func testCursorPaginationReturnsPageTraversingBackwardNewestFirst(t *testing.T) {
  1275  	bs, md := setupMarketsTest(t)
  1276  	ctx := tempTransaction(t)
  1277  
  1278  	blockTimes := make(map[string]time.Time)
  1279  	populateTestMarkets(ctx, t, bs, md, blockTimes)
  1280  	last := int32(3)
  1281  	before := entities.NewCursor(
  1282  		entities.MarketCursor{
  1283  			VegaTime: blockTimes["65be62cd"],
  1284  			ID:       "65be62cd",
  1285  		}.String(),
  1286  	).Encode()
  1287  	pagination, err := entities.NewCursorPagination(nil, nil, &last, &before, true)
  1288  	require.NoError(t, err)
  1289  
  1290  	got, pageInfo, err := md.GetAllPaged(ctx, "", pagination, true)
  1291  	require.NoError(t, err)
  1292  
  1293  	assert.Equal(t, 3, len(got))
  1294  	assert.Equal(t, "b7c84b8e", got[0].ID.String())
  1295  	assert.Equal(t, "7a797e0e", got[2].ID.String())
  1296  	assert.Equal(t, "FFF", got[0].InstrumentID)
  1297  	assert.Equal(t, "DDD", got[2].InstrumentID)
  1298  
  1299  	wantStartCursor := entities.NewCursor(
  1300  		entities.MarketCursor{
  1301  			VegaTime: blockTimes["b7c84b8e"],
  1302  			ID:       "b7c84b8e",
  1303  		}.String(),
  1304  	).Encode()
  1305  	wantEndCursor := entities.NewCursor(
  1306  		entities.MarketCursor{
  1307  			VegaTime: blockTimes["7a797e0e"],
  1308  			ID:       "7a797e0e",
  1309  		}.String(),
  1310  	).Encode()
  1311  	assert.Equal(t, entities.PageInfo{
  1312  		HasNextPage:     true,
  1313  		HasPreviousPage: true,
  1314  		StartCursor:     wantStartCursor,
  1315  		EndCursor:       wantEndCursor,
  1316  	}, pageInfo)
  1317  }
  1318  
  1319  func TestSuccessorMarkets(t *testing.T) {
  1320  	t.Run("should create a market lineage record when a successor market proposal is approved", testMarketLineageCreated)
  1321  	t.Run("ListSuccessorMarkets should return the market lineage", testListSuccessorMarkets)
  1322  	t.Run("GetMarket should return the market with its parent and successor if they exist", testGetMarketWithParentAndSuccessor)
  1323  }
  1324  
  1325  func testMarketLineageCreated(t *testing.T) {
  1326  	ctx := tempTransaction(t)
  1327  
  1328  	bs, md := setupMarketsTest(t)
  1329  	parentMarket := entities.Market{
  1330  		ID:           entities.MarketID("deadbeef01"),
  1331  		InstrumentID: "deadbeef01",
  1332  	}
  1333  
  1334  	successorMarketA := entities.Market{
  1335  		ID:             entities.MarketID("deadbeef02"),
  1336  		InstrumentID:   "deadbeef02",
  1337  		ParentMarketID: parentMarket.ID,
  1338  	}
  1339  
  1340  	successorMarketB := entities.Market{
  1341  		ID:             entities.MarketID("deadbeef03"),
  1342  		InstrumentID:   "deadbeef03",
  1343  		ParentMarketID: successorMarketA.ID,
  1344  	}
  1345  
  1346  	var rowCount int64
  1347  
  1348  	source := &testBlockSource{bs, time.Now()}
  1349  	block := source.getNextBlock(t, ctx)
  1350  	t.Run("parent market should create a market lineage record with no parent market id", func(t *testing.T) {
  1351  		parentMarket.VegaTime = block.VegaTime
  1352  		parentMarket.State = entities.MarketStateProposed
  1353  		err := md.Upsert(ctx, &parentMarket)
  1354  		require.NoError(t, err)
  1355  		err = connectionSource.QueryRow(ctx, `select count(*) from market_lineage where market_id = $1`, parentMarket.ID).Scan(&rowCount)
  1356  		require.NoError(t, err)
  1357  		assert.Equal(t, int64(0), rowCount)
  1358  
  1359  		block = source.getNextBlock(t, ctx)
  1360  		parentMarket.State = entities.MarketStatePending
  1361  		parentMarket.TradingMode = entities.MarketTradingModeOpeningAuction
  1362  		parentMarket.VegaTime = block.VegaTime
  1363  		err = md.Upsert(ctx, &parentMarket)
  1364  		require.NoError(t, err)
  1365  
  1366  		block = source.getNextBlock(t, ctx)
  1367  		parentMarket.State = entities.MarketStateActive
  1368  		parentMarket.TradingMode = entities.MarketTradingModeContinuous
  1369  		parentMarket.VegaTime = block.VegaTime
  1370  		err = md.Upsert(ctx, &parentMarket)
  1371  		require.NoError(t, err)
  1372  
  1373  		var marketID, parentMarketID, rootID entities.MarketID
  1374  		err = connectionSource.QueryRow(ctx,
  1375  			`select market_id, parent_market_id, root_id from market_lineage where market_id = $1`,
  1376  			parentMarket.ID,
  1377  		).Scan(&marketID, &parentMarketID, &rootID)
  1378  		require.NoError(t, err)
  1379  		assert.Equal(t, parentMarket.ID, marketID)
  1380  		assert.Equal(t, entities.MarketID(""), parentMarketID)
  1381  		assert.Equal(t, parentMarket.ID, rootID)
  1382  	})
  1383  
  1384  	block = source.getNextBlock(t, ctx)
  1385  	t.Run("successor market should create a market lineage record pointing to the parent market and the root market", func(t *testing.T) {
  1386  		successorMarketA.VegaTime = block.VegaTime
  1387  		successorMarketA.State = entities.MarketStateProposed
  1388  		err := md.Upsert(ctx, &successorMarketA)
  1389  		require.NoError(t, err)
  1390  		// proposed market successor only, so it should not create a lineage record yet
  1391  		err = connectionSource.QueryRow(ctx, `select count(*) from market_lineage where market_id = $1`, successorMarketA.ID).Scan(&rowCount)
  1392  		require.NoError(t, err)
  1393  		assert.Equal(t, int64(0), rowCount)
  1394  
  1395  		block = source.getNextBlock(t, ctx)
  1396  		successorMarketA.State = entities.MarketStatePending
  1397  		successorMarketA.TradingMode = entities.MarketTradingModeOpeningAuction
  1398  		successorMarketA.VegaTime = block.VegaTime
  1399  		err = md.Upsert(ctx, &successorMarketA)
  1400  		require.NoError(t, err)
  1401  
  1402  		block = source.getNextBlock(t, ctx)
  1403  		successorMarketA.State = entities.MarketStateActive
  1404  		successorMarketA.TradingMode = entities.MarketTradingModeContinuous
  1405  		successorMarketA.VegaTime = block.VegaTime
  1406  		err = md.Upsert(ctx, &successorMarketA)
  1407  		require.NoError(t, err)
  1408  		// proposed market successor has been accepted and is pending, so we should now have a lineage record pointing to the parent
  1409  		var marketID, parentMarketID, rootID entities.MarketID
  1410  		err = connectionSource.QueryRow(ctx,
  1411  			`select market_id, parent_market_id, root_id from market_lineage where market_id = $1`,
  1412  			successorMarketA.ID,
  1413  		).Scan(&marketID, &parentMarketID, &rootID)
  1414  		require.NoError(t, err)
  1415  		assert.Equal(t, successorMarketA.ID, marketID)
  1416  		assert.Equal(t, parentMarket.ID, parentMarketID)
  1417  		assert.Equal(t, parentMarket.ID, rootID)
  1418  	})
  1419  
  1420  	block = source.getNextBlock(t, ctx)
  1421  	t.Run("second successor market should create a lineage record pointing to the parent market and the root market", func(t *testing.T) {
  1422  		successorMarketB.VegaTime = block.VegaTime
  1423  		successorMarketB.State = entities.MarketStateProposed
  1424  		err := md.Upsert(ctx, &successorMarketB)
  1425  		require.NoError(t, err)
  1426  		// proposed market successor only, so it should not create a lineage record yet
  1427  		err = connectionSource.QueryRow(ctx, `select count(*) from market_lineage where market_id = $1`, successorMarketB.ID).Scan(&rowCount)
  1428  		require.NoError(t, err)
  1429  		assert.Equal(t, int64(0), rowCount)
  1430  
  1431  		block = source.getNextBlock(t, ctx)
  1432  		successorMarketB.State = entities.MarketStatePending
  1433  		successorMarketB.TradingMode = entities.MarketTradingModeOpeningAuction
  1434  		successorMarketB.VegaTime = block.VegaTime
  1435  		err = md.Upsert(ctx, &successorMarketB)
  1436  		require.NoError(t, err)
  1437  		// proposed market successor has been accepted and is pending, so we should now have a lineage record pointing to the parent
  1438  		block = source.getNextBlock(t, ctx)
  1439  		successorMarketB.State = entities.MarketStateActive
  1440  		successorMarketB.TradingMode = entities.MarketTradingModeContinuous
  1441  		successorMarketB.VegaTime = block.VegaTime
  1442  		err = md.Upsert(ctx, &successorMarketB)
  1443  		require.NoError(t, err)
  1444  		var marketID, parentMarketID, rootID entities.MarketID
  1445  		err = connectionSource.QueryRow(ctx,
  1446  			`select market_id, parent_market_id, root_id from market_lineage where market_id = $1`,
  1447  			successorMarketB.ID,
  1448  		).Scan(&marketID, &parentMarketID, &rootID)
  1449  		require.NoError(t, err)
  1450  		assert.Equal(t, successorMarketB.ID, marketID)
  1451  		assert.Equal(t, successorMarketA.ID, parentMarketID)
  1452  		assert.Equal(t, parentMarket.ID, rootID)
  1453  	})
  1454  }
  1455  
  1456  func testListSuccessorMarkets(t *testing.T) {
  1457  	ctx := tempTransaction(t)
  1458  
  1459  	md, markets, proposals := setupSuccessorMarkets(t, ctx)
  1460  
  1461  	successors := []entities.SuccessorMarket{
  1462  		{
  1463  			Market: markets[5],
  1464  			Proposals: []*entities.Proposal{
  1465  				&proposals[1],
  1466  				&proposals[2],
  1467  			},
  1468  		},
  1469  		{
  1470  			Market: markets[6],
  1471  			Proposals: []*entities.Proposal{
  1472  				&proposals[3],
  1473  				&proposals[4],
  1474  			},
  1475  		},
  1476  		{
  1477  			Market: markets[9],
  1478  		},
  1479  	}
  1480  
  1481  	t.Run("should list the full history if children only is false", func(t *testing.T) {
  1482  		got, _, err := md.ListSuccessorMarkets(ctx, "deadbeef02", true, entities.CursorPagination{})
  1483  		require.NoError(t, err)
  1484  		want := successors[:]
  1485  		assert.Equal(t, want, got)
  1486  	})
  1487  
  1488  	t.Run("should list only the successor markets if children only is true", func(t *testing.T) {
  1489  		got, _, err := md.ListSuccessorMarkets(ctx, "deadbeef02", false, entities.CursorPagination{})
  1490  		require.NoError(t, err)
  1491  		want := successors[1:]
  1492  
  1493  		assert.Equal(t, want, got)
  1494  	})
  1495  
  1496  	t.Run("should paginate results if pagination is provided", func(t *testing.T) {
  1497  		first := int32(2)
  1498  		after := entities.NewCursor(
  1499  			entities.MarketCursor{
  1500  				VegaTime: markets[5].VegaTime,
  1501  				ID:       markets[5].ID,
  1502  			}.String(),
  1503  		).Encode()
  1504  		pagination, err := entities.NewCursorPagination(&first, &after, nil, nil, false)
  1505  		require.NoError(t, err)
  1506  		got, pageInfo, err := md.ListSuccessorMarkets(ctx, "deadbeef01", true, pagination)
  1507  		require.NoError(t, err)
  1508  		want := successors[1:]
  1509  
  1510  		assert.Equal(t, want, got, "paged successor markets do not match")
  1511  		wantStartCursor := entities.NewCursor(
  1512  			entities.MarketCursor{
  1513  				VegaTime: markets[6].VegaTime,
  1514  				ID:       markets[6].ID,
  1515  			}.String(),
  1516  		).Encode()
  1517  		wantEndCursor := entities.NewCursor(
  1518  			entities.MarketCursor{
  1519  				VegaTime: markets[9].VegaTime,
  1520  				ID:       markets[9].ID,
  1521  			}.String(),
  1522  		).Encode()
  1523  		assert.Equal(t, entities.PageInfo{
  1524  			HasNextPage:     false,
  1525  			HasPreviousPage: true,
  1526  			StartCursor:     wantStartCursor,
  1527  			EndCursor:       wantEndCursor,
  1528  		}, pageInfo)
  1529  	})
  1530  
  1531  	t.Run("should list the parent market even if it has not entered continuous trading and has no successors", func(t *testing.T) {
  1532  		got, _, err := md.ListSuccessorMarkets(ctx, "deadbeef04", false, entities.CursorPagination{})
  1533  		require.NoError(t, err)
  1534  		want := []entities.SuccessorMarket{
  1535  			{
  1536  				Market: markets[10],
  1537  			},
  1538  		}
  1539  		assert.Equal(t, want, got)
  1540  	})
  1541  }
  1542  
  1543  func testGetMarketWithParentAndSuccessor(t *testing.T) {
  1544  	ctx := tempTransaction(t)
  1545  
  1546  	md, _, _ := setupSuccessorMarkets(t, ctx)
  1547  
  1548  	t.Run("should return successor market id only if the first market in a succession line", func(t *testing.T) {
  1549  		got, err := md.GetByID(ctx, "deadbeef01")
  1550  		require.NoError(t, err)
  1551  		assert.Equal(t, "", got.ParentMarketID.String())
  1552  		assert.Equal(t, "deadbeef02", got.SuccessorMarketID.String())
  1553  	})
  1554  
  1555  	t.Run("should return parent and successor market id if the market is within a succession line", func(t *testing.T) {
  1556  		got, err := md.GetByID(ctx, "deadbeef02")
  1557  		require.NoError(t, err)
  1558  		assert.Equal(t, "deadbeef01", got.ParentMarketID.String())
  1559  		assert.Equal(t, "deadbeef03", got.SuccessorMarketID.String())
  1560  	})
  1561  
  1562  	t.Run("should return parent market id only if the last market in a succession line", func(t *testing.T) {
  1563  		got, err := md.GetByID(ctx, "deadbeef03")
  1564  		require.NoError(t, err)
  1565  		assert.Equal(t, "deadbeef02", got.ParentMarketID.String())
  1566  		assert.Equal(t, "", got.SuccessorMarketID.String())
  1567  	})
  1568  }
  1569  
  1570  func setupSuccessorMarkets(t *testing.T, ctx context.Context) (*sqlstore.Markets, []entities.Market, []entities.Proposal) {
  1571  	t.Helper()
  1572  
  1573  	bs, md := setupMarketsTest(t)
  1574  	ps := sqlstore.NewProposals(connectionSource)
  1575  	ts := sqlstore.NewParties(connectionSource)
  1576  
  1577  	emptyLS := &vega.LiquidationStrategy{
  1578  		DisposalTimeStep:      0,
  1579  		DisposalFraction:      "0",
  1580  		FullDisposalSize:      0,
  1581  		MaxFractionConsumed:   "0",
  1582  		DisposalSlippageRange: "0",
  1583  	}
  1584  	liquidationStrat := entities.LiquidationStrategyFromProto(emptyLS)
  1585  	parentMarket := entities.Market{
  1586  		ID:           entities.MarketID("deadbeef01"),
  1587  		InstrumentID: "deadbeef01",
  1588  		TradableInstrument: entities.TradableInstrument{
  1589  			TradableInstrument: &vega.TradableInstrument{},
  1590  		},
  1591  		LiquiditySLAParameters: entities.LiquiditySLAParameters{
  1592  			PriceRange:                  num.NewDecimalFromFloat(0),
  1593  			CommitmentMinTimeFraction:   num.NewDecimalFromFloat(0),
  1594  			PerformanceHysteresisEpochs: 0,
  1595  			SlaCompetitionFactor:        num.NewDecimalFromFloat(0),
  1596  		},
  1597  		LiquidationStrategy: liquidationStrat,
  1598  	}
  1599  
  1600  	successorMarketA := entities.Market{
  1601  		ID:           entities.MarketID("deadbeef02"),
  1602  		InstrumentID: "deadbeef02",
  1603  		TradableInstrument: entities.TradableInstrument{
  1604  			TradableInstrument: &vega.TradableInstrument{},
  1605  		},
  1606  		ParentMarketID: parentMarket.ID,
  1607  		LiquiditySLAParameters: entities.LiquiditySLAParameters{
  1608  			PriceRange:                  num.NewDecimalFromFloat(0),
  1609  			CommitmentMinTimeFraction:   num.NewDecimalFromFloat(0),
  1610  			PerformanceHysteresisEpochs: 0,
  1611  			SlaCompetitionFactor:        num.NewDecimalFromFloat(0),
  1612  		},
  1613  		LiquidationStrategy: liquidationStrat,
  1614  	}
  1615  
  1616  	parentMarket.SuccessorMarketID = successorMarketA.ID
  1617  
  1618  	successorMarketB := entities.Market{
  1619  		ID:           entities.MarketID("deadbeef03"),
  1620  		InstrumentID: "deadbeef03",
  1621  		TradableInstrument: entities.TradableInstrument{
  1622  			TradableInstrument: &vega.TradableInstrument{},
  1623  		},
  1624  		ParentMarketID: successorMarketA.ID,
  1625  		LiquiditySLAParameters: entities.LiquiditySLAParameters{
  1626  			PriceRange:                  num.NewDecimalFromFloat(0),
  1627  			CommitmentMinTimeFraction:   num.NewDecimalFromFloat(0),
  1628  			PerformanceHysteresisEpochs: 0,
  1629  			SlaCompetitionFactor:        num.NewDecimalFromFloat(0),
  1630  		},
  1631  		LiquidationStrategy: liquidationStrat,
  1632  	}
  1633  
  1634  	parentMarket2 := entities.Market{
  1635  		ID:           entities.MarketID("deadbeef04"),
  1636  		InstrumentID: "deadbeef04",
  1637  		TradableInstrument: entities.TradableInstrument{
  1638  			TradableInstrument: &vega.TradableInstrument{},
  1639  		},
  1640  		LiquiditySLAParameters: entities.LiquiditySLAParameters{
  1641  			PriceRange:                  num.NewDecimalFromFloat(0),
  1642  			CommitmentMinTimeFraction:   num.NewDecimalFromFloat(0),
  1643  			PerformanceHysteresisEpochs: 0,
  1644  			SlaCompetitionFactor:        num.NewDecimalFromFloat(0),
  1645  		},
  1646  		LiquidationStrategy: liquidationStrat,
  1647  	}
  1648  
  1649  	successorMarketA.SuccessorMarketID = successorMarketB.ID
  1650  
  1651  	source := &testBlockSource{bs, time.Now()}
  1652  
  1653  	block := source.getNextBlock(t, ctx)
  1654  
  1655  	pt1 := addTestParty(t, ctx, ts, block)
  1656  	pt2 := addTestParty(t, ctx, ts, block)
  1657  
  1658  	proposals := []struct {
  1659  		id        string
  1660  		party     entities.Party
  1661  		reference string
  1662  		block     entities.Block
  1663  		state     entities.ProposalState
  1664  		rationale entities.ProposalRationale
  1665  		terms     entities.ProposalTerms
  1666  		reason    entities.ProposalError
  1667  	}{
  1668  		{
  1669  			id:        "deadbeef01",
  1670  			party:     pt1,
  1671  			reference: "deadbeef01",
  1672  			block:     source.getNextBlock(t, ctx),
  1673  			state:     entities.ProposalStateEnacted,
  1674  			rationale: entities.ProposalRationale{ProposalRationale: &vega.ProposalRationale{Title: "myurl1.com", Description: "mydescription1"}},
  1675  			terms: entities.ProposalTerms{ProposalTerms: &vega.ProposalTerms{Change: &vega.ProposalTerms_NewMarket{NewMarket: &vega.NewMarket{
  1676  				Changes: &vega.NewMarketConfiguration{
  1677  					LiquidationStrategy: emptyLS,
  1678  				},
  1679  			}}}},
  1680  			reason: entities.ProposalErrorUnspecified,
  1681  		},
  1682  		{
  1683  			id:        "deadbeef02",
  1684  			party:     pt1,
  1685  			reference: "deadbeef02",
  1686  			block:     source.getNextBlock(t, ctx),
  1687  			state:     entities.ProposalStateEnacted,
  1688  			rationale: entities.ProposalRationale{ProposalRationale: &vega.ProposalRationale{Title: "myurl1.com", Description: "mydescription1"}},
  1689  			terms: entities.ProposalTerms{ProposalTerms: &vega.ProposalTerms{Change: &vega.ProposalTerms_NewMarket{NewMarket: &vega.NewMarket{
  1690  				Changes: &vega.NewMarketConfiguration{
  1691  					Successor: &vega.SuccessorConfiguration{
  1692  						ParentMarketId:        "deadbeef01",
  1693  						InsurancePoolFraction: "1.0",
  1694  					},
  1695  					LiquidationStrategy: emptyLS,
  1696  				},
  1697  			}}}},
  1698  			reason: entities.ProposalErrorUnspecified,
  1699  		},
  1700  		{
  1701  			id:        "deadbeefaa",
  1702  			party:     pt2,
  1703  			reference: "deadbeefaa",
  1704  			block:     source.getNextBlock(t, ctx),
  1705  			state:     entities.ProposalStateEnacted,
  1706  			rationale: entities.ProposalRationale{ProposalRationale: &vega.ProposalRationale{Title: "myurl1.com", Description: "mydescription1"}},
  1707  			terms: entities.ProposalTerms{ProposalTerms: &vega.ProposalTerms{Change: &vega.ProposalTerms_NewMarket{NewMarket: &vega.NewMarket{
  1708  				Changes: &vega.NewMarketConfiguration{
  1709  					Successor: &vega.SuccessorConfiguration{
  1710  						ParentMarketId:        "deadbeef01",
  1711  						InsurancePoolFraction: "1.0",
  1712  					},
  1713  					LiquidationStrategy: emptyLS,
  1714  				},
  1715  			}}}},
  1716  			reason: entities.ProposalErrorParticipationThresholdNotReached,
  1717  		},
  1718  		{
  1719  			id:        "deadbeef03",
  1720  			party:     pt1,
  1721  			reference: "deadbeef03",
  1722  			block:     source.getNextBlock(t, ctx),
  1723  			state:     entities.ProposalStateEnacted,
  1724  			rationale: entities.ProposalRationale{ProposalRationale: &vega.ProposalRationale{Title: "myurl1.com", Description: "mydescription1"}},
  1725  			terms: entities.ProposalTerms{ProposalTerms: &vega.ProposalTerms{Change: &vega.ProposalTerms_NewMarket{NewMarket: &vega.NewMarket{
  1726  				Changes: &vega.NewMarketConfiguration{
  1727  					Successor: &vega.SuccessorConfiguration{
  1728  						ParentMarketId:        "deadbeef02",
  1729  						InsurancePoolFraction: "1.0",
  1730  					},
  1731  					LiquidationStrategy: emptyLS,
  1732  				},
  1733  			}}}},
  1734  			reason: entities.ProposalErrorUnspecified,
  1735  		},
  1736  		{
  1737  			id:        "deadbeefbb",
  1738  			party:     pt2,
  1739  			reference: "deadbeefbb",
  1740  			block:     source.getNextBlock(t, ctx),
  1741  			state:     entities.ProposalStateEnacted,
  1742  			rationale: entities.ProposalRationale{ProposalRationale: &vega.ProposalRationale{Title: "myurl1.com", Description: "mydescription1"}},
  1743  			terms: entities.ProposalTerms{ProposalTerms: &vega.ProposalTerms{Change: &vega.ProposalTerms_NewMarket{NewMarket: &vega.NewMarket{
  1744  				Changes: &vega.NewMarketConfiguration{
  1745  					Successor: &vega.SuccessorConfiguration{
  1746  						ParentMarketId:        "deadbeef02",
  1747  						InsurancePoolFraction: "1.0",
  1748  					},
  1749  					LiquidationStrategy: emptyLS,
  1750  				},
  1751  			}}}},
  1752  			reason: entities.ProposalErrorParticipationThresholdNotReached,
  1753  		},
  1754  	}
  1755  
  1756  	props := []entities.Proposal{}
  1757  	for _, p := range proposals {
  1758  		p := addTestProposal(t, ctx, ps, p.id, p.party, p.reference, p.block, p.state,
  1759  			p.rationale, p.terms, p.reason, nil, entities.BatchProposalTerms{})
  1760  
  1761  		props = append(props, p)
  1762  	}
  1763  
  1764  	markets := []struct {
  1765  		market      entities.Market
  1766  		state       entities.MarketState
  1767  		tradingMode entities.MarketTradingMode
  1768  	}{
  1769  		{
  1770  			market:      parentMarket,
  1771  			state:       entities.MarketStateProposed,
  1772  			tradingMode: entities.MarketTradingModeOpeningAuction,
  1773  		},
  1774  		{
  1775  			market:      parentMarket,
  1776  			state:       entities.MarketStatePending,
  1777  			tradingMode: entities.MarketTradingModeOpeningAuction,
  1778  		},
  1779  		{
  1780  			market:      parentMarket,
  1781  			state:       entities.MarketStateActive,
  1782  			tradingMode: entities.MarketTradingModeContinuous,
  1783  		},
  1784  		{
  1785  			market:      successorMarketA,
  1786  			state:       entities.MarketStateProposed,
  1787  			tradingMode: entities.MarketTradingModeOpeningAuction,
  1788  		},
  1789  		{
  1790  			market:      successorMarketA,
  1791  			state:       entities.MarketStatePending,
  1792  			tradingMode: entities.MarketTradingModeOpeningAuction,
  1793  		},
  1794  		{
  1795  			market:      parentMarket,
  1796  			state:       entities.MarketStateSettled,
  1797  			tradingMode: entities.MarketTradingModeNoTrading,
  1798  		},
  1799  		{
  1800  			market:      successorMarketA,
  1801  			state:       entities.MarketStateActive,
  1802  			tradingMode: entities.MarketTradingModeContinuous,
  1803  		},
  1804  		{
  1805  			market:      successorMarketB,
  1806  			state:       entities.MarketStateProposed,
  1807  			tradingMode: entities.MarketTradingModeOpeningAuction,
  1808  		},
  1809  		{
  1810  			market:      successorMarketB,
  1811  			state:       entities.MarketStatePending,
  1812  			tradingMode: entities.MarketTradingModeOpeningAuction,
  1813  		},
  1814  		{
  1815  			market:      successorMarketB,
  1816  			state:       entities.MarketStateActive,
  1817  			tradingMode: entities.MarketTradingModeContinuous,
  1818  		},
  1819  		{
  1820  			market:      parentMarket2,
  1821  			state:       entities.MarketStatePending,
  1822  			tradingMode: entities.MarketTradingModeOpeningAuction,
  1823  		},
  1824  	}
  1825  
  1826  	entries := make([]entities.Market, 0, len(markets))
  1827  
  1828  	for _, u := range markets {
  1829  		block := source.getNextBlock(t, ctx)
  1830  		u.market.VegaTime = block.VegaTime
  1831  		u.market.State = u.state
  1832  		u.market.TradingMode = u.tradingMode
  1833  		err := md.Upsert(ctx, &u.market)
  1834  		entries = append(entries, u.market)
  1835  		require.NoError(t, err)
  1836  	}
  1837  
  1838  	return md, entries, props
  1839  }
  1840  
  1841  func TestMarketsEnums(t *testing.T) {
  1842  	t.Run("All proto market states should be supported", testMarketState)
  1843  	t.Run("All proto market trading modes should be supported", testMarketTradingMode)
  1844  }
  1845  
  1846  func testMarketState(t *testing.T) {
  1847  	var marketState vega.Market_State
  1848  	states := getEnums(t, marketState)
  1849  	assert.Len(t, states, 11)
  1850  	for s, state := range states {
  1851  		t.Run(state, func(t *testing.T) {
  1852  			bs, md := setupMarketsTest(t)
  1853  
  1854  			ctx := tempTransaction(t)
  1855  
  1856  			block := addTestBlock(t, ctx, bs)
  1857  
  1858  			marketProto := getTestFutureMarket(true)
  1859  			marketProto.State = vega.Market_State(s)
  1860  
  1861  			market, err := entities.NewMarketFromProto(marketProto, generateTxHash(), block.VegaTime)
  1862  			require.NoError(t, err, "Converting market proto to database entity")
  1863  			require.NoError(t, md.Upsert(ctx, market))
  1864  			got, err := md.GetByID(ctx, market.ID.String())
  1865  			require.NoError(t, err)
  1866  			assert.Equal(t, market.State, got.State)
  1867  		})
  1868  	}
  1869  }
  1870  
  1871  func testMarketTradingMode(t *testing.T) {
  1872  	var marketTradingMode vega.Market_TradingMode
  1873  	modes := getEnums(t, marketTradingMode)
  1874  	assert.Len(t, modes, 9)
  1875  	for m, mode := range modes {
  1876  		t.Run(mode, func(t *testing.T) {
  1877  			bs, md := setupMarketsTest(t)
  1878  
  1879  			ctx := tempTransaction(t)
  1880  
  1881  			block := addTestBlock(t, ctx, bs)
  1882  
  1883  			marketProto := getTestFutureMarket(true)
  1884  			marketProto.TradingMode = vega.Market_TradingMode(m)
  1885  
  1886  			market, err := entities.NewMarketFromProto(marketProto, generateTxHash(), block.VegaTime)
  1887  			require.NoError(t, err, "Converting market proto to database entity")
  1888  			require.NoError(t, md.Upsert(ctx, market))
  1889  			got, err := md.GetByID(ctx, market.ID.String())
  1890  			require.NoError(t, err)
  1891  			assert.Equal(t, market.TradingMode, got.TradingMode)
  1892  		})
  1893  	}
  1894  }