kunchenguid--no-mistakes
26f897c1ec
release / release-please (push) Failing after 1m49s
docs / build (push) Failing after 6m34s
release / build-and-upload (arm64, linux) (push) Has been cancelled
release / build-and-upload (arm64, windows) (push) Has been cancelled
release / build-darwin (amd64, darwin) (push) Has been cancelled
release / checksums (push) Has been cancelled
release / finalize (push) Has been cancelled
release / build-darwin (arm64, darwin) (push) Has been cancelled
release / build-and-upload (amd64, linux) (push) Has been cancelled
release / build-and-upload (amd64, windows) (push) Has been cancelled
docs / deploy (push) Has been cancelled
506 行
16 KiB
Go
506 行
16 KiB
Go
package db
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/kunchenguid/no-mistakes/internal/types"
|
|
)
|
|
|
|
func TestRunInsertAndGet(t *testing.T) {
|
|
d := openTestDB(t)
|
|
repo, _ := d.InsertRepo("/home/user/project", "git@github.com:user/project.git", "main")
|
|
|
|
run, err := d.InsertRun(repo.ID, "feature", "abc123", "def456")
|
|
if err != nil {
|
|
t.Fatalf("insert run: %v", err)
|
|
}
|
|
if run.ID == "" {
|
|
t.Fatal("expected non-empty ID")
|
|
}
|
|
if run.Status != types.RunPending {
|
|
t.Errorf("status = %q, want %q", run.Status, types.RunPending)
|
|
}
|
|
|
|
got, err := d.GetRun(run.ID)
|
|
if err != nil {
|
|
t.Fatalf("get run: %v", err)
|
|
}
|
|
if got.Branch != "feature" {
|
|
t.Errorf("branch = %q, want %q", got.Branch, "feature")
|
|
}
|
|
if got.HeadSHA != "abc123" {
|
|
t.Errorf("head sha = %q, want %q", got.HeadSHA, "abc123")
|
|
}
|
|
}
|
|
|
|
func TestRunAwaitingAgentSetAndClear(t *testing.T) {
|
|
d := openTestDB(t)
|
|
repo, _ := d.InsertRepo("/home/user/project", "git@github.com:user/project.git", "main")
|
|
run, err := d.InsertRun(repo.ID, "feature", "abc123", "def456")
|
|
if err != nil {
|
|
t.Fatalf("insert run: %v", err)
|
|
}
|
|
|
|
// A fresh run is not parked.
|
|
if run.AwaitingAgentSince != nil {
|
|
t.Fatalf("new run AwaitingAgentSince = %v, want nil", *run.AwaitingAgentSince)
|
|
}
|
|
|
|
// Entering a gate stamps the marker with a recent timestamp.
|
|
before := now()
|
|
if err := d.SetRunAwaitingAgent(run.ID); err != nil {
|
|
t.Fatalf("set awaiting agent: %v", err)
|
|
}
|
|
got, err := d.GetRun(run.ID)
|
|
if err != nil {
|
|
t.Fatalf("get run: %v", err)
|
|
}
|
|
if got.AwaitingAgentSince == nil {
|
|
t.Fatal("AwaitingAgentSince = nil after SetRunAwaitingAgent, want a timestamp")
|
|
}
|
|
if *got.AwaitingAgentSince < before {
|
|
t.Errorf("AwaitingAgentSince = %d, want >= %d", *got.AwaitingAgentSince, before)
|
|
}
|
|
|
|
// Responding clears the marker.
|
|
if err := d.ClearRunAwaitingAgent(run.ID); err != nil {
|
|
t.Fatalf("clear awaiting agent: %v", err)
|
|
}
|
|
got, err = d.GetRun(run.ID)
|
|
if err != nil {
|
|
t.Fatalf("get run after clear: %v", err)
|
|
}
|
|
if got.AwaitingAgentSince != nil {
|
|
t.Errorf("AwaitingAgentSince = %d after clear, want nil", *got.AwaitingAgentSince)
|
|
}
|
|
}
|
|
|
|
func TestRecoverStaleRunsClearsAwaitingAgent(t *testing.T) {
|
|
d := openTestDB(t)
|
|
repo, _ := d.InsertRepo("/home/user/project", "git@github.com:user/project.git", "main")
|
|
run, _ := d.InsertRun(repo.ID, "feature", "abc123", "def456")
|
|
if err := d.UpdateRunStatus(run.ID, types.RunRunning); err != nil {
|
|
t.Fatalf("set running: %v", err)
|
|
}
|
|
if err := d.SetRunAwaitingAgent(run.ID); err != nil {
|
|
t.Fatalf("set awaiting agent: %v", err)
|
|
}
|
|
|
|
// Crash recovery must fail the run and drop the parked marker so a dead run
|
|
// is never reported as still awaiting the agent.
|
|
if _, err := d.RecoverStaleRuns("daemon restarted"); err != nil {
|
|
t.Fatalf("recover stale runs: %v", err)
|
|
}
|
|
got, err := d.GetRun(run.ID)
|
|
if err != nil {
|
|
t.Fatalf("get run: %v", err)
|
|
}
|
|
if got.Status != types.RunFailed {
|
|
t.Errorf("status = %q, want failed", got.Status)
|
|
}
|
|
if got.AwaitingAgentSince != nil {
|
|
t.Errorf("AwaitingAgentSince = %d after recovery, want nil", *got.AwaitingAgentSince)
|
|
}
|
|
}
|
|
|
|
func TestRunGetNotFound(t *testing.T) {
|
|
d := openTestDB(t)
|
|
got, err := d.GetRun("nonexistent")
|
|
if err != nil {
|
|
t.Fatalf("get run: %v", err)
|
|
}
|
|
if got != nil {
|
|
t.Fatal("expected nil for nonexistent run")
|
|
}
|
|
}
|
|
|
|
func TestRunsByRepo(t *testing.T) {
|
|
d := openTestDB(t)
|
|
repo, _ := d.InsertRepo("/home/user/project", "git@github.com:user/project.git", "main")
|
|
d.InsertRun(repo.ID, "feature-1", "aaa", "bbb")
|
|
d.InsertRun(repo.ID, "feature-2", "ccc", "ddd")
|
|
|
|
runs, err := d.GetRunsByRepo(repo.ID)
|
|
if err != nil {
|
|
t.Fatalf("get runs: %v", err)
|
|
}
|
|
if len(runs) != 2 {
|
|
t.Fatalf("got %d runs, want 2", len(runs))
|
|
}
|
|
// newest first
|
|
if runs[0].Branch != "feature-2" {
|
|
t.Errorf("first run branch = %q, want feature-2", runs[0].Branch)
|
|
}
|
|
}
|
|
|
|
func TestRunsByRepoHead(t *testing.T) {
|
|
d := openTestDB(t)
|
|
repo, _ := d.InsertRepo("/home/user/project", "git@github.com:user/project.git", "main")
|
|
|
|
older, _ := d.InsertRun(repo.ID, "feature", "head-1", "base")
|
|
d.InsertRun(repo.ID, "feature", "head-2", "base") // same branch, other head
|
|
d.InsertRun(repo.ID, "other", "head-1", "base") // same head, other branch
|
|
newer, _ := d.InsertRun(repo.ID, "feature", "head-1", "base")
|
|
|
|
runs, err := d.GetRunsByRepoHead(repo.ID, "feature", "head-1")
|
|
if err != nil {
|
|
t.Fatalf("get runs by repo head: %v", err)
|
|
}
|
|
if len(runs) != 2 {
|
|
t.Fatalf("got %d runs for feature/head-1, want 2: %+v", len(runs), runs)
|
|
}
|
|
// newest first
|
|
if runs[0].ID != newer.ID || runs[1].ID != older.ID {
|
|
t.Errorf("runs = [%s %s], want [%s %s] (newest first)", runs[0].ID, runs[1].ID, newer.ID, older.ID)
|
|
}
|
|
|
|
none, err := d.GetRunsByRepoHead(repo.ID, "feature", "missing")
|
|
if err != nil {
|
|
t.Fatalf("get runs by repo head (missing): %v", err)
|
|
}
|
|
if len(none) != 0 {
|
|
t.Errorf("got %d runs for unknown head, want 0", len(none))
|
|
}
|
|
}
|
|
|
|
func TestActiveRun(t *testing.T) {
|
|
d := openTestDB(t)
|
|
repo, _ := d.InsertRepo("/home/user/project", "git@github.com:user/project.git", "main")
|
|
|
|
// no active run initially
|
|
active, err := d.GetActiveRun(repo.ID, "")
|
|
if err != nil {
|
|
t.Fatalf("get active run: %v", err)
|
|
}
|
|
if active != nil {
|
|
t.Fatal("expected nil active run")
|
|
}
|
|
|
|
run, _ := d.InsertRun(repo.ID, "feature", "abc", "def")
|
|
active, _ = d.GetActiveRun(repo.ID, "")
|
|
if active == nil || active.ID != run.ID {
|
|
t.Fatal("expected active run matching inserted run")
|
|
}
|
|
|
|
// after completing, no active run
|
|
d.UpdateRunStatus(run.ID, types.RunCompleted)
|
|
active, _ = d.GetActiveRun(repo.ID, "")
|
|
if active != nil {
|
|
t.Fatal("expected nil after completing run")
|
|
}
|
|
}
|
|
|
|
func TestActiveRunStrictBranchMatch(t *testing.T) {
|
|
d := openTestDB(t)
|
|
repo, _ := d.InsertRepo("/home/user/branchpref", "git@github.com:user/branchpref.git", "main")
|
|
|
|
// Create two active runs on different branches.
|
|
runA, _ := d.InsertRun(repo.ID, "feature-a", "aaa", "000")
|
|
runB, _ := d.InsertRun(repo.ID, "feature-b", "bbb", "000")
|
|
|
|
// Without branch hint, newest (runB) wins.
|
|
active, err := d.GetActiveRun(repo.ID, "")
|
|
if err != nil {
|
|
t.Fatalf("get active run: %v", err)
|
|
}
|
|
if active == nil || active.ID != runB.ID {
|
|
t.Fatalf("expected newest run %q, got %v", runB.ID, active)
|
|
}
|
|
|
|
// With branch hint "feature-a", the matching run is returned.
|
|
active, err = d.GetActiveRun(repo.ID, "feature-a")
|
|
if err != nil {
|
|
t.Fatalf("get active run with branch: %v", err)
|
|
}
|
|
if active == nil || active.ID != runA.ID {
|
|
t.Fatalf("expected branch-matching run %q, got %q", runA.ID, active.ID)
|
|
}
|
|
|
|
// With branch hint "feature-b", runB is returned.
|
|
active, err = d.GetActiveRun(repo.ID, "feature-b")
|
|
if err != nil {
|
|
t.Fatalf("get active run with branch: %v", err)
|
|
}
|
|
if active == nil || active.ID != runB.ID {
|
|
t.Fatalf("expected branch-matching run %q, got %q", runB.ID, active.ID)
|
|
}
|
|
|
|
// With branch hint for a non-existent branch, return nil (strict match,
|
|
// no fallback). This is what lets the setup wizard know a fresh run is
|
|
// needed for the current branch.
|
|
active, err = d.GetActiveRun(repo.ID, "feature-c")
|
|
if err != nil {
|
|
t.Fatalf("get active run with unknown branch: %v", err)
|
|
}
|
|
if active != nil {
|
|
t.Fatalf("expected nil with no matching branch, got run %q on %q", active.ID, active.Branch)
|
|
}
|
|
}
|
|
|
|
func TestActiveRunsAcrossRepos(t *testing.T) {
|
|
d := openTestDB(t)
|
|
repoA, _ := d.InsertRepo("/home/user/project-a", "git@github.com:user/project-a.git", "main")
|
|
repoB, _ := d.InsertRepo("/home/user/project-b", "git@github.com:user/project-b.git", "main")
|
|
|
|
pendingRun, _ := d.InsertRun(repoA.ID, "feature-a", "aaa", "000")
|
|
runningRun, _ := d.InsertRun(repoB.ID, "feature-b", "bbb", "000")
|
|
if err := d.UpdateRunStatus(runningRun.ID, types.RunRunning); err != nil {
|
|
t.Fatalf("mark running: %v", err)
|
|
}
|
|
completedRun, _ := d.InsertRun(repoA.ID, "done", "ccc", "000")
|
|
if err := d.UpdateRunStatus(completedRun.ID, types.RunCompleted); err != nil {
|
|
t.Fatalf("mark completed: %v", err)
|
|
}
|
|
failedRun, _ := d.InsertRun(repoB.ID, "failed", "ddd", "000")
|
|
if err := d.UpdateRunStatus(failedRun.ID, types.RunFailed); err != nil {
|
|
t.Fatalf("mark failed: %v", err)
|
|
}
|
|
cancelledRun, _ := d.InsertRun(repoB.ID, "cancelled", "eee", "000")
|
|
if err := d.UpdateRunStatus(cancelledRun.ID, types.RunCancelled); err != nil {
|
|
t.Fatalf("mark cancelled: %v", err)
|
|
}
|
|
|
|
runs, err := d.GetActiveRuns()
|
|
if err != nil {
|
|
t.Fatalf("get active runs: %v", err)
|
|
}
|
|
if len(runs) != 2 {
|
|
t.Fatalf("got %d active runs, want 2", len(runs))
|
|
}
|
|
|
|
got := map[string]types.RunStatus{}
|
|
for _, run := range runs {
|
|
got[run.ID] = run.Status
|
|
}
|
|
if got[pendingRun.ID] != types.RunPending {
|
|
t.Fatalf("pending run missing from active runs: %#v", got)
|
|
}
|
|
if got[runningRun.ID] != types.RunRunning {
|
|
t.Fatalf("running run missing from active runs: %#v", got)
|
|
}
|
|
if _, ok := got[completedRun.ID]; ok {
|
|
t.Fatal("completed run should not be active")
|
|
}
|
|
if _, ok := got[failedRun.ID]; ok {
|
|
t.Fatal("failed run should not be active")
|
|
}
|
|
if _, ok := got[cancelledRun.ID]; ok {
|
|
t.Fatal("cancelled run should not be active")
|
|
}
|
|
}
|
|
|
|
func TestUpdateRunStatus(t *testing.T) {
|
|
d := openTestDB(t)
|
|
repo, _ := d.InsertRepo("/home/user/project", "git@github.com:user/project.git", "main")
|
|
run, _ := d.InsertRun(repo.ID, "feature", "abc", "def")
|
|
|
|
if err := d.UpdateRunStatus(run.ID, types.RunRunning); err != nil {
|
|
t.Fatalf("update status: %v", err)
|
|
}
|
|
got, _ := d.GetRun(run.ID)
|
|
if got.Status != types.RunRunning {
|
|
t.Errorf("status = %q, want %q", got.Status, types.RunRunning)
|
|
}
|
|
}
|
|
|
|
func TestUpdateRunPRURL(t *testing.T) {
|
|
d := openTestDB(t)
|
|
repo, _ := d.InsertRepo("/home/user/project", "git@github.com:user/project.git", "main")
|
|
run, _ := d.InsertRun(repo.ID, "feature", "abc", "def")
|
|
|
|
prURL := "https://github.com/user/project/pull/1"
|
|
if err := d.UpdateRunPRURL(run.ID, prURL); err != nil {
|
|
t.Fatalf("update pr url: %v", err)
|
|
}
|
|
got, _ := d.GetRun(run.ID)
|
|
if got.PRURL == nil || *got.PRURL != prURL {
|
|
t.Errorf("pr url = %v, want %q", got.PRURL, prURL)
|
|
}
|
|
}
|
|
|
|
func TestUpdateRunHeadSHA(t *testing.T) {
|
|
d := openTestDB(t)
|
|
repo, _ := d.InsertRepo("/home/user/project", "git@github.com:user/project.git", "main")
|
|
run, _ := d.InsertRun(repo.ID, "feature", "abc", "def")
|
|
|
|
if err := d.UpdateRunHeadSHA(run.ID, "xyz"); err != nil {
|
|
t.Fatalf("update head sha: %v", err)
|
|
}
|
|
got, _ := d.GetRun(run.ID)
|
|
if got.HeadSHA != "xyz" {
|
|
t.Errorf("head sha = %q, want %q", got.HeadSHA, "xyz")
|
|
}
|
|
}
|
|
|
|
func TestUpdateRunError(t *testing.T) {
|
|
d := openTestDB(t)
|
|
repo, _ := d.InsertRepo("/home/user/project", "git@github.com:user/project.git", "main")
|
|
run, _ := d.InsertRun(repo.ID, "feature", "abc", "def")
|
|
|
|
if err := d.UpdateRunError(run.ID, "something broke"); err != nil {
|
|
t.Fatalf("update error: %v", err)
|
|
}
|
|
got, _ := d.GetRun(run.ID)
|
|
if got.Error == nil || *got.Error != "something broke" {
|
|
t.Errorf("error = %v, want %q", got.Error, "something broke")
|
|
}
|
|
if got.Status != types.RunFailed {
|
|
t.Errorf("status = %q, want %q", got.Status, types.RunFailed)
|
|
}
|
|
}
|
|
|
|
func TestCascadeDeleteRepo(t *testing.T) {
|
|
d := openTestDB(t)
|
|
repo, _ := d.InsertRepo("/home/user/project", "git@github.com:user/project.git", "main")
|
|
run, _ := d.InsertRun(repo.ID, "feature", "abc", "def")
|
|
step, _ := d.InsertStepResult(run.ID, types.StepReview)
|
|
|
|
if err := d.DeleteRepo(repo.ID); err != nil {
|
|
t.Fatalf("delete repo: %v", err)
|
|
}
|
|
gotRun, _ := d.GetRun(run.ID)
|
|
if gotRun != nil {
|
|
t.Fatal("expected run to be cascade deleted")
|
|
}
|
|
gotStep, _ := d.GetStepResult(step.ID)
|
|
if gotStep != nil {
|
|
t.Fatal("expected step to be cascade deleted")
|
|
}
|
|
}
|
|
|
|
func TestRecoverStaleRunsMarksRunsFailed(t *testing.T) {
|
|
d := openTestDB(t)
|
|
repo, _ := d.InsertRepo("/home/user/project", "git@github.com:user/project.git", "main")
|
|
|
|
// Create runs in various statuses.
|
|
pendingRun, _ := d.InsertRun(repo.ID, "feat-a", "aaa", "bbb")
|
|
runningRun, _ := d.InsertRun(repo.ID, "feat-b", "ccc", "ddd")
|
|
d.UpdateRunStatus(runningRun.ID, types.RunRunning)
|
|
completedRun, _ := d.InsertRun(repo.ID, "feat-c", "eee", "fff")
|
|
d.UpdateRunStatus(completedRun.ID, types.RunCompleted)
|
|
|
|
count, err := d.RecoverStaleRuns("daemon crashed")
|
|
if err != nil {
|
|
t.Fatalf("recover stale runs: %v", err)
|
|
}
|
|
if count != 2 {
|
|
t.Errorf("recovered count = %d, want 2", count)
|
|
}
|
|
|
|
// Pending and running should be failed.
|
|
got, _ := d.GetRun(pendingRun.ID)
|
|
if got.Status != types.RunFailed {
|
|
t.Errorf("pending run status = %q, want %q", got.Status, types.RunFailed)
|
|
}
|
|
if got.Error == nil || *got.Error != "daemon crashed" {
|
|
t.Errorf("pending run error = %v, want %q", got.Error, "daemon crashed")
|
|
}
|
|
|
|
got, _ = d.GetRun(runningRun.ID)
|
|
if got.Status != types.RunFailed {
|
|
t.Errorf("running run status = %q, want %q", got.Status, types.RunFailed)
|
|
}
|
|
|
|
// Completed should be untouched.
|
|
got, _ = d.GetRun(completedRun.ID)
|
|
if got.Status != types.RunCompleted {
|
|
t.Errorf("completed run status = %q, want %q", got.Status, types.RunCompleted)
|
|
}
|
|
}
|
|
|
|
func TestRecoverStaleRunsExceptPreservesOnlyValidatedRuns(t *testing.T) {
|
|
d := openTestDB(t)
|
|
repo, _ := d.InsertRepo("/home/user/recovery-project", "git@github.com:user/recovery-project.git", "main")
|
|
preserved, _ := d.InsertRun(repo.ID, "feat-a", "aaa", "bbb")
|
|
stale, _ := d.InsertRun(repo.ID, "feat-b", "ccc", "ddd")
|
|
if err := d.UpdateRunStatus(preserved.ID, types.RunRunning); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := d.UpdateRunStatus(stale.ID, types.RunRunning); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
preservedStep, _ := d.InsertStepResult(preserved.ID, types.StepReview)
|
|
staleStep, _ := d.InsertStepResult(stale.ID, types.StepReview)
|
|
if err := d.StartStep(preservedStep.ID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := d.StartStep(staleStep.ID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
count, err := d.RecoverStaleRunsExcept("daemon crashed", map[string]struct{}{preserved.ID: {}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if count != 1 {
|
|
t.Fatalf("recovered count = %d, want 1", count)
|
|
}
|
|
gotPreserved, _ := d.GetRun(preserved.ID)
|
|
gotStale, _ := d.GetRun(stale.ID)
|
|
if gotPreserved.Status != types.RunRunning || gotStale.Status != types.RunFailed {
|
|
t.Fatalf("run statuses = preserved %s stale %s, want running and failed", gotPreserved.Status, gotStale.Status)
|
|
}
|
|
gotPreservedStep, _ := d.GetStepResult(preservedStep.ID)
|
|
gotStaleStep, _ := d.GetStepResult(staleStep.ID)
|
|
if gotPreservedStep.Status != types.StepStatusRunning || gotStaleStep.Status != types.StepStatusFailed {
|
|
t.Fatalf("step statuses = preserved %s stale %s, want running and failed", gotPreservedStep.Status, gotStaleStep.Status)
|
|
}
|
|
}
|
|
|
|
func TestRecoverStaleRunsMarksStepsFailed(t *testing.T) {
|
|
d := openTestDB(t)
|
|
repo, _ := d.InsertRepo("/home/user/project2", "git@github.com:user/project2.git", "main")
|
|
run, _ := d.InsertRun(repo.ID, "feature", "abc", "def")
|
|
|
|
// Create steps in various statuses.
|
|
runningStep, _ := d.InsertStepResult(run.ID, types.StepReview)
|
|
d.StartStep(runningStep.ID)
|
|
awaitingStep, _ := d.InsertStepResult(run.ID, types.StepTest)
|
|
d.UpdateStepStatus(awaitingStep.ID, types.StepStatusAwaitingApproval)
|
|
fixingStep, _ := d.InsertStepResult(run.ID, types.StepLint)
|
|
d.UpdateStepStatus(fixingStep.ID, types.StepStatusFixing)
|
|
completedStep, _ := d.InsertStepResult(run.ID, types.StepPush)
|
|
d.CompleteStep(completedStep.ID, 0, 100, "/tmp/log")
|
|
pendingStep, _ := d.InsertStepResult(run.ID, types.StepPR)
|
|
|
|
_, err := d.RecoverStaleRuns("daemon crashed")
|
|
if err != nil {
|
|
t.Fatalf("recover stale runs: %v", err)
|
|
}
|
|
|
|
// Running, awaiting_approval, fixing should be failed.
|
|
for _, tc := range []struct {
|
|
id string
|
|
name string
|
|
want types.StepStatus
|
|
}{
|
|
{runningStep.ID, "running", types.StepStatusFailed},
|
|
{awaitingStep.ID, "awaiting", types.StepStatusFailed},
|
|
{fixingStep.ID, "fixing", types.StepStatusFailed},
|
|
{completedStep.ID, "completed", types.StepStatusCompleted},
|
|
{pendingStep.ID, "pending", types.StepStatusPending},
|
|
} {
|
|
got, _ := d.GetStepResult(tc.id)
|
|
if got.Status != tc.want {
|
|
t.Errorf("step %s: status = %q, want %q", tc.name, got.Status, tc.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRecoverStaleRunsNoStaleRuns(t *testing.T) {
|
|
d := openTestDB(t)
|
|
repo, _ := d.InsertRepo("/home/user/project3", "git@github.com:user/project3.git", "main")
|
|
|
|
// Only completed runs.
|
|
run, _ := d.InsertRun(repo.ID, "feat", "abc", "def")
|
|
d.UpdateRunStatus(run.ID, types.RunCompleted)
|
|
|
|
count, err := d.RecoverStaleRuns("daemon crashed")
|
|
if err != nil {
|
|
t.Fatalf("recover: %v", err)
|
|
}
|
|
if count != 0 {
|
|
t.Errorf("recovered count = %d, want 0", count)
|
|
}
|
|
}
|