Files
NixMsg/test/accept/server.go
T

129 lines
3.2 KiB
Go

package accept
import (
"bytes"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
"git.asio.asia/nixevol/NixMsg/test/harness"
)
// LoggedServer 在捕获 stdout/stderr 的情况下启动 serve(用于断言密码不进日志)。
type LoggedServer struct {
*harness.Server
LogBuf *bytes.Buffer
cmd *exec.Cmd
}
// StartWithLogCapture 执行 admin init 后启动 serve,并把进程日志写入 LogBuf。
func StartWithLogCapture() (*LoggedServer, error) {
bin, err := harness.Binary()
if err != nil {
return nil, err
}
dataDir, err := os.MkdirTemp("", "nixmsg-accept-*")
if err != nil {
return nil, err
}
cfgPath := filepath.Join(dataDir, "config.yaml")
cfg := fmt.Sprintf("listen: %q\ndata_dir: %q\n", "127.0.0.1:0", filepath.ToSlash(dataDir))
if err = os.WriteFile(cfgPath, []byte(cfg), 0o644); err != nil {
_ = os.RemoveAll(dataDir)
return nil, err
}
initCmd := exec.Command(bin, "admin", "init")
initCmd.Env = append(os.Environ(), "NIXMSG_CONFIG="+cfgPath)
initOut, initErr := initCmd.CombinedOutput()
if initErr != nil {
_ = os.RemoveAll(dataDir)
return nil, fmt.Errorf("admin init: %w\n%s", initErr, initOut)
}
password := parsePassword(string(initOut))
if password == "" {
_ = os.RemoveAll(dataDir)
return nil, fmt.Errorf("admin init password not found:\n%s", initOut)
}
logBuf := &bytes.Buffer{}
cmd := exec.Command(bin, "serve")
cmd.Env = append(os.Environ(), "NIXMSG_CONFIG="+cfgPath)
cmd.Stdout = logBuf
cmd.Stderr = logBuf
if err = cmd.Start(); err != nil {
_ = os.RemoveAll(dataDir)
return nil, fmt.Errorf("start serve: %w", err)
}
s := &harness.Server{
BinPath: bin,
ConfigPath: cfgPath,
DataDir: dataDir,
AdminPassword: password,
}
addr, err := waitListenAddr(filepath.Join(dataDir, "listen.addr"), 15*time.Second)
if err != nil {
_ = cmd.Process.Kill()
_, _ = cmd.Process.Wait()
_ = os.RemoveAll(dataDir)
return nil, fmt.Errorf("wait listen.addr: %w\nlogs:\n%s", err, logBuf.String())
}
s.Addr = addr
s.HTTPBase = "http://" + addr
s.AdminHTTPBase = s.HTTPBase
return &LoggedServer{Server: s, LogBuf: logBuf, cmd: cmd}, nil
}
// Stop 结束进程并删除临时目录。
func (s *LoggedServer) Stop() error {
if s == nil {
return nil
}
if s.cmd != nil && s.cmd.Process != nil {
_ = s.cmd.Process.Kill()
_, _ = s.cmd.Process.Wait()
}
if s.DataDir != "" {
return os.RemoveAll(s.DataDir)
}
return nil
}
func parsePassword(out string) string {
for _, line := range strings.Split(out, "\n") {
line = strings.TrimSpace(line)
lower := strings.ToLower(line)
if strings.HasPrefix(lower, "admin password:") {
return strings.TrimSpace(line[len("admin password:"):])
}
if strings.HasPrefix(lower, "password:") {
return strings.TrimSpace(line[len("password:"):])
}
}
return ""
}
func waitListenAddr(path string, timeout time.Duration) (string, error) {
deadline := time.Now().Add(timeout)
var lastErr error
for time.Now().Before(deadline) {
b, err := os.ReadFile(path)
if err == nil {
addr := strings.TrimSpace(string(b))
if addr != "" {
return addr, nil
}
lastErr = fmt.Errorf("empty addr file")
} else {
lastErr = err
}
time.Sleep(20 * time.Millisecond)
}
return "", lastErr
}