hardening e2e tests for newly discovered bugs

This commit is contained in:
pragmaxim
2026-02-26 07:00:23 +01:00
parent 0703a69e63
commit 87c20fd4e2
4 changed files with 117 additions and 9 deletions

View File

@@ -32,6 +32,7 @@ func testGetAddressTxidsPaginationEVM(t *testing.T, h *TestHandler) {
assertAddressMatches(t, page1.Address, address, "GetAddressTxidsPaginationEVM.page1.address")
assertPageMeta(t, page1.Page, page1.ItemsOnPage, page1.TotalPages, page1.Txs, "GetAddressTxidsPaginationEVM.page1")
assertPageSizeUpperBound(t, len(page1.Txids), page1.ItemsOnPage, evmHistoryPageSize, "GetAddressTxidsPaginationEVM.page1.txids")
if len(page1.Txids) == 0 {
t.Fatalf("GetAddressTxidsPaginationEVM page 1 returned no txids")
}
@@ -48,6 +49,7 @@ func testGetAddressTxidsPaginationEVM(t *testing.T, h *TestHandler) {
assertAddressMatches(t, page2.Address, address, "GetAddressTxidsPaginationEVM.page2.address")
assertPageMeta(t, page2.Page, page2.ItemsOnPage, page2.TotalPages, page2.Txs, "GetAddressTxidsPaginationEVM.page2")
assertPageSizeUpperBound(t, len(page2.Txids), page2.ItemsOnPage, evmHistoryPageSize, "GetAddressTxidsPaginationEVM.page2.txids")
if page2.Page != evmHistoryPage+1 {
t.Fatalf("GetAddressTxidsPaginationEVM page mismatch: got %d, want %d", page2.Page, evmHistoryPage+1)
}
@@ -67,6 +69,7 @@ func testGetAddressTxsPaginationEVM(t *testing.T, h *TestHandler) {
assertAddressMatches(t, page1.Address, address, "GetAddressTxsPaginationEVM.page1.address")
assertPageMeta(t, page1.Page, page1.ItemsOnPage, page1.TotalPages, page1.Txs, "GetAddressTxsPaginationEVM.page1")
assertPageSizeUpperBound(t, len(page1.Transactions), page1.ItemsOnPage, evmHistoryPageSize, "GetAddressTxsPaginationEVM.page1.transactions")
if len(page1.Transactions) == 0 {
t.Fatalf("GetAddressTxsPaginationEVM page 1 returned no transactions")
}
@@ -81,6 +84,7 @@ func testGetAddressTxsPaginationEVM(t *testing.T, h *TestHandler) {
assertAddressMatches(t, page2.Address, address, "GetAddressTxsPaginationEVM.page2.address")
assertPageMeta(t, page2.Page, page2.ItemsOnPage, page2.TotalPages, page2.Txs, "GetAddressTxsPaginationEVM.page2")
assertPageSizeUpperBound(t, len(page2.Transactions), page2.ItemsOnPage, evmHistoryPageSize, "GetAddressTxsPaginationEVM.page2.transactions")
if page2.Page != evmHistoryPage+1 {
t.Fatalf("GetAddressTxsPaginationEVM page mismatch: got %d, want %d", page2.Page, evmHistoryPage+1)
}

View File

@@ -130,7 +130,7 @@ func testGetAddressTxids(t *testing.T, h *TestHandler) {
var addr addressTxidsResponse
h.mustGetJSON(t, path, &addr)
assertAddressTxidsPayload(t, &addr, address, txid, "GetAddressTxids")
assertAddressTxidsPayload(t, &addr, address, txid, "GetAddressTxids", addressPageSize)
}
func testGetAddressTxs(t *testing.T, h *TestHandler) {
@@ -141,7 +141,7 @@ func testGetAddressTxs(t *testing.T, h *TestHandler) {
var addr addressTxsResponse
h.mustGetJSON(t, path, &addr)
assertAddressTxsPayload(t, &addr, address, txid, "GetAddressTxs")
assertAddressTxsPayload(t, &addr, address, txid, "GetAddressTxs", addressPageSize)
}
func testGetUtxo(t *testing.T, h *TestHandler) {
@@ -155,18 +155,40 @@ func testGetUtxo(t *testing.T, h *TestHandler) {
func testGetUtxoConfirmedFilter(t *testing.T, h *TestHandler) {
address := h.sampleAddressOrSkip(t)
var all []utxoResponse
h.mustGetJSON(t, "/api/v2/utxo/"+url.PathEscape(address), &all)
var confirmed []utxoResponse
h.mustGetJSON(t, "/api/v2/utxo/"+url.PathEscape(address)+"?confirmed=true", &confirmed)
if len(all) == 0 && len(confirmed) == 0 {
var all []utxoResponse
h.mustGetJSON(t, "/api/v2/utxo/"+url.PathEscape(address), &all)
var explicitFalse []utxoResponse
h.mustGetJSON(t, "/api/v2/utxo/"+url.PathEscape(address)+"?confirmed=false", &explicitFalse)
if len(all) == 0 && len(explicitFalse) == 0 && len(confirmed) == 0 {
t.Skipf("Skipping test, address %s currently has no UTXOs", address)
}
assertUTXOListConfirmed(t, confirmed, "GetUtxoConfirmedFilter")
assertUTXOList(t, all, "GetUtxoConfirmedFilter.all")
assertUTXOList(t, explicitFalse, "GetUtxoConfirmedFilter.confirmed=false")
// confirmed=false should be equivalent to omitted confirmed query parameter.
// Retry once to reduce false positives from highly dynamic mempool state.
if !utxoSetsEqualByOutpoint(all, explicitFalse) {
var allRetry []utxoResponse
h.mustGetJSON(t, "/api/v2/utxo/"+url.PathEscape(address), &allRetry)
var explicitFalseRetry []utxoResponse
h.mustGetJSON(t, "/api/v2/utxo/"+url.PathEscape(address)+"?confirmed=false", &explicitFalseRetry)
assertUTXOList(t, allRetry, "GetUtxoConfirmedFilter.all.retry")
assertUTXOList(t, explicitFalseRetry, "GetUtxoConfirmedFilter.confirmed=false.retry")
assertUTXOSetsEqualByOutpoint(t, allRetry, explicitFalseRetry, "GetUtxoConfirmedFilter.default-vs-confirmed=false")
all = allRetry
explicitFalse = explicitFalseRetry
}
// confirmed=false includes mempool effects, but any confirmed outpoint in that
// response must also exist in confirmed=true.
assertConfirmedUTXOsIncludedByOutpoint(t, explicitFalse, confirmed, "GetUtxoConfirmedFilter.confirmed-false-vs-true")
}
func (h *TestHandler) mustGetJSON(t *testing.T, path string, out interface{}) {

View File

@@ -27,17 +27,19 @@ func buildAddressDetailsPathWithTo(address, details string, page, pageSize, toHe
return path
}
func assertAddressTxidsPayload(t *testing.T, payload *addressTxidsResponse, address, txid, context string) {
func assertAddressTxidsPayload(t *testing.T, payload *addressTxidsResponse, address, txid, context string, pageSize int) {
t.Helper()
assertAddressMatches(t, payload.Address, address, context+".address")
assertPageMeta(t, payload.Page, payload.ItemsOnPage, payload.TotalPages, payload.Txs, context)
assertPageSizeUpperBound(t, len(payload.Txids), payload.ItemsOnPage, pageSize, context+".txids")
assertTxIDListContains(t, payload.Txids, txid, context+".txids")
}
func assertAddressTxsPayload(t *testing.T, payload *addressTxsResponse, address, txid, context string) {
func assertAddressTxsPayload(t *testing.T, payload *addressTxsResponse, address, txid, context string, pageSize int) {
t.Helper()
assertAddressMatches(t, payload.Address, address, context+".address")
assertPageMeta(t, payload.Page, payload.ItemsOnPage, payload.TotalPages, payload.Txs, context)
assertPageSizeUpperBound(t, len(payload.Transactions), payload.ItemsOnPage, pageSize, context+".transactions")
assertTransactionsContainTxID(t, payload.Transactions, txid, context+".transactions")
}
@@ -79,6 +81,22 @@ func assertPageMetaAllowUnknownTotal(t *testing.T, page, itemsOnPage, totalPages
}
}
func assertPageSizeUpperBound(t *testing.T, payloadLen, itemsOnPage, requestedPageSize int, context string) {
t.Helper()
if requestedPageSize <= 0 {
return
}
if itemsOnPage > requestedPageSize {
t.Fatalf("%s invalid itemsOnPage %d > requested pageSize %d", context, itemsOnPage, requestedPageSize)
}
if payloadLen > requestedPageSize {
t.Fatalf("%s returned %d items, requested pageSize=%d", context, payloadLen, requestedPageSize)
}
if itemsOnPage > 0 && payloadLen > itemsOnPage {
t.Fatalf("%s returned %d items, greater than itemsOnPage=%d", context, payloadLen, itemsOnPage)
}
}
func assertTxIDListContains(t *testing.T, txids []string, txid, context string) {
t.Helper()
if len(txids) == 0 {
@@ -137,6 +155,70 @@ func assertUTXOListNonNegativeConfirmations(t *testing.T, utxos []utxoResponse,
}
}
func assertUTXOSetsEqualByOutpoint(t *testing.T, got, want []utxoResponse, context string) {
t.Helper()
gotSet := utxoSetByOutpoint(t, got, context+".got")
wantSet := utxoSetByOutpoint(t, want, context+".want")
if len(gotSet) != len(wantSet) {
t.Fatalf("%s outpoint count mismatch: got=%d want=%d", context, len(gotSet), len(wantSet))
}
for key := range wantSet {
if _, ok := gotSet[key]; !ok {
t.Fatalf("%s missing outpoint in got set: %s", context, key)
}
}
}
func assertConfirmedUTXOsIncludedByOutpoint(t *testing.T, mixed, confirmed []utxoResponse, context string) {
t.Helper()
confirmedSet := utxoSetByOutpoint(t, confirmed, context+".confirmed")
for i := range mixed {
if isUnconfirmedUtxo(mixed[i]) {
continue
}
key := utxoOutpointKey(mixed[i])
if _, ok := confirmedSet[key]; !ok {
t.Fatalf("%s missing confirmed outpoint %s in confirmed=true response", context, key)
}
}
}
func utxoSetsEqualByOutpoint(a, b []utxoResponse) bool {
if len(a) != len(b) {
return false
}
set := make(map[string]struct{}, len(a))
for i := range a {
set[utxoOutpointKey(a[i])] = struct{}{}
}
if len(set) != len(a) {
return false
}
for i := range b {
if _, ok := set[utxoOutpointKey(b[i])]; !ok {
return false
}
}
return true
}
func utxoSetByOutpoint(t *testing.T, utxos []utxoResponse, context string) map[string]utxoResponse {
t.Helper()
set := make(map[string]utxoResponse, len(utxos))
for i := range utxos {
key := utxoOutpointKey(utxos[i])
if _, exists := set[key]; exists {
t.Fatalf("%s duplicate outpoint: %s", context, key)
}
set[key] = utxos[i]
}
return set
}
func utxoOutpointKey(utxo utxoResponse) string {
return strings.ToLower(strings.TrimSpace(utxo.Txid)) + ":" + strconv.Itoa(utxo.Vout)
}
func assertEVMTokenBalancesPayload(t *testing.T, payload *evmAddressTokenBalanceResponse, address, context string) {
t.Helper()
assertAddressMatches(t, payload.Address, address, context+".address")

View File

@@ -66,7 +66,7 @@ func testWsGetAccountInfo(t *testing.T, h *TestHandler) {
if err := json.Unmarshal(resp.Data, &info); err != nil {
t.Fatalf("decode websocket getAccountInfo response: %v", err)
}
assertAddressTxidsPayload(t, &info, address, txid, "WsGetAccountInfo")
assertAddressTxidsPayload(t, &info, address, txid, "WsGetAccountInfo", addressPageSize)
}
func testWsGetAccountUtxo(t *testing.T, h *TestHandler) {