@@ -10,6 +10,7 @@ import {
1010} from "node:fs" ;
1111import { join } from "node:path" ;
1212import { tmpdir } from "node:os" ;
13+ import { createServer } from "node:http" ;
1314import { execFileSync , spawnSync } from "node:child_process" ;
1415import { appendRecord , readManifest } from "./lib/manifest.mjs" ;
1516import { regenerateIndex } from "./lib/index-gen.mjs" ;
@@ -687,6 +688,81 @@ test("identical grade resolve hits the project cache without re-freezing", () =>
687688 cleanup ( ) ;
688689} ) ;
689690
691+ // --- telemetry isolation (U7) ---
692+
693+ // Every other test relies on runResolve/spawnResolve's default DO_NOT_TRACK:
694+ // "1" to keep track() a no-op. That default is fragile on its own (a future
695+ // call site or test could forget to set it), so telemetry.mjs also exposes a
696+ // MEDIA_USE_TELEMETRY_HOST override read at the point the POST URL is built.
697+ // This test proves that seam actually intercepts a real event end to end: a
698+ // resolve that reaches track("media_use_resolve", ...) with tracking allowed
699+ // posts to a local HTTP server instead of production, and the server actually
700+ // receives it (not just "nothing happened because nothing was listening").
701+ test ( "track() posts to MEDIA_USE_TELEMETRY_HOST when set, proving real interception" , async ( ) => {
702+ setup ( ) ;
703+ const received = [ ] ;
704+ const server = createServer ( ( req , res ) => {
705+ let body = "" ;
706+ req . on ( "data" , ( chunk ) => ( body += chunk ) ) ;
707+ req . on ( "end" , ( ) => {
708+ try {
709+ received . push ( JSON . parse ( body ) ) ;
710+ } catch {
711+ // ignore malformed body; assertions below fail on empty `received`
712+ }
713+ res . writeHead ( 200 , { "Content-Type" : "application/json" } ) ;
714+ res . end ( "{}" ) ;
715+ } ) ;
716+ } ) ;
717+ await new Promise ( ( resolve ) => server . listen ( 0 , "127.0.0.1" , resolve ) ) ;
718+ const port = server . address ( ) . port ;
719+ const sandboxHome = mkdtempSync ( join ( tmpdir ( ) , "mu-resolve-telemetry-home-" ) ) ;
720+
721+ try {
722+ const record = makeRecord ( {
723+ provenance : { prompt : "telemetry seam test" , provider : "test" } ,
724+ } ) ;
725+ appendRecord ( tmp , record ) ;
726+ const filePath = join ( tmp , record . path ) ;
727+ mkdirSync ( join ( filePath , ".." ) , { recursive : true } ) ;
728+ writeFileSync ( filePath , "telemetry seam audio" ) ;
729+
730+ // Override this one invocation's env only: allow tracking (DO_NOT_TRACK
731+ // default flipped off), sandbox HOME so anonymousId()/showTelemetryNotice()
732+ // never touch the real developer machine, and point the host at the local
733+ // server. Every other test in this file keeps its untouched default env.
734+ runResolve ( [ "--type" , "bgm" , "--intent" , "telemetry seam test" , "--project" , tmp , "--json" ] , {
735+ env : {
736+ DO_NOT_TRACK : "0" ,
737+ HYPERFRAMES_NO_TELEMETRY : "0" ,
738+ CI : "" ,
739+ NODE_ENV : "test" ,
740+ HOME : sandboxHome ,
741+ MEDIA_USE_TELEMETRY_HOST : `http://127.0.0.1:${ port } ` ,
742+ } ,
743+ } ) ;
744+
745+ // runResolve blocks synchronously (execFileSync) until the child exits, which
746+ // pauses this process's own event loop for that whole span — the child's
747+ // request to our local server sits accepted-but-unprocessed in the kernel
748+ // backlog until control returns here. Poll briefly to let the event loop
749+ // drain it rather than asserting before the server has had a turn to run.
750+ for ( let i = 0 ; i < 100 && received . length === 0 ; i ++ ) {
751+ await new Promise ( ( resolve ) => setTimeout ( resolve , 20 ) ) ;
752+ }
753+ } finally {
754+ await new Promise ( ( resolve ) => server . close ( resolve ) ) ;
755+ rmSync ( sandboxHome , { recursive : true , force : true } ) ;
756+ cleanup ( ) ;
757+ }
758+
759+ assert . ok ( received . length > 0 , "expected the local telemetry server to receive a POST" ) ;
760+ const resolveEvent = received [ 0 ] . batch . find ( ( event ) => event . event === "media_use_resolve" ) ;
761+ assert . ok ( resolveEvent , "expected a media_use_resolve event in the intercepted batch" ) ;
762+ assert . equal ( resolveEvent . properties . provider , "test" ) ;
763+ assert . equal ( resolveEvent . properties . type , "bgm" ) ;
764+ } ) ;
765+
690766// --- run ---
691767
692768async function main ( ) {
0 commit comments