Files
Havline/internal/frp/restart_test.go
T
mrlee 2c1cf1260b
CI / verify (push) Canceled after 0s
修正frpc重启按钮的工作流程
2026-10-19 13:50:05 +08:00

98 lines
3.3 KiB
Go

package frp
import (
"context"
"io"
"log/slog"
"path/filepath"
"strings"
"testing"
"github.com/havline/havline/internal/certificate"
"github.com/havline/havline/internal/config"
"github.com/havline/havline/internal/db"
"github.com/havline/havline/internal/secret"
"github.com/havline/havline/internal/settings"
)
func newRestartTestService(t *testing.T) (*Service, *settings.Store, context.Context) {
t.Helper()
dir := t.TempDir()
conn, err := db.Open(filepath.Join(dir, "havline.db"))
if err != nil {
t.Fatalf("打开数据库失败:%v", err)
}
t.Cleanup(func() { _ = conn.Close() })
if err := db.Migrate(context.Background(), conn, filepath.Join("..", "..", "migrations")); err != nil {
t.Fatalf("迁移失败:%v", err)
}
box, err := secret.NewBox("restart-test-key")
if err != nil {
t.Fatal(err)
}
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
svc := NewService(config.Config{DataDir: dir}, NewStore(conn), box, logger, certificate.NewStore(conn))
if err := svc.EnsureDirs(); err != nil {
t.Fatalf("创建目录失败:%v", err)
}
settingsStore := settings.NewStore(conn)
svc.SetSettingsStore(settingsStore)
return svc, settingsStore, context.Background()
}
// 用户往往是从隧道那头点「重启」:Stop 一杀 frpc,连接就断、请求上下文随即取消。
// 重启流程必须无视这个取消走完,否则就是「停掉了却再也起不来」。
func TestRestartIgnoresCancelledRequestContext(t *testing.T) {
svc, _, _ := newRestartTestService(t)
ctx, cancel := context.WithCancel(context.Background())
cancel()
err := svc.Restart(ctx)
if err != nil && strings.Contains(err.Error(), "context canceled") {
t.Fatalf("重启不应被请求取消打断:%v", err)
}
}
// frpc 的启停状态要能被记录与读回:应用停用再启用后据此自动恢复。
func TestFrpResumeStateRoundTrip(t *testing.T) {
svc, _, ctx := newRestartTestService(t)
if svc.lastSessionWasRunning(ctx) {
t.Fatal("默认应为「上次未运行」")
}
svc.rememberResumeState(ctx, true)
if !svc.lastSessionWasRunning(ctx) {
t.Fatal("记录运行后应能读回,供下次启动自动恢复")
}
svc.rememberResumeState(ctx, false)
if svc.lastSessionWasRunning(ctx) {
t.Fatal("手动停止后不应再自动恢复")
}
}
// 应用退出(StopForShutdown)不能把「上次在运行」标记抹掉,
// 否则「停用应用 → 重新启用」永远触发不了自动恢复。
func TestStopForShutdownKeepsResumeFlag(t *testing.T) {
svc, _, ctx := newRestartTestService(t)
// 模拟退出前 frpc 正在运行(真实环境里由 Status 判断,测试里直接给结论)
svc.runningProbe = func(context.Context) bool { return true }
svc.rememberResumeState(ctx, true)
if err := svc.StopForShutdown(ctx); err != nil {
t.Fatalf("退出时停止失败:%v", err)
}
if !svc.lastSessionWasRunning(ctx) {
t.Fatal("退出后仍应保留「上次在运行」标记,供下次启用时自动恢复")
}
// 退出前本来就是停止的:不能凭空写成「在运行」
svc.runningProbe = func(context.Context) bool { return false }
if err := svc.StopForShutdown(ctx); err != nil {
t.Fatalf("退出时停止失败:%v", err)
}
if svc.lastSessionWasRunning(ctx) {
t.Fatal("退出前没在运行就不该标记为自动恢复")
}
}