feat: implement user balance management with hooks and store
This commit is contained in:
176
tests/stores/userBalanceStore.test.ts
Normal file
176
tests/stores/userBalanceStore.test.ts
Normal file
@@ -0,0 +1,176 @@
|
||||
import { act, renderHook } from '@testing-library/react-native'
|
||||
|
||||
import { useUserBalanceStore } from '@/stores/userBalanceStore'
|
||||
import { subscription } from '@/lib/auth'
|
||||
|
||||
// Mock subscription API
|
||||
jest.mock('@/lib/auth', () => ({
|
||||
subscription: {
|
||||
list: jest.fn(),
|
||||
},
|
||||
}))
|
||||
|
||||
// Mock AppState
|
||||
jest.mock('react-native', () => ({
|
||||
AppState: {
|
||||
addEventListener: jest.fn(() => ({ remove: jest.fn() })),
|
||||
},
|
||||
}))
|
||||
|
||||
describe('userBalanceStore', () => {
|
||||
beforeEach(() => {
|
||||
// Reset store state before each test
|
||||
const { reset } = useUserBalanceStore.getState()
|
||||
reset()
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('load', () => {
|
||||
it('should load balance from API', async () => {
|
||||
const mockData = [
|
||||
{
|
||||
type: 'metered',
|
||||
creditBalance: {
|
||||
remainingTokenBalance: 1000,
|
||||
},
|
||||
},
|
||||
]
|
||||
;(subscription.list as jest.Mock).mockResolvedValue({ data: mockData })
|
||||
|
||||
const { load } = useUserBalanceStore.getState()
|
||||
await act(async () => {
|
||||
await load(true)
|
||||
})
|
||||
|
||||
const { balance } = useUserBalanceStore.getState()
|
||||
expect(balance).toBe(1000)
|
||||
})
|
||||
|
||||
it('should handle API error', async () => {
|
||||
const mockError = { message: 'API Error', status: 500, statusText: 'Internal Server Error' }
|
||||
;(subscription.list as jest.Mock).mockResolvedValue({ error: mockError })
|
||||
|
||||
const { load } = useUserBalanceStore.getState()
|
||||
await act(async () => {
|
||||
await load(true)
|
||||
})
|
||||
|
||||
const { error } = useUserBalanceStore.getState()
|
||||
expect(error).toEqual(mockError)
|
||||
})
|
||||
|
||||
it('should debounce requests within 5 seconds', async () => {
|
||||
const mockData = [
|
||||
{
|
||||
type: 'metered',
|
||||
creditBalance: {
|
||||
remainingTokenBalance: 500,
|
||||
},
|
||||
},
|
||||
]
|
||||
;(subscription.list as jest.Mock).mockResolvedValue({ data: mockData })
|
||||
|
||||
const { load } = useUserBalanceStore.getState()
|
||||
|
||||
// First call
|
||||
await act(async () => {
|
||||
await load(false)
|
||||
})
|
||||
|
||||
// Second call within 5 seconds (should be skipped)
|
||||
await act(async () => {
|
||||
await load(false)
|
||||
})
|
||||
|
||||
// API should only be called once
|
||||
expect(subscription.list).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('should force load when force=true', async () => {
|
||||
const mockData = [
|
||||
{
|
||||
type: 'metered',
|
||||
creditBalance: {
|
||||
remainingTokenBalance: 500,
|
||||
},
|
||||
},
|
||||
]
|
||||
;(subscription.list as jest.Mock).mockResolvedValue({ data: mockData })
|
||||
|
||||
const { load } = useUserBalanceStore.getState()
|
||||
|
||||
// First call
|
||||
await act(async () => {
|
||||
await load(true)
|
||||
})
|
||||
|
||||
// Second call with force=true (should not be skipped)
|
||||
await act(async () => {
|
||||
await load(true)
|
||||
})
|
||||
|
||||
// API should be called twice
|
||||
expect(subscription.list).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
})
|
||||
|
||||
describe('setBalance', () => {
|
||||
it('should set balance directly', () => {
|
||||
const { setBalance } = useUserBalanceStore.getState()
|
||||
|
||||
act(() => {
|
||||
setBalance(2000)
|
||||
})
|
||||
|
||||
const { balance } = useUserBalanceStore.getState()
|
||||
expect(balance).toBe(2000)
|
||||
})
|
||||
})
|
||||
|
||||
describe('deductBalance', () => {
|
||||
it('should deduct balance', () => {
|
||||
const { setBalance, deductBalance } = useUserBalanceStore.getState()
|
||||
|
||||
act(() => {
|
||||
setBalance(1000)
|
||||
deductBalance(300)
|
||||
})
|
||||
|
||||
const { balance } = useUserBalanceStore.getState()
|
||||
expect(balance).toBe(700)
|
||||
})
|
||||
|
||||
it('should not go below zero', () => {
|
||||
const { setBalance, deductBalance } = useUserBalanceStore.getState()
|
||||
|
||||
act(() => {
|
||||
setBalance(100)
|
||||
deductBalance(500)
|
||||
})
|
||||
|
||||
const { balance } = useUserBalanceStore.getState()
|
||||
expect(balance).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('reset', () => {
|
||||
it('should reset all state', () => {
|
||||
const { setBalance, reset } = useUserBalanceStore.getState()
|
||||
|
||||
act(() => {
|
||||
setBalance(5000)
|
||||
})
|
||||
|
||||
expect(useUserBalanceStore.getState().balance).toBe(5000)
|
||||
|
||||
act(() => {
|
||||
reset()
|
||||
})
|
||||
|
||||
const state = useUserBalanceStore.getState()
|
||||
expect(state.balance).toBe(0)
|
||||
expect(state.loading).toBe(false)
|
||||
expect(state.error).toBe(null)
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user