// 本文件只测连接态三态的**纯判定**(ConnectStateOf):它不读时钟、不触网、不加锁, // 因此三态可以逐条单测。适配器侧的落点(qq_obv11 的 ConnectOutcome 与脱敏)见 // plugins/channels/qq_obv11/connect_outcome_test.go,网关侧的映射见 // internal/services/gateway/admin_channel_connect_outcome_test.go。 package adapter import "testing" // TestConnectStateOfNotStartedHasNoOpinion 断言未启动的适配器**不编状态**:回空串, // 调用方据此退回只看 IsConnected(),而不是随便认一个。 func TestConnectStateOfNotStartedHasNoOpinion(t *testing.T) { t.Parallel() for _, sig := range []ConnectSignals{ {}, {Connected: true}, {Retrying: true, Reason: "boom"}, {Listening: true}, } { if state, reason := ConnectStateOf(sig); state != "" || reason != "" { t.Errorf("ConnectStateOf(%+v) = (%q,%q), want 无意见", sig, state, reason) } } } // TestConnectStateOfConnectedWins 断言真连上优先于其它分支(监听与重试信号同时为真 // 也不该改变结论),且**不**带原因 —— 「已连接」旁边不该挂一条过期的失败原因。 func TestConnectStateOfConnectedWins(t *testing.T) { t.Parallel() state, reason := ConnectStateOf(ConnectSignals{ Started: true, Connected: true, Retrying: true, Listening: true, Reason: "stale failure", }) if state != ConnectStateConnected || reason != "" { t.Fatalf("ConnectStateOf = (%q,%q), want (%q,\"\")", state, reason, ConnectStateConnected) } } // TestConnectStateOfRetryingIsPending 断言「未连上但重试在跑」是 pending 且带上原因。 // 这正是 2026-10-07 事故里被写成「已连接」(或静态「未连接」)的那一格。 func TestConnectStateOfRetryingIsPending(t *testing.T) { t.Parallel() state, reason := ConnectStateOf(ConnectSignals{ Started: true, Retrying: true, Reason: "dial tcp 127.0.0.1:10301: connection refused", }) if state != ConnectStatePending { t.Fatalf("state = %q, want %q", state, ConnectStatePending) } if reason != "dial tcp 127.0.0.1:10301: connection refused" { t.Fatalf("reason = %q, want the failure reason verbatim", reason) } // 三个取值互不相同:把 pending 写成 connected 是这次要根除的谎。 if ConnectStatePending == ConnectStateConnected || ConnectStatePending == ConnectStateReady { t.Fatal("pending 的取值必须与 connected / ready 都不同") } } // TestConnectStateOfListeningIsReady 断言「已绑定监听、等对端接入」是 ready 而不是 // pending:它不是失败,因此**没有**原因可说(哪怕结构体里带着一条陈旧的 reason)。 func TestConnectStateOfListeningIsReady(t *testing.T) { t.Parallel() state, reason := ConnectStateOf(ConnectSignals{ Started: true, Listening: true, Reason: "should not be surfaced", }) if state != ConnectStateReady || reason != "" { t.Fatalf("ConnectStateOf = (%q,%q), want (%q,\"\")", state, reason, ConnectStateReady) } } // TestConnectStateOfUnexplainedStaysSilent 断言「已启动、未连上、也没有可解释的就绪面」 // 时**不编**状态:既不说「正在连接」(不知道有没有在连),也不说「就绪」。 func TestConnectStateOfUnexplainedStaysSilent(t *testing.T) { t.Parallel() if state, reason := ConnectStateOf(ConnectSignals{Started: true, Reason: "orphan"}); state != "" || reason != "" { t.Fatalf("ConnectStateOf = (%q,%q), want 无意见", state, reason) } }