Files
archy/neode-ui/src/stores/__tests__/homeStatus.test.ts
T

96 lines
4.4 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest'
import { setActivePinia, createPinia } from 'pinia'
// Mock the rpc-client module
vi.mock('@/api/rpc-client', () => ({
rpcClient: {
call: vi.fn(),
vpnStatus: vi.fn(),
},
}))
import { useHomeStatusStore } from '../homeStatus'
import { rpcClient } from '@/api/rpc-client'
import { PackageState, type PackageDataEntry } from '@/types/api'
const mockedRpc = vi.mocked(rpcClient)
function pkg(state: string): Record<string, PackageDataEntry> {
return { 'bitcoin-knots': { state } as unknown as PackageDataEntry }
}
describe('homeStatus — B16 bitcoin sync status retain (no vanish, no stale-as-live)', () => {
beforeEach(() => {
setActivePinia(createPinia())
vi.clearAllMocks()
})
it('records a successful poll as available + not stale', async () => {
const store = useHomeStatusStore()
mockedRpc.call.mockResolvedValueOnce({ block_height: 800000, sync_progress: 1 })
await store.refreshBitcoin({})
expect(store.stats.bitcoinAvailable).toBe(true)
expect(store.stats.bitcoinSyncPercent).toBe(100)
expect(store.bitcoinStale).toBe(false)
expect(store.bitcoinLoadState).toBe('ready')
})
it('keeps the tile visible (available) but marks stale when getinfo fails while the container is Running', async () => {
const store = useHomeStatusStore()
// First a good poll so we have real sync numbers.
mockedRpc.call.mockResolvedValueOnce({ block_height: 800000, sync_progress: 0.5 })
await store.refreshBitcoin(pkg(PackageState.Running))
expect(store.stats.bitcoinAvailable).toBe(true)
// Now a transient RPC failure (e.g. RPC busy during heavy IBD) — container still Running.
mockedRpc.call.mockRejectedValueOnce(new Error('timeout'))
await store.refreshBitcoin(pkg(PackageState.Running))
expect(store.stats.bitcoinAvailable).toBe(true) // does NOT vanish
expect(store.bitcoinStale).toBe(true) // shown as "Updating…", not live
expect(store.stats.bitcoinSyncPercent).toBe(50) // last-known retained
})
it('flips to NOT available (and not stale) when getinfo fails and the container is Stopped — no stale-as-live', async () => {
const store = useHomeStatusStore()
mockedRpc.call.mockResolvedValueOnce({ block_height: 800000, sync_progress: 1 })
await store.refreshBitcoin(pkg(PackageState.Running))
expect(store.stats.bitcoinAvailable).toBe(true)
mockedRpc.call.mockRejectedValueOnce(new Error('refused'))
await store.refreshBitcoin(pkg(PackageState.Stopped))
expect(store.stats.bitcoinAvailable).toBe(false) // genuinely down → reflect it
expect(store.bitcoinStale).toBe(false) // not "Updating…": it's authoritatively stopped
})
it('retains the last-known available value (marked stale) when package data is momentarily absent', async () => {
const store = useHomeStatusStore()
mockedRpc.call.mockResolvedValueOnce({ block_height: 800000, sync_progress: 1 })
await store.refreshBitcoin(pkg(PackageState.Running))
expect(store.stats.bitcoinAvailable).toBe(true)
// getinfo fails AND the packages map has no authoritative bitcoin entry (route change / scan).
mockedRpc.call.mockRejectedValueOnce(new Error('timeout'))
await store.refreshBitcoin({})
expect(store.stats.bitcoinAvailable).toBe(true) // retained, does NOT flash "Not running"
expect(store.bitcoinStale).toBe(true)
expect(store.bitcoinLoadState).toBe('ready')
})
it('stays unknown (null) without fabricating availability when the first ever poll fails with no package data', async () => {
const store = useHomeStatusStore()
mockedRpc.call.mockRejectedValueOnce(new Error('timeout'))
await store.refreshBitcoin({})
expect(store.stats.bitcoinAvailable).toBeNull() // nothing known yet — don't invent a tile
expect(store.bitcoinLoadState).toBe('error')
})
it('marks bitcoin available + stale when the first poll times out but the container is Running (syncing node)', async () => {
const store = useHomeStatusStore()
// No prior success; getinfo times out during heavy initial sync, but container is up.
mockedRpc.call.mockRejectedValueOnce(new Error('timeout'))
await store.refreshBitcoin(pkg(PackageState.Running))
expect(store.stats.bitcoinAvailable).toBe(true) // tile appears instead of staying hidden
expect(store.bitcoinStale).toBe(true) // labeled "Updating…" since we have no live numbers yet
})
})