package api import ( "go/ast" "go/parser" "go/token" "path/filepath" "strings" "testing" ) // TestGoogleAPICallsHaveContext is a static regression guard for the // 2026-08-04 incident: three .Do() calls in google_calendar.go accepted a // ctx parameter but never chained .Context(ctx) into the actual Google API // SDK builder, so the caller's timeout/cancellation silently never reached // the network call -- one hung call wedged the production server for three // days. Go's compiler allows this (both Context() and Do() are optional // builder methods on the generated SDK types), so this walks the AST of // every google_*.go file instead and fails if any .Do() call's method // chain doesn't include a .Context(...) call anywhere in it. // // Scoped to files named google_*.go, not the whole package: internal/api's // hand-rolled BaseClient (http.go) bakes ctx into the request object itself // via http.NewRequestWithContext before calling HTTPClient.Do(req), which // is a structurally different (and already-safe) pattern that this check // would false-positive on if it ran there too. func TestGoogleAPICallsHaveContext(t *testing.T) { files, err := filepath.Glob("google_*.go") if err != nil { t.Fatalf("glob: %v", err) } fset := token.NewFileSet() found := 0 for _, file := range files { if strings.HasSuffix(file, "_test.go") { continue } f, err := parser.ParseFile(fset, file, nil, 0) if err != nil { t.Fatalf("parse %s: %v", file, err) } ast.Inspect(f, func(n ast.Node) bool { call, ok := n.(*ast.CallExpr) if !ok { return true } sel, ok := call.Fun.(*ast.SelectorExpr) if !ok || sel.Sel.Name != "Do" { return true } found++ if !chainHasContext(sel.X) { pos := fset.Position(call.Pos()) t.Errorf("%s:%d: .Do() call has no .Context(ctx) anywhere in its method chain -- "+ "cancellation and timeouts set by the caller will be silently ignored "+ "(this is exactly the 2026-08-04 incident's root cause)", pos.Filename, pos.Line) } return true }) } if found == 0 { t.Fatal("found zero .Do() calls in google_*.go -- this check may have stopped matching " + "the code it's meant to guard (file renamed? SDK call style changed?), which would " + "make it silently pass without checking anything") } } // chainHasContext walks backward through a method-chain expression (e.g. the // receiver of a .Do() call) looking for a .Context(...) call anywhere in it. func chainHasContext(expr ast.Expr) bool { for { call, ok := expr.(*ast.CallExpr) if !ok { return false } sel, ok := call.Fun.(*ast.SelectorExpr) if !ok { return false } if sel.Sel.Name == "Context" { return true } expr = sel.X } }