feat(release): prepare 1.1.0 candidate

This commit is contained in:
Yun Chan 2026-08-29 18:33:45 +09:00
parent 5a34f66981
commit 5205dcdfa9
736 changed files with 115667 additions and 12203 deletions

View file

@ -1,9 +1,7 @@
// apps/desktop/tests/unit/AdMediationEngine.spec.ts
// Comprehensive Real Service Test Suite for 10+ Multi-Ad Network Mediation & Settlement
import { describe, it, expect, beforeEach } from 'vitest'
import { getAdMediationEngine, AdMediationEngine } from '../../src/main/services/ads/AdMediationEngine'
import { getAdSettlementService, AdSettlementService } from '../../src/main/services/ads/AdSettlementService'
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'
@ -15,182 +13,176 @@ import { PubMaticAdapter } from '../../src/main/services/ads/PubMaticAdapter'
import { MintegralAdapter } from '../../src/main/services/ads/MintegralAdapter'
import { DirectHouseSponsorAdapter } from '../../src/main/services/ads/DirectHouseSponsorAdapter'
describe('10+ Multi-Ad Network Adapters & Real Service Tests', () => {
it('1. EthicalAds Adapter bids on developer dock slots', async () => {
const adapter = new EthicalAdsAdapter()
await adapter.init()
const bid = await adapter.requestBid({ placement: 'bottom_dock_banner', format: 'banner_dock' })
expect(bid.hasBid).toBe(true)
expect(bid.bidEcpm).toBeGreaterThanOrEqual(3.0)
expect(bid.creative?.networkId).toBe('ethical_ads')
expect(bid.creative?.title).toContain('MongoDB')
})
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
it('2. Carbon Ads Adapter provides tech native single-unit creatives', async () => {
const adapter = new CarbonAdsAdapter()
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: 'bottom_dock_banner', format: 'banner_dock' })
expect(bid.hasBid).toBe(true)
expect(bid.bidEcpm).toBeGreaterThanOrEqual(3.5)
expect(bid.creative?.networkId).toBe('carbon_ads')
expect(bid.creative?.title).toContain('Linear')
})
const bid = await adapter.requestBid({
placement: adapter.supportedFormats.includes('rewarded_video')
? 'rewarded_video_quota'
: 'bottom_dock_banner',
format: adapter.supportedFormats[0],
})
it('3. Playwire RAMP Adapter participates in desktop header bidding', async () => {
const adapter = new PlaywireAdapter()
await adapter.init()
const bid = await adapter.requestBid({ placement: 'bottom_dock_banner', format: 'banner_dock' })
expect(bid.hasBid).toBe(true)
expect(bid.bidEcpm).toBeGreaterThanOrEqual(4.5)
expect(bid.creative?.networkId).toBe('playwire')
})
it('4. Unity LevelPlay Adapter delivers high-yield rewarded video ads', async () => {
const adapter = new UnityAdsAdapter()
await adapter.init()
const bid = await adapter.requestBid({ placement: 'rewarded_video_quota', format: 'rewarded_video' })
expect(bid.hasBid).toBe(true)
expect(bid.bidEcpm).toBeGreaterThanOrEqual(6.0)
expect(bid.creative?.networkId).toBe('unity_ads')
expect(bid.creative?.rewardTokens).toBe(50)
})
it('5. AppLovin MAX Adapter provides competitive in-app bidding', async () => {
const adapter = new AppLovinAdapter()
await adapter.init()
const bid = await adapter.requestBid({ placement: 'rewarded_video_quota', format: 'rewarded_video' })
expect(bid.hasBid).toBe(true)
expect(bid.bidEcpm).toBeGreaterThanOrEqual(5.5)
expect(bid.creative?.networkId).toBe('applovin_max')
})
it('6. Google Ad Manager 360 Adapter offers reliable global demand', async () => {
const adapter = new GoogleAdManagerAdapter()
await adapter.init()
const bid = await adapter.requestBid({ placement: 'bottom_dock_banner', format: 'banner_dock' })
expect(bid.hasBid).toBe(true)
expect(bid.bidEcpm).toBeGreaterThanOrEqual(2.0)
expect(bid.creative?.networkId).toBe('google_ad_manager')
})
it('7. InMobi Adapter handles programmatic exchange bids', async () => {
const adapter = new InMobiAdapter()
await adapter.init()
const bid = await adapter.requestBid({ placement: 'bottom_dock_banner', format: 'banner_dock' })
expect(bid.hasBid).toBe(true)
expect(bid.bidEcpm).toBeGreaterThanOrEqual(2.8)
expect(bid.creative?.networkId).toBe('inmobi')
})
it('8. PubMatic OpenWrap SSP Adapter responds with Prebid bids', async () => {
const adapter = new PubMaticAdapter()
await adapter.init()
const bid = await adapter.requestBid({ placement: 'bottom_dock_banner', format: 'banner_dock' })
expect(bid.hasBid).toBe(true)
expect(bid.bidEcpm).toBeGreaterThanOrEqual(3.0)
expect(bid.creative?.networkId).toBe('pubmatic')
})
it('9. Mintegral Adapter delivers APAC/global video demand', async () => {
const adapter = new MintegralAdapter()
await adapter.init()
const bid = await adapter.requestBid({ placement: 'rewarded_video_quota', format: 'rewarded_video' })
expect(bid.hasBid).toBe(true)
expect(bid.bidEcpm).toBeGreaterThanOrEqual(4.0)
expect(bid.creative?.networkId).toBe('mintegral')
})
it('10. Direct House Sponsor Adapter provides 100% margin premium AI ads', async () => {
const adapter = new DirectHouseSponsorAdapter()
await adapter.init()
const bid = await adapter.requestBid({ placement: 'bottom_dock_banner', format: 'banner_dock' })
expect(bid.hasBid).toBe(true)
expect(bid.bidEcpm).toBeGreaterThanOrEqual(12.0)
expect(bid.creative?.networkId).toBe('direct_sponsor')
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 Header Bidding Auction & Telemetry', () => {
let engine: AdMediationEngine
describe('AdMediationEngine fail-closed auction and telemetry', () => {
const engine = getAdMediationEngine()
const settlement = getAdSettlementService()
const knownNetworks = engine.getConfig().networks
let disabledNetworks: AdMediationConfig['networks']
beforeEach(() => {
engine = getAdMediationEngine()
disabledNetworks = knownNetworks.map((network) => ({
...network,
enabled: false,
}))
engine.setConfig({ networks: disabledNetworks, houseAdFallback: false })
})
it('executes parallel header bidding auction across all active networks', async () => {
const auctionResult = await engine.runAuction({
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.0,
floorEcpm: 2,
auctionTimeoutMs: 800,
})
expect(auctionResult).toBeDefined()
expect(auctionResult.winner).toBeDefined()
expect(auctionResult.winningBidEcpm).toBeGreaterThanOrEqual(2.0)
expect(auctionResult.participatingBids.length).toBeGreaterThanOrEqual(6)
expect(auctionResult.totalAuctionLatencyMs).toBeLessThanOrEqual(1200)
expect(result.winner).toBeNull()
expect(result.winningBidEcpm).toBe(0)
expect(result.participatingBids).toEqual([])
expect(result.totalAuctionLatencyMs).toBeGreaterThanOrEqual(0)
})
it('records ad impression and triggers adapter beacon', () => {
expect(() => {
engine.recordImpression({
adId: 'test_ad_001',
format: 'banner_dock',
network: 'ethical_ads',
earnedEcpm: 4.2,
})
}).not.toThrow()
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('records ad click-through tracking event', () => {
expect(() => {
engine.recordClick('test_ad_001', 'ethical_ads')
}).not.toThrow()
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('dispenses +50 tokens upon rewarded video completion and enforces cooldown', async () => {
const rewardRes = await engine.claimReward('test_video_ad', 'unity_ads')
expect(rewardRes.success).toBe(true)
expect(rewardRes.tokensAdded).toBe(50)
it('returns a defensive config copy', () => {
const exposedConfig = engine.getConfig()
exposedConfig.networks[0].enabled = true
// Immediate second claim should be rejected by cooldown
const cooldownRes = await engine.claimReward('test_video_ad_2', 'unity_ads')
expect(cooldownRes.success).toBe(false)
expect(cooldownRes.tokensAdded).toBe(0)
expect(cooldownRes.nextAvailableAt).toBeDefined()
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 Revenue Ledger & Tax Withholding', () => {
let settlement: AdSettlementService
describe('AdSettlementService fail-closed ledger', () => {
const settlement = getAdSettlementService()
beforeEach(() => {
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('retrieves registered publisher account metadata for yunchanpaca@gmail.com', () => {
const account = settlement.getPublisherAccount()
expect(account.accountEmail).toBe('yunchanpaca@gmail.com')
expect(account.payoutBank).toContain('KB국민은행')
expect(account.networksConfigured).toBe(10)
})
it('generates revenue statistics and computes Korean 3.3% withholding tax', () => {
it('does not synthesize revenue, fill rate, or settlement history', () => {
const stats = settlement.getRevenueStats('2026-08')
expect(stats.totalRevenueUsd).toBeGreaterThan(0)
expect(stats.avgEcpm).toBeGreaterThan(0)
expect(stats.settlements.length).toBeGreaterThanOrEqual(5)
const first = stats.settlements[0]
expect(first.withholdingTaxRate).toBe(0.033)
expect(first.netPayoutKrw).toBe(Math.round(first.netRevenueUsd * 1350))
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('processes payout request and updates status to paid', () => {
const res = settlement.requestPayout('stl_202607_direct_sponsor')
expect(res.success).toBe(true)
expect(res.settlement?.payoutStatus).toBe('paid')
expect(res.message).toContain('KB국민은행')
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)
})
})