通过本机Unix套接字报告HTTPS监听状态,同步服务配置与安装模板,保持初始化业务就绪隔离。补充生命周期回归并修复旧CLI测试误用正式目录的问题。更新Linux原生测试证据,明确新候选尚未部署、生产验收未放行。
93 lines
2.4 KiB
Go
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("初始化服务未按生命周期退出")
|
|
}
|
|
})
|
|
}
|
|
}
|