package infra import ( "fmt" "math" "os" "path/filepath" "strings" ) // AuditE2ECoverage walks each directory in roots looking for app.md files and // reports which ones declare e2e_checks in their YAML frontmatter. // // For each app.md found, it reads the file and searches for the substring // "e2e_checks:" inside the first frontmatter block (between the opening and // closing "---" delimiters). Files that contain the key count as covered. // // Parameters: // - roots: slice of directory paths to scan (e.g. ["apps", "cpp/apps", "projects"]) // // Returns an E2ECoverageReport with total, with_checks, missing paths and // coverage_pct rounded to 2 decimal places. Returns a non-nil error only when // filesystem I/O fails for a reason other than a missing/empty root. func AuditE2ECoverage(roots []string) (E2ECoverageReport, error) { var report E2ECoverageReport for _, root := range roots { info, err := os.Stat(root) if err != nil { if os.IsNotExist(err) { continue } return report, fmt.Errorf("audit_e2e_coverage: stat %q: %w", root, err) } if !info.IsDir() { continue } walkErr := filepath.WalkDir(root, func(p string, d os.DirEntry, we error) error { if we != nil { // Skip unreadable entries without aborting the whole walk. return nil } if d.IsDir() { return nil } if filepath.Base(p) != "app.md" { return nil } report.Total++ data, err := os.ReadFile(p) if err != nil { // Count but mark as missing — we cannot determine coverage. report.Missing = append(report.Missing, p) return nil } if hasE2EChecks(string(data)) { report.WithChecks++ } else { report.Missing = append(report.Missing, p) } return nil }) if walkErr != nil { return report, fmt.Errorf("audit_e2e_coverage: walk %q: %w", root, walkErr) } } report.CoveragePct = computeCoveragePct(report.WithChecks, report.Total) return report, nil } // hasE2EChecks returns true when the app.md content contains "e2e_checks:" // inside the YAML frontmatter block (between the first pair of "---" lines). // If the file has no frontmatter, it falls back to a whole-file scan so that // non-standard formats are also handled gracefully. func hasE2EChecks(content string) bool { const key = "e2e_checks:" if !strings.HasPrefix(content, "---") { // No frontmatter delimiter — scan full content. return strings.Contains(content, key) } // Skip the opening "---". rest := content[3:] if strings.HasPrefix(rest, "\r\n") { rest = rest[2:] } else if strings.HasPrefix(rest, "\n") { rest = rest[1:] } // Find the closing "---". end := strings.Index(rest, "\n---") if end < 0 { // Malformed frontmatter — scan full content. return strings.Contains(content, key) } frontmatter := rest[:end] return strings.Contains(frontmatter, key) } // computeCoveragePct returns (withChecks/total)*100 rounded to 2 decimal // places. Returns 0.0 when total is zero to avoid division by zero. func computeCoveragePct(withChecks, total int) float64 { if total == 0 { return 0.0 } raw := float64(withChecks) / float64(total) * 100.0 return math.Round(raw*100) / 100 }