Scert/backend/internal/bootstrap/server_ready_test.go
xuans 0b74886c45 修复Linux启动就绪通知并隔离命令行测试目录
通过本机Unix套接字报告HTTPS监听状态,同步服务配置与安装模板,保持初始化业务就绪隔离。补充生命周期回归并修复旧CLI测试误用正式目录的问题。更新Linux原生测试证据,明确新候选尚未部署、生产验收未放行。
2026-10-02 12:20:47 +08:00

93 lines
2.4 KiB
Go

package bootstrap
import (
"context"
"crypto/tls"
"crypto/x509"
"errors"
"io"
"net"
"net/http"
"strings"
"testing"
"time"
"git.pcux.cn/xuans/Scert/backend/internal/config"
"git.pcux.cn/xuans/Scert/backend/internal/serviceready"
)
func TestBootstrapListeningIsNotBusinessReady(t *testing.T) {
for _, fail := range []bool{false, true} {
name := "listening"
if fail {
name = "notify-failure"
}
t.Run(name, func(t *testing.T) {
material, err := NewTemporaryTLSMaterial("127.0.0.1")
if err != nil {
t.Fatal(err)
}
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
defer listener.Close()
cfg := config.Default(t.TempDir())
handler, closeFiles := bootstrapHandler(nil, cfg)
defer closeFiles()
expected := errors.New("本机通知失败")
notified := make(chan struct{})
wrapped := &serviceready.Listener{Listener: listener, Notify: func() error {
close(notified)
if fail {
return expected
}
return nil
}}
ctx, cancel := context.WithCancel(t.Context())
defer cancel()
result := make(chan error, 1)
go func() {
complete, err := serveTemporaryHTTPS(ctx, wrapped, material, handler, make(chan struct{}))
if complete {
t.Error("监听成功被当作初始化完成")
}
result <- err
}()
select {
case <-notified:
case <-time.After(3 * time.Second):
t.Fatal("初始化接收循环未发出通知")
}
if !fail {
roots := x509.NewCertPool()
roots.AddCert(material.Leaf)
transport := &http.Transport{TLSClientConfig: &tls.Config{MinVersion: tls.VersionTLS12, RootCAs: roots}}
defer transport.CloseIdleConnections()
client := &http.Client{Transport: transport, Timeout: 3 * time.Second}
response, err := client.Get("https://" + listener.Addr().String() + "/readyz")
if err != nil {
t.Fatal(err)
}
body, err := io.ReadAll(response.Body)
response.Body.Close()
if err != nil || response.StatusCode != http.StatusServiceUnavailable || !strings.Contains(string(body), "setup_incomplete") {
t.Fatal("监听通知改变了初始化业务就绪边界")
}
cancel()
}
select {
case err := <-result:
if fail && !errors.Is(err, expected) {
t.Fatalf("未保留通知失败原因:%v", err)
}
if !fail && err != nil {
t.Fatal(err)
}
case <-time.After(5 * time.Second):
t.Fatal("初始化服务未按生命周期退出")
}
})
}
}