- IMAP 推送:imapBackend 实现 backend.BackendUpdater,SMTP 本地投递 与 Web 写信投递成功后 NotifyNewMessage,挂起 IDLE 的客户端即时收到 新邮件 FETCH 通知(按用户名+INBOX 过滤广播,通道满非阻塞丢弃) - 当前连接:新增 internal/connhub 连接注册中心,SMTP/IMAP/POP3 三协议 注册/注销/用户名/TLS/活跃时间追踪;后台新增「当前连接」页 (/admin/connections,统计卡片+连接表格,每 5 秒自动刷新) - 新增测试:connhub 并发安全、推送内容/非阻塞/nil 安全、 后台页面渲染;全量 -race 通过
116 lines
2.5 KiB
Go
116 lines
2.5 KiB
Go
package connhub
|
|
|
|
import (
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestRegisterListCountClose(t *testing.T) {
|
|
h := New()
|
|
|
|
c1 := h.Register("smtp", "10.0.0.1", 25, false)
|
|
c2 := h.Register("imap", "10.0.0.2", 993, true)
|
|
c3 := h.Register("pop3", "10.0.0.3", 110, false)
|
|
|
|
if n := len(h.List()); n != 3 {
|
|
t.Fatalf("list len = %d, want 3", n)
|
|
}
|
|
counts := h.Counts()
|
|
if counts["smtp"] != 1 || counts["imap"] != 1 || counts["pop3"] != 1 {
|
|
t.Fatalf("counts = %v", counts)
|
|
}
|
|
|
|
c2.SetUser("alice@example.com")
|
|
c1.SetTLS(true)
|
|
time.Sleep(time.Millisecond)
|
|
c3.Touch()
|
|
|
|
// 用户名 / TLS 状态生效
|
|
var imapUser string
|
|
for _, c := range h.List() {
|
|
if c.Protocol == "imap" {
|
|
imapUser = c.User
|
|
}
|
|
if c.Protocol == "smtp" && !c.TLS {
|
|
t.Fatal("smtp conn should be TLS after SetTLS(true)")
|
|
}
|
|
if c.Protocol == "pop3" && !c.LastActive.After(c.Connected) {
|
|
t.Fatal("pop3 conn LastActive should be after Connected after Touch")
|
|
}
|
|
}
|
|
if imapUser != "alice@example.com" {
|
|
t.Fatalf("imap user = %q", imapUser)
|
|
}
|
|
|
|
c1.Close()
|
|
c2.Close()
|
|
if n := len(h.List()); n != 1 {
|
|
t.Fatalf("after close, len = %d, want 1", n)
|
|
}
|
|
if h.Counts()["imap"] != 0 {
|
|
t.Fatalf("imap count after close = %d, want 0", h.Counts()["imap"])
|
|
}
|
|
}
|
|
|
|
func TestNilHubSafe(t *testing.T) {
|
|
var h *Hub
|
|
if c := h.Register("smtp", "1.2.3.4", 25, false); c != nil {
|
|
t.Fatal("nil hub Register must return nil")
|
|
}
|
|
if h.List() != nil || h.Counts() != nil {
|
|
t.Fatal("nil hub List/Counts must be nil")
|
|
}
|
|
var c *Conn
|
|
c.SetUser("x") // 不应 panic
|
|
c.Touch()
|
|
c.SetTLS(true)
|
|
c.Close()
|
|
}
|
|
|
|
func TestConcurrentRegisterClose(t *testing.T) {
|
|
h := New()
|
|
const n = 50
|
|
var wg sync.WaitGroup
|
|
conns := make([]*Conn, n)
|
|
for i := 0; i < n; i++ {
|
|
wg.Add(1)
|
|
go func(i int) {
|
|
defer wg.Done()
|
|
conns[i] = h.Register("smtp", "10.0.0.1", 25, false)
|
|
conns[i].SetUser("u")
|
|
conns[i].Touch()
|
|
}(i)
|
|
}
|
|
wg.Wait()
|
|
|
|
if len(h.List()) != n {
|
|
t.Fatalf("len = %d, want %d", len(h.List()), n)
|
|
}
|
|
|
|
for i := 0; i < n; i++ {
|
|
wg.Add(1)
|
|
go func(i int) {
|
|
defer wg.Done()
|
|
conns[i].Close()
|
|
}(i)
|
|
}
|
|
wg.Wait()
|
|
if len(h.List()) != 0 {
|
|
t.Fatalf("after concurrent close, len = %d, want 0", len(h.List()))
|
|
}
|
|
}
|
|
|
|
func TestListOrderedByID(t *testing.T) {
|
|
h := New()
|
|
h.Register("smtp", "1.1.1.1", 25, false)
|
|
h.Register("imap", "2.2.2.2", 143, false)
|
|
h.Register("pop3", "3.3.3.3", 110, false)
|
|
list := h.List()
|
|
for i := 1; i < len(list); i++ {
|
|
if list[i].ID <= list[i-1].ID {
|
|
t.Fatalf("list not ordered by ID: %+v", list)
|
|
}
|
|
}
|
|
}
|