code.vegaprotocol.io/vega@v0.79.0/datanode/sqlstore/oracle_spec_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  	dstypes "code.vegaprotocol.io/vega/core/datasource/common"
    25  	"code.vegaprotocol.io/vega/datanode/entities"
    26  	"code.vegaprotocol.io/vega/datanode/sqlstore"
    27  	"code.vegaprotocol.io/vega/protos/vega"
    28  	vegapb "code.vegaprotocol.io/vega/protos/vega"
    29  	datapb "code.vegaprotocol.io/vega/protos/vega/data/v1"
    30  
    31  	"github.com/stretchr/testify/assert"
    32  	"github.com/stretchr/testify/require"
    33  	"google.golang.org/protobuf/types/known/structpb"
    34  )
    35  
    36  func TestOracleSpec(t *testing.T) {
    37  	t.Run("Upsert should insert an OracleSpec when the id does not exist in the current block", testInsertIntoNewBlock)
    38  	t.Run("Upsert should update an OracleSpec when the id already exists in the current block", testUpdateExistingInBlock)
    39  	t.Run("GetSpecByID should retrieve the latest version of the specified OracleSpec", testGetSpecByID)
    40  	t.Run("GetByTxHash", testGetSpecByTxHash)
    41  }
    42  
    43  func setupOracleSpecTest(t *testing.T) (*sqlstore.Blocks, *sqlstore.OracleSpec, sqlstore.Connection) {
    44  	t.Helper()
    45  
    46  	bs := sqlstore.NewBlocks(connectionSource)
    47  	os := sqlstore.NewOracleSpec(connectionSource)
    48  
    49  	return bs, os, connectionSource
    50  }
    51  
    52  func testInsertIntoNewBlock(t *testing.T) {
    53  	ctx := tempTransaction(t)
    54  
    55  	bs, os, conn := setupOracleSpecTest(t)
    56  
    57  	var rowCount int
    58  	assert.NoError(t, conn.QueryRow(ctx, "select count(*) from oracle_specs").Scan(&rowCount))
    59  	assert.Equal(t, 0, rowCount)
    60  
    61  	block := addTestBlock(t, ctx, bs)
    62  	specProtos := getTestSpecs()
    63  
    64  	proto := specProtos[0]
    65  	data := entities.OracleSpecFromProto(proto, generateTxHash(), block.VegaTime)
    66  	assert.NoError(t, os.Upsert(ctx, data))
    67  
    68  	assert.NoError(t, conn.QueryRow(ctx, "select count(*) from oracle_specs").Scan(&rowCount))
    69  	assert.Equal(t, 1, rowCount)
    70  
    71  	proto = specProtos[4]
    72  	data = entities.OracleSpecFromProto(proto, generateTxHash(), block.VegaTime)
    73  	assert.NoError(t, os.Upsert(ctx, data))
    74  
    75  	assert.NoError(t, conn.QueryRow(ctx, "select count(*) from oracle_specs").Scan(&rowCount))
    76  	assert.Equal(t, 2, rowCount)
    77  }
    78  
    79  func testUpdateExistingInBlock(t *testing.T) {
    80  	ctx := tempTransaction(t)
    81  
    82  	bs, os, conn := setupOracleSpecTest(t)
    83  
    84  	var rowCount int
    85  	assert.NoError(t, conn.QueryRow(ctx, "select count(*) from oracle_specs").Scan(&rowCount))
    86  	assert.Equal(t, 0, rowCount)
    87  
    88  	block := addTestBlock(t, ctx, bs)
    89  	specProtos := getTestSpecs()
    90  
    91  	proto := specProtos[0]
    92  	data := entities.OracleSpecFromProto(proto, generateTxHash(), block.VegaTime)
    93  	assert.NoError(t, os.Upsert(ctx, data))
    94  
    95  	data.ExternalDataSourceSpec.Spec.Status = entities.OracleSpecDeactivated
    96  	assert.NoError(t, os.Upsert(ctx, data))
    97  
    98  	assert.NoError(t, conn.QueryRow(ctx, "select count(*) from oracle_specs").Scan(&rowCount))
    99  	assert.Equal(t, 1, rowCount)
   100  
   101  	proto = specProtos[4]
   102  	data = entities.OracleSpecFromProto(proto, generateTxHash(), block.VegaTime)
   103  	assert.NoError(t, os.Upsert(ctx, data))
   104  
   105  	data.ExternalDataSourceSpec.Spec.Status = entities.OracleSpecDeactivated
   106  	assert.NoError(t, os.Upsert(ctx, data))
   107  
   108  	assert.NoError(t, conn.QueryRow(ctx, "select count(*) from oracle_specs").Scan(&rowCount))
   109  	assert.Equal(t, 2, rowCount)
   110  }
   111  
   112  func testGetSpecByID(t *testing.T) {
   113  	ctx := tempTransaction(t)
   114  
   115  	bs, os, conn := setupOracleSpecTest(t)
   116  
   117  	var rowCount int
   118  	assert.NoError(t, conn.QueryRow(ctx, "select count(*) from oracle_specs").Scan(&rowCount))
   119  	assert.Equal(t, 0, rowCount)
   120  
   121  	block := addTestBlock(t, ctx, bs)
   122  	specProtos := getTestSpecs()
   123  
   124  	for _, proto := range specProtos {
   125  		data := entities.OracleSpecFromProto(proto, generateTxHash(), block.VegaTime)
   126  		assert.NoError(t, os.Upsert(ctx, data))
   127  	}
   128  
   129  	assert.NoError(t, conn.QueryRow(ctx, "select count(*) from oracle_specs").Scan(&rowCount))
   130  	assert.Equal(t, 6, rowCount)
   131  
   132  	got, err := os.GetSpecByID(ctx, "DEADBEEF")
   133  	require.NoError(t, err)
   134  
   135  	want := entities.DataSourceSpecFromProto(specProtos[0].ExternalDataSourceSpec.Spec, got.ExternalDataSourceSpec.Spec.TxHash, block.VegaTime)
   136  
   137  	// truncate the time to microseconds as postgres doesn't support nanosecond granularity.
   138  	want.UpdatedAt = want.UpdatedAt.Truncate(time.Microsecond)
   139  	want.CreatedAt = want.CreatedAt.Truncate(time.Microsecond)
   140  	s := got.ExternalDataSourceSpec.Spec
   141  	assert.Equal(t, want, s)
   142  
   143  	got, err = os.GetSpecByID(ctx, "beef000d")
   144  	require.NoError(t, err)
   145  
   146  	want = entities.DataSourceSpecFromProto(specProtos[4].ExternalDataSourceSpec.Spec, got.ExternalDataSourceSpec.Spec.TxHash, block.VegaTime)
   147  	want.UpdatedAt = want.UpdatedAt.Truncate(time.Microsecond)
   148  	want.CreatedAt = want.CreatedAt.Truncate(time.Microsecond)
   149  	s = got.ExternalDataSourceSpec.Spec
   150  	assert.Equal(t, want, s)
   151  
   152  	got, err = os.GetSpecByID(ctx, "beef000e")
   153  	require.NoError(t, err)
   154  
   155  	want = entities.DataSourceSpecFromProto(specProtos[5].ExternalDataSourceSpec.Spec, got.ExternalDataSourceSpec.Spec.TxHash, block.VegaTime)
   156  	want.UpdatedAt = want.UpdatedAt.Truncate(time.Microsecond)
   157  	want.CreatedAt = want.CreatedAt.Truncate(time.Microsecond)
   158  	s = got.ExternalDataSourceSpec.Spec
   159  	assert.Equal(t, want, s)
   160  }
   161  
   162  func testGetSpecByTxHash(t *testing.T) {
   163  	ctx := tempTransaction(t)
   164  
   165  	bs, os, conn := setupOracleSpecTest(t)
   166  
   167  	var rowCount int
   168  	assert.NoError(t, conn.QueryRow(ctx, "select count(*) from oracle_specs").Scan(&rowCount))
   169  	assert.Equal(t, 0, rowCount)
   170  
   171  	block := addTestBlock(t, ctx, bs)
   172  	specProtos := getTestSpecs()
   173  
   174  	specs := make([]entities.OracleSpec, 0, len(specProtos))
   175  	for _, proto := range specProtos {
   176  		data := entities.OracleSpecFromProto(proto, generateTxHash(), block.VegaTime)
   177  		assert.NoError(t, os.Upsert(ctx, data))
   178  
   179  		specs = append(specs, *data)
   180  	}
   181  
   182  	assert.NoError(t, conn.QueryRow(ctx, "select count(*) from oracle_specs").Scan(&rowCount))
   183  	assert.Equal(t, 6, rowCount)
   184  
   185  	foundSpecs, err := os.GetByTxHash(ctx, specs[0].ExternalDataSourceSpec.Spec.TxHash)
   186  	require.NoError(t, err)
   187  
   188  	got := foundSpecs[0]
   189  	want := entities.DataSourceSpecFromProto(specProtos[0].ExternalDataSourceSpec.Spec, got.ExternalDataSourceSpec.Spec.TxHash, block.VegaTime)
   190  
   191  	// truncate the time to microseconds as postgres doesn't support nanosecond granularity.
   192  	want.UpdatedAt = want.UpdatedAt.Truncate(time.Microsecond)
   193  	want.CreatedAt = want.CreatedAt.Truncate(time.Microsecond)
   194  	s := got.ExternalDataSourceSpec.Spec
   195  	assert.Equal(t, want, s)
   196  }
   197  
   198  func getTestSpecs() []*vegapb.OracleSpec {
   199  	pk1 := dstypes.CreateSignerFromString("b105f00d", dstypes.SignerTypePubKey)
   200  	pk2 := dstypes.CreateSignerFromString("0x124dd8a6044ef048614aea0aac86643a8ae1312d", dstypes.SignerTypeEthAddress)
   201  
   202  	timeNow := uint64(time.Now().UnixNano())
   203  	return []*vegapb.OracleSpec{
   204  		{
   205  			ExternalDataSourceSpec: &vegapb.ExternalDataSourceSpec{
   206  				Spec: &vegapb.DataSourceSpec{
   207  					Id:        "deadbeef",
   208  					CreatedAt: time.Now().UnixNano(),
   209  					UpdatedAt: time.Now().UnixNano(),
   210  					Data: vegapb.NewDataSourceDefinition(
   211  						vegapb.DataSourceContentTypeOracle,
   212  					).SetOracleConfig(
   213  						&vega.DataSourceDefinitionExternal_Oracle{
   214  							Oracle: &vegapb.DataSourceSpecConfiguration{
   215  								Signers: []*datapb.Signer{pk1.IntoProto(), pk2.IntoProto()},
   216  								Filters: []*datapb.Filter{
   217  									{
   218  										Key: &datapb.PropertyKey{
   219  											Name: "Ticker",
   220  											Type: datapb.PropertyKey_TYPE_STRING,
   221  										},
   222  										Conditions: []*datapb.Condition{
   223  											{
   224  												Operator: datapb.Condition_OPERATOR_EQUALS,
   225  												Value:    "USDETH",
   226  											},
   227  										},
   228  									},
   229  								},
   230  							},
   231  						},
   232  					),
   233  					Status: vegapb.DataSourceSpec_STATUS_ACTIVE,
   234  				},
   235  			},
   236  		},
   237  		{
   238  			ExternalDataSourceSpec: &vegapb.ExternalDataSourceSpec{
   239  				Spec: &vegapb.DataSourceSpec{
   240  					Id:        "cafed00d",
   241  					CreatedAt: time.Now().UnixNano(),
   242  					UpdatedAt: time.Now().UnixNano(),
   243  					Data: vegapb.NewDataSourceDefinition(
   244  						vegapb.DataSourceContentTypeOracle,
   245  					).SetOracleConfig(
   246  						&vega.DataSourceDefinitionExternal_Oracle{
   247  							Oracle: &vegapb.DataSourceSpecConfiguration{
   248  								Signers: []*datapb.Signer{pk1.IntoProto(), pk2.IntoProto()},
   249  								Filters: []*datapb.Filter{
   250  									{
   251  										Key: &datapb.PropertyKey{
   252  											Name: "Ticker",
   253  											Type: datapb.PropertyKey_TYPE_STRING,
   254  										},
   255  										Conditions: []*datapb.Condition{
   256  											{
   257  												Operator: datapb.Condition_OPERATOR_EQUALS,
   258  												Value:    "USDBTC",
   259  											},
   260  										},
   261  									},
   262  								},
   263  							},
   264  						},
   265  					),
   266  					Status: vegapb.DataSourceSpec_STATUS_ACTIVE,
   267  				},
   268  			},
   269  		},
   270  		{
   271  			ExternalDataSourceSpec: &vegapb.ExternalDataSourceSpec{
   272  				Spec: &vegapb.DataSourceSpec{
   273  					Id:        "deadbaad",
   274  					CreatedAt: time.Now().UnixNano(),
   275  					UpdatedAt: time.Now().UnixNano(),
   276  					Data: vegapb.NewDataSourceDefinition(
   277  						vegapb.DataSourceContentTypeOracle,
   278  					).SetOracleConfig(
   279  						&vega.DataSourceDefinitionExternal_Oracle{
   280  							Oracle: &vegapb.DataSourceSpecConfiguration{
   281  								Signers: []*datapb.Signer{pk1.IntoProto(), pk2.IntoProto()},
   282  								Filters: []*datapb.Filter{
   283  									{
   284  										Key: &datapb.PropertyKey{
   285  											Name: "Ticker",
   286  											Type: datapb.PropertyKey_TYPE_STRING,
   287  										},
   288  										Conditions: []*datapb.Condition{
   289  											{
   290  												Operator: datapb.Condition_OPERATOR_EQUALS,
   291  												Value:    "USDSOL",
   292  											},
   293  										},
   294  									},
   295  								},
   296  							},
   297  						},
   298  					),
   299  					Status: vegapb.DataSourceSpec_STATUS_ACTIVE,
   300  				},
   301  			},
   302  		},
   303  		{
   304  			ExternalDataSourceSpec: &vegapb.ExternalDataSourceSpec{
   305  				Spec: &vegapb.DataSourceSpec{
   306  					Id:        "beefbeef",
   307  					CreatedAt: time.Now().UnixNano(),
   308  					UpdatedAt: time.Now().UnixNano(),
   309  					Data: vegapb.NewDataSourceDefinition(
   310  						vegapb.DataSourceContentTypeInternalTimeTermination,
   311  					).SetTimeTriggerConditionConfig(
   312  						[]*datapb.Condition{
   313  							{
   314  								Operator: datapb.Condition_OPERATOR_EQUALS,
   315  								Value:    fmt.Sprintf("%v", time.Now().UnixNano()),
   316  							},
   317  						},
   318  					),
   319  					Status: vegapb.DataSourceSpec_STATUS_ACTIVE,
   320  				},
   321  			},
   322  		},
   323  		{
   324  			ExternalDataSourceSpec: &vegapb.ExternalDataSourceSpec{
   325  				Spec: &vegapb.DataSourceSpec{
   326  					Id:        "beef000d",
   327  					CreatedAt: time.Now().UnixNano(),
   328  					UpdatedAt: time.Now().UnixNano(),
   329  					Data: vegapb.NewDataSourceDefinition(
   330  						vegapb.DataSourceContentTypeEthOracle,
   331  					).SetOracleConfig(
   332  						&vega.DataSourceDefinitionExternal_EthOracle{
   333  							EthOracle: &vegapb.EthCallSpec{
   334  								Address: "some-eth-address",
   335  								Abi:     "{\"string-value\"}",
   336  								Method:  "test-method",
   337  								Args: []*structpb.Value{
   338  									{
   339  										Kind: &structpb.Value_StringValue{
   340  											StringValue: "string-arg",
   341  										},
   342  									},
   343  								},
   344  								Trigger: &vegapb.EthCallTrigger{
   345  									Trigger: &vegapb.EthCallTrigger_TimeTrigger{
   346  										TimeTrigger: &vegapb.EthTimeTrigger{
   347  											Initial: &timeNow,
   348  										},
   349  									},
   350  								},
   351  								RequiredConfirmations: 256,
   352  								Filters: []*datapb.Filter{
   353  									{
   354  										Key: &datapb.PropertyKey{
   355  											Name: "test-key-name-0",
   356  											Type: dstypes.SpecPropertyKeyType(2),
   357  										},
   358  									},
   359  								},
   360  							},
   361  						},
   362  					),
   363  					Status: vegapb.DataSourceSpec_STATUS_ACTIVE,
   364  				},
   365  			},
   366  		},
   367  		{
   368  			ExternalDataSourceSpec: &vegapb.ExternalDataSourceSpec{
   369  				Spec: &vegapb.DataSourceSpec{
   370  					Id:        "beef000e",
   371  					CreatedAt: time.Now().UnixNano(),
   372  					UpdatedAt: time.Now().UnixNano(),
   373  					Data: vegapb.NewDataSourceDefinition(
   374  						vegapb.DataSourceContentTypeEthOracle,
   375  					).SetOracleConfig(
   376  						&vega.DataSourceDefinitionExternal_EthOracle{
   377  							EthOracle: &vegapb.EthCallSpec{
   378  								Address: "some-eth-address",
   379  								Abi:     "{\"string-value\"}",
   380  								Method:  "test-method",
   381  								// Args: []*structpb.Value{},
   382  								Trigger: &vegapb.EthCallTrigger{
   383  									Trigger: &vegapb.EthCallTrigger_TimeTrigger{
   384  										TimeTrigger: &vegapb.EthTimeTrigger{
   385  											Initial: &timeNow,
   386  										},
   387  									},
   388  								},
   389  								RequiredConfirmations: 256,
   390  								Filters: []*datapb.Filter{
   391  									{
   392  										Key: &datapb.PropertyKey{
   393  											Name: "test-key-name-0",
   394  											Type: dstypes.SpecPropertyKeyType(2),
   395  										},
   396  									},
   397  								},
   398  							},
   399  						},
   400  					),
   401  					Status: vegapb.DataSourceSpec_STATUS_ACTIVE,
   402  				},
   403  			},
   404  		},
   405  	}
   406  }
   407  
   408  func TestOracleSpec_GetSpecsWithCursorPagination(t *testing.T) {
   409  	t.Run("should return the request spec of spec id is requested", testOracleSpecPaginationGetSpecID)
   410  	t.Run("should return all specs if no spec id and no pagination is provided", testOracleSpecPaginationNoPagination)
   411  	t.Run("should return the first page if no spec id and first is provided", testOracleSpecPaginationFirst)
   412  	t.Run("should return the last page if no spec id and last is provided", testOracleSpecPaginationLast)
   413  	t.Run("should return the requested page if no spec id and first and after is provided", testOracleSpecPaginationFirstAfter)
   414  	t.Run("should return the requested page if no spec id and last and before is provided", testOracleSpecPaginationLastBefore)
   415  
   416  	t.Run("should return all specs if no spec id and no pagination is provided - newest first", testOracleSpecPaginationNoPaginationNewestFirst)
   417  	t.Run("should return the first page if no spec id and first is provided - newest first", testOracleSpecPaginationFirstNewestFirst)
   418  	t.Run("should return the last page if no spec id and last is provided - newest first", testOracleSpecPaginationLastNewestFirst)
   419  	t.Run("should return the requested page if no spec id and first and after is provided - newest first", testOracleSpecPaginationFirstAfterNewestFirst)
   420  	t.Run("should return the requested page if no spec id and last and before is provided - newest first", testOracleSpecPaginationLastBeforeNewestFirst)
   421  }
   422  
   423  func createOracleSpecPaginationTestData(t *testing.T, ctx context.Context, bs *sqlstore.Blocks, os *sqlstore.OracleSpec) []entities.OracleSpec {
   424  	t.Helper()
   425  	specs := make([]entities.OracleSpec, 0, 10)
   426  
   427  	block := addTestBlockForTime(t, ctx, bs, time.Now().Truncate(time.Second))
   428  
   429  	for i := 0; i < 10; i++ {
   430  		pubKey := dstypes.CreateSignerFromString(GenerateID(), dstypes.SignerTypePubKey)
   431  
   432  		spec := entities.OracleSpec{
   433  			ExternalDataSourceSpec: &entities.ExternalDataSourceSpec{
   434  				Spec: &entities.DataSourceSpec{
   435  					ID:        entities.SpecID(fmt.Sprintf("deadbeef%02d", i+1)),
   436  					CreatedAt: time.Now().Truncate(time.Microsecond),
   437  					UpdatedAt: time.Now().Truncate(time.Microsecond),
   438  					Data: &entities.DataSourceDefinition{
   439  						DataSourceDefinition: &vegapb.DataSourceDefinition{
   440  							SourceType: &vegapb.DataSourceDefinition_External{
   441  								External: &vegapb.DataSourceDefinitionExternal{
   442  									SourceType: &vegapb.DataSourceDefinitionExternal_Oracle{
   443  										Oracle: &vegapb.DataSourceSpecConfiguration{
   444  											Signers: []*datapb.Signer{
   445  												{
   446  													Signer: &datapb.Signer_PubKey{
   447  														PubKey: pubKey.IntoProto().GetPubKey(),
   448  													},
   449  												},
   450  											},
   451  											Filters: nil,
   452  										},
   453  									},
   454  								},
   455  							},
   456  						},
   457  					},
   458  					Status:   entities.OracleSpecActive,
   459  					VegaTime: block.VegaTime,
   460  				},
   461  			},
   462  		}
   463  
   464  		err := os.Upsert(ctx, &spec)
   465  		require.NoError(t, err)
   466  		specs = append(specs, spec)
   467  	}
   468  
   469  	return specs
   470  }
   471  
   472  func testOracleSpecPaginationGetSpecID(t *testing.T) {
   473  	ctx := tempTransaction(t)
   474  
   475  	bs, os, _ := setupOracleSpecTest(t)
   476  	specs := createOracleSpecPaginationTestData(t, ctx, bs, os)
   477  
   478  	got, pageInfo, err := os.GetSpecsWithCursorPagination(ctx, "deadbeef05", entities.CursorPagination{})
   479  	require.NoError(t, err)
   480  
   481  	assert.Equal(t, specs[4], got[0])
   482  	assert.Equal(t, entities.PageInfo{
   483  		HasNextPage:     false,
   484  		HasPreviousPage: false,
   485  		StartCursor:     specs[4].Cursor().Encode(),
   486  		EndCursor:       specs[4].Cursor().Encode(),
   487  	}, pageInfo)
   488  }
   489  
   490  func testOracleSpecPaginationNoPagination(t *testing.T) {
   491  	ctx := tempTransaction(t)
   492  
   493  	bs, os, _ := setupOracleSpecTest(t)
   494  	specs := createOracleSpecPaginationTestData(t, ctx, bs, os)
   495  	got, pageInfo, err := os.GetSpecsWithCursorPagination(ctx, "", entities.CursorPagination{})
   496  	require.NoError(t, err)
   497  
   498  	assert.Equal(t, specs, got)
   499  	assert.Equal(t, entities.PageInfo{
   500  		HasNextPage:     false,
   501  		HasPreviousPage: false,
   502  		StartCursor:     specs[0].Cursor().Encode(),
   503  		EndCursor:       specs[9].Cursor().Encode(),
   504  	}, pageInfo)
   505  }
   506  
   507  func testOracleSpecPaginationFirst(t *testing.T) {
   508  	ctx := tempTransaction(t)
   509  
   510  	bs, os, _ := setupOracleSpecTest(t)
   511  	specs := createOracleSpecPaginationTestData(t, ctx, bs, os)
   512  	first := int32(3)
   513  	pagination, err := entities.NewCursorPagination(&first, nil, nil, nil, false)
   514  	require.NoError(t, err)
   515  
   516  	got, pageInfo, err := os.GetSpecsWithCursorPagination(ctx, "", pagination)
   517  	require.NoError(t, err)
   518  
   519  	assert.Equal(t, specs[:3], got)
   520  	assert.Equal(t, entities.PageInfo{
   521  		HasNextPage:     true,
   522  		HasPreviousPage: false,
   523  		StartCursor:     specs[0].Cursor().Encode(),
   524  		EndCursor:       specs[2].Cursor().Encode(),
   525  	}, pageInfo)
   526  }
   527  
   528  func testOracleSpecPaginationLast(t *testing.T) {
   529  	ctx := tempTransaction(t)
   530  
   531  	bs, os, _ := setupOracleSpecTest(t)
   532  	specs := createOracleSpecPaginationTestData(t, ctx, bs, os)
   533  	last := int32(3)
   534  	pagination, err := entities.NewCursorPagination(nil, nil, &last, nil, false)
   535  	require.NoError(t, err)
   536  
   537  	got, pageInfo, err := os.GetSpecsWithCursorPagination(ctx, "", pagination)
   538  	require.NoError(t, err)
   539  
   540  	assert.Equal(t, specs[7:], got)
   541  	assert.Equal(t, entities.PageInfo{
   542  		HasNextPage:     false,
   543  		HasPreviousPage: true,
   544  		StartCursor:     specs[7].Cursor().Encode(),
   545  		EndCursor:       specs[9].Cursor().Encode(),
   546  	}, pageInfo)
   547  }
   548  
   549  func testOracleSpecPaginationFirstAfter(t *testing.T) {
   550  	ctx := tempTransaction(t)
   551  
   552  	bs, os, _ := setupOracleSpecTest(t)
   553  	specs := createOracleSpecPaginationTestData(t, ctx, bs, os)
   554  	first := int32(3)
   555  	after := specs[2].Cursor().Encode()
   556  	pagination, err := entities.NewCursorPagination(&first, &after, nil, nil, false)
   557  	require.NoError(t, err)
   558  
   559  	got, pageInfo, err := os.GetSpecsWithCursorPagination(ctx, "", pagination)
   560  	require.NoError(t, err)
   561  
   562  	assert.Equal(t, specs[3:6], got)
   563  	assert.Equal(t, entities.PageInfo{
   564  		HasNextPage:     true,
   565  		HasPreviousPage: true,
   566  		StartCursor:     specs[3].Cursor().Encode(),
   567  		EndCursor:       specs[5].Cursor().Encode(),
   568  	}, pageInfo)
   569  }
   570  
   571  func testOracleSpecPaginationLastBefore(t *testing.T) {
   572  	ctx := tempTransaction(t)
   573  
   574  	bs, os, _ := setupOracleSpecTest(t)
   575  	specs := createOracleSpecPaginationTestData(t, ctx, bs, os)
   576  	last := int32(3)
   577  	before := specs[7].Cursor().Encode()
   578  	pagination, err := entities.NewCursorPagination(nil, nil, &last, &before, false)
   579  	require.NoError(t, err)
   580  
   581  	got, pageInfo, err := os.GetSpecsWithCursorPagination(ctx, "", pagination)
   582  	require.NoError(t, err)
   583  
   584  	assert.Equal(t, specs[4:7], got)
   585  	assert.Equal(t, entities.PageInfo{
   586  		HasNextPage:     true,
   587  		HasPreviousPage: true,
   588  		StartCursor:     specs[4].Cursor().Encode(),
   589  		EndCursor:       specs[6].Cursor().Encode(),
   590  	}, pageInfo)
   591  }
   592  
   593  func testOracleSpecPaginationNoPaginationNewestFirst(t *testing.T) {
   594  	ctx := tempTransaction(t)
   595  
   596  	bs, os, _ := setupOracleSpecTest(t)
   597  	specs := entities.ReverseSlice(createOracleSpecPaginationTestData(t, ctx, bs, os))
   598  	got, pageInfo, err := os.GetSpecsWithCursorPagination(ctx, "", entities.CursorPagination{NewestFirst: true})
   599  	require.NoError(t, err)
   600  
   601  	assert.Equal(t, specs, got)
   602  	assert.Equal(t, entities.PageInfo{
   603  		HasNextPage:     false,
   604  		HasPreviousPage: false,
   605  		StartCursor:     specs[0].Cursor().Encode(),
   606  		EndCursor:       specs[9].Cursor().Encode(),
   607  	}, pageInfo)
   608  }
   609  
   610  func testOracleSpecPaginationFirstNewestFirst(t *testing.T) {
   611  	ctx := tempTransaction(t)
   612  
   613  	bs, os, _ := setupOracleSpecTest(t)
   614  	specs := entities.ReverseSlice(createOracleSpecPaginationTestData(t, ctx, bs, os))
   615  	first := int32(3)
   616  	pagination, err := entities.NewCursorPagination(&first, nil, nil, nil, true)
   617  	require.NoError(t, err)
   618  
   619  	got, pageInfo, err := os.GetSpecsWithCursorPagination(ctx, "", pagination)
   620  	require.NoError(t, err)
   621  
   622  	assert.Equal(t, specs[:3], got)
   623  	assert.Equal(t, entities.PageInfo{
   624  		HasNextPage:     true,
   625  		HasPreviousPage: false,
   626  		StartCursor:     specs[0].Cursor().Encode(),
   627  		EndCursor:       specs[2].Cursor().Encode(),
   628  	}, pageInfo)
   629  }
   630  
   631  func testOracleSpecPaginationLastNewestFirst(t *testing.T) {
   632  	ctx := tempTransaction(t)
   633  
   634  	bs, os, _ := setupOracleSpecTest(t)
   635  	specs := entities.ReverseSlice(createOracleSpecPaginationTestData(t, ctx, bs, os))
   636  	last := int32(3)
   637  	pagination, err := entities.NewCursorPagination(nil, nil, &last, nil, true)
   638  	require.NoError(t, err)
   639  
   640  	got, pageInfo, err := os.GetSpecsWithCursorPagination(ctx, "", pagination)
   641  	require.NoError(t, err)
   642  
   643  	assert.Equal(t, specs[7:], got)
   644  	assert.Equal(t, entities.PageInfo{
   645  		HasNextPage:     false,
   646  		HasPreviousPage: true,
   647  		StartCursor:     specs[7].Cursor().Encode(),
   648  		EndCursor:       specs[9].Cursor().Encode(),
   649  	}, pageInfo)
   650  }
   651  
   652  func testOracleSpecPaginationFirstAfterNewestFirst(t *testing.T) {
   653  	ctx := tempTransaction(t)
   654  
   655  	bs, os, _ := setupOracleSpecTest(t)
   656  	specs := entities.ReverseSlice(createOracleSpecPaginationTestData(t, ctx, bs, os))
   657  	first := int32(3)
   658  	after := specs[2].Cursor().Encode()
   659  	pagination, err := entities.NewCursorPagination(&first, &after, nil, nil, true)
   660  	require.NoError(t, err)
   661  
   662  	got, pageInfo, err := os.GetSpecsWithCursorPagination(ctx, "", pagination)
   663  	require.NoError(t, err)
   664  
   665  	assert.Equal(t, specs[3:6], got)
   666  	assert.Equal(t, entities.PageInfo{
   667  		HasNextPage:     true,
   668  		HasPreviousPage: true,
   669  		StartCursor:     specs[3].Cursor().Encode(),
   670  		EndCursor:       specs[5].Cursor().Encode(),
   671  	}, pageInfo)
   672  }
   673  
   674  func testOracleSpecPaginationLastBeforeNewestFirst(t *testing.T) {
   675  	ctx := tempTransaction(t)
   676  
   677  	bs, os, _ := setupOracleSpecTest(t)
   678  	specs := entities.ReverseSlice(createOracleSpecPaginationTestData(t, ctx, bs, os))
   679  	last := int32(3)
   680  	before := specs[7].Cursor().Encode()
   681  	pagination, err := entities.NewCursorPagination(nil, nil, &last, &before, true)
   682  	require.NoError(t, err)
   683  
   684  	got, pageInfo, err := os.GetSpecsWithCursorPagination(ctx, "", pagination)
   685  	require.NoError(t, err)
   686  
   687  	assert.Equal(t, specs[4:7], got)
   688  	assert.Equal(t, entities.PageInfo{
   689  		HasNextPage:     true,
   690  		HasPreviousPage: true,
   691  		StartCursor:     specs[4].Cursor().Encode(),
   692  		EndCursor:       specs[6].Cursor().Encode(),
   693  	}, pageInfo)
   694  }
   695  
   696  func TestOracleSpecStatusEnum(t *testing.T) {
   697  	var oracleSpecStatus vegapb.DataSourceSpec_Status
   698  	states := getEnums(t, oracleSpecStatus)
   699  	assert.Len(t, states, 3)
   700  	for s, state := range states {
   701  		t.Run(state, func(t *testing.T) {
   702  			ctx := tempTransaction(t)
   703  
   704  			bs, os, _ := setupOracleSpecTest(t)
   705  
   706  			block := addTestBlock(t, ctx, bs)
   707  			specProtos := getTestSpecs()
   708  
   709  			proto := specProtos[0]
   710  			proto.ExternalDataSourceSpec.Spec.Status = vegapb.DataSourceSpec_Status(s)
   711  			data := entities.OracleSpecFromProto(proto, generateTxHash(), block.VegaTime)
   712  			assert.NoError(t, os.Upsert(ctx, data))
   713  
   714  			got, err := os.GetByTxHash(ctx, data.ExternalDataSourceSpec.Spec.TxHash)
   715  			require.NoError(t, err)
   716  			assert.Len(t, got, 1)
   717  			assert.Equal(t, data.ExternalDataSourceSpec.Spec.Status, got[0].ExternalDataSourceSpec.Spec.Status)
   718  		})
   719  	}
   720  }