import { describe, it, expect, vi, beforeEach } from 'vitest' vi.mock('./company.repo') vi.mock('./settingsEntitlements', () => ({ resolveSettingsEntitlements: vi.fn(), })) vi.mock('../../lib/storage', () => ({ uploadImage: vi.fn().mockResolvedValue('http://localhost:4000/storage/companies/comp_1/brand/logo.jpg'), })) const repo = await import('./company.repo') const entitlements = await import('./settingsEntitlements') const service = await import('./company.service') const editableSettingsFeatures = [ 'settings.company_profile', 'settings.public_contact', 'settings.locale_currency', 'settings.carplace_listing', 'settings.branding_basic', 'settings.branding_custom', 'settings.branding_hero', 'settings.renter_payments', 'settings.rental_policies_basic', 'settings.additional_driver_fees', 'settings.insurance_policies', 'settings.pricing_rules', 'settings.accounting_defaults', 'settings.accounting_scheduled_delivery', 'settings.accounting_advanced_formats', ] as const function buildEditableEntitlements() { return { plan: 'PRO', subscriptionStatus: 'ACTIVE', currentPeriodEnd: null, trialEndAt: null, accessLevel: 'full', features: Object.fromEntries(editableSettingsFeatures.map((featureKey) => [featureKey, { available: true, editable: true, requiredPlan: null, reason: null, }])), } } const mockCompany = { id: 'comp_1', name: 'Test Rentals', slug: 'test-rentals', email: 'info@test.com', brand: null, subscription: null, _count: { vehicles: 5, customers: 10, reservations: 20 }, } const mockBrand = { id: 'brand_1', companyId: 'comp_1', displayName: 'Test Rentals', subdomain: 'test-rentals', logoUrl: null, heroImageUrl: null, customDomain: null, customDomainVerified: false, amanpayMerchantId: null, amanpaySecretKey: null, paypalEmail: null, } beforeEach(() => { vi.clearAllMocks() vi.mocked(entitlements.resolveSettingsEntitlements).mockResolvedValue(buildEditableEntitlements() as any) }) describe('company.service', () => { describe('getCompany', () => { it('returns company with brand and subscription', async () => { vi.mocked(repo.findCompany).mockResolvedValue(mockCompany as any) const result = await service.getCompany('comp_1') expect(result).toEqual(mockCompany) expect(repo.findCompany).toHaveBeenCalledWith('comp_1') }) }) describe('updateCompany', () => { it('updates company profile', async () => { vi.mocked(repo.updateCompany).mockResolvedValue({ ...mockCompany, name: 'Updated Rentals' } as any) const result = await service.updateCompany('comp_1', { name: 'Updated Rentals' }) expect(repo.updateCompany).toHaveBeenCalledWith('comp_1', { name: 'Updated Rentals' }) }) }) describe('uploadLogo', () => { it('uploads logo and upserts brand', async () => { vi.mocked(repo.upsertBrand).mockResolvedValue({ ...mockBrand, logoUrl: 'http://localhost:4000/storage/companies/comp_1/brand/logo.jpg' } as any) const result = await service.uploadLogo('comp_1', 'Test Rentals', 'test-rentals', Buffer.from('')) expect(repo.upsertBrand).toHaveBeenCalledWith( 'comp_1', expect.objectContaining({ logoUrl: 'http://localhost:4000/storage/companies/comp_1/brand/logo.jpg' }), expect.objectContaining({ logoUrl: 'http://localhost:4000/storage/companies/comp_1/brand/logo.jpg' }), ) }) }) describe('uploadHeroImage', () => { it('uploads hero image and upserts brand', async () => { vi.mocked(repo.upsertBrand).mockResolvedValue({ ...mockBrand, heroImageUrl: 'http://localhost:4000/storage/companies/comp_1/brand/logo.jpg' } as any) await service.uploadHeroImage('comp_1', 'Test Rentals', 'test-rentals', Buffer.from('')) expect(repo.upsertBrand).toHaveBeenCalledWith( 'comp_1', expect.objectContaining({ heroImageUrl: expect.any(String) }), expect.objectContaining({ heroImageUrl: expect.any(String) }), ) }) }) describe('checkSubdomainAvailability', () => { it('returns available=true when subdomain is free', async () => { vi.mocked(repo.findBrandBySubdomain).mockResolvedValue(null) const result = await service.checkSubdomainAvailability('my-rental', 'comp_1') expect(result.available).toBe(true) }) it('returns available=false when subdomain is taken', async () => { vi.mocked(repo.findBrandBySubdomain).mockResolvedValue(mockBrand as any) const result = await service.checkSubdomainAvailability('taken-rental', 'comp_1') expect(result.available).toBe(false) }) }) describe('setCustomDomain', () => { it('throws ConflictError when custom domain is already in use', async () => { vi.mocked(repo.findBrandByCustomDomain).mockResolvedValue(mockBrand as any) await expect(service.setCustomDomain('comp_1', 'Test Rentals', 'test-rentals', 'taken.com')).rejects.toThrow('This custom domain is already in use') }) it('sets custom domain when available', async () => { vi.mocked(repo.findBrandByCustomDomain).mockResolvedValue(null) vi.mocked(repo.upsertBrand).mockResolvedValue({ ...mockBrand, customDomain: 'my-rental.com' } as any) await service.setCustomDomain('comp_1', 'Test Rentals', 'test-rentals', 'My-Rental.com') expect(repo.upsertBrand).toHaveBeenCalledWith( 'comp_1', expect.objectContaining({ customDomain: 'my-rental.com' }), expect.objectContaining({ customDomain: 'my-rental.com' }), ) }) }) describe('updateInsurancePolicy', () => { it('throws NotFoundError when policy does not belong to company', async () => { vi.mocked(repo.updateInsurancePolicy).mockResolvedValue({ count: 0 } as any) await expect(service.updateInsurancePolicy('pol_1', 'comp_1', { name: 'CDW' })).rejects.toThrow('Insurance policy not found') }) }) describe('deletePricingRule', () => { it('throws NotFoundError when rule does not belong to company', async () => { vi.mocked(repo.deletePricingRule).mockResolvedValue({ count: 0 } as any) await expect(service.deletePricingRule('rule_1', 'comp_1')).rejects.toThrow('Pricing rule not found') }) }) })