code.vegaprotocol.io/vega@v0.79.0/datanode/sqlstore/proposals_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/libs/num" 28 "code.vegaprotocol.io/vega/protos/vega" 29 datav1 "code.vegaprotocol.io/vega/protos/vega/data/v1" 30 31 "github.com/google/go-cmp/cmp" 32 "github.com/google/go-cmp/cmp/cmpopts" 33 "github.com/stretchr/testify/assert" 34 "github.com/stretchr/testify/require" 35 ) 36 37 var ( 38 testProposals []entities.Proposal 39 proposalTestParties []entities.Party 40 ) 41 42 func addTestProposal( 43 t *testing.T, 44 ctx context.Context, 45 ps *sqlstore.Proposals, 46 id string, 47 party entities.Party, 48 reference string, 49 block entities.Block, 50 state entities.ProposalState, 51 rationale entities.ProposalRationale, 52 terms entities.ProposalTerms, 53 reason entities.ProposalError, 54 batchID *string, 55 batchTerms entities.BatchProposalTerms, 56 ) entities.Proposal { 57 t.Helper() 58 59 var batchProposalID entities.ProposalID 60 if batchID != nil { 61 batchProposalID = entities.ProposalID(*batchID) 62 } 63 64 p := entities.Proposal{ 65 ID: entities.ProposalID(id), 66 BatchID: batchProposalID, 67 PartyID: party.ID, 68 Reference: reference, 69 Terms: terms, 70 BatchTerms: batchTerms, 71 State: state, 72 VegaTime: block.VegaTime, 73 ProposalTime: block.VegaTime, 74 Rationale: rationale, 75 Reason: reason, 76 RequiredMajority: num.MustDecimalFromString("0.5"), 77 RequiredParticipation: num.MustDecimalFromString("0.7"), 78 RequiredLPMajority: nil, 79 RequiredLPParticipation: nil, 80 TxHash: generateTxHash(), 81 } 82 assert.NoError(t, ps.Add(ctx, p)) 83 return p 84 } 85 86 func proposalLessThan(x, y entities.Proposal) bool { 87 return x.ID.String() < y.ID.String() 88 } 89 90 func assertProposalsMatch(t *testing.T, expected, actual []entities.Proposal) { 91 t.Helper() 92 sortProposals := cmpopts.SortSlices(proposalLessThan) 93 ignoreProtoState := cmpopts.IgnoreUnexported( 94 vega.ProposalTerms{}, 95 vega.BatchProposalTerms{}, 96 vega.BatchProposalTermsChange{}, 97 vega.ProposalRationale{}, 98 vega.NewMarket{}, 99 vega.NewAsset{}, 100 vega.UpdateAsset{}, 101 vega.NewMarketConfiguration{}, 102 vega.SuccessorConfiguration{}, 103 ) 104 assert.Empty(t, cmp.Diff(actual, expected, sortProposals, ignoreProtoState)) 105 } 106 107 func assertProposalMatch(t *testing.T, expected, actual entities.Proposal) { 108 t.Helper() 109 ignoreProtoState := cmpopts.IgnoreUnexported( 110 vega.ProposalTerms{}, 111 vega.BatchProposalTerms{}, 112 vega.BatchProposalTermsChange{}, 113 vega.ProposalRationale{}, 114 vega.NewMarket{}, 115 vega.NewAsset{}, 116 vega.UpdateAsset{}, 117 vega.NewMarketConfiguration{}, 118 vega.SuccessorConfiguration{}, 119 ) 120 assert.Empty(t, cmp.Diff(actual, expected, ignoreProtoState)) 121 } 122 123 func TestProposals(t *testing.T) { 124 ctx := tempTransaction(t) 125 126 partyStore := sqlstore.NewParties(connectionSource) 127 propStore := sqlstore.NewProposals(connectionSource) 128 blockStore := sqlstore.NewBlocks(connectionSource) 129 block1 := addTestBlock(t, ctx, blockStore) 130 131 party1 := addTestParty(t, ctx, partyStore, block1) 132 party2 := addTestParty(t, ctx, partyStore, block1) 133 rationale1 := entities.ProposalRationale{ProposalRationale: &vega.ProposalRationale{Title: "myurl1.com", Description: "desc"}} 134 rationale2 := entities.ProposalRationale{ProposalRationale: &vega.ProposalRationale{Title: "myurl2.com", Description: "desc"}} 135 terms1 := entities.ProposalTerms{ProposalTerms: &vega.ProposalTerms{Change: &vega.ProposalTerms_NewMarket{NewMarket: &vega.NewMarket{}}}} 136 terms2 := entities.ProposalTerms{ProposalTerms: &vega.ProposalTerms{Change: &vega.ProposalTerms_NewAsset{NewAsset: &vega.NewAsset{}}}} 137 id1 := GenerateID() 138 id2 := GenerateID() 139 140 reference1 := GenerateID() 141 reference2 := GenerateID() 142 prop1 := addTestProposal(t, ctx, propStore, id1, party1, reference1, block1, entities.ProposalStateEnacted, rationale1, terms1, entities.ProposalErrorUnspecified, nil, entities.BatchProposalTerms{}) 143 prop2 := addTestProposal(t, ctx, propStore, id2, party2, reference2, block1, entities.ProposalStateEnacted, rationale2, terms2, entities.ProposalErrorUnspecified, nil, entities.BatchProposalTerms{}) 144 145 party1ID := party1.ID.String() 146 prop1ID := prop1.ID.String() 147 propType := &entities.ProposalTypeNewMarket 148 149 t.Run("GetById", func(t *testing.T) { 150 expected := prop1 151 actual, err := propStore.GetByID(ctx, prop1ID) 152 require.NoError(t, err) 153 assertProposalMatch(t, expected, actual) 154 }) 155 156 t.Run("GetByTxHash", func(t *testing.T) { 157 expected := prop1 158 actual, err := propStore.GetByTxHash(ctx, expected.TxHash) 159 require.NoError(t, err) 160 assertProposalMatch(t, expected, actual[0]) 161 162 expected = prop2 163 actual, err = propStore.GetByTxHash(ctx, expected.TxHash) 164 require.NoError(t, err) 165 assertProposalMatch(t, expected, actual[0]) 166 }) 167 168 t.Run("GetByReference", func(t *testing.T) { 169 expected := prop2 170 actual, err := propStore.GetByReference(ctx, prop2.Reference) 171 require.NoError(t, err) 172 assertProposalMatch(t, expected, actual) 173 }) 174 175 t.Run("GetInState", func(t *testing.T) { 176 enacted := entities.ProposalStateEnacted 177 expected := []entities.Proposal{prop1, prop2} 178 actual, _, err := propStore.Get(ctx, &enacted, nil, nil, entities.CursorPagination{}) 179 require.NoError(t, err) 180 assertProposalsMatch(t, expected, actual) 181 }) 182 183 t.Run("GetByParty", func(t *testing.T) { 184 expected := []entities.Proposal{prop1} 185 actual, _, err := propStore.Get(ctx, nil, &party1ID, nil, entities.CursorPagination{}) 186 require.NoError(t, err) 187 assertProposalsMatch(t, expected, actual) 188 }) 189 190 t.Run("GetByType", func(t *testing.T) { 191 expected := []entities.Proposal{prop1} 192 actual, _, err := propStore.Get(ctx, nil, nil, propType, entities.CursorPagination{}) 193 require.NoError(t, err) 194 assertProposalsMatch(t, expected, actual) 195 }) 196 197 t.Run("Add with proposal error", func(t *testing.T) { 198 propError := entities.ProposalInvalidPerpetualProduct 199 expected := addTestProposal(t, ctx, propStore, GenerateID(), party1, reference1, block1, entities.ProposalStateEnacted, rationale1, terms1, propError, nil, entities.BatchProposalTerms{}) 200 actual, err := propStore.GetByID(ctx, string(expected.ID)) 201 require.NoError(t, err) 202 assert.Equal(t, expected.Reason, actual.Reason) 203 }) 204 } 205 206 func newBatchProposalProposal() entities.BatchProposalTerms { 207 return entities.BatchProposalTerms{ 208 BatchProposalTerms: &vega.BatchProposalTerms{ 209 ClosingTimestamp: 10, 210 Changes: []*vega.BatchProposalTermsChange{ 211 { 212 EnactmentTimestamp: 20, 213 Change: &vega.BatchProposalTermsChange_NewMarket{NewMarket: &vega.NewMarket{}}, 214 }, 215 { 216 EnactmentTimestamp: 30, 217 Change: &vega.BatchProposalTermsChange_UpdateAsset{UpdateAsset: &vega.UpdateAsset{}}, 218 }, 219 }, 220 }, 221 } 222 } 223 224 func TestBatchProposals(t *testing.T) { 225 ctx := context.Background() 226 // We cannot use the tempTransaction for this test due to the fact that the connection gets blocked when 227 // we recursively look for proposals that belong in a batch. The use of the transaction prevents another connection being 228 // taken from the connection pool, and causes a conn is busy error, we therefore just use a background context for these 229 // tests, and make sure we clean up after ourselves instead of rolling back the transaction. 230 defer cleanupTestProposals(t) 231 232 partyStore := sqlstore.NewParties(connectionSource) 233 propStore := sqlstore.NewProposals(connectionSource) 234 blockStore := sqlstore.NewBlocks(connectionSource) 235 block1 := addTestBlock(t, ctx, blockStore) 236 237 party1 := addTestParty(t, ctx, partyStore, block1) 238 party2 := addTestParty(t, ctx, partyStore, block1) 239 rationale1 := entities.ProposalRationale{ProposalRationale: &vega.ProposalRationale{Title: "myurl1.com", Description: "desc"}} 240 rationale2 := entities.ProposalRationale{ProposalRationale: &vega.ProposalRationale{Title: "myurl2.com", Description: "desc"}} 241 terms1 := newBatchProposalProposal() 242 terms2 := newBatchProposalProposal() 243 id1 := GenerateID() 244 id2 := GenerateID() 245 subId1 := GenerateID() 246 subId2 := GenerateID() 247 subId3 := GenerateID() 248 subId4 := GenerateID() 249 250 now := time.Now() 251 252 reference1 := GenerateID() 253 reference2 := GenerateID() 254 reference3 := GenerateID() 255 reference4 := GenerateID() 256 reference5 := GenerateID() 257 reference6 := GenerateID() 258 prop1 := addTestProposal(t, ctx, propStore, id1, party1, reference1, block1, entities.ProposalStateEnacted, rationale1, 259 entities.ProposalTerms{}, entities.ProposalErrorUnspecified, nil, terms1) 260 prop1.Proposals = append(prop1.Proposals, 261 addTestProposal(t, ctx, propStore, subId1, party2, reference3, block1, entities.ProposalStateEnacted, rationale2, 262 entities.ProposalTerms{ProposalTerms: &vega.ProposalTerms{EnactmentTimestamp: now.Unix(), Change: &vega.ProposalTerms_NewMarket{NewMarket: &vega.NewMarket{}}}}, 263 entities.ProposalErrorUnspecified, &id1, entities.BatchProposalTerms{}, 264 ), 265 addTestProposal(t, ctx, propStore, subId2, party2, reference4, block1, entities.ProposalStateEnacted, rationale2, 266 entities.ProposalTerms{ProposalTerms: &vega.ProposalTerms{EnactmentTimestamp: now.Add(time.Second).Unix(), Change: &vega.ProposalTerms_UpdateAsset{UpdateAsset: &vega.UpdateAsset{}}}}, 267 entities.ProposalErrorUnspecified, &id1, entities.BatchProposalTerms{}, 268 ), 269 ) 270 271 prop2 := addTestProposal(t, ctx, propStore, id2, party2, reference2, block1, entities.ProposalStateEnacted, rationale2, 272 entities.ProposalTerms{}, entities.ProposalErrorUnspecified, nil, terms2) 273 prop2.Proposals = append(prop2.Proposals, 274 addTestProposal( 275 t, ctx, propStore, subId3, party2, reference5, block1, entities.ProposalStateEnacted, rationale2, 276 entities.ProposalTerms{ProposalTerms: &vega.ProposalTerms{EnactmentTimestamp: now.Unix(), Change: &vega.ProposalTerms_NewMarket{NewMarket: &vega.NewMarket{}}}}, 277 entities.ProposalErrorUnspecified, &id2, entities.BatchProposalTerms{}, 278 ), 279 addTestProposal( 280 t, ctx, propStore, subId4, party2, reference6, block1, entities.ProposalStateEnacted, rationale2, 281 entities.ProposalTerms{ProposalTerms: &vega.ProposalTerms{EnactmentTimestamp: now.Add(time.Second).Unix(), Change: &vega.ProposalTerms_UpdateAsset{UpdateAsset: &vega.UpdateAsset{}}}}, 282 entities.ProposalErrorUnspecified, &id2, entities.BatchProposalTerms{}, 283 ), 284 ) 285 286 party1ID := party1.ID.String() 287 prop1ID := prop1.ID.String() 288 propType := &entities.ProposalTypeNewMarket 289 290 t.Run("GetById batch", func(t *testing.T) { 291 expected := prop1 292 actual, err := propStore.GetByID(ctx, prop1ID) 293 require.NoError(t, err) 294 assertProposalMatch(t, expected, actual) 295 }) 296 297 t.Run("GetById proposal from batch returns the whole batch", func(t *testing.T) { 298 expected := prop1 299 actual, err := propStore.GetByID(ctx, string(expected.Proposals[0].ID)) 300 require.NoError(t, err) 301 assertProposalMatch(t, expected, actual) 302 }) 303 304 t.Run("GetByTxHash", func(t *testing.T) { 305 expected := prop1 306 actual, err := propStore.GetByTxHash(ctx, expected.TxHash) 307 require.NoError(t, err) 308 assertProposalMatch(t, expected, actual[0]) 309 310 expected = prop2 311 actual, err = propStore.GetByTxHash(ctx, expected.TxHash) 312 require.NoError(t, err) 313 assertProposalMatch(t, expected, actual[0]) 314 }) 315 316 t.Run("GetByReference batch", func(t *testing.T) { 317 expected := prop2 318 actual, err := propStore.GetByReference(ctx, expected.Reference) 319 require.NoError(t, err) 320 assertProposalMatch(t, expected, actual) 321 }) 322 323 t.Run("GetByReference proposal from batch returns the whole batch", func(t *testing.T) { 324 expected := prop2 325 actual, err := propStore.GetByReference(ctx, expected.Proposals[0].Reference) 326 require.NoError(t, err) 327 assertProposalMatch(t, expected, actual) 328 }) 329 330 t.Run("GetInState", func(t *testing.T) { 331 enacted := entities.ProposalStateEnacted 332 expected := []entities.Proposal{prop1, prop2} 333 actual, _, err := propStore.Get(ctx, &enacted, nil, nil, entities.CursorPagination{}) 334 require.NoError(t, err) 335 assertProposalsMatch(t, expected, actual) 336 }) 337 338 t.Run("GetByParty", func(t *testing.T) { 339 expected := []entities.Proposal{prop1} 340 actual, _, err := propStore.Get(ctx, nil, &party1ID, nil, entities.CursorPagination{}) 341 require.NoError(t, err) 342 assertProposalsMatch(t, expected, actual) 343 }) 344 345 t.Run("GetByType", func(t *testing.T) { 346 expected := []entities.Proposal{prop1, prop2} 347 actual, _, err := propStore.Get(ctx, nil, nil, propType, entities.CursorPagination{}) 348 require.NoError(t, err) 349 assertProposalsMatch(t, expected, actual) 350 }) 351 352 t.Run("Add with proposal error", func(t *testing.T) { 353 propError := entities.ProposalInvalidPerpetualProduct 354 expected := addTestProposal(t, ctx, propStore, GenerateID(), party1, reference1, block1, entities.ProposalStateEnacted, rationale1, entities.ProposalTerms{}, propError, nil, terms1) 355 actual, err := propStore.GetByID(ctx, string(expected.ID)) 356 require.NoError(t, err) 357 assert.Equal(t, expected.Reason, actual.Reason) 358 }) 359 } 360 361 func TestProposalCursorPagination(t *testing.T) { 362 ctx := context.Background() 363 ps := sqlstore.NewProposals(connectionSource) 364 testProposals, proposalTestParties = createPaginationTestProposals(t, ctx, ps) 365 // We cannot use the tempTransaction for this test due to the fact that the connection gets blocked when 366 // we recursively look for proposals that belong in a batch. The use of the transaction prevents another connection being 367 // taken from the connection pool, and causes a conn is busy error, we therefore just use a background context for these 368 // tests, and make sure we clean up after ourselves instead of rolling back the transaction. 369 defer cleanupTestProposals(t) 370 371 t.Run("should return all proposals when no paging is provided", testProposalCursorPaginationNoPagination) 372 t.Run("should return only the first page of proposals when first is provided", testProposalCursorPaginationWithFirst) 373 t.Run("should return only the requested page of proposals when first and after is provided", testProposalCursorPaginationWithFirstAndAfter) 374 t.Run("should return only the last page of proposals when last is provided", testProposalCursorPaginationWithLast) 375 t.Run("should return only the requested page of proposals when last and before is provided", testProposalCursorPaginationWithLastAndBefore) 376 377 t.Run("should return all proposals when no paging is provided - newest first", testProposalCursorPaginationNoPaginationNewestFirst) 378 t.Run("should return only the first page of proposals when first is provided - newest first", testProposalCursorPaginationWithFirstNewestFirst) 379 t.Run("should return only the requested page of proposals when first and after is provided - newest first", testProposalCursorPaginationWithFirstAndAfterNewestFirst) 380 t.Run("should return only the last page of proposals when last is provided - newest first", testProposalCursorPaginationWithLastNewestFirst) 381 t.Run("should return only the requested page of proposals when last and before is provided - newest first", testProposalCursorPaginationWithLastAndBeforeNewestFirst) 382 383 t.Run("should return all proposals for a given party when no paging is provided", testProposalCursorPaginationNoPaginationByParty) 384 t.Run("should return only the first page of proposals for a given party when first is provided", testProposalCursorPaginationWithFirstByParty) 385 t.Run("should return only the requested page of proposals for a given party when first and after is provided", testProposalCursorPaginationWithFirstAndAfterByParty) 386 t.Run("should return only the last page of proposals for a given party when last is provided", testProposalCursorPaginationWithLastByParty) 387 t.Run("should return only the requested page of proposals for a given party when last and before is provided", testProposalCursorPaginationWithLastAndBeforeByParty) 388 389 t.Run("should return all proposals for a given party when no paging is provided - newest first", testProposalCursorPaginationNoPaginationByPartyNewestFirst) 390 t.Run("should return only the first page of proposals for a given party when first is provided - newest first", testProposalCursorPaginationWithFirstByPartyNewestFirst) 391 t.Run("should return only the requested page of proposals for a given party when first and after is provided - newest first", testProposalCursorPaginationWithFirstAndAfterByPartyNewestFirst) 392 t.Run("should return only the last page of proposals for a given party when last is provided - newest first", testProposalCursorPaginationWithLastByPartyNewestFirst) 393 t.Run("should return only the requested page of proposals for a given party when last and before is provided - newest first", testProposalCursorPaginationWithLastAndBeforeByPartyNewestFirst) 394 395 t.Run("should return only the open proposals if open state is provided in the filter", testProposalCursorPaginationOpenOnly) 396 t.Run("should return the specified proposal state if one is provided", testProposalCursorPaginationGivenState) 397 } 398 399 func testProposalCursorPaginationNoPagination(t *testing.T) { 400 ctx := context.Background() 401 ps := sqlstore.NewProposals(connectionSource) 402 pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, false) 403 require.NoError(t, err) 404 405 got, pageInfo, err := ps.Get(ctx, nil, nil, nil, pagination) 406 require.NoError(t, err) 407 // Proposals should be listed in order of their status, then time, then id 408 want := []entities.Proposal{ 409 testProposals[0], 410 testProposals[10], 411 testProposals[1], 412 testProposals[11], 413 testProposals[2], 414 testProposals[12], 415 testProposals[8], 416 testProposals[18], 417 testProposals[3], 418 testProposals[13], 419 testProposals[4], 420 testProposals[14], 421 testProposals[5], 422 testProposals[15], 423 testProposals[6], 424 testProposals[16], 425 testProposals[7], 426 testProposals[17], 427 testProposals[9], 428 testProposals[19], 429 } 430 assert.Equal(t, want, got) 431 assert.Equal(t, entities.PageInfo{ 432 HasNextPage: false, 433 HasPreviousPage: false, 434 StartCursor: testProposals[0].Cursor().Encode(), 435 EndCursor: testProposals[19].Cursor().Encode(), 436 }, pageInfo) 437 } 438 439 func testProposalCursorPaginationWithFirst(t *testing.T) { 440 ctx := context.Background() 441 442 ps := sqlstore.NewProposals(connectionSource) 443 first := int32(3) 444 pagination, err := entities.NewCursorPagination(&first, nil, nil, nil, false) 445 require.NoError(t, err) 446 447 got, pageInfo, err := ps.Get(ctx, nil, nil, nil, pagination) 448 require.NoError(t, err) 449 // Proposals should be listed in order of their status, then time, then id 450 want := []entities.Proposal{ 451 testProposals[0], 452 testProposals[10], 453 testProposals[1], 454 } 455 assert.Equal(t, want, got) 456 assert.Equal(t, entities.PageInfo{ 457 HasNextPage: true, 458 HasPreviousPage: false, 459 StartCursor: testProposals[0].Cursor().Encode(), 460 EndCursor: testProposals[1].Cursor().Encode(), 461 }, pageInfo) 462 } 463 464 func testProposalCursorPaginationWithFirstAndAfter(t *testing.T) { 465 ctx := context.Background() 466 467 ps := sqlstore.NewProposals(connectionSource) 468 first := int32(8) 469 after := testProposals[1].Cursor().Encode() 470 pagination, err := entities.NewCursorPagination(&first, &after, nil, nil, false) 471 require.NoError(t, err) 472 473 got, pageInfo, err := ps.Get(ctx, nil, nil, nil, pagination) 474 require.NoError(t, err) 475 // Proposals should be listed in order of their status, then time, then id 476 want := []entities.Proposal{ 477 testProposals[11], 478 testProposals[2], 479 testProposals[12], 480 testProposals[8], 481 testProposals[18], 482 testProposals[3], 483 testProposals[13], 484 testProposals[4], 485 } 486 assert.Equal(t, want, got) 487 assert.Equal(t, entities.PageInfo{ 488 HasNextPage: true, 489 HasPreviousPage: true, 490 StartCursor: testProposals[11].Cursor().Encode(), 491 EndCursor: testProposals[4].Cursor().Encode(), 492 }, pageInfo) 493 } 494 495 func testProposalCursorPaginationWithLast(t *testing.T) { 496 ctx := context.Background() 497 498 ps := sqlstore.NewProposals(connectionSource) 499 last := int32(3) 500 pagination, err := entities.NewCursorPagination(nil, nil, &last, nil, false) 501 require.NoError(t, err) 502 503 got, pageInfo, err := ps.Get(ctx, nil, nil, nil, pagination) 504 require.NoError(t, err) 505 // Proposals should be listed in order of their status, then time, then id 506 want := []entities.Proposal{ 507 testProposals[17], 508 testProposals[9], 509 testProposals[19], 510 } 511 assert.Equal(t, want, got) 512 assert.Equal(t, entities.PageInfo{ 513 HasNextPage: false, 514 HasPreviousPage: true, 515 StartCursor: testProposals[17].Cursor().Encode(), 516 EndCursor: testProposals[19].Cursor().Encode(), 517 }, pageInfo) 518 } 519 520 func testProposalCursorPaginationWithLastAndBefore(t *testing.T) { 521 ctx := context.Background() 522 523 ps := sqlstore.NewProposals(connectionSource) 524 last := int32(8) 525 before := testProposals[5].Cursor().Encode() 526 pagination, err := entities.NewCursorPagination(nil, nil, &last, &before, false) 527 require.NoError(t, err) 528 529 got, pageInfo, err := ps.Get(ctx, nil, nil, nil, pagination) 530 require.NoError(t, err) 531 // Proposals should be listed in order of their status, then time, then id 532 want := []entities.Proposal{ 533 testProposals[2], 534 testProposals[12], 535 testProposals[8], 536 testProposals[18], 537 testProposals[3], 538 testProposals[13], 539 testProposals[4], 540 testProposals[14], 541 } 542 assert.Equal(t, want, got) 543 assert.Equal(t, entities.PageInfo{ 544 HasNextPage: true, 545 HasPreviousPage: true, 546 StartCursor: testProposals[2].Cursor().Encode(), 547 EndCursor: testProposals[14].Cursor().Encode(), 548 }, pageInfo) 549 } 550 551 func testProposalCursorPaginationNoPaginationNewestFirst(t *testing.T) { 552 ctx := context.Background() 553 554 ps := sqlstore.NewProposals(connectionSource) 555 pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, true) 556 require.NoError(t, err) 557 558 got, pageInfo, err := ps.Get(ctx, nil, nil, nil, pagination) 559 require.NoError(t, err) 560 // Proposals should be listed in order of their status, then time, then id 561 want := []entities.Proposal{ 562 testProposals[18], 563 testProposals[8], 564 testProposals[12], 565 testProposals[2], 566 testProposals[11], 567 testProposals[1], 568 testProposals[10], 569 testProposals[0], 570 testProposals[19], 571 testProposals[9], 572 testProposals[17], 573 testProposals[7], 574 testProposals[16], 575 testProposals[6], 576 testProposals[15], 577 testProposals[5], 578 testProposals[14], 579 testProposals[4], 580 testProposals[13], 581 testProposals[3], 582 } 583 assert.Equal(t, want, got) 584 assert.Equal(t, entities.PageInfo{ 585 HasNextPage: false, 586 HasPreviousPage: false, 587 StartCursor: testProposals[18].Cursor().Encode(), 588 EndCursor: testProposals[3].Cursor().Encode(), 589 }, pageInfo) 590 } 591 592 func testProposalCursorPaginationWithFirstNewestFirst(t *testing.T) { 593 ctx := context.Background() 594 595 ps := sqlstore.NewProposals(connectionSource) 596 first := int32(3) 597 pagination, err := entities.NewCursorPagination(&first, nil, nil, nil, true) 598 require.NoError(t, err) 599 600 got, pageInfo, err := ps.Get(ctx, nil, nil, nil, pagination) 601 require.NoError(t, err) 602 // Proposals should be listed in order of their status, then time, then id 603 want := []entities.Proposal{ 604 testProposals[18], 605 testProposals[8], 606 testProposals[12], 607 } 608 assert.Equal(t, want, got) 609 assert.Equal(t, entities.PageInfo{ 610 HasNextPage: true, 611 HasPreviousPage: false, 612 StartCursor: testProposals[18].Cursor().Encode(), 613 EndCursor: testProposals[12].Cursor().Encode(), 614 }, pageInfo) 615 } 616 617 func testProposalCursorPaginationWithFirstAndAfterNewestFirst(t *testing.T) { 618 ctx := context.Background() 619 620 ps := sqlstore.NewProposals(connectionSource) 621 first := int32(8) 622 after := testProposals[12].Cursor().Encode() 623 pagination, err := entities.NewCursorPagination(&first, &after, nil, nil, true) 624 require.NoError(t, err) 625 626 got, pageInfo, err := ps.Get(ctx, nil, nil, nil, pagination) 627 require.NoError(t, err) 628 // Proposals should be listed in order of their status, then time, then id 629 want := []entities.Proposal{ 630 testProposals[2], 631 testProposals[11], 632 testProposals[1], 633 testProposals[10], 634 testProposals[0], 635 testProposals[19], 636 testProposals[9], 637 testProposals[17], 638 } 639 assert.Equal(t, want, got) 640 assert.Equal(t, entities.PageInfo{ 641 HasNextPage: true, 642 HasPreviousPage: true, 643 StartCursor: testProposals[2].Cursor().Encode(), 644 EndCursor: testProposals[17].Cursor().Encode(), 645 }, pageInfo) 646 } 647 648 func testProposalCursorPaginationWithLastNewestFirst(t *testing.T) { 649 ctx := context.Background() 650 651 ps := sqlstore.NewProposals(connectionSource) 652 last := int32(3) 653 pagination, err := entities.NewCursorPagination(nil, nil, &last, nil, true) 654 require.NoError(t, err) 655 656 got, pageInfo, err := ps.Get(ctx, nil, nil, nil, pagination) 657 require.NoError(t, err) 658 // Proposals should be listed in order of their status, then time, then id 659 want := []entities.Proposal{ 660 testProposals[4], 661 testProposals[13], 662 testProposals[3], 663 } 664 assert.Equal(t, want, got) 665 assert.Equal(t, entities.PageInfo{ 666 HasNextPage: false, 667 HasPreviousPage: true, 668 StartCursor: testProposals[4].Cursor().Encode(), 669 EndCursor: testProposals[3].Cursor().Encode(), 670 }, pageInfo) 671 } 672 673 func testProposalCursorPaginationWithLastAndBeforeNewestFirst(t *testing.T) { 674 ctx := context.Background() 675 676 ps := sqlstore.NewProposals(connectionSource) 677 last := int32(8) 678 before := testProposals[16].Cursor().Encode() 679 pagination, err := entities.NewCursorPagination(nil, nil, &last, &before, true) 680 require.NoError(t, err) 681 682 got, pageInfo, err := ps.Get(ctx, nil, nil, nil, pagination) 683 require.NoError(t, err) 684 // Proposals should be listed in order of their status, then time, then id 685 want := []entities.Proposal{ 686 testProposals[11], 687 testProposals[1], 688 testProposals[10], 689 testProposals[0], 690 testProposals[19], 691 testProposals[9], 692 testProposals[17], 693 testProposals[7], 694 } 695 assert.Equal(t, want, got) 696 assert.Equal(t, entities.PageInfo{ 697 HasNextPage: true, 698 HasPreviousPage: true, 699 StartCursor: testProposals[11].Cursor().Encode(), 700 EndCursor: testProposals[7].Cursor().Encode(), 701 }, pageInfo) 702 } 703 704 func testProposalCursorPaginationNoPaginationByParty(t *testing.T) { 705 ctx := context.Background() 706 707 ps := sqlstore.NewProposals(connectionSource) 708 pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, false) 709 require.NoError(t, err) 710 711 partyID := proposalTestParties[0].ID.String() 712 got, pageInfo, err := ps.Get(ctx, nil, &partyID, nil, pagination) 713 require.NoError(t, err) 714 // Proposals should be listed in order of their status, then time, then id 715 want := []entities.Proposal{ 716 testProposals[0], 717 testProposals[1], 718 testProposals[2], 719 testProposals[8], 720 testProposals[3], 721 testProposals[4], 722 testProposals[5], 723 testProposals[6], 724 testProposals[7], 725 testProposals[9], 726 } 727 assert.Equal(t, want, got) 728 assert.Equal(t, entities.PageInfo{ 729 HasNextPage: false, 730 HasPreviousPage: false, 731 StartCursor: testProposals[0].Cursor().Encode(), 732 EndCursor: testProposals[9].Cursor().Encode(), 733 }, pageInfo) 734 } 735 736 func testProposalCursorPaginationWithFirstByParty(t *testing.T) { 737 ctx := context.Background() 738 739 ps := sqlstore.NewProposals(connectionSource) 740 first := int32(3) 741 pagination, err := entities.NewCursorPagination(&first, nil, nil, nil, false) 742 require.NoError(t, err) 743 744 partyID := proposalTestParties[0].ID.String() 745 got, pageInfo, err := ps.Get(ctx, nil, &partyID, nil, pagination) 746 require.NoError(t, err) 747 // Proposals should be listed in order of their status, then time, then id 748 want := []entities.Proposal{ 749 testProposals[0], 750 testProposals[1], 751 testProposals[2], 752 } 753 assert.Equal(t, want, got) 754 assert.Equal(t, entities.PageInfo{ 755 HasNextPage: true, 756 HasPreviousPage: false, 757 StartCursor: testProposals[0].Cursor().Encode(), 758 EndCursor: testProposals[2].Cursor().Encode(), 759 }, pageInfo) 760 } 761 762 func testProposalCursorPaginationWithFirstAndAfterByParty(t *testing.T) { 763 ctx := context.Background() 764 765 ps := sqlstore.NewProposals(connectionSource) 766 first := int32(3) 767 after := testProposals[2].Cursor().Encode() 768 pagination, err := entities.NewCursorPagination(&first, &after, nil, nil, false) 769 require.NoError(t, err) 770 771 partyID := proposalTestParties[0].ID.String() 772 got, pageInfo, err := ps.Get(ctx, nil, &partyID, nil, pagination) 773 require.NoError(t, err) 774 // Proposals should be listed in order of their status, then time, then id 775 want := []entities.Proposal{ 776 testProposals[8], 777 testProposals[3], 778 testProposals[4], 779 } 780 assert.Equal(t, want, got) 781 assert.Equal(t, entities.PageInfo{ 782 HasNextPage: true, 783 HasPreviousPage: true, 784 StartCursor: testProposals[8].Cursor().Encode(), 785 EndCursor: testProposals[4].Cursor().Encode(), 786 }, pageInfo) 787 } 788 789 func testProposalCursorPaginationWithLastByParty(t *testing.T) { 790 ctx := context.Background() 791 792 ps := sqlstore.NewProposals(connectionSource) 793 last := int32(3) 794 pagination, err := entities.NewCursorPagination(nil, nil, &last, nil, false) 795 require.NoError(t, err) 796 797 partyID := proposalTestParties[0].ID.String() 798 got, pageInfo, err := ps.Get(ctx, nil, &partyID, nil, pagination) 799 require.NoError(t, err) 800 // Proposals should be listed in order of their status, then time, then id 801 want := []entities.Proposal{ 802 testProposals[6], 803 testProposals[7], 804 testProposals[9], 805 } 806 assert.Equal(t, want, got) 807 assert.Equal(t, entities.PageInfo{ 808 HasNextPage: false, 809 HasPreviousPage: true, 810 StartCursor: testProposals[6].Cursor().Encode(), 811 EndCursor: testProposals[9].Cursor().Encode(), 812 }, pageInfo) 813 } 814 815 func testProposalCursorPaginationWithLastAndBeforeByParty(t *testing.T) { 816 ctx := context.Background() 817 818 ps := sqlstore.NewProposals(connectionSource) 819 last := int32(5) 820 before := testProposals[6].Cursor().Encode() 821 pagination, err := entities.NewCursorPagination(nil, nil, &last, &before, false) 822 require.NoError(t, err) 823 824 partyID := proposalTestParties[0].ID.String() 825 got, pageInfo, err := ps.Get(ctx, nil, &partyID, nil, pagination) 826 require.NoError(t, err) 827 // Proposals should be listed in order of their status, then time, then id 828 want := []entities.Proposal{ 829 testProposals[2], 830 testProposals[8], 831 testProposals[3], 832 testProposals[4], 833 testProposals[5], 834 } 835 assert.Equal(t, want, got) 836 assert.Equal(t, entities.PageInfo{ 837 HasNextPage: true, 838 HasPreviousPage: true, 839 StartCursor: testProposals[2].Cursor().Encode(), 840 EndCursor: testProposals[5].Cursor().Encode(), 841 }, pageInfo) 842 } 843 844 func testProposalCursorPaginationNoPaginationByPartyNewestFirst(t *testing.T) { 845 ctx := context.Background() 846 847 ps := sqlstore.NewProposals(connectionSource) 848 pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, true) 849 require.NoError(t, err) 850 851 partyID := proposalTestParties[0].ID.String() 852 853 got, pageInfo, err := ps.Get(ctx, nil, &partyID, nil, pagination) 854 require.NoError(t, err) 855 // Proposals should be listed in order of their status, then time, then id 856 want := []entities.Proposal{ 857 testProposals[8], 858 testProposals[2], 859 testProposals[1], 860 testProposals[0], 861 testProposals[9], 862 testProposals[7], 863 testProposals[6], 864 testProposals[5], 865 testProposals[4], 866 testProposals[3], 867 } 868 assert.Equal(t, want, got) 869 assert.Equal(t, entities.PageInfo{ 870 HasNextPage: false, 871 HasPreviousPage: false, 872 StartCursor: testProposals[8].Cursor().Encode(), 873 EndCursor: testProposals[3].Cursor().Encode(), 874 }, pageInfo) 875 } 876 877 func testProposalCursorPaginationWithFirstByPartyNewestFirst(t *testing.T) { 878 ctx := context.Background() 879 880 ps := sqlstore.NewProposals(connectionSource) 881 first := int32(3) 882 pagination, err := entities.NewCursorPagination(&first, nil, nil, nil, true) 883 require.NoError(t, err) 884 885 partyID := proposalTestParties[0].ID.String() 886 887 got, pageInfo, err := ps.Get(ctx, nil, &partyID, nil, pagination) 888 require.NoError(t, err) 889 // Proposals should be listed in order of their status, then time, then id 890 want := []entities.Proposal{ 891 testProposals[8], 892 testProposals[2], 893 testProposals[1], 894 } 895 assert.Equal(t, want, got) 896 assert.Equal(t, entities.PageInfo{ 897 HasNextPage: true, 898 HasPreviousPage: false, 899 StartCursor: testProposals[8].Cursor().Encode(), 900 EndCursor: testProposals[1].Cursor().Encode(), 901 }, pageInfo) 902 } 903 904 func testProposalCursorPaginationWithFirstAndAfterByPartyNewestFirst(t *testing.T) { 905 ctx := context.Background() 906 907 ps := sqlstore.NewProposals(connectionSource) 908 first := int32(3) 909 after := testProposals[1].Cursor().Encode() 910 pagination, err := entities.NewCursorPagination(&first, &after, nil, nil, true) 911 require.NoError(t, err) 912 913 partyID := proposalTestParties[0].ID.String() 914 915 got, pageInfo, err := ps.Get(ctx, nil, &partyID, nil, pagination) 916 require.NoError(t, err) 917 // Proposals should be listed in order of their status, then time, then id 918 want := []entities.Proposal{ 919 testProposals[0], 920 testProposals[9], 921 testProposals[7], 922 } 923 assert.Equal(t, want, got) 924 assert.Equal(t, entities.PageInfo{ 925 HasNextPage: true, 926 HasPreviousPage: true, 927 StartCursor: testProposals[0].Cursor().Encode(), 928 EndCursor: testProposals[7].Cursor().Encode(), 929 }, pageInfo) 930 } 931 932 func testProposalCursorPaginationWithLastByPartyNewestFirst(t *testing.T) { 933 ctx := context.Background() 934 935 ps := sqlstore.NewProposals(connectionSource) 936 last := int32(3) 937 pagination, err := entities.NewCursorPagination(nil, nil, &last, nil, true) 938 require.NoError(t, err) 939 940 partyID := proposalTestParties[0].ID.String() 941 942 got, pageInfo, err := ps.Get(ctx, nil, &partyID, nil, pagination) 943 require.NoError(t, err) 944 // Proposals should be listed in order of their status, then time, then id 945 want := []entities.Proposal{ 946 testProposals[5], 947 testProposals[4], 948 testProposals[3], 949 } 950 assert.Equal(t, want, got) 951 assert.Equal(t, entities.PageInfo{ 952 HasNextPage: false, 953 HasPreviousPage: true, 954 StartCursor: testProposals[5].Cursor().Encode(), 955 EndCursor: testProposals[3].Cursor().Encode(), 956 }, pageInfo) 957 } 958 959 func testProposalCursorPaginationWithLastAndBeforeByPartyNewestFirst(t *testing.T) { 960 ctx := context.Background() 961 962 ps := sqlstore.NewProposals(connectionSource) 963 last := int32(5) 964 before := testProposals[5].Cursor().Encode() 965 pagination, err := entities.NewCursorPagination(nil, nil, &last, &before, true) 966 require.NoError(t, err) 967 968 partyID := proposalTestParties[0].ID.String() 969 970 got, pageInfo, err := ps.Get(ctx, nil, &partyID, nil, pagination) 971 require.NoError(t, err) 972 // Proposals should be listed in order of their status, then time, then id 973 want := []entities.Proposal{ 974 testProposals[1], 975 testProposals[0], 976 testProposals[9], 977 testProposals[7], 978 testProposals[6], 979 } 980 assert.Equal(t, want, got) 981 assert.Equal(t, entities.PageInfo{ 982 HasNextPage: true, 983 HasPreviousPage: true, 984 StartCursor: testProposals[1].Cursor().Encode(), 985 EndCursor: testProposals[6].Cursor().Encode(), 986 }, pageInfo) 987 } 988 989 func testProposalCursorPaginationOpenOnly(t *testing.T) { 990 ctx := context.Background() 991 992 ps := sqlstore.NewProposals(connectionSource) 993 pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, false) 994 require.NoError(t, err) 995 996 state := entities.ProposalStateOpen 997 got, pageInfo, err := ps.Get(ctx, &state, nil, nil, pagination) 998 require.NoError(t, err) 999 // Proposals should be listed in order of their status, then time, then id 1000 want := []entities.Proposal{ 1001 testProposals[0], 1002 testProposals[10], 1003 testProposals[1], 1004 testProposals[11], 1005 testProposals[2], 1006 testProposals[12], 1007 testProposals[8], 1008 testProposals[18], 1009 } 1010 assert.Equal(t, want, got) 1011 assert.Equal(t, entities.PageInfo{ 1012 HasNextPage: false, 1013 HasPreviousPage: false, 1014 StartCursor: testProposals[0].Cursor().Encode(), 1015 EndCursor: testProposals[18].Cursor().Encode(), 1016 }, pageInfo) 1017 } 1018 1019 func testProposalCursorPaginationGivenState(t *testing.T) { 1020 ctx := context.Background() 1021 1022 ps := sqlstore.NewProposals(connectionSource) 1023 pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, false) 1024 require.NoError(t, err) 1025 1026 t.Run("State is Enacted", func(t *testing.T) { 1027 state := entities.ProposalStateEnacted 1028 got, pageInfo, err := ps.Get(ctx, &state, nil, nil, pagination) 1029 require.NoError(t, err) 1030 // Proposals should be listed in order of their status, then time, then id 1031 want := []entities.Proposal{ 1032 testProposals[3], 1033 testProposals[13], 1034 testProposals[6], 1035 testProposals[16], 1036 testProposals[9], 1037 testProposals[19], 1038 } 1039 assert.Equal(t, want, got) 1040 assert.Equal(t, entities.PageInfo{ 1041 HasNextPage: false, 1042 HasPreviousPage: false, 1043 StartCursor: testProposals[3].Cursor().Encode(), 1044 EndCursor: testProposals[19].Cursor().Encode(), 1045 }, pageInfo) 1046 }) 1047 1048 t.Run("State is Passed", func(t *testing.T) { 1049 state := entities.ProposalStatePassed 1050 got, pageInfo, err := ps.Get(ctx, &state, nil, nil, pagination) 1051 require.NoError(t, err) 1052 // Proposals should be listed in order of their status, then time, then id 1053 want := []entities.Proposal{ 1054 testProposals[4], 1055 testProposals[14], 1056 testProposals[5], 1057 testProposals[15], 1058 } 1059 assert.Equal(t, want, got) 1060 assert.Equal(t, entities.PageInfo{ 1061 HasNextPage: false, 1062 HasPreviousPage: false, 1063 StartCursor: testProposals[4].Cursor().Encode(), 1064 EndCursor: testProposals[15].Cursor().Encode(), 1065 }, pageInfo) 1066 }) 1067 } 1068 1069 func createPaginationTestProposals(t *testing.T, ctx context.Context, pps *sqlstore.Proposals) ([]entities.Proposal, []entities.Party) { 1070 t.Helper() 1071 ps := sqlstore.NewParties(connectionSource) 1072 bs := sqlstore.NewBlocks(connectionSource) 1073 1074 testProposals := make([]entities.Proposal, 20) 1075 1076 blockTime := time.Date(2022, 7, 15, 8, 0, 0, 0, time.Local) 1077 block := addTestBlockForTime(t, ctx, bs, blockTime) 1078 1079 parties := []entities.Party{ 1080 addTestParty(t, ctx, ps, block), 1081 addTestParty(t, ctx, ps, block), 1082 } 1083 1084 states := []entities.ProposalState{ 1085 entities.ProposalStateOpen, 1086 entities.ProposalStateOpen, 1087 entities.ProposalStateOpen, 1088 entities.ProposalStateEnacted, 1089 entities.ProposalStatePassed, 1090 entities.ProposalStatePassed, 1091 entities.ProposalStateEnacted, 1092 entities.ProposalStateDeclined, 1093 entities.ProposalStateOpen, 1094 entities.ProposalStateEnacted, 1095 } 1096 i := 0 1097 for i < 10 { 1098 blockTime = blockTime.Add(time.Minute) 1099 block = addTestBlockForTime(t, ctx, bs, blockTime) 1100 block2 := addTestBlockForTime(t, ctx, bs, blockTime.Add(time.Second*30)) 1101 1102 id1 := fmt.Sprintf("deadbeef%02d", i) 1103 id2 := fmt.Sprintf("deadbeef%02d", i+10) 1104 1105 ref1 := fmt.Sprintf("cafed00d%02d", i) 1106 ref2 := fmt.Sprintf("cafed00d%02d", i+10) 1107 rationale1 := entities.ProposalRationale{ProposalRationale: &vega.ProposalRationale{Title: fmt.Sprintf("https://rationale1-%02d.com", i), Description: "desc"}} 1108 rationale2 := entities.ProposalRationale{ProposalRationale: &vega.ProposalRationale{Title: fmt.Sprintf("https://rationale1-%02d.com", i+10), Description: "desc"}} 1109 terms1 := entities.ProposalTerms{ProposalTerms: &vega.ProposalTerms{Change: &vega.ProposalTerms_NewMarket{NewMarket: &vega.NewMarket{}}}} 1110 terms2 := entities.ProposalTerms{ProposalTerms: &vega.ProposalTerms{Change: &vega.ProposalTerms_NewAsset{NewAsset: &vega.NewAsset{}}}} 1111 1112 testProposals[i] = addTestProposal(t, ctx, pps, id1, parties[0], ref1, block, states[i], rationale1, terms1, entities.ProposalErrorUnspecified, nil, entities.BatchProposalTerms{}) 1113 testProposals[i+10] = addTestProposal(t, ctx, pps, id2, parties[1], ref2, block2, states[i], rationale2, terms2, entities.ProposalErrorUnspecified, nil, entities.BatchProposalTerms{}) 1114 i++ 1115 } 1116 1117 return testProposals, parties 1118 } 1119 1120 func cleanupTestProposals(t *testing.T) { 1121 t.Helper() 1122 // Remove the proposals, then the parties and then the blocks 1123 _, err := connectionSource.Exec(context.Background(), `TRUNCATE TABLE proposals`) 1124 require.NoError(t, err) 1125 _, err = connectionSource.Exec(context.Background(), `TRUNCATE TABLE parties`) 1126 require.NoError(t, err) 1127 _, err = connectionSource.Exec(context.Background(), `TRUNCATE TABLE blocks`) 1128 require.NoError(t, err) 1129 } 1130 1131 func TestProposeSuccessorMarket(t *testing.T) { 1132 ctx := tempTransaction(t) 1133 1134 partyStore := sqlstore.NewParties(connectionSource) 1135 propStore := sqlstore.NewProposals(connectionSource) 1136 blockStore := sqlstore.NewBlocks(connectionSource) 1137 block1 := addTestBlock(t, ctx, blockStore) 1138 1139 party1 := addTestParty(t, ctx, partyStore, block1) 1140 rationale1 := entities.ProposalRationale{ProposalRationale: &vega.ProposalRationale{Title: "myurl1.com", Description: "desc"}} 1141 rationale2 := entities.ProposalRationale{ProposalRationale: &vega.ProposalRationale{Title: "myurl2.com", Description: "desc"}} 1142 terms1 := entities.ProposalTerms{ProposalTerms: &vega.ProposalTerms{Change: &vega.ProposalTerms_NewMarket{NewMarket: &vega.NewMarket{ 1143 Changes: &vega.NewMarketConfiguration{ 1144 Instrument: nil, 1145 DecimalPlaces: 0, 1146 Metadata: nil, 1147 PriceMonitoringParameters: nil, 1148 LiquidityMonitoringParameters: nil, 1149 RiskParameters: nil, 1150 PositionDecimalPlaces: 0, 1151 LinearSlippageFactor: "", 1152 QuadraticSlippageFactor: "", 1153 Successor: nil, 1154 }, 1155 }}}} 1156 terms2 := entities.ProposalTerms{ProposalTerms: &vega.ProposalTerms{Change: &vega.ProposalTerms_NewMarket{NewMarket: &vega.NewMarket{ 1157 Changes: &vega.NewMarketConfiguration{ 1158 Instrument: nil, 1159 DecimalPlaces: 0, 1160 Metadata: nil, 1161 PriceMonitoringParameters: nil, 1162 LiquidityMonitoringParameters: nil, 1163 RiskParameters: nil, 1164 PositionDecimalPlaces: 0, 1165 LinearSlippageFactor: "", 1166 QuadraticSlippageFactor: "", 1167 Successor: &vega.SuccessorConfiguration{ 1168 ParentMarketId: "deadbeef", 1169 InsurancePoolFraction: "0.5", 1170 }, 1171 }, 1172 }}}} 1173 id1 := GenerateID() 1174 id2 := GenerateID() 1175 1176 reference1 := GenerateID() 1177 reference2 := GenerateID() 1178 prop1 := addTestProposal(t, ctx, propStore, id1, party1, reference1, block1, entities.ProposalStateEnacted, rationale1, terms1, entities.ProposalErrorUnspecified, nil, entities.BatchProposalTerms{}) 1179 prop2 := addTestProposal(t, ctx, propStore, id2, party1, reference2, block1, entities.ProposalStateRejected, rationale2, terms2, entities.ProposalErrorInvalidSuccessorMarket, nil, entities.BatchProposalTerms{}) 1180 1181 t.Run("GetByID", func(t *testing.T) { 1182 want := prop1 1183 got, err := propStore.GetByID(ctx, prop1.ID.String()) 1184 require.NoError(t, err) 1185 assertProposalMatch(t, want, got) 1186 1187 want = prop2 1188 got, err = propStore.GetByID(ctx, prop2.ID.String()) 1189 require.NoError(t, err) 1190 assertProposalMatch(t, want, got) 1191 }) 1192 } 1193 1194 func getNewProposal(partyID string) *vega.Proposal { 1195 return &vega.Proposal{ 1196 Id: GenerateID(), 1197 Reference: GenerateID(), 1198 PartyId: partyID, 1199 State: vega.Proposal_STATE_OPEN, 1200 Timestamp: time.Now().UnixNano(), 1201 Rationale: &vega.ProposalRationale{Title: "myurl1.com", Description: "desc"}, 1202 Terms: &vega.ProposalTerms{ 1203 Change: &vega.ProposalTerms_NewMarket{ 1204 NewMarket: &vega.NewMarket{ 1205 Changes: &vega.NewMarketConfiguration{ 1206 Instrument: &vega.InstrumentConfiguration{}, 1207 }, 1208 }, 1209 }, 1210 }, 1211 } 1212 } 1213 1214 func getNewSpotMarketProposal(partyID string) *vega.Proposal { 1215 proposal := getNewProposal(partyID) 1216 1217 proposal.Terms.Change = &vega.ProposalTerms_NewSpotMarket{ 1218 NewSpotMarket: &vega.NewSpotMarket{ 1219 Changes: &vega.NewSpotMarketConfiguration{ 1220 Instrument: &vega.InstrumentConfiguration{ 1221 Product: &vega.InstrumentConfiguration_Spot{ 1222 Spot: &vega.SpotProduct{ 1223 BaseAsset: "USD", 1224 QuoteAsset: "ETH", 1225 }, 1226 }, 1227 }, 1228 }, 1229 }, 1230 } 1231 return proposal 1232 } 1233 1234 func getSpotMarketUpdateProposal(partyID string) *vega.Proposal { 1235 proposal := getNewProposal(partyID) 1236 proposal.Terms.Change = &vega.ProposalTerms_UpdateSpotMarket{ 1237 UpdateSpotMarket: &vega.UpdateSpotMarket{ 1238 MarketId: "USD/ETH", 1239 Changes: &vega.UpdateSpotMarketConfiguration{ 1240 Metadata: []string{"ETH", "USD"}, 1241 PriceMonitoringParameters: &vega.PriceMonitoringParameters{ 1242 Triggers: []*vega.PriceMonitoringTrigger{ 1243 { 1244 Horizon: 1, 1245 Probability: "0.5", 1246 AuctionExtension: 0, 1247 }, 1248 }, 1249 }, 1250 TargetStakeParameters: &vega.TargetStakeParameters{ 1251 TimeWindow: 1, 1252 ScalingFactor: 1, 1253 }, 1254 RiskParameters: &vega.UpdateSpotMarketConfiguration_Simple{ 1255 Simple: &vega.SimpleModelParams{ 1256 FactorLong: 1, 1257 FactorShort: 1, 1258 MaxMoveUp: 1, 1259 MinMoveDown: 1, 1260 ProbabilityOfTrading: 1, 1261 }, 1262 }, 1263 SlaParams: &vega.LiquiditySLAParameters{ 1264 PriceRange: "", 1265 CommitmentMinTimeFraction: "0.5", 1266 PerformanceHysteresisEpochs: 2, 1267 SlaCompetitionFactor: "0.75", 1268 }, 1269 }, 1270 }, 1271 } 1272 return proposal 1273 } 1274 1275 func getNewPerpetualMarketProposal(partyID string) *vega.Proposal { 1276 pk := dstypes.CreateSignerFromString("0xDEADBEEF", dstypes.SignerTypePubKey) 1277 proposal := getNewProposal(partyID) 1278 1279 proposal.Terms.Change = &vega.ProposalTerms_NewMarket{ 1280 NewMarket: &vega.NewMarket{ 1281 Changes: &vega.NewMarketConfiguration{ 1282 Instrument: &vega.InstrumentConfiguration{ 1283 Product: &vega.InstrumentConfiguration_Perpetual{ 1284 Perpetual: &vega.PerpetualProduct{ 1285 SettlementAsset: "Ethereum/Ether", 1286 QuoteName: "ETH-230929", 1287 MarginFundingFactor: "0.5", 1288 InterestRate: "0.0125", 1289 ClampLowerBound: "0.2", 1290 ClampUpperBound: "0.8", 1291 DataSourceSpecForSettlementSchedule: &vega.DataSourceDefinition{ 1292 SourceType: &vega.DataSourceDefinition_External{ 1293 External: &vega.DataSourceDefinitionExternal{ 1294 SourceType: &vega.DataSourceDefinitionExternal_Oracle{ 1295 Oracle: &vega.DataSourceSpecConfiguration{ 1296 Signers: []*datav1.Signer{pk.IntoProto()}, 1297 Filters: []*datav1.Filter{ 1298 { 1299 Key: &datav1.PropertyKey{ 1300 Name: "prices.ETH.value", 1301 Type: datav1.PropertyKey_TYPE_INTEGER, 1302 }, 1303 Conditions: []*datav1.Condition{}, 1304 }, 1305 }, 1306 }, 1307 }, 1308 }, 1309 }, 1310 }, 1311 DataSourceSpecForSettlementData: &vega.DataSourceDefinition{ 1312 SourceType: &vega.DataSourceDefinition_Internal{ 1313 Internal: &vega.DataSourceDefinitionInternal{ 1314 SourceType: &vega.DataSourceDefinitionInternal_Time{ 1315 Time: &vega.DataSourceSpecConfigurationTime{ 1316 Conditions: []*datav1.Condition{ 1317 { 1318 Operator: datav1.Condition_OPERATOR_GREATER_THAN_OR_EQUAL, 1319 Value: "2023-09-29T00:00:00.000000000Z", 1320 }, 1321 }, 1322 }, 1323 }, 1324 }, 1325 }, 1326 }, 1327 DataSourceSpecBinding: &vega.DataSourceSpecToPerpetualBinding{ 1328 SettlementDataProperty: "prices.ETH.value", 1329 SettlementScheduleProperty: "2023-09-29T00:00:00.000000000Z", 1330 }, 1331 }, 1332 }, 1333 }, 1334 }, 1335 }, 1336 } 1337 return proposal 1338 } 1339 1340 func getPerpetualMarketUpdateProposal(partyID string) *vega.Proposal { 1341 pk := dstypes.CreateSignerFromString("0xDEADBEEF", dstypes.SignerTypePubKey) 1342 proposal := getNewProposal(partyID) 1343 1344 proposal.Terms.Change = &vega.ProposalTerms_UpdateMarket{ 1345 UpdateMarket: &vega.UpdateMarket{ 1346 Changes: &vega.UpdateMarketConfiguration{ 1347 Instrument: &vega.UpdateInstrumentConfiguration{ 1348 Product: &vega.UpdateInstrumentConfiguration_Perpetual{ 1349 Perpetual: &vega.UpdatePerpetualProduct{ 1350 QuoteName: "ETH-230929", 1351 MarginFundingFactor: "0.6", 1352 InterestRate: "0.015", 1353 ClampLowerBound: "0.1", 1354 ClampUpperBound: "0.9", 1355 DataSourceSpecForSettlementSchedule: &vega.DataSourceDefinition{ 1356 SourceType: &vega.DataSourceDefinition_External{ 1357 External: &vega.DataSourceDefinitionExternal{ 1358 SourceType: &vega.DataSourceDefinitionExternal_Oracle{ 1359 Oracle: &vega.DataSourceSpecConfiguration{ 1360 Signers: []*datav1.Signer{pk.IntoProto()}, 1361 Filters: []*datav1.Filter{ 1362 { 1363 Key: &datav1.PropertyKey{ 1364 Name: "prices.ETH.value", 1365 Type: datav1.PropertyKey_TYPE_INTEGER, 1366 }, 1367 Conditions: []*datav1.Condition{}, 1368 }, 1369 }, 1370 }, 1371 }, 1372 }, 1373 }, 1374 }, 1375 DataSourceSpecForSettlementData: &vega.DataSourceDefinition{ 1376 SourceType: &vega.DataSourceDefinition_Internal{ 1377 Internal: &vega.DataSourceDefinitionInternal{ 1378 SourceType: &vega.DataSourceDefinitionInternal_Time{ 1379 Time: &vega.DataSourceSpecConfigurationTime{ 1380 Conditions: []*datav1.Condition{ 1381 { 1382 Operator: datav1.Condition_OPERATOR_GREATER_THAN_OR_EQUAL, 1383 Value: "2023-09-29T00:00:00.000000000Z", 1384 }, 1385 }, 1386 }, 1387 }, 1388 }, 1389 }, 1390 }, 1391 DataSourceSpecBinding: &vega.DataSourceSpecToPerpetualBinding{ 1392 SettlementDataProperty: "prices.ETH.value", 1393 SettlementScheduleProperty: "2023-09-29T00:00:00.000000000Z", 1394 }, 1395 }, 1396 }, 1397 }, 1398 }, 1399 }, 1400 } 1401 1402 return proposal 1403 } 1404 1405 func setupProposalTest(t *testing.T) (*sqlstore.Blocks, *sqlstore.Parties, *sqlstore.Proposals) { 1406 t.Helper() 1407 partyStore := sqlstore.NewParties(connectionSource) 1408 propStore := sqlstore.NewProposals(connectionSource) 1409 blockStore := sqlstore.NewBlocks(connectionSource) 1410 1411 return blockStore, partyStore, propStore 1412 } 1413 1414 func TestSpotMarketProposal(t *testing.T) { 1415 t.Run("Should save and retrieve new spot market proposals to the store", testShouldSaveNewSpotMarketProposalsToStore) 1416 t.Run("Should save amd retrieve update spot market proposals to the store", testShouldSaveUpdateSpotMarketProposalsToStore) 1417 } 1418 1419 func testShouldSaveNewSpotMarketProposalsToStore(t *testing.T) { 1420 bs, pts, ps := setupProposalTest(t) 1421 1422 ctx := tempTransaction(t) 1423 1424 block1 := addTestBlock(t, ctx, bs) 1425 party1 := addTestParty(t, ctx, pts, block1) 1426 1427 proposalProto := getNewSpotMarketProposal(party1.ID.String()) 1428 proposal, err := entities.ProposalFromProto(proposalProto, generateTxHash()) 1429 require.NoError(t, err) 1430 t.Run("Add should save the spot market proposal to the database", func(t *testing.T) { 1431 err = ps.Add(ctx, proposal) 1432 require.NoError(t, err) 1433 }) 1434 1435 var savedProp []entities.Proposal 1436 t.Run("Get should return the saved spot market proposal", func(t *testing.T) { 1437 savedProp, _, err = ps.Get(ctx, nil, nil, nil, entities.CursorPagination{}) 1438 require.NoError(t, err) 1439 require.Len(t, savedProp, 1) 1440 }) 1441 1442 t.Run("Proposal terms should be for a new spot market", func(t *testing.T) { 1443 savedToProto := savedProp[0].ToProto() 1444 assert.Nil(t, savedToProto.Terms.GetUpdateSpotMarket()) 1445 assert.NotNil(t, savedToProto.Terms.GetNewSpotMarket()) 1446 }) 1447 } 1448 1449 func testShouldSaveUpdateSpotMarketProposalsToStore(t *testing.T) { 1450 bs, pts, ps := setupProposalTest(t) 1451 1452 ctx := tempTransaction(t) 1453 1454 block1 := addTestBlock(t, ctx, bs) 1455 party1 := addTestParty(t, ctx, pts, block1) 1456 1457 proposalProto := getSpotMarketUpdateProposal(party1.ID.String()) 1458 proposal, err := entities.ProposalFromProto(proposalProto, generateTxHash()) 1459 require.NoError(t, err) 1460 t.Run("Add should save the spot market proposal to the database", func(t *testing.T) { 1461 err = ps.Add(ctx, proposal) 1462 require.NoError(t, err) 1463 }) 1464 1465 var savedProp []entities.Proposal 1466 t.Run("Get should return the saved spot market proposal", func(t *testing.T) { 1467 savedProp, _, err = ps.Get(ctx, nil, nil, nil, entities.CursorPagination{}) 1468 require.NoError(t, err) 1469 require.Len(t, savedProp, 1) 1470 }) 1471 1472 t.Run("Proposal terms should be for a new spot market", func(t *testing.T) { 1473 savedToProto := savedProp[0].ToProto() 1474 assert.Nil(t, savedToProto.Terms.GetUpdateMarket()) 1475 assert.NotNil(t, savedToProto.Terms.GetUpdateSpotMarket()) 1476 }) 1477 } 1478 1479 func TestPerpetualMarketProposal(t *testing.T) { 1480 t.Run("Should save and retrieve new perpetual market proposals to the store", testShouldSaveNewPerpetualMarketProposalsToStore) 1481 t.Run("Should save and retrieve update perpetual market proposals to the store", testShouldUpdateSavePerpetualMarketProposalsToStore) 1482 } 1483 1484 func testShouldSaveNewPerpetualMarketProposalsToStore(t *testing.T) { 1485 bs, pts, ps := setupProposalTest(t) 1486 1487 ctx := tempTransaction(t) 1488 1489 block1 := addTestBlock(t, ctx, bs) 1490 party1 := addTestParty(t, ctx, pts, block1) 1491 1492 proposalProto := getNewPerpetualMarketProposal(party1.ID.String()) 1493 proposal, err := entities.ProposalFromProto(proposalProto, generateTxHash()) 1494 require.NoError(t, err) 1495 t.Run("Add should create a new perps market proposal in the database", func(t *testing.T) { 1496 err = ps.Add(ctx, proposal) 1497 require.NoError(t, err) 1498 }) 1499 1500 var savedProp []entities.Proposal 1501 t.Run("Get should return the perps market proposal from the database", func(t *testing.T) { 1502 savedProp, _, err = ps.Get(ctx, nil, nil, nil, entities.CursorPagination{}) 1503 require.NoError(t, err) 1504 require.Len(t, savedProp, 1) 1505 }) 1506 1507 t.Run("The saved proposal's instrument should be a perp and not a future or spot", func(t *testing.T) { 1508 savedToProto := savedProp[0].ToProto() 1509 assert.Nil(t, savedToProto.Terms.GetNewSpotMarket()) 1510 assert.NotNil(t, savedToProto.Terms.GetNewMarket()) 1511 future := savedToProto.Terms.GetNewMarket().GetChanges().GetInstrument().GetFuture() 1512 assert.Nil(t, future) 1513 perps := savedToProto.Terms.GetNewMarket().GetChanges().GetInstrument().GetPerpetual() 1514 assert.NotNil(t, perps) 1515 }) 1516 } 1517 1518 func testShouldUpdateSavePerpetualMarketProposalsToStore(t *testing.T) { 1519 bs, pts, ps := setupProposalTest(t) 1520 1521 ctx := tempTransaction(t) 1522 1523 block1 := addTestBlock(t, ctx, bs) 1524 party1 := addTestParty(t, ctx, pts, block1) 1525 1526 proposalProto := getPerpetualMarketUpdateProposal(party1.ID.String()) 1527 proposal, err := entities.ProposalFromProto(proposalProto, generateTxHash()) 1528 require.NoError(t, err) 1529 t.Run("Add should create a update perps market proposal in the database", func(t *testing.T) { 1530 err = ps.Add(ctx, proposal) 1531 require.NoError(t, err) 1532 }) 1533 1534 var savedProp []entities.Proposal 1535 t.Run("Get should return the perps market proposal from the database", func(t *testing.T) { 1536 savedProp, _, err = ps.Get(ctx, nil, nil, nil, entities.CursorPagination{}) 1537 require.NoError(t, err) 1538 require.Len(t, savedProp, 1) 1539 }) 1540 1541 t.Run("The saved proposal's instrument should be a perp and not a future or spot", func(t *testing.T) { 1542 savedToProto := savedProp[0].ToProto() 1543 assert.Nil(t, savedToProto.Terms.GetNewSpotMarket()) 1544 assert.NotNil(t, savedToProto.Terms.GetUpdateMarket()) 1545 future := savedToProto.Terms.GetUpdateMarket().GetChanges().GetInstrument().GetFuture() 1546 assert.Nil(t, future) 1547 perps := savedToProto.Terms.GetUpdateMarket().GetChanges().GetInstrument().GetPerpetual() 1548 assert.NotNil(t, perps) 1549 }) 1550 } 1551 1552 func TestProposalEnums(t *testing.T) { 1553 t.Run("Should save and retrieve proposals with all possible errors", testProposalError) 1554 t.Run("Should save and retrieve proposals with all possible states", testProposalState) 1555 } 1556 1557 func testProposalError(t *testing.T) { 1558 var proposalError vega.ProposalError 1559 errs := getEnums(t, proposalError) 1560 assert.Len(t, errs, 57) 1561 1562 for e, err := range errs { 1563 t.Run(err, func(t *testing.T) { 1564 ctx := tempTransaction(t) 1565 1566 partyStore := sqlstore.NewParties(connectionSource) 1567 propStore := sqlstore.NewProposals(connectionSource) 1568 blockStore := sqlstore.NewBlocks(connectionSource) 1569 block1 := addTestBlock(t, ctx, blockStore) 1570 1571 party1 := addTestParty(t, ctx, partyStore, block1) 1572 rationale1 := entities.ProposalRationale{ProposalRationale: &vega.ProposalRationale{Title: "myurl1.com", Description: "desc"}} 1573 terms1 := entities.ProposalTerms{ProposalTerms: &vega.ProposalTerms{Change: &vega.ProposalTerms_NewMarket{NewMarket: &vega.NewMarket{}}}} 1574 id1 := GenerateID() 1575 1576 reference1 := GenerateID() 1577 prop1 := addTestProposal(t, ctx, propStore, id1, party1, reference1, block1, entities.ProposalStateEnacted, rationale1, terms1, entities.ProposalError(e), nil, entities.BatchProposalTerms{}) 1578 1579 prop1ID := prop1.ID.String() 1580 1581 expected := prop1 1582 actual, err := propStore.GetByID(ctx, prop1ID) 1583 require.NoError(t, err) 1584 assertProposalMatch(t, expected, actual) 1585 }) 1586 } 1587 } 1588 1589 func testProposalState(t *testing.T) { 1590 var proposalState vega.Proposal_State 1591 errs := getEnums(t, proposalState) 1592 assert.Len(t, errs, 8) 1593 1594 for s, state := range errs { 1595 t.Run(state, func(t *testing.T) { 1596 ctx := tempTransaction(t) 1597 1598 partyStore := sqlstore.NewParties(connectionSource) 1599 propStore := sqlstore.NewProposals(connectionSource) 1600 blockStore := sqlstore.NewBlocks(connectionSource) 1601 block1 := addTestBlock(t, ctx, blockStore) 1602 1603 party1 := addTestParty(t, ctx, partyStore, block1) 1604 rationale1 := entities.ProposalRationale{ProposalRationale: &vega.ProposalRationale{Title: "myurl1.com", Description: "desc"}} 1605 terms1 := entities.ProposalTerms{ProposalTerms: &vega.ProposalTerms{Change: &vega.ProposalTerms_NewMarket{NewMarket: &vega.NewMarket{}}}} 1606 id1 := GenerateID() 1607 1608 reference1 := GenerateID() 1609 prop1 := addTestProposal(t, ctx, propStore, id1, party1, reference1, block1, entities.ProposalState(s), rationale1, terms1, entities.ProposalErrorUnspecified, nil, entities.BatchProposalTerms{}) 1610 1611 prop1ID := prop1.ID.String() 1612 1613 expected := prop1 1614 actual, err := propStore.GetByID(ctx, prop1ID) 1615 require.NoError(t, err) 1616 assertProposalMatch(t, expected, actual) 1617 }) 1618 } 1619 }