package console import ( "bytes" "go/ast" "go/format" "go/parser" "go/token" "os" "path/filepath" "slices" "strings" "testing" ) // The reviewed inventory is committed. The test never regenerates it: new // constructors or typed control callers need a matching coverage change. func claimAuthoritySites(t *testing.T) []string { t.Helper() selected := map[string]bool{ "NewDirStore": true, "Open": true, "OpenDB": true, "NewConditionalRecordStore": true, "NewVersionedBoundary": true, "NewFromConfig": true, "S3ClientFor": true, "NewControlWriter": true, "NewRestoreControl": true, "NewMutationBoundary": true, "NewObservedMutationBoundary": true, "NewProductionMutationCheck": true, "NewS3Store": true, "NewS3StoreIn": true, "NewDeploymentS3Store": true, "NewDeploymentS3StoreIn": true, "NewVersionedS3": true, "NewAWSProvisioner": true, "NewClamAVScanner": true, "NewRoute53Writer": true, "BeginExclusive": true, "ResumeExclusive": true, "RegisterStart": true, "FinishWakeRetryReset": true, "ReservePurge": true, "ConsoleMove": true, "InspectMoveLoss": true, "DeclareMoveLoss": true, "RunRecovery": true, "runMoveRecovery": true, "captureMoveStartBase": true, "resolveUncertain": true, "ConvertMovingToRelocating": true, "ReleaseFencedTarget": true, "VerifyMoveTargetFence": true, "ReportAutomaticLosses": true, // The console's move pass (moves Plan 5 Task 7): every write of the // move-recovery grant, and the lost target's stamp. "ResolveStart": true, "Convert": true, "Claim": true, "Record": true, "StampLostMoveTargetParked": true, // The pass's fence action (moves after claim Plan 12, Task 5, Q8) // calls ReleaseFencedTarget, selected above, on the same grant: one // step of the target's fence per observation, with the grant-only // release token minted inside it. // The recovery pass's move log writer (Plan 6 Task 3, fix round 3 // ruling 1): minted inside RunRecovery for the console's outbox // drainer; one create-only entry per call, under the entry's own // move's report prefix, for a host whose claim still names the app. "runMoveLogDrain": true, "WriteMoveLog": true, "BeginPurge": true, "SavePurgeJournal": true, "RemovePurgeJournal": true, "AcquireMailGate": true, "ReleaseMailGate": true, "EnrollNewApp": true, "EnrollBackfillApp": true, "OpenGated": true, "WithAppMutation": true, "withClaimSQL": true, "withPreparedClaimSQL": true, "RegisterIngest": true, "RecoverIngest": true, "RecoverSQLTransaction": true, "BindTransaction": true, "PrepareSQLPermit": true, "FenceSQL": true, "FenceSQLOutcome": true, "WithProjection": true, "BindMutationRunner": true, "claimEnrollmentContext": true, // Region HA spec 2's automatic enrollment pass (plan 2b Task 5): its // construction hands this console's roster directory and root // signer to hostactors.AutoEnroller, its start runs it, and its one // invocation is the pass that invites and enrolls boxes. "newAutoEnroller": true, "runEnrollPass": true, "EnrollPass": true, } fs := token.NewFileSet() var sites []string for _, dir := range []string{".", "../hostops", "../mail", "../../cmd/console"} { es, err := os.ReadDir(dir) if err != nil { t.Fatal(err) } for _, e := range es { if !strings.HasSuffix(e.Name(), ".go") || strings.HasSuffix(e.Name(), "_test.go") { continue } name := filepath.Join(dir, e.Name()) f, err := parser.ParseFile(fs, name, nil, 0) if err != nil { t.Fatal(err) } for _, d := range f.Decls { fn, ok := d.(*ast.FuncDecl) if !ok || fn.Body == nil { continue } ast.Inspect(fn.Body, func(n ast.Node) bool { call, ok := n.(*ast.CallExpr) if !ok { return true } var method string switch c := call.Fun.(type) { case *ast.SelectorExpr: method = c.Sel.Name case *ast.Ident: method = c.Name } // Loss paging reads signed control evidence and writes only its // NonApp delivery journal. Inventory those sites explicitly. lossPageSite := e.Name() == "claim_loss_pages.go" && (method == "List" || method == "ReadLossRecord" || method == "Exec" || method == "ExecContext" || method == "QueryRowContext" || method == "ClaimLossPageAttempt") lossPageSchema := e.Name() == "store_gate.go" && fn.Name.Name == "OpenGated" && method == "Exec" if !selected[method] && !lossPageSite && !lossPageSchema { return true } var b bytes.Buffer if err := format.Node(&b, fs, call); err != nil { t.Fatal(err) } sites = append(sites, filepath.ToSlash(name)+"|"+fn.Name.Name+"|"+strings.Join(strings.Fields(b.String()), " ")) return true }) } } } slices.Sort(sites) return sites } func TestClaimAuthoritySourceCensus(t *testing.T) { b, err := os.ReadFile("testdata/claim-authority-sites.txt") if err != nil { t.Fatal(err) } want := strings.Split(strings.TrimSpace(string(b)), "\n") slices.Sort(want) got := claimAuthoritySites(t) if !slices.Equal(got, want) { t.Errorf("authority census differs: got %d calls, want %d", len(got), len(want)) for _, s := range got { if !slices.Contains(want, s) { t.Errorf("unreviewed authority use: %s", s) } } for _, s := range want { if !slices.Contains(got, s) { t.Errorf("stale authority fixture: %s", s) } } } }