Scert/backend/internal/bootstrap/server_test.go

116 lines
3.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package bootstrap
import (
"context"
"crypto/tls"
"crypto/x509"
"io"
"net"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"testing"
"time"
"git.pcux.cn/xuans/Scert/backend/internal/config"
)
func TestTemporaryHTTPSDrainsCompletionResponse(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)
}
completed := make(chan struct{})
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
result := make(chan error, 1)
body := strings.Repeat("response-data", 10000)
go func() {
ok, err := serveTemporaryHTTPS(ctx, listener, material, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
close(completed)
time.Sleep(30 * time.Millisecond)
_, _ = io.WriteString(w, body)
}), completed)
if !ok && err == nil {
err = context.Canceled
}
result <- err
}()
roots := x509.NewCertPool()
roots.AddCert(material.Leaf)
transport := &http.Transport{TLSClientConfig: &tls.Config{RootCAs: roots, MinVersion: tls.VersionTLS12}}
defer transport.CloseIdleConnections()
client := &http.Client{Transport: transport, Timeout: 5 * time.Second}
response, err := client.Get("https://" + listener.Addr().String() + "/")
if err != nil {
t.Fatal(err)
}
data, err := io.ReadAll(response.Body)
response.Body.Close()
if err != nil || string(data) != body {
t.Fatalf("成功响应被关闭截断:%v,长度 %d", err, len(data))
}
select {
case err := <-result:
if err != nil {
t.Fatal(err)
}
case <-time.After(5 * time.Second):
t.Fatal("临时服务没有关闭")
}
if conn, err := net.DialTimeout("tcp", listener.Addr().String(), 100*time.Millisecond); err == nil {
conn.Close()
t.Fatal("关闭后仍可连接")
}
}
func TestBootstrapHandlerIsolatesBusinessRoutesAndSettings(t *testing.T) {
root := t.TempDir()
if err := os.MkdirAll(filepath.Join(root, "web"), 0o700); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(root, "web", "index.html"), []byte("bootstrap-page"), 0o600); err != nil {
t.Fatal(err)
}
cfg := config.Default(root)
cfg.Paths.Install = root
sessions, token, err := NewSessionManager()
if err != nil {
t.Fatal(err)
}
router := NewRouter(sessions, nil, nil, nil)
handler, closeFiles := bootstrapHandler(router, cfg)
defer closeFiles()
for path, status := range map[string]int{
"/": 200, "/api/v1/bootstrap/status": 200, "/api/v1/bootstrap/settings": 401,
"/api/v1/setup": 404, "/api/v1/domains": 404, "/reset": 404, "/scert.json": 404,
"/assets/../../config.json": 400,
} {
response := httptest.NewRecorder()
handler.ServeHTTP(response, httptest.NewRequest(http.MethodGet, path, nil))
if response.Code != status {
t.Fatalf("%s 状态码 %d,期望 %d", path, response.Code, status)
}
if response.Header().Get("Cache-Control") != "no-store" {
t.Fatal("临时页面允许缓存")
}
}
session, err := sessions.Exchange(token, "127.0.0.1")
if err != nil {
t.Fatal(err)
}
request := httptest.NewRequest(http.MethodGet, "/api/v1/bootstrap/settings", nil)
request.AddCookie(BootstrapSessionCookie(session.Token, session.ExpiresAt))
response := httptest.NewRecorder()
handler.ServeHTTP(response, request)
if response.Code != 200 || !strings.Contains(response.Body.String(), `"mode":"bootstrap"`) {
t.Fatal("认证后无法读取初始化设置")
}
}