@@ -96,6 +96,123 @@ describe.runIf(runIntegration)("api integration", () => {
9696 } ) ;
9797 expect ( stepResponse . statusCode ) . toBe ( 404 ) ;
9898 expect ( stepResponse . json ( ) ) . toMatchObject ( { message : "Step not found" } ) ;
99+
100+ const treemapResponse = await app . inject ( {
101+ method : "GET" ,
102+ url : "/runs/00000000-0000-0000-0000-000000000001/treemap" ,
103+ } ) ;
104+ expect ( treemapResponse . statusCode ) . toBe ( 404 ) ;
105+ expect ( treemapResponse . json ( ) ) . toMatchObject ( { message : "Run not found" } ) ;
106+ } , 30_000 ) ;
107+
108+ it ( "returns a run treemap with step, file, and process nodes" , async ( ) => {
109+ const createResponse = await app . inject ( {
110+ method : "POST" ,
111+ url : "/runs/manual" ,
112+ payload : {
113+ repositorySlug : "verge" ,
114+ commitSha : "treemap-sha" ,
115+ requestedStepKeys : [ "test" ] ,
116+ disableReuse : true ,
117+ } ,
118+ } ) ;
119+
120+ expect ( createResponse . statusCode ) . toBe ( 200 ) ;
121+ const createPayload = createResponse . json ( ) as {
122+ runId : string ;
123+ stepRunIds : string [ ] ;
124+ } ;
125+
126+ const initialTreemapResponse = await app . inject ( {
127+ method : "GET" ,
128+ url : `/runs/${ createPayload . runId } /treemap` ,
129+ } ) ;
130+ expect ( initialTreemapResponse . statusCode ) . toBe ( 200 ) ;
131+ const initialTreemap = initialTreemapResponse . json ( ) as {
132+ runId : string ;
133+ tree : {
134+ kind : string ;
135+ children ?: Array < {
136+ kind : string ;
137+ stepKey ?: string | null ;
138+ children ?: Array < { kind : string ; children ?: Array < { kind : string } > } > ;
139+ } > ;
140+ } ;
141+ } ;
142+
143+ expect ( initialTreemap . runId ) . toBe ( createPayload . runId ) ;
144+ expect ( initialTreemap . tree . kind ) . toBe ( "run" ) ;
145+ const testStepNode = initialTreemap . tree . children ?. find ( ( node ) => node . stepKey === "test" ) ;
146+ expect ( testStepNode ?. kind ) . toBe ( "step" ) ;
147+ expect ( testStepNode ?. children ?. some ( ( child ) => child . kind === "file" ) ) . toBe ( true ) ;
148+ expect (
149+ testStepNode ?. children ?. some (
150+ ( child ) =>
151+ child . kind === "file" &&
152+ child . children ?. some ( ( grandchild ) => grandchild . kind === "process" ) ,
153+ ) ,
154+ ) . toBe ( true ) ;
155+
156+ const claimResponse = await app . inject ( {
157+ method : "POST" ,
158+ url : "/workers/claim" ,
159+ payload : {
160+ workerId : "treemap-worker" ,
161+ } ,
162+ } ) ;
163+ expect ( claimResponse . statusCode ) . toBe ( 200 ) ;
164+ const assignment = claimResponse . json ( ) as {
165+ assignment : {
166+ stepRunId : string ;
167+ processRunId : string ;
168+ processKey : string ;
169+ } | null ;
170+ } ;
171+ expect ( assignment . assignment ?. stepRunId ) . toBe ( createPayload . stepRunIds [ 0 ] ) ;
172+
173+ await app . inject ( {
174+ method : "POST" ,
175+ url : `/workers/steps/${ assignment . assignment ?. stepRunId } /events` ,
176+ payload : {
177+ workerId : "treemap-worker" ,
178+ processRunId : assignment . assignment ?. processRunId ,
179+ kind : "started" ,
180+ message : "Started treemap process" ,
181+ } ,
182+ } ) ;
183+
184+ await new Promise ( ( resolve ) => setTimeout ( resolve , 15 ) ) ;
185+
186+ await app . inject ( {
187+ method : "POST" ,
188+ url : `/workers/steps/${ assignment . assignment ?. stepRunId } /events` ,
189+ payload : {
190+ workerId : "treemap-worker" ,
191+ processRunId : assignment . assignment ?. processRunId ,
192+ kind : "passed" ,
193+ message : "Completed treemap process" ,
194+ } ,
195+ } ) ;
196+
197+ const finalTreemapResponse = await app . inject ( {
198+ method : "GET" ,
199+ url : `/runs/${ createPayload . runId } /treemap` ,
200+ } ) ;
201+ expect ( finalTreemapResponse . statusCode ) . toBe ( 200 ) ;
202+ const finalTreemap = finalTreemapResponse . json ( ) as {
203+ tree : {
204+ children ?: Array < {
205+ children ?: Array < { children ?: Array < { processKey ?: string | null ; valueMs ?: number } > } > ;
206+ } > ;
207+ } ;
208+ } ;
209+
210+ const processNode = finalTreemap . tree . children
211+ ?. flatMap ( ( child ) => child . children ?? [ ] )
212+ . flatMap ( ( child ) => child . children ?? [ ] )
213+ . find ( ( node ) => node . processKey === assignment . assignment ?. processKey ) ;
214+
215+ expect ( processNode ?. valueMs ) . toBeGreaterThan ( 0 ) ;
99216 } , 30_000 ) ;
100217
101218 it ( "ingests GitHub webhooks idempotently and exposes pull request detail" , async ( ) => {
0 commit comments