import { beforeEach, describe, expect, it, vi } from 'vitest' vi.mock('./offer.repo', () => ({ findMany: vi.fn(), findByIdOrThrow: vi.fn(), create: vi.fn(), findFirst: vi.fn(), update: vi.fn(), deleteMany: vi.fn(), setActive: vi.fn(), getStats: vi.fn(), })) import { NotFoundError } from '../../http/errors' import * as repo from './offer.repo' import { createOffer, deleteOffer, getOffer, getOfferStats, listOffers, setOfferActive, updateOffer } from './offer.service' describe('offer.service', () => { beforeEach(() => vi.clearAllMocks()) it('keeps listing filters tenant-scoped and delegates them unchanged to the repository', async () => { vi.mocked(repo.findMany).mockResolvedValue([{ id: 'offer_1' }] as never) const result = await listOffers('company_1', { active: 'true', public: 'false' }) expect(repo.findMany).toHaveBeenCalledWith('company_1', { active: 'true', public: 'false' }) expect(result).toEqual([{ id: 'offer_1' }]) }) it('creates offers with separate offer data and vehicle assignment ids', async () => { vi.mocked(repo.create).mockResolvedValue({ id: 'offer_1' } as never) const payload = { title: 'Weekend deal', type: 'PERCENTAGE', discountValue: 15 } await createOffer('company_1', payload, ['vehicle_1', 'vehicle_2']) expect(repo.create).toHaveBeenCalledWith('company_1', payload, ['vehicle_1', 'vehicle_2']) }) it('throws NotFoundError before updating an offer outside the tenant boundary', async () => { vi.mocked(repo.findFirst).mockResolvedValue(null as never) await expect(updateOffer('offer_1', 'company_1', { title: 'Nope' })).rejects.toBeInstanceOf(NotFoundError) expect(repo.update).not.toHaveBeenCalled() }) it('updates an existing offer and preserves explicit vehicle assignment updates', async () => { vi.mocked(repo.findFirst).mockResolvedValue({ id: 'offer_1' } as never) vi.mocked(repo.update).mockResolvedValue({ id: 'offer_1', title: 'Summer' } as never) const result = await updateOffer('offer_1', 'company_1', { title: 'Summer' }, []) expect(repo.update).toHaveBeenCalledWith('offer_1', { title: 'Summer' }, []) expect(result).toEqual({ id: 'offer_1', title: 'Summer' }) }) it('activates, deactivates, deletes, and reads stats through tenant-scoped repository methods', async () => { vi.mocked(repo.setActive).mockResolvedValue({ id: 'offer_1', isActive: true } as never) vi.mocked(repo.deleteMany).mockResolvedValue({ count: 1 } as never) vi.mocked(repo.getStats).mockResolvedValue({ redemptions: 3 } as never) vi.mocked(repo.findByIdOrThrow).mockResolvedValue({ id: 'offer_1' } as never) await getOffer('offer_1', 'company_1') await setOfferActive('offer_1', 'company_1', true) await deleteOffer('offer_1', 'company_1') await getOfferStats('offer_1', 'company_1') expect(repo.findByIdOrThrow).toHaveBeenCalledWith('offer_1', 'company_1') expect(repo.setActive).toHaveBeenCalledWith('offer_1', 'company_1', true) expect(repo.deleteMany).toHaveBeenCalledWith('offer_1', 'company_1') expect(repo.getStats).toHaveBeenCalledWith('offer_1', 'company_1') }) })