code.vegaprotocol.io/vega@v0.79.0/datanode/sqlstore/positions_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  	"time"
    23  
    24  	"code.vegaprotocol.io/vega/datanode/entities"
    25  	"code.vegaprotocol.io/vega/datanode/sqlstore"
    26  	vegapb "code.vegaprotocol.io/vega/protos/vega"
    27  
    28  	"github.com/google/go-cmp/cmp"
    29  	"github.com/google/go-cmp/cmp/cmpopts"
    30  	"github.com/shopspring/decimal"
    31  	"github.com/stretchr/testify/assert"
    32  	"github.com/stretchr/testify/require"
    33  )
    34  
    35  func addTestPosition(t *testing.T,
    36  	ctx context.Context,
    37  	ps *sqlstore.Positions,
    38  	market entities.Market,
    39  	party entities.Party,
    40  	volume int64,
    41  	block entities.Block,
    42  	txHash entities.TxHash,
    43  ) entities.Position {
    44  	t.Helper()
    45  	pos := entities.NewEmptyPosition(market.ID, party.ID)
    46  	pos.OpenVolume = volume
    47  	pos.PendingOpenVolume = volume
    48  	pos.VegaTime = block.VegaTime
    49  	pos.RealisedPnl = decimal.New(0, 0)
    50  	pos.PendingRealisedPnl = decimal.New(0, 0)
    51  	pos.UnrealisedPnl = decimal.New(0, 0)
    52  	pos.PendingUnrealisedPnl = decimal.New(0, 0)
    53  	pos.AverageEntryPrice = decimal.New(0, 0)
    54  	pos.PendingAverageEntryPrice = decimal.New(0, 0)
    55  	pos.AverageEntryMarketPrice = decimal.New(0, 0)
    56  	pos.PendingAverageEntryMarketPrice = decimal.New(0, 0)
    57  	pos.Adjustment = decimal.New(0, 0)
    58  	pos.Loss = decimal.New(0, 0)
    59  	pos.TxHash = txHash
    60  	err := ps.Add(ctx, pos)
    61  	require.NoError(t, err)
    62  	return pos
    63  }
    64  
    65  func positionLessThan(x, y entities.Position) bool {
    66  	if x.MarketID != y.MarketID {
    67  		return x.MarketID.String() < y.MarketID.String()
    68  	}
    69  	return x.PartyID.String() < y.PartyID.String()
    70  }
    71  
    72  func assertPositionsMatch(t *testing.T, expected, actual []entities.Position) {
    73  	t.Helper()
    74  	sortPositions := cmpopts.SortSlices(positionLessThan)
    75  	assert.Empty(t, cmp.Diff(actual, expected, sortPositions))
    76  }
    77  
    78  func TestPosition(t *testing.T) {
    79  	ctx := tempTransaction(t)
    80  
    81  	ps := sqlstore.NewPositions(connectionSource)
    82  	qs := sqlstore.NewParties(connectionSource)
    83  	bs := sqlstore.NewBlocks(connectionSource)
    84  
    85  	block1 := addTestBlockForTime(t, ctx, bs, time.Now().Add((-26*time.Hour)-(2*time.Second)))
    86  	block2 := addTestBlockForTime(t, ctx, bs, time.Now().Add((-26*time.Hour)-(1*time.Second)))
    87  	block3 := addTestBlockForTime(t, ctx, bs, time.Now().Add(-26*time.Hour))
    88  
    89  	market1 := entities.Market{ID: entities.MarketID("dead")}
    90  	market2 := entities.Market{ID: entities.MarketID("beef")}
    91  	party1 := addTestParty(t, ctx, qs, block1)
    92  	party2 := addTestParty(t, ctx, qs, block1)
    93  
    94  	pos1a := addTestPosition(t, ctx, ps, market1, party1, 100, block1, txHashFromString("pos_1a"))
    95  	pos1b := addTestPosition(t, ctx, ps, market1, party1, 200, block1, txHashFromString("pos_1b"))
    96  
    97  	pos2 := addTestPosition(t, ctx, ps, market1, party2, 300, block2, txHashFromString("pos_2"))
    98  	pos3 := addTestPosition(t, ctx, ps, market2, party1, 400, block2, txHashFromString("pos_3"))
    99  
   100  	_, err := ps.Flush(ctx)
   101  	require.NoError(t, err)
   102  
   103  	_, _ = pos1a, pos1b
   104  
   105  	assert.NoError(t, err)
   106  
   107  	// Add some new positions
   108  	pos1c := addTestPosition(t, ctx, ps, market1, party1, 200, block3, txHashFromString("pos_1c"))
   109  	pos4 := addTestPosition(t, ctx, ps, market2, party2, 500, block3, txHashFromString("pos_4"))
   110  	ps.Flush(ctx)
   111  
   112  	t.Run("GetAll", func(t *testing.T) {
   113  		expected := []entities.Position{pos1c, pos2, pos3, pos4}
   114  		actual, err := ps.GetAll(ctx)
   115  		require.NoError(t, err)
   116  		assertPositionsMatch(t, expected, actual)
   117  	})
   118  
   119  	t.Run("GetByParty", func(t *testing.T) {
   120  		expected := []entities.Position{pos1c, pos3}
   121  		actual, err := ps.GetByParty(ctx, party1.ID.String())
   122  		require.NoError(t, err)
   123  		assertPositionsMatch(t, expected, actual)
   124  	})
   125  
   126  	t.Run("GetByMarket", func(t *testing.T) {
   127  		expected := []entities.Position{pos1c, pos2}
   128  		actual, err := ps.GetByMarket(ctx, market1.ID.String())
   129  		require.NoError(t, err)
   130  		assertPositionsMatch(t, expected, actual)
   131  	})
   132  
   133  	t.Run("GetByMarketAndParty", func(t *testing.T) {
   134  		expected := pos4
   135  		actual, err := ps.GetByMarketAndParty(ctx, market2.ID.String(), party2.ID.String())
   136  		require.NoError(t, err)
   137  		assert.True(t, expected.Equal(actual))
   138  	})
   139  
   140  	t.Run("GetByTxHash", func(t *testing.T) {
   141  		expected := pos4
   142  		actual, err := ps.GetByTxHash(ctx, expected.TxHash)
   143  		require.NoError(t, err)
   144  		assert.True(t, expected.Equal(actual[0]))
   145  	})
   146  
   147  	t.Run("GetBadMarketAndParty", func(t *testing.T) {
   148  		_, err := ps.GetByMarketAndParty(ctx, market2.ID.String(), "ffff")
   149  		assert.ErrorIs(t, err, entities.ErrNotFound)
   150  	})
   151  }
   152  
   153  func setupPositionPaginationData(t *testing.T, ctx context.Context, bs *sqlstore.Blocks, ps *sqlstore.Positions, pts *sqlstore.Parties) []entities.Position {
   154  	t.Helper()
   155  	positions := make([]entities.Position, 0, 10)
   156  	blockTime := time.Now()
   157  	for i := 0; i < 10; i++ {
   158  		market := entities.Market{ID: entities.MarketID(fmt.Sprintf("deadbeef%02d", i))}
   159  		for j := 0; j < 10; j++ {
   160  			block := addTestBlockForTime(t, ctx, bs, blockTime)
   161  			party := entities.Party{ID: entities.PartyID(fmt.Sprintf("deadbeef%02d", j)), VegaTime: &block.VegaTime}
   162  			err := pts.Add(ctx, party)
   163  			require.NoError(t, err)
   164  			position := addTestPosition(t, ctx, ps, market, party, int64(i), block, defaultTxHash)
   165  			positions = append(positions, position)
   166  			blockTime = blockTime.Add(time.Minute)
   167  		}
   168  		blockTime = blockTime.Add(time.Hour)
   169  	}
   170  	_, err := ps.Flush(ctx)
   171  	require.NoError(t, err)
   172  
   173  	return positions
   174  }
   175  
   176  func TestPositions_CursorPagination(t *testing.T) {
   177  	t.Run("should return all positions for party when no cursor is provided", testPositionCursorPaginationPartyNoCursor)
   178  	t.Run("should return first page of positions for party when first is provided", testPositionCursorPaginationPartyFirstCursor)
   179  	t.Run("should return last page of positions for party when last is provided", testPositionCursorPaginationPartyLastCursor)
   180  	t.Run("should return requested page of positions for party when first and after is provided", testPositionCursorPaginationPartyFirstAfterCursor)
   181  	t.Run("should return requested page of positions for party when last and before is provided", testPositionCursorPaginationPartyLastBeforeCursor)
   182  	t.Run("should return all positions for party and market when no cursor is provided", testPositionCursorPaginationPartyMarketNoCursor)
   183  
   184  	t.Run("should return all positions for party when no cursor is provided - newest first", testPositionCursorPaginationPartyNoCursorNewestFirst)
   185  	t.Run("should return first page of positions for party when first is provided - newest first", testPositionCursorPaginationPartyFirstCursorNewestFirst)
   186  	t.Run("should return last page of positions for party when last is provided - newest first", testPositionCursorPaginationPartyLastCursorNewestFirst)
   187  	t.Run("should return requested page of positions for party when first and after is provided - newest first", testPositionCursorPaginationPartyFirstAfterCursorNewestFirst)
   188  	t.Run("should return requested page of positions for party when last and before is provided - newest first", testPositionCursorPaginationPartyLastBeforeCursorNewestFirst)
   189  	t.Run("should return all positions for party and market when no cursor is provided - newest first", testPositionCursorPaginationPartyMarketNoCursorNewestFirst)
   190  }
   191  
   192  func testPositionCursorPaginationPartyNoCursor(t *testing.T) {
   193  	ctx := tempTransaction(t)
   194  
   195  	ps := sqlstore.NewPositions(connectionSource)
   196  	pts := sqlstore.NewParties(connectionSource)
   197  	bs := sqlstore.NewBlocks(connectionSource)
   198  
   199  	emptyMarketID := entities.MarketID("")
   200  
   201  	positions := setupPositionPaginationData(t, ctx, bs, ps, pts)
   202  
   203  	pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, false)
   204  	require.NoError(t, err)
   205  
   206  	party := entities.Party{ID: entities.PartyID("deadbeef00")}
   207  	want := []entities.Position{
   208  		positions[0],
   209  		positions[10],
   210  		positions[20],
   211  		positions[30],
   212  		positions[40],
   213  		positions[50],
   214  		positions[60],
   215  		positions[70],
   216  		positions[80],
   217  		positions[90],
   218  	}
   219  
   220  	got, pageInfo, err := ps.GetByPartyConnection(ctx, []string{party.ID.String()}, []string{emptyMarketID.String()}, pagination)
   221  	require.NoError(t, err)
   222  	for i, g := range got {
   223  		assert.True(t, want[i].Equal(g))
   224  	}
   225  	// assert.Equal(t, want, got)
   226  	assert.Equal(t, entities.PageInfo{
   227  		HasNextPage:     false,
   228  		HasPreviousPage: false,
   229  		StartCursor:     want[0].Cursor().Encode(),
   230  		EndCursor:       want[9].Cursor().Encode(),
   231  	}, pageInfo)
   232  }
   233  
   234  func testPositionCursorPaginationPartyFirstCursor(t *testing.T) {
   235  	ctx := tempTransaction(t)
   236  
   237  	ps := sqlstore.NewPositions(connectionSource)
   238  	pts := sqlstore.NewParties(connectionSource)
   239  	bs := sqlstore.NewBlocks(connectionSource)
   240  
   241  	emptyMarketID := entities.MarketID("")
   242  
   243  	positions := setupPositionPaginationData(t, ctx, bs, ps, pts)
   244  	first := int32(3)
   245  
   246  	pagination, err := entities.NewCursorPagination(&first, nil, nil, nil, false)
   247  	require.NoError(t, err)
   248  
   249  	party := entities.Party{ID: entities.PartyID("deadbeef00")}
   250  	want := []entities.Position{
   251  		positions[0],
   252  		positions[10],
   253  		positions[20],
   254  	}
   255  
   256  	got, pageInfo, err := ps.GetByPartyConnection(ctx, []string{party.ID.String()}, []string{emptyMarketID.String()}, pagination)
   257  	require.NoError(t, err)
   258  	for i, g := range got {
   259  		assert.True(t, want[i].Equal(g))
   260  	}
   261  	// assert.Equal(t, want, got)
   262  	assert.Equal(t, entities.PageInfo{
   263  		HasNextPage:     true,
   264  		HasPreviousPage: false,
   265  		StartCursor:     want[0].Cursor().Encode(),
   266  		EndCursor:       want[2].Cursor().Encode(),
   267  	}, pageInfo)
   268  }
   269  
   270  func testPositionCursorPaginationPartyLastCursor(t *testing.T) {
   271  	ctx := tempTransaction(t)
   272  
   273  	ps := sqlstore.NewPositions(connectionSource)
   274  	pts := sqlstore.NewParties(connectionSource)
   275  	bs := sqlstore.NewBlocks(connectionSource)
   276  
   277  	emptyMarketID := entities.MarketID("")
   278  
   279  	positions := setupPositionPaginationData(t, ctx, bs, ps, pts)
   280  
   281  	last := int32(3)
   282  	pagination, err := entities.NewCursorPagination(nil, nil, &last, nil, false)
   283  	require.NoError(t, err)
   284  
   285  	party := entities.Party{ID: entities.PartyID("deadbeef00")}
   286  	want := []entities.Position{
   287  		positions[70],
   288  		positions[80],
   289  		positions[90],
   290  	}
   291  
   292  	got, pageInfo, err := ps.GetByPartyConnection(ctx, []string{party.ID.String()}, []string{emptyMarketID.String()}, pagination)
   293  	require.NoError(t, err)
   294  	for i, g := range got {
   295  		assert.True(t, want[i].Equal(g))
   296  	}
   297  	// assert.Equal(t, want, got)
   298  	assert.Equal(t, entities.PageInfo{
   299  		HasNextPage:     false,
   300  		HasPreviousPage: true,
   301  		StartCursor:     want[0].Cursor().Encode(),
   302  		EndCursor:       want[2].Cursor().Encode(),
   303  	}, pageInfo)
   304  }
   305  
   306  func testPositionCursorPaginationPartyFirstAfterCursor(t *testing.T) {
   307  	ctx := tempTransaction(t)
   308  
   309  	ps := sqlstore.NewPositions(connectionSource)
   310  	pts := sqlstore.NewParties(connectionSource)
   311  	bs := sqlstore.NewBlocks(connectionSource)
   312  
   313  	emptyMarketID := entities.MarketID("")
   314  
   315  	positions := setupPositionPaginationData(t, ctx, bs, ps, pts)
   316  
   317  	first := int32(3)
   318  	after := positions[20].Cursor().Encode()
   319  	pagination, err := entities.NewCursorPagination(&first, &after, nil, nil, false)
   320  	require.NoError(t, err)
   321  
   322  	party := entities.Party{ID: entities.PartyID("deadbeef00")}
   323  	want := []entities.Position{
   324  		positions[30],
   325  		positions[40],
   326  		positions[50],
   327  	}
   328  
   329  	got, pageInfo, err := ps.GetByPartyConnection(ctx, []string{party.ID.String()}, []string{emptyMarketID.String()}, pagination)
   330  	require.NoError(t, err)
   331  	for i, g := range got {
   332  		assert.True(t, want[i].Equal(g))
   333  	}
   334  	// assert.Equal(t, want, got)
   335  	assert.Equal(t, entities.PageInfo{
   336  		HasNextPage:     true,
   337  		HasPreviousPage: true,
   338  		StartCursor:     want[0].Cursor().Encode(),
   339  		EndCursor:       want[2].Cursor().Encode(),
   340  	}, pageInfo)
   341  }
   342  
   343  func testPositionCursorPaginationPartyLastBeforeCursor(t *testing.T) {
   344  	ctx := tempTransaction(t)
   345  
   346  	ps := sqlstore.NewPositions(connectionSource)
   347  	pts := sqlstore.NewParties(connectionSource)
   348  	bs := sqlstore.NewBlocks(connectionSource)
   349  
   350  	emptyMarketID := entities.MarketID("")
   351  
   352  	positions := setupPositionPaginationData(t, ctx, bs, ps, pts)
   353  
   354  	last := int32(3)
   355  	before := positions[70].Cursor().Encode()
   356  	pagination, err := entities.NewCursorPagination(nil, nil, &last, &before, false)
   357  	require.NoError(t, err)
   358  
   359  	party := entities.Party{ID: entities.PartyID("deadbeef00")}
   360  	want := []entities.Position{
   361  		positions[40],
   362  		positions[50],
   363  		positions[60],
   364  	}
   365  
   366  	got, pageInfo, err := ps.GetByPartyConnection(ctx, []string{party.ID.String()}, []string{emptyMarketID.String()}, pagination)
   367  	require.NoError(t, err)
   368  	for i, g := range got {
   369  		assert.True(t, want[i].Equal(g))
   370  	}
   371  	// assert.Equal(t, want, got)
   372  	assert.Equal(t, entities.PageInfo{
   373  		HasNextPage:     true,
   374  		HasPreviousPage: true,
   375  		StartCursor:     want[0].Cursor().Encode(),
   376  		EndCursor:       want[2].Cursor().Encode(),
   377  	}, pageInfo)
   378  }
   379  
   380  func testPositionCursorPaginationPartyMarketNoCursor(t *testing.T) {
   381  	ctx := tempTransaction(t)
   382  
   383  	ps := sqlstore.NewPositions(connectionSource)
   384  	pts := sqlstore.NewParties(connectionSource)
   385  	bs := sqlstore.NewBlocks(connectionSource)
   386  
   387  	emptyMarketID := entities.MarketID("deadbeef00")
   388  
   389  	positions := setupPositionPaginationData(t, ctx, bs, ps, pts)
   390  
   391  	pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, false)
   392  	require.NoError(t, err)
   393  
   394  	party := entities.Party{ID: entities.PartyID("deadbeef00")}
   395  	want := []entities.Position{
   396  		positions[0],
   397  	}
   398  
   399  	got, pageInfo, err := ps.GetByPartyConnection(ctx, []string{party.ID.String()}, []string{emptyMarketID.String()}, pagination)
   400  	require.NoError(t, err)
   401  	for i, g := range got {
   402  		assert.True(t, want[i].Equal(g))
   403  	}
   404  	// assert.Equal(t, want, got)
   405  	assert.Equal(t, entities.PageInfo{
   406  		HasNextPage:     false,
   407  		HasPreviousPage: false,
   408  		StartCursor:     want[0].Cursor().Encode(),
   409  		EndCursor:       want[0].Cursor().Encode(),
   410  	}, pageInfo)
   411  }
   412  
   413  func testPositionCursorPaginationPartyNoCursorNewestFirst(t *testing.T) {
   414  	ctx := tempTransaction(t)
   415  
   416  	ps := sqlstore.NewPositions(connectionSource)
   417  	pts := sqlstore.NewParties(connectionSource)
   418  	bs := sqlstore.NewBlocks(connectionSource)
   419  
   420  	emptyMarketID := entities.MarketID("")
   421  
   422  	positions := setupPositionPaginationData(t, ctx, bs, ps, pts)
   423  
   424  	pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, true)
   425  	require.NoError(t, err)
   426  
   427  	party := entities.Party{ID: entities.PartyID("deadbeef00")}
   428  	want := []entities.Position{
   429  		positions[0],
   430  		positions[10],
   431  		positions[20],
   432  		positions[30],
   433  		positions[40],
   434  		positions[50],
   435  		positions[60],
   436  		positions[70],
   437  		positions[80],
   438  		positions[90],
   439  	}
   440  
   441  	want = entities.ReverseSlice(want)
   442  
   443  	got, pageInfo, err := ps.GetByPartyConnection(ctx, []string{party.ID.String()}, []string{emptyMarketID.String()}, pagination)
   444  	require.NoError(t, err)
   445  	for i, g := range got {
   446  		assert.True(t, g.Equal(want[i]))
   447  	}
   448  	// assert.Equal(t, want, got)
   449  	assert.Equal(t, entities.PageInfo{
   450  		HasNextPage:     false,
   451  		HasPreviousPage: false,
   452  		StartCursor:     want[0].Cursor().Encode(),
   453  		EndCursor:       want[9].Cursor().Encode(),
   454  	}, pageInfo)
   455  }
   456  
   457  func testPositionCursorPaginationPartyFirstCursorNewestFirst(t *testing.T) {
   458  	ctx := tempTransaction(t)
   459  
   460  	ps := sqlstore.NewPositions(connectionSource)
   461  	pts := sqlstore.NewParties(connectionSource)
   462  	bs := sqlstore.NewBlocks(connectionSource)
   463  
   464  	emptyMarketID := entities.MarketID("")
   465  
   466  	positions := setupPositionPaginationData(t, ctx, bs, ps, pts)
   467  	first := int32(3)
   468  
   469  	pagination, err := entities.NewCursorPagination(&first, nil, nil, nil, true)
   470  	require.NoError(t, err)
   471  
   472  	party := entities.Party{ID: entities.PartyID("deadbeef00")}
   473  	want := []entities.Position{
   474  		positions[90],
   475  		positions[80],
   476  		positions[70],
   477  	}
   478  
   479  	got, pageInfo, err := ps.GetByPartyConnection(ctx, []string{party.ID.String()}, []string{emptyMarketID.String()}, pagination)
   480  	require.NoError(t, err)
   481  	for i, g := range got {
   482  		assert.True(t, g.Equal(want[i]))
   483  	}
   484  	// assert.Equal(t, want, got)
   485  	assert.Equal(t, entities.PageInfo{
   486  		HasNextPage:     true,
   487  		HasPreviousPage: false,
   488  		StartCursor:     want[0].Cursor().Encode(),
   489  		EndCursor:       want[2].Cursor().Encode(),
   490  	}, pageInfo)
   491  }
   492  
   493  func testPositionCursorPaginationPartyLastCursorNewestFirst(t *testing.T) {
   494  	ctx := tempTransaction(t)
   495  
   496  	ps := sqlstore.NewPositions(connectionSource)
   497  	pts := sqlstore.NewParties(connectionSource)
   498  	bs := sqlstore.NewBlocks(connectionSource)
   499  
   500  	emptyMarketID := entities.MarketID("")
   501  
   502  	positions := setupPositionPaginationData(t, ctx, bs, ps, pts)
   503  
   504  	last := int32(3)
   505  	pagination, err := entities.NewCursorPagination(nil, nil, &last, nil, true)
   506  	require.NoError(t, err)
   507  
   508  	party := entities.Party{ID: entities.PartyID("deadbeef00")}
   509  	want := []entities.Position{
   510  		positions[20],
   511  		positions[10],
   512  		positions[0],
   513  	}
   514  
   515  	got, pageInfo, err := ps.GetByPartyConnection(ctx, []string{party.ID.String()}, []string{emptyMarketID.String()}, pagination)
   516  	require.NoError(t, err)
   517  	for i, g := range got {
   518  		assert.True(t, g.Equal(want[i]))
   519  	}
   520  	// assert.Equal(t, want, got)
   521  	assert.Equal(t, entities.PageInfo{
   522  		HasNextPage:     false,
   523  		HasPreviousPage: true,
   524  		StartCursor:     want[0].Cursor().Encode(),
   525  		EndCursor:       want[2].Cursor().Encode(),
   526  	}, pageInfo)
   527  }
   528  
   529  func testPositionCursorPaginationPartyFirstAfterCursorNewestFirst(t *testing.T) {
   530  	ctx := tempTransaction(t)
   531  
   532  	ps := sqlstore.NewPositions(connectionSource)
   533  	pts := sqlstore.NewParties(connectionSource)
   534  	bs := sqlstore.NewBlocks(connectionSource)
   535  
   536  	emptyMarketID := entities.MarketID("")
   537  
   538  	positions := setupPositionPaginationData(t, ctx, bs, ps, pts)
   539  
   540  	first := int32(3)
   541  	after := positions[70].Cursor().Encode()
   542  	pagination, err := entities.NewCursorPagination(&first, &after, nil, nil, true)
   543  	require.NoError(t, err)
   544  
   545  	party := entities.Party{ID: entities.PartyID("deadbeef00")}
   546  	want := []entities.Position{
   547  		positions[60],
   548  		positions[50],
   549  		positions[40],
   550  	}
   551  
   552  	got, pageInfo, err := ps.GetByPartyConnection(ctx, []string{party.ID.String()}, []string{emptyMarketID.String()}, pagination)
   553  	require.NoError(t, err)
   554  	for i, g := range got {
   555  		assert.True(t, g.Equal(want[i]))
   556  	}
   557  	// assert.Equal(t, want, got)
   558  	assert.Equal(t, entities.PageInfo{
   559  		HasNextPage:     true,
   560  		HasPreviousPage: true,
   561  		StartCursor:     want[0].Cursor().Encode(),
   562  		EndCursor:       want[2].Cursor().Encode(),
   563  	}, pageInfo)
   564  }
   565  
   566  func testPositionCursorPaginationPartyLastBeforeCursorNewestFirst(t *testing.T) {
   567  	ctx := tempTransaction(t)
   568  
   569  	ps := sqlstore.NewPositions(connectionSource)
   570  	pts := sqlstore.NewParties(connectionSource)
   571  	bs := sqlstore.NewBlocks(connectionSource)
   572  
   573  	emptyMarketID := entities.MarketID("")
   574  
   575  	positions := setupPositionPaginationData(t, ctx, bs, ps, pts)
   576  
   577  	last := int32(3)
   578  	before := positions[20].Cursor().Encode()
   579  	pagination, err := entities.NewCursorPagination(nil, nil, &last, &before, true)
   580  	require.NoError(t, err)
   581  
   582  	party := entities.Party{ID: entities.PartyID("deadbeef00")}
   583  	want := []entities.Position{
   584  		positions[50],
   585  		positions[40],
   586  		positions[30],
   587  	}
   588  
   589  	got, pageInfo, err := ps.GetByPartyConnection(ctx, []string{party.ID.String()}, []string{emptyMarketID.String()}, pagination)
   590  	require.NoError(t, err)
   591  	for i, g := range got {
   592  		assert.True(t, g.Equal(want[i]))
   593  	}
   594  	// assert.Equal(t, want, got)
   595  	assert.Equal(t, entities.PageInfo{
   596  		HasNextPage:     true,
   597  		HasPreviousPage: true,
   598  		StartCursor:     want[0].Cursor().Encode(),
   599  		EndCursor:       want[2].Cursor().Encode(),
   600  	}, pageInfo)
   601  }
   602  
   603  func testPositionCursorPaginationPartyMarketNoCursorNewestFirst(t *testing.T) {
   604  	ctx := tempTransaction(t)
   605  
   606  	ps := sqlstore.NewPositions(connectionSource)
   607  	pts := sqlstore.NewParties(connectionSource)
   608  	bs := sqlstore.NewBlocks(connectionSource)
   609  
   610  	emptyMarketID := entities.MarketID("deadbeef00")
   611  
   612  	positions := setupPositionPaginationData(t, ctx, bs, ps, pts)
   613  
   614  	pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, true)
   615  	require.NoError(t, err)
   616  
   617  	party := entities.Party{ID: entities.PartyID("deadbeef00")}
   618  	want := []entities.Position{
   619  		positions[0],
   620  	}
   621  
   622  	got, pageInfo, err := ps.GetByPartyConnection(ctx, []string{party.ID.String()}, []string{emptyMarketID.String()}, pagination)
   623  	require.NoError(t, err)
   624  	for i, g := range got {
   625  		assert.True(t, g.Equal(want[i]))
   626  	}
   627  	// assert.Equal(t, want, got)
   628  	assert.Equal(t, entities.PageInfo{
   629  		HasNextPage:     false,
   630  		HasPreviousPage: false,
   631  		StartCursor:     want[0].Cursor().Encode(),
   632  		EndCursor:       want[0].Cursor().Encode(),
   633  	}, pageInfo)
   634  }
   635  
   636  func TestPositionStatusEnum(t *testing.T) {
   637  	var positionStatus vegapb.PositionStatus
   638  	states := getEnums(t, positionStatus)
   639  	for s, state := range states {
   640  		t.Run(state, func(t *testing.T) {
   641  			ctx := tempTransaction(t)
   642  
   643  			ps := sqlstore.NewPositions(connectionSource)
   644  			qs := sqlstore.NewParties(connectionSource)
   645  			bs := sqlstore.NewBlocks(connectionSource)
   646  
   647  			block := addTestBlockForTime(t, ctx, bs, time.Now().Add((-26*time.Hour)-(2*time.Second)))
   648  
   649  			market := entities.Market{ID: entities.MarketID("dead")}
   650  			party := addTestParty(t, ctx, qs, block)
   651  			pos := entities.NewEmptyPosition(market.ID, party.ID)
   652  			volume := int64(100)
   653  			pos.OpenVolume = volume
   654  			pos.PendingOpenVolume = volume
   655  			pos.VegaTime = block.VegaTime
   656  			pos.RealisedPnl = decimal.New(0, 0)
   657  			pos.PendingRealisedPnl = decimal.New(0, 0)
   658  			pos.UnrealisedPnl = decimal.New(0, 0)
   659  			pos.PendingUnrealisedPnl = decimal.New(0, 0)
   660  			pos.AverageEntryPrice = decimal.New(0, 0)
   661  			pos.PendingAverageEntryPrice = decimal.New(0, 0)
   662  			pos.AverageEntryMarketPrice = decimal.New(0, 0)
   663  			pos.PendingAverageEntryMarketPrice = decimal.New(0, 0)
   664  			pos.Adjustment = decimal.New(0, 0)
   665  			pos.Loss = decimal.New(0, 0)
   666  			pos.TxHash = generateTxHash()
   667  			pos.DistressedStatus = entities.PositionStatus(s)
   668  			require.NoError(t, ps.Add(ctx, pos))
   669  			_, err := ps.Flush(ctx)
   670  			require.NoError(t, err)
   671  
   672  			got, err := ps.GetByTxHash(ctx, pos.TxHash)
   673  			require.NoError(t, err)
   674  			assert.Len(t, got, 1)
   675  			assert.Equal(t, pos.DistressedStatus, got[0].DistressedStatus)
   676  		})
   677  	}
   678  }