@bigmi/core
Version:
TypeScript library for Bitcoin apps.
104 lines (89 loc) • 3.68 kB
text/typescript
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { getUTXOs } from '../actions/getUTXOs'
import { bitcoin } from '../chains/bitcoin'
import { AllTransportsFailedError } from '../errors/transport'
import { InsufficientUTXOBalanceError } from '../errors/utxo'
import { createClient, rpcSchema } from '../factories/createClient'
import { createMockResponse } from '../test/utils'
import blockchairLimitedResponse from './blockchair/__mocks__/getUTXOs/rateLimited.json'
import blockchairValidResponse from './blockchair/__mocks__/getUTXOs/valid.json'
import { blockchair } from './blockchair/blockchair'
import blockcypherLimitedResponse from './blockcypher/__mocks__/getUTXOs/rateLimited.json'
import blockcypherValidResponse from './blockcypher/__mocks__/getUTXOs/valid.json'
import { blockcypher } from './blockcypher/blockcypher'
import { fallback } from './fallback'
import type { UTXOSchema } from './types'
const address = import.meta.env.VITE_TEST_ADDRESS
const apiKey = import.meta.env.VITE_TEST_BLOCKCHAIR_KEY
const blockCypherKey = import.meta.env.VITE_TEST_BLOCKCYPHER_API_KEY
const publicClient = createClient({
chain: bitcoin,
rpcSchema: rpcSchema<UTXOSchema>(),
transport: fallback([
blockcypher({ apiKey: blockCypherKey }),
blockchair({ apiKey }),
]),
})
describe('Fallback Transport', () => {
beforeEach(() => {
vi.restoreAllMocks()
})
describe('getUTXOs', () => {
it('should use blockcypher when it succeeds', async () => {
vi.spyOn(global, 'fetch').mockResolvedValue(
createMockResponse(blockcypherValidResponse)
)
const utxos = await getUTXOs(publicClient, { address })
expect(utxos.length).toBeGreaterThan(0)
})
it('should fallback to blockchair when blockcypher fails', async () => {
vi.spyOn(global, 'fetch').mockImplementation((request) => {
const url = new URL(request.toString())
if (url.hostname.includes('blockcypher')) {
return Promise.resolve(
createMockResponse(blockcypherLimitedResponse, { status: 429 })
)
}
return Promise.resolve(createMockResponse(blockchairValidResponse))
})
const utxos = await getUTXOs(publicClient, { address })
expect(utxos.length).toBeGreaterThan(0)
})
it('should throw error when both transports fail', async () => {
vi.spyOn(global, 'fetch').mockImplementation((request) => {
const url = new URL(request.toString())
if (url.hostname.includes('blockcypher')) {
return Promise.resolve(
createMockResponse(blockcypherLimitedResponse, { status: 429 })
)
}
return Promise.resolve(
createMockResponse(blockchairLimitedResponse, { status: 429 })
)
})
await expect(getUTXOs(publicClient, { address })).rejects.toThrow(
AllTransportsFailedError
)
})
it('should not call blockchair if user has insufficientUTXOs', async () => {
const blockchairSpy = vi
.spyOn(global, 'fetch')
.mockImplementation((request) => {
const url = new URL(request.toString())
if (url.hostname.includes('blockcypher')) {
return Promise.resolve(createMockResponse(blockcypherValidResponse))
}
return Promise.resolve(createMockResponse(blockchairValidResponse))
})
await expect(
getUTXOs(publicClient, {
address,
minValue: 1000000,
})
).rejects.toThrow(InsufficientUTXOBalanceError)
expect(blockchairSpy).not.toHaveBeenCalledWith(
expect.stringContaining('blockchair')
)
})
})
})