加固桌面运行时空状态与在途初始化取消边界

This commit is contained in:
xuans 2026-09-16 20:26:59 +08:00
parent a1d1b03db0
commit 5509a611f0
3 changed files with 54 additions and 1 deletions

View File

@ -15,3 +15,15 @@ func TestHandlerReturnsServiceUnavailableWhenRuntimeIsNil(t *testing.T) {
t.Fatalf("空运行时状态码错误:%d", response.Code)
}
}
func TestHandlerReturnsServiceUnavailableForZeroRuntime(t *testing.T) {
runtime := &Runtime{}
response := httptest.NewRecorder()
runtime.Handler().ServeHTTP(response, httptest.NewRequest(http.MethodGet, "/healthz", nil))
if response.Code != http.StatusServiceUnavailable {
t.Fatalf("未打开的运行时应返回服务不可用:%d", response.Code)
}
if err := runtime.Close(); err != nil {
t.Fatal(err)
}
}

View File

@ -93,7 +93,7 @@ func Open(configPath, baseDir string) (*Runtime, error) {
func (r *Runtime) Handler() http.Handler {
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
if r == nil {
if r == nil || r.ctx == nil {
http.Error(writer, "Scert 核心服务未初始化", http.StatusServiceUnavailable)
return
}

View File

@ -1,6 +1,7 @@
package service
import (
"context"
"net/http"
"net/http/httptest"
"os"
@ -8,6 +9,7 @@ import (
"strings"
"sync"
"testing"
"time"
"git.pcux.cn/xuans/Scert/backend/internal/setup"
)
@ -123,3 +125,42 @@ func TestDesktopTransitionRejectsCorruptedSavedConfig(t *testing.T) {
t.Fatal("损坏配置不得回退到新的初始化或启动正式服务")
}
}
func TestDesktopCloseCancelsInFlightInitialization(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
started := make(chan struct{})
finished := make(chan struct{})
runtime := &Runtime{
ctx: ctx, cancel: cancel, configPath: filepath.Join(t.TempDir(), "scert.json"),
initial: http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
close(started)
<-request.Context().Done()
writer.WriteHeader(http.StatusServiceUnavailable)
}),
}
go func() {
defer close(finished)
runtime.Handler().ServeHTTP(httptest.NewRecorder(), httptest.NewRequest(http.MethodPost, "/api/v1/setup", nil))
}()
select {
case <-started:
case <-time.After(5 * time.Second):
t.Fatal("初始化处理器没有启动")
}
closed := make(chan error, 1)
go func() { closed <- runtime.Close() }()
select {
case err := <-closed:
if err != nil {
t.Fatal(err)
}
case <-time.After(5 * time.Second):
t.Fatal("关闭运行时没有取消在途初始化请求")
}
select {
case <-finished:
case <-time.After(5 * time.Second):
t.Fatal("关闭后仍有初始化处理器运行")
}
}