package imapx import ( "context" "testing" "time" ) // TestKeepaliveReturnsOnContextCancel proves Keepalive is a well-behaved // goroutine: it exits promptly when its context is cancelled instead of // leaking. func TestKeepaliveReturnsOnContextCancel(t *testing.T) { ep := testEP(t) ctx := context.Background() c, err := Connect(ctx, ep) if err != nil { t.Fatal(err) } defer func() { _ = c.Logout().Wait() }() if err := c.Login("ka1@localhost", "p").Wait(); err != nil { t.Fatal(err) } kctx, cancel := context.WithCancel(ctx) done := make(chan struct{}) go func() { Keepalive(kctx, c, 10*time.Millisecond); close(done) }() // Let a few NOOPs fire, then cancel and require a prompt return. time.Sleep(50 * time.Millisecond) cancel() select { case <-done: case <-time.After(2 * time.Second): t.Fatal("Keepalive did not return within 2s of context cancel") } } // TestKeepaliveDoesNotDisruptCopy runs Keepalive on the destination connection // at an aggressive interval while CopyFolder is APPENDing to it, proving the // concurrent NOOPs do not corrupt in-flight commands (the real risk of pinging // a connection that is also being used for real work). func TestKeepaliveDoesNotDisruptCopy(t *testing.T) { ep := testEP(t) ctx := context.Background() const n = 8 seedInbox(t, ep, "kasrc@localhost", "p", n) src, err := Connect(ctx, ep) if err != nil { t.Fatal(err) } defer func() { _ = src.Logout().Wait() }() if err := src.Login("kasrc@localhost", "p").Wait(); err != nil { t.Fatal(err) } dst, err := Connect(ctx, ep) if err != nil { t.Fatal(err) } defer func() { _ = dst.Logout().Wait() }() if err := dst.Login("kadst@localhost", "p").Wait(); err != nil { t.Fatal(err) } kctx, cancel := context.WithCancel(ctx) defer cancel() go Keepalive(kctx, dst, 1*time.Millisecond) seen := map[string]bool{} deps := CopyDeps{ IsMigrated: func(k string) (bool, error) { return seen[k], nil }, MarkMigrated: func(_, k string) error { seen[k] = true; return nil }, OnProgress: func(_, _ int) {}, } r, err := CopyFolder(kctx, src, dst, "INBOX", "INBOX", deps) if err != nil { t.Fatalf("CopyFolder with concurrent keepalive: %v", err) } if r.Copied != n { t.Fatalf("copied=%d want %d", r.Copied, n) } if r.Errors != 0 { t.Fatalf("errors=%d want 0", r.Errors) } }