@@ -26,6 +26,7 @@ import (
2626 "os"
2727 "path/filepath"
2828 "regexp"
29+ goRuntime "runtime"
2930 "strings"
3031
3132 cdcTests "github.com/onflow/cadence-tools/test"
@@ -41,6 +42,7 @@ import (
4142
4243 "github.com/onflow/flow-cli/common/branding"
4344
45+ "github.com/onflow/flow-cli/build"
4446 "github.com/onflow/flow-cli/internal/command"
4547 "github.com/onflow/flow-cli/internal/util"
4648)
@@ -184,6 +186,12 @@ func testCode(
184186 flags flagsTests ,
185187) (* result , error ) {
186188 logger := zerolog .New (zerolog.ConsoleWriter {Out : os .Stderr }).With ().Timestamp ().Logger ()
189+
190+ // Track network resolutions per file for pragma-based fork detection
191+ // Map: filename -> resolved network name
192+ fileNetworkResolutions := make (map [string ]string )
193+ var currentTestFile string
194+
187195 // Resolve network labels using flow.json state
188196 resolveNetworkFromState := func (label string ) (string , bool ) {
189197 network , err := state .Networks ().ByName (strings .ToLower (strings .TrimSpace (label )))
@@ -193,6 +201,16 @@ func testCode(
193201 if strings .TrimSpace (network .Host ) == "" {
194202 return "" , false
195203 }
204+
205+ // Track network resolution for current test file (indicates pragma-based fork usage)
206+ // Only track if it's not the default "testing" network
207+ normalizedLabel := strings .ToLower (strings .TrimSpace (label ))
208+ if currentTestFile != "" && normalizedLabel != "testing" {
209+ if _ , exists := fileNetworkResolutions [currentTestFile ]; ! exists {
210+ fileNetworkResolutions [currentTestFile ] = normalizedLabel
211+ }
212+ }
213+
196214 return network .Host , true
197215 }
198216
@@ -284,6 +302,9 @@ func testCode(
284302 testResults := make (map [string ]cdcTests.Results , 0 )
285303 exitCode := 0
286304 for scriptPath , code := range testFiles {
305+ // Set current test file for network resolution tracking
306+ currentTestFile = scriptPath
307+
287308 fileRunner := runner .
288309 WithImportResolver (importResolver (scriptPath , state )).
289310 WithFileResolver (fileResolver (scriptPath , state )).
@@ -344,6 +365,61 @@ func testCode(
344365 break
345366 }
346367 }
368+
369+ // Clear current test file after processing
370+ currentTestFile = ""
371+ }
372+
373+ // Track fork test usage metrics - aggregate into single event
374+ hasPragmaFiles := len (fileNetworkResolutions ) > 0
375+ hasStaticFork := forkCfg != nil
376+
377+ if hasPragmaFiles || hasStaticFork {
378+ // Determine primary fork source
379+ forkSource := "none"
380+ var primaryNetwork string
381+ var chainID string
382+ hasHeight := false
383+
384+ if hasPragmaFiles {
385+ // Pragma takes priority - collect unique networks
386+ forkSource = "pragma"
387+ networkSet := make (map [string ]bool )
388+ for _ , network := range fileNetworkResolutions {
389+ networkSet [network ] = true
390+ }
391+ // Use first resolved network as primary (for single-value tracking)
392+ for _ , network := range fileNetworkResolutions {
393+ primaryNetwork = network
394+ break
395+ }
396+ // If multiple networks, note that in source
397+ if len (networkSet ) > 1 {
398+ forkSource = "pragma-mixed"
399+ }
400+ } else if hasStaticFork {
401+ // Static flags
402+ if flags .ForkHost != "" {
403+ forkSource = "fork-host-flag"
404+ } else if flags .Fork != "" {
405+ forkSource = "fork-flag"
406+ }
407+ primaryNetwork = networkLabel
408+ chainID = forkCfg .ChainID .String ()
409+ hasHeight = forkCfg .ForkHeight > 0
410+ }
411+
412+ command .TrackEvent ("test-fork" , map [string ]any {
413+ "fork_source" : forkSource ,
414+ "network" : primaryNetwork ,
415+ "chain_id" : chainID ,
416+ "has_height" : hasHeight ,
417+ "pragma_files" : len (fileNetworkResolutions ),
418+ "total_files" : len (testFiles ),
419+ "version" : build .Semver (),
420+ "os" : goRuntime .GOOS ,
421+ "ci" : os .Getenv ("CI" ) != "" ,
422+ })
347423 }
348424
349425 return & result {
0 commit comments