Enhance Beryl Agent detail and report views
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
import { expect,it } from "vitest";
|
||||
import { decodeCreatives,evaluateFatigue,type Creative } from "../src/fatigue.js";
|
||||
import {defaults,settingsSchema} from "../src/monitor.js";
|
||||
const creative:Creative={id:"video",campaignId:"c",kind:"item",variant:"ADS_AND_ORGANIC",points:Array.from({length:10},(_,i)=>({date:`2026-09-${String(i+1).padStart(2,"0")}`,impressions:1000,clicks:i<7?100:50,plays:1000,views:i<7?500:200}))};
|
||||
it("requires corroborating viewing decline, not CTR alone",()=>{
|
||||
const r=evaluateFatigue(creative,"2026-09-08",defaults,true);
|
||||
expect(r.state).toBe("suspected_fatigue");expect(r.ctrDrop).toBe(50);expect(r.baseline.ctr).toBe(.1);
|
||||
expect(evaluateFatigue({...creative,points:creative.points.map(p=>({...p,plays:0,views:0}))},"2026-09-08",defaults,true).state).toBe("creative_decline");
|
||||
});
|
||||
it("fails closed on stale, pending, sparse and missing baseline",()=>{
|
||||
expect(evaluateFatigue(creative,"2026-09-08",defaults,false).state).toBe("data_issue");
|
||||
expect(evaluateFatigue(creative,"2026-09-08",defaults,true,"配置修改中").state).toBe("data_issue");
|
||||
expect(evaluateFatigue({...creative,points:creative.points.slice(7)},"2026-09-08",defaults,true).state).toBe("observe");
|
||||
expect(evaluateFatigue({...creative,points:creative.points.map(p=>({...p,impressions:100,clicks:1}))},"2026-09-08",defaults,true).state).toBe("observe");
|
||||
});
|
||||
it("uses weighted rates and allows zero recent clicks to trigger decline",()=>{
|
||||
const r=evaluateFatigue({...creative,points:creative.points.map((p,i)=>({...p,impressions:i===0?10000:1000,clicks:i<7?100:0}))},"2026-09-08",defaults,true);
|
||||
expect(r.baseline.ctr).toBe(700/16000);expect(r.ctrDrop).toBe(100);
|
||||
});
|
||||
it("migrates old settings and rejects invalid thresholds",()=>{
|
||||
expect(settingsSchema.parse({enabled:false,minSpend:100,minOrders:5,minImpressions:1000,staleHours:12}).fatigueCtrDrop).toBe(25);
|
||||
expect(()=>settingsSchema.parse({...defaults,fatigueCtrDrop:101})).toThrow();
|
||||
});
|
||||
it("validates scope, duplicates, dates and truncation; excludes product cards",()=>{
|
||||
const raw={columns:["external_campaign_id","external_creative_id","creative_identity_kind","creative_variant","date","product_impressions","product_clicks","video_play_actions","video_watched_6s"].map(name=>({name})),rows:[["c","v","item","ADS","2026-09-01",100,5,0,0],["c","p","product_card","ADS","2026-09-01",100,5,0,0]],account_context:{external_account_id:"a",tenant_id:"t"},query_meta:{truncated:false}};
|
||||
const decode=(r:typeof raw)=>decodeCreatives(r,"a","t","2026-09-01","2026-09-10");
|
||||
expect(decode(raw).map.size).toBe(1);expect(decode(raw).excluded).toBe(1);
|
||||
expect(()=>decode({...raw,rows:[raw.rows[0]!,raw.rows[0]!]})).toThrow("重复");
|
||||
expect(()=>decode({...raw,query_meta:{truncated:true}})).toThrow("截断");
|
||||
expect(()=>decode({...raw,account_context:{external_account_id:"b",tenant_id:"t"}})).toThrow("账户");
|
||||
});
|
||||
Reference in New Issue
Block a user