Files
explorer-monorepo/frontend/src/services/api/transactions.test.ts
2026-04-16 14:27:44 -07:00

132 lines
3.9 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest'
import { transactionsApi } from './transactions'
describe('transactionsApi.listByBlockSafe', () => {
beforeEach(() => {
vi.restoreAllMocks()
})
it('returns normalized transactions for a specific block', async () => {
const fetchMock = vi.fn().mockResolvedValue({
ok: true,
json: async () => ({
items: [
{
hash: '0xabc',
block_number: 123,
block_hash: '0xdef',
transaction_index: 0,
from: { hash: '0x0000000000000000000000000000000000000001' },
to: { hash: '0x0000000000000000000000000000000000000002' },
value: '0',
gas_price: '1',
gas: '21000',
gas_used: '21000',
status: 'ok',
timestamp: '2026-04-16T09:40:12.000000Z',
},
],
next_page_params: { page: 2, page_size: 10 },
}),
})
vi.stubGlobal('fetch', fetchMock)
const result = await transactionsApi.listByBlockSafe(138, 123, 1, 10)
expect(result.ok).toBe(true)
expect(result.data).toHaveLength(1)
expect(result.hasNextPage).toBe(true)
expect(result.data[0]?.hash).toBe('0xabc')
expect(fetchMock).toHaveBeenCalledTimes(1)
expect(fetchMock.mock.calls[0]?.[0]).toEqual(
expect.stringContaining('/api/v2/blocks/123/transactions?page=1&page_size=10'),
)
})
it('returns a non-throwing failure result when the block transaction request fails', async () => {
vi.stubGlobal('fetch', vi.fn().mockRejectedValue(new Error('network down')))
const result = await transactionsApi.listByBlockSafe(138, 123, 1, 10)
expect(result).toEqual({
ok: false,
data: [],
hasNextPage: false,
})
})
})
describe('transactionsApi.diagnoseMissing', () => {
beforeEach(() => {
vi.restoreAllMocks()
})
it('distinguishes missing explorer and missing rpc results', async () => {
const fetchMock = vi.fn()
.mockResolvedValueOnce({
ok: false,
status: 404,
json: async () => ({ message: 'Not found' }),
})
.mockResolvedValueOnce({
ok: true,
json: async () => ({ result: null }),
})
.mockResolvedValueOnce({
ok: true,
json: async () => ({ result: null }),
})
.mockResolvedValueOnce({
ok: true,
json: async () => ({ result: '0x39fb85' }),
})
vi.stubGlobal('fetch', fetchMock)
const diagnostic = await transactionsApi.diagnoseMissing(
138,
'0x14d7259e6e75bbcd8979dc9e19f3001b0f328bbcdb730937f8cc2e6a52f26da6'
)
expect(diagnostic.checked_hash).toBe('0x14d7259e6e75bbcd8979dc9e19f3001b0f328bbcdb730937f8cc2e6a52f26da6')
expect(diagnostic.chain_id).toBe(138)
expect(diagnostic.explorer_indexed).toBe(false)
expect(diagnostic.rpc_transaction_found).toBe(false)
expect(diagnostic.rpc_receipt_found).toBe(false)
expect(diagnostic.latest_block_number).toBeTypeOf('number')
expect(diagnostic.rpc_url).toBe('https://rpc-http-pub.d-bis.org')
})
it('reports when rpc can still see a transaction the explorer has not indexed', async () => {
const fetchMock = vi.fn()
.mockResolvedValueOnce({
ok: false,
status: 404,
json: async () => ({ message: 'Not found' }),
})
.mockResolvedValueOnce({
ok: true,
json: async () => ({ result: { hash: '0xabc' } }),
})
.mockResolvedValueOnce({
ok: true,
json: async () => ({ result: null }),
})
.mockResolvedValueOnce({
ok: true,
json: async () => ({ result: '0x10' }),
})
vi.stubGlobal('fetch', fetchMock)
const diagnostic = await transactionsApi.diagnoseMissing(138, '0xabc')
expect(diagnostic.explorer_indexed).toBe(false)
expect(diagnostic.rpc_transaction_found).toBe(true)
expect(diagnostic.rpc_receipt_found).toBe(false)
expect(diagnostic.latest_block_number).toBe(16)
})
})