Files
SubTracker/apps/api/tests/unit/wallos-import-commit.test.ts
SmileQWQ 9b8d33fffb feat: align app timezone behavior on main
- add configurable business timezone handling across settings, calendar, notifications, statistics, renewals, and subscription forms

- preserve TZ as the default runtime fallback while letting saved settings override business date calculations

- port Wallos source-timezone preview handling from worker and store imported business dates using the active app timezone

- gate daily refresh cron execution against settings.timezone so low-frequency tasks follow the configured business day
2026-04-25 21:16:51 +08:00

149 lines
4.8 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
const { prismaMock, appendSubscriptionOrdersMock, appendSubscriptionOrderMock, replaceSubscriptionTagsMock, saveImportedLogoBufferMock } =
vi.hoisted(() => ({
prismaMock: {
tag: {
findMany: vi.fn(),
createMany: vi.fn()
},
subscription: {
createMany: vi.fn()
},
subscriptionTag: {
createMany: vi.fn()
}
},
appendSubscriptionOrdersMock: vi.fn(async () => undefined),
appendSubscriptionOrderMock: vi.fn(async () => undefined),
replaceSubscriptionTagsMock: vi.fn(async () => undefined),
saveImportedLogoBufferMock: vi.fn()
}))
vi.mock('../../src/db', () => ({
prisma: prismaMock
}))
vi.mock('../../src/services/settings.service', () => ({
getAppSettings: vi.fn(async () => ({
defaultNotifyDays: 3,
baseCurrency: 'CNY'
})),
getAppTimezone: vi.fn(async () => 'Asia/Shanghai')
}))
vi.mock('../../src/services/subscription-order.service', () => ({
appendSubscriptionOrder: appendSubscriptionOrderMock,
appendSubscriptionOrders: appendSubscriptionOrdersMock
}))
vi.mock('../../src/services/tag.service', () => ({
replaceSubscriptionTags: replaceSubscriptionTagsMock
}))
vi.mock('../../src/services/logo.service', () => ({
saveImportedLogoBuffer: saveImportedLogoBufferMock
}))
import { commitWallosImport, inspectWallosImportFile } from '../../src/services/wallos-import.service'
function encodeJson(value: unknown) {
return Buffer.from(JSON.stringify(value)).toString('base64')
}
describe('commitWallosImport', () => {
beforeEach(() => {
vi.useFakeTimers()
vi.setSystemTime(new Date('2026-04-25T00:00:00.000Z'))
prismaMock.tag.findMany.mockReset()
prismaMock.tag.createMany.mockReset()
prismaMock.subscription.createMany.mockReset()
prismaMock.subscriptionTag.createMany.mockReset()
appendSubscriptionOrdersMock.mockReset()
appendSubscriptionOrderMock.mockReset()
replaceSubscriptionTagsMock.mockReset()
saveImportedLogoBufferMock.mockReset()
})
afterEach(() => {
vi.useRealTimers()
})
it('batches imported tags, subscriptions, tag joins, and order writes', async () => {
const preview = await inspectWallosImportFile({
filename: 'wallos.json',
contentType: 'application/json',
base64: encodeJson([
{
ID: 1,
Type: 'Subscription',
Name: 'Netflix',
Price: '$10',
Category: 'Video',
'Payment Cycle': 'Yearly',
'Next Payment': '2025-01-10',
Renewal: 'Automatic',
URL: 'netflix.com',
Notes: ''
},
{
ID: 2,
Type: 'Subscription',
Name: 'Spotify',
Price: '$15',
Category: 'Music',
'Payment Cycle': 'Monthly',
'Next Payment': '2026-05-02',
URL: 'https://spotify.com',
Notes: ''
}
])
})
prismaMock.tag.findMany
.mockResolvedValueOnce([])
.mockResolvedValueOnce([
{ id: 'tag_video', name: 'Video' },
{ id: 'tag_music', name: 'Music' }
])
prismaMock.tag.createMany.mockResolvedValue({ count: 2 })
prismaMock.subscription.createMany.mockResolvedValue({ count: 2 })
prismaMock.subscriptionTag.createMany.mockResolvedValue({ count: 2 })
const result = await commitWallosImport({ importToken: preview.importToken })
expect(prismaMock.tag.findMany).toHaveBeenCalledTimes(2)
expect(prismaMock.tag.createMany).toHaveBeenCalledWith({
data: [
{ name: 'Video', color: '#ef4444', sortOrder: 1 },
{ name: 'Music', color: '#ef4444', sortOrder: 2 }
]
})
expect(prismaMock.subscription.createMany).toHaveBeenCalledTimes(1)
expect(prismaMock.subscription.createMany.mock.calls[0][0].data).toHaveLength(2)
const createdRows = prismaMock.subscription.createMany.mock.calls[0][0].data
const createdIds = createdRows.map((item: { id: string }) => item.id)
expect(createdRows[0]).toMatchObject({
currency: 'USD',
websiteUrl: 'https://netflix.com/',
nextRenewalDate: new Date('2027-01-09T16:00:00.000Z'),
status: 'active'
})
expect(prismaMock.subscriptionTag.createMany).toHaveBeenCalledWith({
data: [
{ subscriptionId: createdIds[0], tagId: 'tag_video' },
{ subscriptionId: createdIds[1], tagId: 'tag_music' }
]
})
expect(appendSubscriptionOrdersMock).toHaveBeenCalledTimes(1)
expect(appendSubscriptionOrdersMock).toHaveBeenCalledWith(createdIds)
expect(appendSubscriptionOrderMock).not.toHaveBeenCalled()
expect(replaceSubscriptionTagsMock).not.toHaveBeenCalled()
expect(result).toMatchObject({
importedTags: 2,
importedSubscriptions: 2,
skippedSubscriptions: 0
})
})
})