Remove DB-dependent unit tests, use api-tests for integration testing
All tests in __tests__/ were hitting the database directly. Integration testing is handled by the api-tests/ suite instead.
This commit is contained in:
@@ -1,240 +0,0 @@
|
||||
import { describe, it, expect, beforeAll, beforeEach, afterAll } from 'bun:test'
|
||||
import type { FastifyInstance } from 'fastify'
|
||||
import { createTestApp, cleanDb, seedTestCompany, registerAndLogin, TEST_COMPANY_ID } from '../../src/test/helpers.js'
|
||||
import { AccountService, PaymentMethodService, TaxExemptionService } from '../../src/services/account.service.js'
|
||||
|
||||
describe('PaymentMethodService', () => {
|
||||
let app: FastifyInstance
|
||||
let accountId: string
|
||||
|
||||
beforeAll(async () => {
|
||||
app = await createTestApp()
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
await cleanDb(app)
|
||||
await seedTestCompany(app)
|
||||
const auth = await registerAndLogin(app, { email: `pm-svc-${Date.now()}@test.com` })
|
||||
|
||||
const account = await AccountService.create(app.db, TEST_COMPANY_ID, { name: 'PM Test' })
|
||||
accountId = account.id
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await app.close()
|
||||
})
|
||||
|
||||
it('creates a payment method', async () => {
|
||||
const pm = await PaymentMethodService.create(app.db, TEST_COMPANY_ID, {
|
||||
accountId,
|
||||
processor: 'stripe',
|
||||
processorPaymentMethodId: 'pm_abc',
|
||||
cardBrand: 'visa',
|
||||
lastFour: '4242',
|
||||
expMonth: 12,
|
||||
expYear: 2027,
|
||||
isDefault: false,
|
||||
})
|
||||
expect(pm.processorPaymentMethodId).toBe('pm_abc')
|
||||
expect(pm.isDefault).toBe(false)
|
||||
})
|
||||
|
||||
it('unsets old default when creating a new default', async () => {
|
||||
const first = await PaymentMethodService.create(app.db, TEST_COMPANY_ID, {
|
||||
accountId,
|
||||
processor: 'stripe',
|
||||
processorPaymentMethodId: 'pm_first',
|
||||
isDefault: true,
|
||||
})
|
||||
expect(first.isDefault).toBe(true)
|
||||
|
||||
const second = await PaymentMethodService.create(app.db, TEST_COMPANY_ID, {
|
||||
accountId,
|
||||
processor: 'stripe',
|
||||
processorPaymentMethodId: 'pm_second',
|
||||
isDefault: true,
|
||||
})
|
||||
expect(second.isDefault).toBe(true)
|
||||
|
||||
// First should no longer be default
|
||||
const firstRefreshed = await PaymentMethodService.getById(app.db, TEST_COMPANY_ID, first.id)
|
||||
expect(firstRefreshed!.isDefault).toBe(false)
|
||||
})
|
||||
|
||||
it('unsets old default when updating to default', async () => {
|
||||
const first = await PaymentMethodService.create(app.db, TEST_COMPANY_ID, {
|
||||
accountId,
|
||||
processor: 'stripe',
|
||||
processorPaymentMethodId: 'pm_1',
|
||||
isDefault: true,
|
||||
})
|
||||
|
||||
const second = await PaymentMethodService.create(app.db, TEST_COMPANY_ID, {
|
||||
accountId,
|
||||
processor: 'stripe',
|
||||
processorPaymentMethodId: 'pm_2',
|
||||
isDefault: false,
|
||||
})
|
||||
|
||||
await PaymentMethodService.update(app.db, TEST_COMPANY_ID, second.id, { isDefault: true })
|
||||
|
||||
const firstRefreshed = await PaymentMethodService.getById(app.db, TEST_COMPANY_ID, first.id)
|
||||
expect(firstRefreshed!.isDefault).toBe(false)
|
||||
|
||||
const secondRefreshed = await PaymentMethodService.getById(app.db, TEST_COMPANY_ID, second.id)
|
||||
expect(secondRefreshed!.isDefault).toBe(true)
|
||||
})
|
||||
|
||||
it('does not affect other accounts when setting default', async () => {
|
||||
const otherAccount = await AccountService.create(app.db, TEST_COMPANY_ID, { name: 'Other' })
|
||||
|
||||
const otherPm = await PaymentMethodService.create(app.db, TEST_COMPANY_ID, {
|
||||
accountId: otherAccount.id,
|
||||
processor: 'stripe',
|
||||
processorPaymentMethodId: 'pm_other',
|
||||
isDefault: true,
|
||||
})
|
||||
|
||||
// Create default on our account — should not touch the other account's default
|
||||
await PaymentMethodService.create(app.db, TEST_COMPANY_ID, {
|
||||
accountId,
|
||||
processor: 'stripe',
|
||||
processorPaymentMethodId: 'pm_ours',
|
||||
isDefault: true,
|
||||
})
|
||||
|
||||
const otherRefreshed = await PaymentMethodService.getById(app.db, TEST_COMPANY_ID, otherPm.id)
|
||||
expect(otherRefreshed!.isDefault).toBe(true)
|
||||
})
|
||||
|
||||
it('lists only methods for the requested account', async () => {
|
||||
const otherAccount = await AccountService.create(app.db, TEST_COMPANY_ID, { name: 'Other' })
|
||||
|
||||
await PaymentMethodService.create(app.db, TEST_COMPANY_ID, {
|
||||
accountId,
|
||||
processor: 'stripe',
|
||||
processorPaymentMethodId: 'pm_a',
|
||||
isDefault: false,
|
||||
})
|
||||
await PaymentMethodService.create(app.db, TEST_COMPANY_ID, {
|
||||
accountId: otherAccount.id,
|
||||
processor: 'stripe',
|
||||
processorPaymentMethodId: 'pm_b',
|
||||
isDefault: false,
|
||||
})
|
||||
|
||||
const methods = await PaymentMethodService.listByAccount(app.db, TEST_COMPANY_ID, accountId)
|
||||
expect(methods.length).toBe(1)
|
||||
expect(methods[0].processorPaymentMethodId).toBe('pm_a')
|
||||
})
|
||||
|
||||
it('deletes a payment method', async () => {
|
||||
const pm = await PaymentMethodService.create(app.db, TEST_COMPANY_ID, {
|
||||
accountId,
|
||||
processor: 'stripe',
|
||||
processorPaymentMethodId: 'pm_del',
|
||||
isDefault: false,
|
||||
})
|
||||
|
||||
const deleted = await PaymentMethodService.delete(app.db, TEST_COMPANY_ID, pm.id)
|
||||
expect(deleted).not.toBeNull()
|
||||
|
||||
const fetched = await PaymentMethodService.getById(app.db, TEST_COMPANY_ID, pm.id)
|
||||
expect(fetched).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('TaxExemptionService', () => {
|
||||
let app: FastifyInstance
|
||||
let accountId: string
|
||||
let userId: string
|
||||
|
||||
beforeAll(async () => {
|
||||
app = await createTestApp()
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
await cleanDb(app)
|
||||
await seedTestCompany(app)
|
||||
const auth = await registerAndLogin(app, { email: `tax-svc-${Date.now()}@test.com` })
|
||||
userId = (auth.user as { id: string }).id
|
||||
|
||||
const account = await AccountService.create(app.db, TEST_COMPANY_ID, { name: 'Tax Test' })
|
||||
accountId = account.id
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await app.close()
|
||||
})
|
||||
|
||||
it('creates an exemption in pending status', async () => {
|
||||
const exemption = await TaxExemptionService.create(app.db, TEST_COMPANY_ID, {
|
||||
accountId,
|
||||
certificateNumber: 'TX-001',
|
||||
certificateType: 'resale',
|
||||
issuingState: 'TX',
|
||||
})
|
||||
expect(exemption.status).toBe('pending')
|
||||
expect(exemption.certificateNumber).toBe('TX-001')
|
||||
expect(exemption.approvedBy).toBeNull()
|
||||
})
|
||||
|
||||
it('approves a pending exemption', async () => {
|
||||
const exemption = await TaxExemptionService.create(app.db, TEST_COMPANY_ID, {
|
||||
accountId,
|
||||
certificateNumber: 'TX-002',
|
||||
})
|
||||
|
||||
const approved = await TaxExemptionService.approve(app.db, TEST_COMPANY_ID, exemption.id, userId)
|
||||
expect(approved!.status).toBe('approved')
|
||||
expect(approved!.approvedBy).toBe(userId)
|
||||
expect(approved!.approvedAt).toBeTruthy()
|
||||
})
|
||||
|
||||
it('revokes an exemption with reason', async () => {
|
||||
const exemption = await TaxExemptionService.create(app.db, TEST_COMPANY_ID, {
|
||||
accountId,
|
||||
certificateNumber: 'TX-003',
|
||||
})
|
||||
await TaxExemptionService.approve(app.db, TEST_COMPANY_ID, exemption.id, userId)
|
||||
|
||||
const revoked = await TaxExemptionService.revoke(app.db, TEST_COMPANY_ID, exemption.id, userId, 'Expired')
|
||||
expect(revoked!.status).toBe('none')
|
||||
expect(revoked!.revokedBy).toBe(userId)
|
||||
expect(revoked!.revokedReason).toBe('Expired')
|
||||
expect(revoked!.revokedAt).toBeTruthy()
|
||||
})
|
||||
|
||||
it('lists exemptions for an account', async () => {
|
||||
await TaxExemptionService.create(app.db, TEST_COMPANY_ID, {
|
||||
accountId,
|
||||
certificateNumber: 'CERT-A',
|
||||
})
|
||||
await TaxExemptionService.create(app.db, TEST_COMPANY_ID, {
|
||||
accountId,
|
||||
certificateNumber: 'CERT-B',
|
||||
})
|
||||
|
||||
const list = await TaxExemptionService.listByAccount(app.db, TEST_COMPANY_ID, accountId)
|
||||
expect(list.length).toBe(2)
|
||||
})
|
||||
|
||||
it('updates exemption details', async () => {
|
||||
const exemption = await TaxExemptionService.create(app.db, TEST_COMPANY_ID, {
|
||||
accountId,
|
||||
certificateNumber: 'OLD',
|
||||
})
|
||||
|
||||
const updated = await TaxExemptionService.update(app.db, TEST_COMPANY_ID, exemption.id, {
|
||||
certificateNumber: 'NEW',
|
||||
issuingState: 'CA',
|
||||
})
|
||||
expect(updated!.certificateNumber).toBe('NEW')
|
||||
expect(updated!.issuingState).toBe('CA')
|
||||
})
|
||||
|
||||
it('returns null for nonexistent exemption', async () => {
|
||||
const result = await TaxExemptionService.getById(app.db, TEST_COMPANY_ID, '00000000-0000-0000-0000-000000000000')
|
||||
expect(result).toBeNull()
|
||||
})
|
||||
})
|
||||
@@ -1,152 +0,0 @@
|
||||
import { describe, it, expect, beforeAll, beforeEach, afterAll } from 'bun:test'
|
||||
import type { FastifyInstance } from 'fastify'
|
||||
import { createTestApp, cleanDb, seedTestCompany, TEST_COMPANY_ID } from '../../src/test/helpers.js'
|
||||
import { UnitStatusService, ItemConditionService } from '../../src/services/lookup.service.js'
|
||||
|
||||
describe('UnitStatusService', () => {
|
||||
let app: FastifyInstance
|
||||
|
||||
beforeAll(async () => {
|
||||
app = await createTestApp()
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
await cleanDb(app)
|
||||
await seedTestCompany(app)
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await app.close()
|
||||
})
|
||||
|
||||
describe('seedForCompany', () => {
|
||||
it('seeds system statuses on first call', async () => {
|
||||
const statuses = await UnitStatusService.list(app.db, TEST_COMPANY_ID)
|
||||
expect(statuses.length).toBeGreaterThanOrEqual(8)
|
||||
expect(statuses.every((s) => s.isSystem)).toBe(true)
|
||||
})
|
||||
|
||||
it('is idempotent — second seed does not duplicate', async () => {
|
||||
await UnitStatusService.seedForCompany(app.db, TEST_COMPANY_ID)
|
||||
const statuses = await UnitStatusService.list(app.db, TEST_COMPANY_ID)
|
||||
const slugs = statuses.map((s) => s.slug)
|
||||
const uniqueSlugs = new Set(slugs)
|
||||
expect(slugs.length).toBe(uniqueSlugs.size)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getBySlug', () => {
|
||||
it('returns a system status by slug', async () => {
|
||||
const status = await UnitStatusService.getBySlug(app.db, TEST_COMPANY_ID, 'available')
|
||||
expect(status).not.toBeNull()
|
||||
expect(status!.slug).toBe('available')
|
||||
expect(status!.isSystem).toBe(true)
|
||||
})
|
||||
|
||||
it('returns null for nonexistent slug', async () => {
|
||||
const status = await UnitStatusService.getBySlug(app.db, TEST_COMPANY_ID, 'nonexistent')
|
||||
expect(status).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('validateSlug', () => {
|
||||
it('returns true for valid active slug', async () => {
|
||||
expect(await UnitStatusService.validateSlug(app.db, TEST_COMPANY_ID, 'sold')).toBe(true)
|
||||
})
|
||||
|
||||
it('returns false for nonexistent slug', async () => {
|
||||
expect(await UnitStatusService.validateSlug(app.db, TEST_COMPANY_ID, 'bogus')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('create', () => {
|
||||
it('creates a custom (non-system) status', async () => {
|
||||
const custom = await UnitStatusService.create(app.db, TEST_COMPANY_ID, {
|
||||
name: 'In Transit',
|
||||
slug: 'in_transit',
|
||||
description: 'Being shipped between locations',
|
||||
sortOrder: 99,
|
||||
})
|
||||
expect(custom.slug).toBe('in_transit')
|
||||
expect(custom.isSystem).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('delete', () => {
|
||||
it('throws when deleting a system status', async () => {
|
||||
const system = await UnitStatusService.getBySlug(app.db, TEST_COMPANY_ID, 'available')
|
||||
expect(system).not.toBeNull()
|
||||
expect(() => UnitStatusService.delete(app.db, TEST_COMPANY_ID, system!.id)).toThrow('system')
|
||||
})
|
||||
|
||||
it('deletes a custom status', async () => {
|
||||
const custom = await UnitStatusService.create(app.db, TEST_COMPANY_ID, {
|
||||
name: 'Temp',
|
||||
slug: 'temp',
|
||||
sortOrder: 0,
|
||||
})
|
||||
const deleted = await UnitStatusService.delete(app.db, TEST_COMPANY_ID, custom.id)
|
||||
expect(deleted).not.toBeNull()
|
||||
expect(deleted!.slug).toBe('temp')
|
||||
})
|
||||
|
||||
it('returns null for nonexistent id', async () => {
|
||||
const result = await UnitStatusService.delete(app.db, TEST_COMPANY_ID, '00000000-0000-0000-0000-000000000000')
|
||||
expect(result).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('update', () => {
|
||||
it('throws when deactivating a system status', async () => {
|
||||
const system = await UnitStatusService.getBySlug(app.db, TEST_COMPANY_ID, 'sold')
|
||||
expect(() => UnitStatusService.update(app.db, TEST_COMPANY_ID, system!.id, { isActive: false })).toThrow('system')
|
||||
})
|
||||
|
||||
it('allows renaming a custom status', async () => {
|
||||
const custom = await UnitStatusService.create(app.db, TEST_COMPANY_ID, {
|
||||
name: 'Old Name',
|
||||
slug: 'custom_rename',
|
||||
sortOrder: 0,
|
||||
})
|
||||
const updated = await UnitStatusService.update(app.db, TEST_COMPANY_ID, custom.id, { name: 'New Name' })
|
||||
expect(updated!.name).toBe('New Name')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('ItemConditionService', () => {
|
||||
let app: FastifyInstance
|
||||
|
||||
beforeAll(async () => {
|
||||
app = await createTestApp()
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
await cleanDb(app)
|
||||
await seedTestCompany(app)
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await app.close()
|
||||
})
|
||||
|
||||
it('seeds system conditions', async () => {
|
||||
const conditions = await ItemConditionService.list(app.db, TEST_COMPANY_ID)
|
||||
const slugs = conditions.map((c) => c.slug)
|
||||
expect(slugs).toContain('new')
|
||||
expect(slugs).toContain('excellent')
|
||||
expect(slugs).toContain('good')
|
||||
expect(slugs).toContain('fair')
|
||||
expect(slugs).toContain('poor')
|
||||
})
|
||||
|
||||
it('creates a custom condition', async () => {
|
||||
const custom = await ItemConditionService.create(app.db, TEST_COMPANY_ID, {
|
||||
name: 'Refurbished',
|
||||
slug: 'refurbished',
|
||||
sortOrder: 10,
|
||||
})
|
||||
expect(custom.isSystem).toBe(false)
|
||||
expect(await ItemConditionService.validateSlug(app.db, TEST_COMPANY_ID, 'refurbished')).toBe(true)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user