import { QueryClientProvider } from '@tanstack/react-query'
import { renderHook, waitFor } from '@testing-library/react-native'
import React from 'react'
import { buildTestQueryClient } from '../../__utils__/query_client'
import * as useApiClientModule from '../../hooks/use_api_client'
import { useConversationValidate } from '../../hooks/use_conversation_validate'
const createWrapper = () => {
const queryClient = buildTestQueryClient()
return ({ children }: { children: React.ReactNode }) => (
{children}
)
}
const mockChatPost = (impl: jest.Mock) => {
jest.spyOn(useApiClientModule, 'useApiClient').mockReturnValue({
chat: { post: impl },
} as unknown as ReturnType)
return impl
}
const resolveWith = (warnings: { attribute: string; fullMessage: string }[]) =>
jest.fn().mockResolvedValue({
data: { type: 'ConversationValidate', id: '1', warnings },
links: {},
meta: {},
})
describe('useConversationValidate', () => {
afterEach(() => {
jest.restoreAllMocks()
})
it('does not fire the request when disabled', () => {
const post = mockChatPost(resolveWith([]))
const { result } = renderHook(
() => useConversationValidate({ payload: 'abc', enabled: false }),
{ wrapper: createWrapper() }
)
expect(post).not.toHaveBeenCalled()
expect(result.current.warnings).toEqual([])
expect(result.current.validationPending).toBe(false)
})
it('does not fire the request when payload is undefined', () => {
const post = mockChatPost(resolveWith([]))
const { result } = renderHook(
() => useConversationValidate({ payload: undefined, isLoadingPayload: true }),
{ wrapper: createWrapper() }
)
expect(post).not.toHaveBeenCalled()
// validationPending stays true while the upstream payload query is still loading
expect(result.current.validationPending).toBe(true)
})
it('returns warnings from the response and clears validationPending', async () => {
const warnings = [{ attribute: 'safety_policy', fullMessage: 'Needs a second adult.' }]
mockChatPost(resolveWith(warnings))
const { result } = renderHook(() => useConversationValidate({ payload: 'abc' }), {
wrapper: createWrapper(),
})
await waitFor(() => expect(result.current.warnings).toEqual(warnings))
expect(result.current.validationPending).toBe(false)
})
it('sends gender_id in attributes when provided', async () => {
const post = mockChatPost(resolveWith([]))
renderHook(() => useConversationValidate({ payload: 'abc', genderId: '42' }), {
wrapper: createWrapper(),
})
await waitFor(() => expect(post).toHaveBeenCalledTimes(1))
expect(post).toHaveBeenCalledWith(
expect.objectContaining({
url: '/me/conversation_validate',
data: {
data: {
type: 'ConversationValidate',
attributes: { payload: 'abc', gender_id: '42' },
},
},
})
)
})
it('omits gender_id when not provided', async () => {
const post = mockChatPost(resolveWith([]))
renderHook(() => useConversationValidate({ payload: 'abc' }), {
wrapper: createWrapper(),
})
await waitFor(() => expect(post).toHaveBeenCalledTimes(1))
const [call] = post.mock.calls
expect(call[0].data.data.attributes).toEqual({ payload: 'abc' })
})
it('falls back to empty warnings and unblocks on request error', async () => {
// Documents the current fail-open behavior: if validate errors, the user is allowed to proceed.
const post = mockChatPost(jest.fn().mockRejectedValue(new Error('boom')))
const { result } = renderHook(() => useConversationValidate({ payload: 'abc' }), {
wrapper: createWrapper(),
})
await waitFor(() => expect(post).toHaveBeenCalledTimes(1))
await waitFor(() => expect(result.current.validationPending).toBe(false))
expect(result.current.warnings).toEqual([])
})
})