fix(tests): enhance fetch mocks and type safety in settings and audit tests
Docker / build (push) Failing after 30s
Docker / build (push) Failing after 30s
Updated fetch mocks in settings and audit test files to improve type safety by specifying the fetch type. Adjusted handling of mock call parameters to ensure proper access to request options. This enhances test reliability and clarity in error handling scenarios.
This commit is contained in:
@@ -26,7 +26,7 @@ describe('settings telegram test', () => {
|
||||
it('returns telegram API error with hint', async () => {
|
||||
vi.stubGlobal(
|
||||
'fetch',
|
||||
vi.fn(async () =>
|
||||
vi.fn<typeof fetch>(async () =>
|
||||
Response.json({ ok: false, description: 'Bad Request: chat not found' }),
|
||||
),
|
||||
)
|
||||
@@ -39,7 +39,7 @@ describe('settings telegram test', () => {
|
||||
})
|
||||
|
||||
it('uses body overrides and falls back to db token', async () => {
|
||||
const fetchMock = vi.fn(async () => Response.json({ ok: true }))
|
||||
const fetchMock = vi.fn<typeof fetch>(async () => Response.json({ ok: true }))
|
||||
vi.stubGlobal('fetch', fetchMock)
|
||||
|
||||
const res = await app.inject({
|
||||
@@ -54,9 +54,9 @@ describe('settings telegram test', () => {
|
||||
const body = res.json() as { ok: boolean }
|
||||
expect(body.ok).toBe(true)
|
||||
|
||||
const call = fetchMock.mock.calls[0] as [string, RequestInit] | undefined
|
||||
const call = fetchMock.mock.calls[0]
|
||||
expect(call).toBeDefined()
|
||||
const sent = JSON.parse(String(call![1].body)) as {
|
||||
const sent = JSON.parse(String(call![1]?.body)) as {
|
||||
chat_id: string
|
||||
message_thread_id: number
|
||||
}
|
||||
@@ -65,7 +65,7 @@ describe('settings telegram test', () => {
|
||||
})
|
||||
|
||||
it('uses body token when provided', async () => {
|
||||
const fetchMock = vi.fn(async () => Response.json({ ok: true }))
|
||||
const fetchMock = vi.fn<typeof fetch>(async () => Response.json({ ok: true }))
|
||||
vi.stubGlobal('fetch', fetchMock)
|
||||
|
||||
await app.inject({
|
||||
@@ -77,7 +77,7 @@ describe('settings telegram test', () => {
|
||||
},
|
||||
})
|
||||
|
||||
const url = String((fetchMock.mock.calls[0] as [string])[0])
|
||||
const url = String(fetchMock.mock.calls[0]![0])
|
||||
expect(url).toContain('botoverride-token/')
|
||||
})
|
||||
})
|
||||
@@ -115,7 +115,7 @@ describe('settings cfdm sync', () => {
|
||||
})
|
||||
|
||||
it('requests full sync from CFDM', async () => {
|
||||
const fetchMock = vi.fn(async () =>
|
||||
const fetchMock = vi.fn<typeof fetch>(async () =>
|
||||
Response.json({
|
||||
ok: true,
|
||||
count: 1,
|
||||
@@ -141,9 +141,9 @@ describe('settings cfdm sync', () => {
|
||||
expect(res.json()).toMatchObject({ ok: true })
|
||||
expect((res.json() as { count: number }).count).toBeGreaterThanOrEqual(1)
|
||||
|
||||
const call = fetchMock.mock.calls[0] as [string, RequestInit] | undefined
|
||||
const call = fetchMock.mock.calls[0]
|
||||
expect(call?.[0]).toBe('http://cfdm.test/api/v1/integrations/vps-tracker/sync')
|
||||
expect((call?.[1].headers as Record<string, string>).Authorization).toBe(
|
||||
expect((call?.[1]?.headers as Record<string, string>).Authorization).toBe(
|
||||
'Bearer shared-token',
|
||||
)
|
||||
})
|
||||
@@ -154,7 +154,7 @@ describe('settings cfdm sync', () => {
|
||||
integrationToken: 'shared-token',
|
||||
cfdmApiUrl: 'http://cfdm.test',
|
||||
})
|
||||
const fetchMock = vi.fn(async () =>
|
||||
const fetchMock = vi.fn<typeof fetch>(async () =>
|
||||
Response.json({ ok: true, count: 0, bindings: [], fullSync: true }),
|
||||
)
|
||||
vi.stubGlobal('fetch', fetchMock)
|
||||
@@ -173,7 +173,7 @@ describe('settings cfdm sync', () => {
|
||||
})
|
||||
vi.stubGlobal(
|
||||
'fetch',
|
||||
vi.fn(async () => {
|
||||
vi.fn<typeof fetch>(async () => {
|
||||
throw new TypeError('fetch failed')
|
||||
}),
|
||||
)
|
||||
@@ -211,18 +211,19 @@ describe('settings cfdm sync', () => {
|
||||
id: 'cfdm',
|
||||
name: 'CFDM',
|
||||
url: 'http://192.168.100.67:6363',
|
||||
icon: 'cloud',
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
const fetchMock = vi.fn(async () =>
|
||||
const fetchMock = vi.fn<typeof fetch>(async () =>
|
||||
Response.json({ ok: true, count: 0, bindings: [], fullSync: true }),
|
||||
)
|
||||
vi.stubGlobal('fetch', fetchMock)
|
||||
|
||||
const res = await app.inject({ method: 'POST', url: '/api/settings/cfdm/sync' })
|
||||
expect(res.statusCode).toBe(200)
|
||||
const call = fetchMock.mock.calls[0] as [string] | undefined
|
||||
const call = fetchMock.mock.calls[0]
|
||||
expect(call?.[0]).toBe('https://cfdm.prod.example/api/v1/integrations/vps-tracker/sync')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -46,7 +46,9 @@ describe('audit dual-write', () => {
|
||||
})
|
||||
|
||||
it('fire-and-forgets portal ingest with vps action key', async () => {
|
||||
const fetchMock = vi.fn(async () => new Response(JSON.stringify({ accepted: 1, duplicates: 0 })))
|
||||
const fetchMock = vi.fn<typeof fetch>(
|
||||
async () => new Response(JSON.stringify({ accepted: 1, duplicates: 0 })),
|
||||
)
|
||||
vi.stubGlobal('fetch', fetchMock)
|
||||
|
||||
auditCreate(
|
||||
@@ -63,15 +65,15 @@ describe('audit dual-write', () => {
|
||||
|
||||
await vi.waitFor(() => expect(fetchMock).toHaveBeenCalledTimes(1))
|
||||
|
||||
const [url, init] = fetchMock.mock.calls[0] as [string, RequestInit]
|
||||
const [url, init] = fetchMock.mock.calls[0]!
|
||||
expect(url).toBe('http://portal.test/api/v1/ingest/audit')
|
||||
expect(init.method).toBe('POST')
|
||||
expect(init.headers).toMatchObject({
|
||||
expect(init?.method).toBe('POST')
|
||||
expect(init?.headers).toMatchObject({
|
||||
Authorization: 'Bearer test-ingest-secret',
|
||||
'Content-Type': 'application/json',
|
||||
})
|
||||
|
||||
const body = JSON.parse(String(init.body)) as {
|
||||
const body = JSON.parse(String(init?.body)) as {
|
||||
events: Array<{
|
||||
event_id: string
|
||||
source_app: string
|
||||
@@ -94,7 +96,7 @@ describe('audit dual-write', () => {
|
||||
it('does not throw when portal ingest fails', () => {
|
||||
vi.stubGlobal(
|
||||
'fetch',
|
||||
vi.fn(async () => {
|
||||
vi.fn<typeof fetch>(async () => {
|
||||
throw new Error('network down')
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -3,18 +3,15 @@
|
||||
*/
|
||||
|
||||
import type { BillmanagerSyncAccount } from './context.js'
|
||||
import type { SyncFromBillmanagerOptions, SyncFromBillmanagerResult } from './sync.js'
|
||||
import type { SyncFromBillmanagerOptions } from './sync.js'
|
||||
import { billmanagerAdapter } from '../providers/billmanager-adapter.js'
|
||||
import { runAccountSync } from '../providers/sync-job.js'
|
||||
import { runAccountSync, type RunAccountSyncResult } from '../providers/sync-job.js'
|
||||
|
||||
export interface RunBillmanagerAccountSyncResult extends SyncFromBillmanagerResult {
|
||||
ok: true
|
||||
logId: string
|
||||
}
|
||||
export type RunBillmanagerAccountSyncResult = RunAccountSyncResult
|
||||
|
||||
export async function runBillmanagerAccountSync(
|
||||
account: BillmanagerSyncAccount,
|
||||
opts: SyncFromBillmanagerOptions = {},
|
||||
): Promise<RunBillmanagerAccountSyncResult> {
|
||||
): Promise<RunAccountSyncResult> {
|
||||
return runAccountSync(billmanagerAdapter, account, opts)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,12 @@ export interface SyncResult {
|
||||
vpsCount: number
|
||||
paymentsCount: number
|
||||
tariffsCount: number
|
||||
balance: { balance?: number; currency?: string } | null
|
||||
balance: {
|
||||
balance?: number
|
||||
currency?: string
|
||||
enoughmoneyto?: string
|
||||
realbalance?: string
|
||||
} | null
|
||||
syncSummary: SyncSummary
|
||||
newTariffs: { name: string; price: string; providerId: string }[]
|
||||
}
|
||||
|
||||
@@ -30,20 +30,10 @@ function roundRate(n: number): number {
|
||||
return Math.round(n * 100) / 100
|
||||
}
|
||||
|
||||
function mapBillingCycle(cycle: string | undefined): {
|
||||
tariffType: string
|
||||
dailyRate: number | null
|
||||
monthlyRate: number | null
|
||||
} {
|
||||
const c = String(cycle ?? '').toLowerCase()
|
||||
return { tariffType: c.includes('day') ? 'daily' : 'monthly', dailyRate: null, monthlyRate: null }
|
||||
}
|
||||
|
||||
function ratesFromTotal(
|
||||
total: number,
|
||||
cycle: string | undefined,
|
||||
): { tariffType: string; dailyRate: number | null; monthlyRate: number | null } {
|
||||
const base = mapBillingCycle(cycle)
|
||||
const c = String(cycle ?? '').toLowerCase()
|
||||
if (c.includes('day')) {
|
||||
return { tariffType: 'daily', dailyRate: roundRate(total), monthlyRate: null }
|
||||
@@ -253,7 +243,7 @@ export function mapVpsRecordToVps(
|
||||
currency,
|
||||
dailyRate: rates.dailyRate,
|
||||
monthlyRate: rates.monthlyRate,
|
||||
createdAt: dateToIso(detail.date_created),
|
||||
createdAt: dateToIso(serviceDetail?.date_created),
|
||||
paidUntil: dateToIso(detail.next_due ?? service.next_due),
|
||||
notes: label ? `${label} [veesp-${externalId}]` : `veesp-${externalId}`,
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ export interface VeespVmDetail extends VeespVmListItem {
|
||||
}
|
||||
|
||||
export interface VeespIpItem {
|
||||
id?: string | number
|
||||
ip?: string
|
||||
address?: string
|
||||
ipaddress?: string
|
||||
@@ -207,7 +208,7 @@ export function isVpsService(service: VeespServiceListItem, vpsCategoryIds?: Set
|
||||
return false
|
||||
}
|
||||
|
||||
function unwrapKeyedList<T extends Record<string, unknown>>(raw: unknown): T[] {
|
||||
function unwrapKeyedList<T>(raw: unknown): T[] {
|
||||
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return []
|
||||
const out: T[] = []
|
||||
for (const [key, item] of Object.entries(raw as Record<string, unknown>)) {
|
||||
@@ -218,7 +219,7 @@ function unwrapKeyedList<T extends Record<string, unknown>>(raw: unknown): T[] {
|
||||
}
|
||||
if (typeof item !== 'object') continue
|
||||
const row = item as Record<string, unknown>
|
||||
out.push({ ...row, id: row.id ?? key } as T)
|
||||
out.push({ ...row, id: row.id ?? key } as unknown as T)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user