import { beforeEach, describe, expect, it } from 'vitest' import type { AdMediationConfig } from '@d3ro/core/types' import { getAdMediationEngine } from '../../src/main/services/ads/AdMediationEngine' import { getAdSettlementService } from '../../src/main/services/ads/AdSettlementService' import { EthicalAdsAdapter } from '../../src/main/services/ads/EthicalAdsAdapter' import { CarbonAdsAdapter } from '../../src/main/services/ads/CarbonAdsAdapter' import { PlaywireAdapter } from '../../src/main/services/ads/PlaywireAdapter' import { UnityAdsAdapter } from '../../src/main/services/ads/UnityAdsAdapter' import { AppLovinAdapter } from '../../src/main/services/ads/AppLovinAdapter' import { GoogleAdManagerAdapter } from '../../src/main/services/ads/GoogleAdManagerAdapter' import { InMobiAdapter } from '../../src/main/services/ads/InMobiAdapter' import { PubMaticAdapter } from '../../src/main/services/ads/PubMaticAdapter' import { MintegralAdapter } from '../../src/main/services/ads/MintegralAdapter' import { DirectHouseSponsorAdapter } from '../../src/main/services/ads/DirectHouseSponsorAdapter' const adapterCases = [ ['EthicalAds', new EthicalAdsAdapter()], ['Carbon Ads', new CarbonAdsAdapter()], ['Playwire', new PlaywireAdapter()], ['Unity Ads', new UnityAdsAdapter()], ['AppLovin', new AppLovinAdapter()], ['Google Ad Manager', new GoogleAdManagerAdapter()], ['InMobi', new InMobiAdapter()], ['PubMatic', new PubMaticAdapter()], ['Mintegral', new MintegralAdapter()], ['Direct sponsor', new DirectHouseSponsorAdapter()], ] as const describe('desktop ad provider boundaries', () => { it.each(adapterCases)('%s returns no bid until a real provider is integrated', async (_name, adapter) => { await adapter.init() const bid = await adapter.requestBid({ placement: adapter.supportedFormats.includes('rewarded_video') ? 'rewarded_video_quota' : 'bottom_dock_banner', format: adapter.supportedFormats[0], }) expect(bid).toEqual({ hasBid: false, bidEcpm: 0, latencyMs: 0, error: 'provider_not_integrated', }) await expect(adapter.reportRewardCompletion('forged-ad-id')).resolves.toEqual({ success: false, tokenReward: 0, }) }) }) describe('AdMediationEngine fail-closed auction and telemetry', () => { const engine = getAdMediationEngine() const settlement = getAdSettlementService() const knownNetworks = engine.getConfig().networks let disabledNetworks: AdMediationConfig['networks'] beforeEach(() => { disabledNetworks = knownNetworks.map((network) => ({ ...network, enabled: false, })) engine.setConfig({ networks: disabledNetworks, houseAdFallback: false }) }) it('returns a real no-fill result instead of inventing a winner', async () => { const result = await engine.runAuction({ placement: 'bottom_dock_banner', format: 'banner_dock', floorEcpm: 2, auctionTimeoutMs: 800, }) expect(result.winner).toBeNull() expect(result.winningBidEcpm).toBe(0) expect(result.participatingBids).toEqual([]) expect(result.totalAuctionLatencyMs).toBeGreaterThanOrEqual(0) }) it('keeps provider shells no-bid even if an operator enables them prematurely', async () => { engine.setConfig({ networks: disabledNetworks.map((network) => ({ ...network, enabled: true })), }) const result = await engine.runAuction({ placement: 'bottom_dock_banner', format: 'banner_dock', floorEcpm: 0.01, auctionTimeoutMs: 800, }) expect(result.winner).toBeNull() expect(result.winningBidEcpm).toBe(0) expect(result.participatingBids.length).toBeGreaterThan(0) expect(result.participatingBids.every((bid) => bid.status === 'no_bid')).toBe(true) }) it('does not enable an adapter merely because its config entry is omitted', async () => { engine.setConfig({ networks: [] }) const result = await engine.runAuction({ placement: 'bottom_dock_banner', format: 'banner_dock', }) expect(result.winner).toBeNull() expect(result.participatingBids).toEqual([]) }) it('returns a defensive config copy', () => { const exposedConfig = engine.getConfig() exposedConfig.networks[0].enabled = true expect(engine.getConfig().networks[0].enabled).toBe(false) }) it('ignores renderer-forged impressions and clicks', () => { const before = settlement.getRevenueStats('2026-08') engine.recordImpression({ adId: 'forged-ad-id', format: 'banner_dock', network: 'ethical_ads', earnedEcpm: 999, }) engine.recordClick('forged-ad-id', 'ethical_ads') const after = settlement.getRevenueStats('2026-08') expect(after.totalImpressions).toBe(before.totalImpressions) expect(after.totalClicks).toBe(before.totalClicks) expect(after.totalRevenueUsd).toBe(before.totalRevenueUsd) }) it('never grants desktop quota without server-verified completion', async () => { await expect(engine.claimReward('forged-reward-id', 'unity_ads')).resolves.toEqual({ success: false, tokensAdded: 0, newTotalQuota: 0, }) }) }) describe('AdSettlementService fail-closed ledger', () => { const settlement = getAdSettlementService() it('does not ship personal payout details or pretend providers are configured', () => { expect(settlement.getPublisherAccount()).toEqual({ accountEmail: '', beneficiaryName: '', payoutBank: '', payoutAccountNumber: '', taxRegistrationNumber: '', paypalEmail: '', networksConfigured: 0, }) }) it('does not synthesize revenue, fill rate, or settlement history', () => { const stats = settlement.getRevenueStats('2026-08') expect(stats.totalImpressions).toBe(0) expect(stats.totalClicks).toBe(0) expect(stats.totalCompletions).toBe(0) expect(stats.totalRevenueUsd).toBe(0) expect(stats.avgEcpm).toBe(0) expect(stats.fillRatePercent).toBe(0) expect(stats.networkBreakdown).toEqual([]) expect(stats.settlements).toEqual([]) }) it('rejects payout without an external settlement provider', () => { expect(settlement.requestPayout('forged-settlement-id')).toEqual({ success: false, message: 'external_settlement_not_configured', }) }) it('ignores invalid or unattested settlement events', () => { const before = settlement.getRevenueStats('2026-08') settlement.recordImpression('', 10) settlement.recordImpression('forged-network', Number.NaN) settlement.recordImpression('forged-network', -10) settlement.recordClick('forged-network') settlement.recordCompletion('forged-network') expect(settlement.getRevenueStats('2026-08')).toEqual(before) }) })