134 lines
3.5 KiB
Go
134 lines
3.5 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.pcux.cn/xuans/Scert/backend/internal/httpapi"
|
|
)
|
|
|
|
func TestManagementRecoveryCloseCancelsAndWaits(t *testing.T) {
|
|
application := &App{}
|
|
started := make(chan struct{})
|
|
canceled := make(chan struct{})
|
|
release := make(chan struct{})
|
|
var once sync.Once
|
|
releaseRecovery := func() { once.Do(func() { close(release) }) }
|
|
t.Cleanup(releaseRecovery)
|
|
application.startManagementRecovery(func(ctx context.Context) error {
|
|
if deadline, ok := ctx.Deadline(); !ok || time.Until(deadline) > 100*time.Second {
|
|
return errors.New("恢复尝试必须有超时边界")
|
|
}
|
|
close(started)
|
|
<-ctx.Done()
|
|
close(canceled)
|
|
<-release
|
|
return ctx.Err()
|
|
})
|
|
t.Cleanup(func() {
|
|
releaseRecovery()
|
|
if err := application.Close(); err != nil {
|
|
t.Error(err)
|
|
}
|
|
})
|
|
select {
|
|
case <-started:
|
|
case <-time.After(3 * time.Second):
|
|
t.Fatal("恢复协程没有开始执行")
|
|
}
|
|
closed := make(chan error, 1)
|
|
go func() { closed <- application.Close() }()
|
|
select {
|
|
case <-canceled:
|
|
case <-time.After(3 * time.Second):
|
|
t.Fatal("关闭应用没有取消恢复尝试")
|
|
}
|
|
select {
|
|
case err := <-closed:
|
|
t.Fatal("恢复尝试退出前不能完成应用关闭", err)
|
|
default:
|
|
}
|
|
releaseRecovery()
|
|
select {
|
|
case err := <-closed:
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
case <-time.After(3 * time.Second):
|
|
t.Fatal("恢复退出后应用关闭没有完成")
|
|
}
|
|
}
|
|
|
|
func TestManagementRecoveryCloseInterruptsRetryWait(t *testing.T) {
|
|
application := &App{}
|
|
returned := make(chan struct{})
|
|
application.startManagementRecovery(func(context.Context) error {
|
|
close(returned)
|
|
return errors.New("注入临时恢复失败")
|
|
})
|
|
t.Cleanup(func() { _ = application.Close() })
|
|
select {
|
|
case <-returned:
|
|
case <-time.After(3 * time.Second):
|
|
t.Fatal("首次恢复未执行")
|
|
}
|
|
closed := make(chan error, 1)
|
|
go func() { closed <- application.Close() }()
|
|
select {
|
|
case err := <-closed:
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
case <-time.After(3 * time.Second):
|
|
t.Fatal("关闭应用不应等待整分钟的重试间隔")
|
|
}
|
|
}
|
|
|
|
func TestManagementRecoveryNilDoesNotStartWorker(t *testing.T) {
|
|
application := &App{}
|
|
application.startManagementRecovery(nil)
|
|
if application.managementStop != nil || application.managementDone != nil {
|
|
t.Fatal("未配置恢复逻辑时不应启动协程")
|
|
}
|
|
if err := application.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestManagementRecoveryWaitsForHTTPS(t *testing.T) {
|
|
application := &App{HTTP: httpapi.New(httpapi.Dependencies{})}
|
|
called := make(chan struct{}, 1)
|
|
application.startManagementRecovery(func(context.Context) error {
|
|
called <- struct{}{}
|
|
return nil
|
|
})
|
|
if err := application.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
select {
|
|
case <-called:
|
|
t.Fatal("HTTPS 尚未监听时不能开始恢复")
|
|
default:
|
|
}
|
|
}
|
|
|
|
func TestManagementActivationRequiresHTTPSListener(t *testing.T) {
|
|
for _, server := range []*httpapi.Server{nil, httpapi.New(httpapi.Dependencies{})} {
|
|
if err := requireManagementListener(t.Context(), server); err == nil {
|
|
t.Fatal("没有监听器的桌面或嵌入式运行时不能申请管理证书")
|
|
}
|
|
activator := directManagementActivator{server: server}
|
|
if err := activator.Ready(t.Context(), httpapi.ManagementCertificateState{HTTPSMode: "direct"}); err == nil {
|
|
t.Fatal("直接模式不能绕过监听检查")
|
|
}
|
|
}
|
|
ctx, cancel := context.WithCancel(t.Context())
|
|
cancel()
|
|
if err := requireManagementListener(ctx, nil); !errors.Is(err, context.Canceled) {
|
|
t.Fatal("监听检查应保留取消原因", err)
|
|
}
|
|
}
|