import { beforeEach, describe, expect, it, vi } from 'vitest' vi.mock('../../lib/storage', () => ({ uploadImage: vi.fn().mockResolvedValue('/storage/companies/company_1/brand/logo.jpg') })) vi.mock('./settingsEntitlements', () => ({ resolveSettingsEntitlements: vi.fn(), })) vi.mock('./company.repo', () => ({ findCompany: vi.fn(), updateCompany: vi.fn(), findBrand: vi.fn(), upsertBrand: vi.fn(), findBrandBySubdomain: vi.fn(), findBrandByCustomDomain: vi.fn(), clearCustomDomain: vi.fn(), findContractSettings: vi.fn(), upsertContractSettings: vi.fn(), findInsurancePolicies: vi.fn(), createInsurancePolicy: vi.fn(), updateInsurancePolicy: vi.fn(), findInsurancePolicyOrThrow: vi.fn(), deleteInsurancePolicy: vi.fn(), findPricingRules: vi.fn(), createPricingRule: vi.fn(), updatePricingRule: vi.fn(), findPricingRuleOrThrow: vi.fn(), deletePricingRule: vi.fn(), findAccountingSettings: vi.fn(), upsertAccountingSettings: vi.fn(), findApiKey: vi.fn(), regenerateApiKey: vi.fn(), })) 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 currentBrand = { id: 'brand_1', companyId: 'company_1', displayName: 'Atlas Cars', subdomain: 'atlas', amanpayMerchantId: 'merchant_1', amanpaySecretKey: 'secret_1', paypalEmail: null, paypalMerchantId: null, paymentMethodsEnabled: ['AMANPAY'], } beforeEach(() => { vi.clearAllMocks() vi.mocked(entitlements.resolveSettingsEntitlements).mockResolvedValue(buildEditableEntitlements() as any) delete process.env.NEXT_PUBLIC_CUSTOM_DOMAIN_TARGET }) describe('company.service edge behavior', () => { it('preserves existing AmanPay credentials when recomputing enabled payment methods', async () => { vi.mocked(repo.findBrand).mockResolvedValue(currentBrand as any) vi.mocked(repo.upsertBrand).mockResolvedValue({ ...currentBrand, paypalEmail: 'billing@example.test' } as any) const result = await service.updateBrand('company_1', { paypalEmail: 'billing@example.test' }, 'Atlas Cars', 'atlas') expect(repo.upsertBrand).toHaveBeenCalledWith( 'company_1', expect.objectContaining({ paypalEmail: 'billing@example.test', paymentMethodsEnabled: ['AMANPAY', 'PAYPAL'], }), expect.objectContaining({ displayName: 'Atlas Cars', subdomain: 'atlas', paypalEmail: 'billing@example.test', paymentMethodsEnabled: ['AMANPAY', 'PAYPAL'], }), ) expect(result).not.toHaveProperty('paypalEmail') expect(result.paypalConfigured).toBe(true) }) it('does not report AmanPay enabled when only one credential is available', async () => { vi.mocked(repo.findBrand).mockResolvedValue({ ...currentBrand, amanpaySecretKey: null } as any) vi.mocked(repo.upsertBrand).mockResolvedValue({ ...currentBrand, amanpaySecretKey: null } as any) await service.updateBrand('company_1', {}, 'Atlas Cars', 'atlas') expect(repo.upsertBrand).toHaveBeenCalledWith( 'company_1', expect.objectContaining({ paymentMethodsEnabled: [] }), expect.objectContaining({ paymentMethodsEnabled: [] }), ) }) it('normalizes custom domains and marks them pending verification', async () => { vi.mocked(repo.findBrandByCustomDomain).mockResolvedValue(null as any) vi.mocked(repo.upsertBrand).mockResolvedValue({ id: 'brand_1', customDomain: 'cars.example.com' } as any) await service.setCustomDomain('company_1', 'Atlas Cars', 'atlas', ' CARS.Example.COM ') expect(repo.findBrandByCustomDomain).toHaveBeenCalledWith('cars.example.com', 'company_1') expect(repo.upsertBrand).toHaveBeenCalledWith( 'company_1', expect.objectContaining({ customDomain: 'cars.example.com', customDomainVerified: false, customDomainAddedAt: expect.any(Date) }), expect.objectContaining({ displayName: 'Atlas Cars', subdomain: 'atlas', customDomain: 'cars.example.com', customDomainVerified: false, customDomainAddedAt: expect.any(Date) }), ) }) it('rejects custom domains already owned by another company', async () => { vi.mocked(repo.findBrandByCustomDomain).mockResolvedValue({ id: 'other_brand' } as any) await expect(service.setCustomDomain('company_1', 'Atlas Cars', 'atlas', 'cars.example.com')).rejects.toMatchObject({ statusCode: 409 }) expect(repo.upsertBrand).not.toHaveBeenCalled() }) it('returns deterministic custom-domain status and honors configured DNS target', async () => { process.env.NEXT_PUBLIC_CUSTOM_DOMAIN_TARGET = 'tenant-target.example.net' vi.mocked(repo.findBrand).mockResolvedValue({ customDomain: 'cars.example.com', customDomainVerified: false } as any) await expect(service.getCustomDomainStatus('company_1')).resolves.toEqual({ customDomain: 'cars.example.com', verified: false, status: 'pending_dns', dnsTarget: 'tenant-target.example.net', }) }) it('raises not found when updating a missing insurance policy', async () => { vi.mocked(repo.updateInsurancePolicy).mockResolvedValue({ count: 0 } as any) await expect(service.updateInsurancePolicy('policy_1', 'company_1', { name: 'CDW' })).rejects.toMatchObject({ statusCode: 404 }) expect(repo.findInsurancePolicyOrThrow).not.toHaveBeenCalled() }) it('raises not found when deleting a missing pricing rule', async () => { vi.mocked(repo.deletePricingRule).mockResolvedValue({ count: 0 } as any) await expect(service.deletePricingRule('rule_1', 'company_1')).rejects.toMatchObject({ statusCode: 404 }) }) })