* feat(windows): 增加原生服务安装包 * fix(windows): 收紧服务安装与升级流程 * fix(windows): 固定服务安装路径与语言资源 * fix(windows): 等待安装器进程完成 * fix(windows): 清理首次安装失败残留
107 lines
2.6 KiB
Go
107 lines
2.6 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
type fakeManagedApplication struct {
|
|
start func() error
|
|
startErr error
|
|
stop func(context.Context) error
|
|
serveErrors chan error
|
|
stopCalls chan context.Context
|
|
}
|
|
|
|
func (application *fakeManagedApplication) Start() error {
|
|
if application.start != nil {
|
|
return application.start()
|
|
}
|
|
return application.startErr
|
|
}
|
|
|
|
func (application *fakeManagedApplication) Stop(ctx context.Context) error {
|
|
if application.stopCalls != nil {
|
|
application.stopCalls <- ctx
|
|
}
|
|
if application.stop != nil {
|
|
return application.stop(ctx)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (application *fakeManagedApplication) ServeErrors() <-chan error {
|
|
return application.serveErrors
|
|
}
|
|
|
|
func TestManagedRuntimeCleansUpAfterStartFailure(t *testing.T) {
|
|
startErr := errors.New("startup failed")
|
|
stopCalls := make(chan context.Context, 1)
|
|
runtime := &managedRuntime{
|
|
application: &fakeManagedApplication{
|
|
startErr: startErr,
|
|
stopCalls: stopCalls,
|
|
},
|
|
shutdownTimeout: 10 * time.Second,
|
|
}
|
|
|
|
err := runtime.start()
|
|
if !errors.Is(err, startErr) {
|
|
t.Fatalf("start() error = %v, want %v", err, startErr)
|
|
}
|
|
select {
|
|
case cleanupCtx := <-stopCalls:
|
|
deadline, ok := cleanupCtx.Deadline()
|
|
if !ok || time.Until(deadline) > time.Second {
|
|
t.Fatalf("startup cleanup deadline = %v, want at most one second", deadline)
|
|
}
|
|
default:
|
|
t.Fatal("start() did not clean up after failure")
|
|
}
|
|
}
|
|
|
|
func TestManagedRuntimeUsesConfiguredShutdownDeadline(t *testing.T) {
|
|
const timeout = 250 * time.Millisecond
|
|
stopCalls := make(chan context.Context, 1)
|
|
runtime := &managedRuntime{
|
|
application: &fakeManagedApplication{stopCalls: stopCalls},
|
|
shutdownTimeout: timeout,
|
|
}
|
|
|
|
if err := runtime.stop(nil); err != nil {
|
|
t.Fatalf("stop() error = %v", err)
|
|
}
|
|
ctx := <-stopCalls
|
|
deadline, ok := ctx.Deadline()
|
|
if !ok {
|
|
t.Fatal("shutdown context has no deadline")
|
|
}
|
|
remaining := time.Until(deadline)
|
|
if remaining <= 0 || remaining > timeout {
|
|
t.Fatalf("shutdown deadline remaining = %v, want within %v", remaining, timeout)
|
|
}
|
|
}
|
|
|
|
func TestManagedRuntimeCancelsShutdownWhenForced(t *testing.T) {
|
|
stopStarted := make(chan struct{})
|
|
runtime := &managedRuntime{
|
|
application: &fakeManagedApplication{stop: func(ctx context.Context) error {
|
|
close(stopStarted)
|
|
<-ctx.Done()
|
|
return context.Cause(ctx)
|
|
}},
|
|
shutdownTimeout: time.Minute,
|
|
}
|
|
force := make(chan struct{})
|
|
result := make(chan error, 1)
|
|
go func() { result <- runtime.stop(force) }()
|
|
|
|
<-stopStarted
|
|
close(force)
|
|
if err := <-result; !errors.Is(err, context.Canceled) {
|
|
t.Fatalf("forced stop error = %v, want context canceled", err)
|
|
}
|
|
}
|