187 lines
5.7 KiB
Go
187 lines
5.7 KiB
Go
package test
|
|
|
|
import (
|
|
"RenewCMS/internal/domain/update"
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func runBinary(t *testing.T, binary string, workDir string, args ...string) (string, string, int) {
|
|
t.Helper()
|
|
command := exec.Command(binary, args...)
|
|
command.Dir = workDir
|
|
command.Env = emptyEnvironment()
|
|
var stdout, stderr bytes.Buffer
|
|
command.Stdout, command.Stderr = &stdout, &stderr
|
|
err := command.Run()
|
|
code := 0
|
|
if exitErr, ok := err.(*exec.ExitError); ok {
|
|
code = exitErr.ExitCode()
|
|
} else if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return stdout.String(), stderr.String(), code
|
|
}
|
|
|
|
func assertEmptyDirectory(t *testing.T, directory string) {
|
|
t.Helper()
|
|
if entries, _ := os.ReadDir(directory); len(entries) != 0 {
|
|
t.Errorf("the working directory must stay empty, got %d entries", len(entries))
|
|
}
|
|
}
|
|
|
|
func TestCommandDispatcher(t *testing.T) {
|
|
binary := buildServerBinary(t, "-X main.version=v9.9.9")
|
|
|
|
t.Run("--version prints the version and exits 0", func(t *testing.T) {
|
|
workDir := t.TempDir()
|
|
|
|
stdout, _, code := runBinary(t, binary, workDir, "--version")
|
|
|
|
if code != 0 || strings.TrimSpace(stdout) != "v9.9.9" {
|
|
t.Errorf("code = %d, stdout = %q", code, stdout)
|
|
}
|
|
assertEmptyDirectory(t, workDir)
|
|
})
|
|
|
|
t.Run("an unknown subcommand prints a usage message and does not start the server", func(t *testing.T) {
|
|
workDir := t.TempDir()
|
|
|
|
stdout, stderr, code := runBinary(t, binary, workDir, "frobnicate")
|
|
|
|
if code == 0 || !strings.Contains(stderr, "usage:") || stdout != "" {
|
|
t.Errorf("code = %d, stdout = %q, stderr = %q", code, stdout, stderr)
|
|
}
|
|
assertEmptyDirectory(t, workDir)
|
|
})
|
|
|
|
t.Run("apply-update runs without .env and creates no data directory", func(t *testing.T) {
|
|
workDir := t.TempDir()
|
|
|
|
_, stderr, code := runBinary(t, binary, workDir, "apply-update")
|
|
|
|
if code == 0 || strings.Contains(stderr, "PUBLIC_URL") || strings.Contains(stderr, ".env") {
|
|
t.Errorf("code = %d, stderr = %q", code, stderr)
|
|
}
|
|
assertEmptyDirectory(t, workDir)
|
|
})
|
|
|
|
t.Run("apply-update with its flags never reads .env or opens the database", func(t *testing.T) {
|
|
if runtime.GOOS == "linux" {
|
|
t.Skip("on Linux the helper would act on the machine")
|
|
}
|
|
workDir := t.TempDir()
|
|
|
|
_, stderr, _ := runBinary(t, binary, workDir, "apply-update", "--binary", filepath.Join(workDir, "bin"), "--data-dir", filepath.Join(workDir, "data"))
|
|
|
|
if strings.Contains(stderr, "PUBLIC_URL") {
|
|
t.Errorf("stderr = %q", stderr)
|
|
}
|
|
if _, err := os.Stat(filepath.Join(workDir, "data")); err == nil {
|
|
t.Error("no data directory may be created")
|
|
}
|
|
})
|
|
|
|
t.Run("enable-browser-updates and disable-browser-updates refuse off Linux", func(t *testing.T) {
|
|
if runtime.GOOS == "linux" {
|
|
t.Skip("on Linux these commands would act on the machine's systemd")
|
|
}
|
|
workDir := t.TempDir()
|
|
|
|
_, stderr, code := runBinary(t, binary, workDir, "enable-browser-updates")
|
|
stdout, _, disableCode := runBinary(t, binary, workDir, "disable-browser-updates")
|
|
|
|
if code == 0 || strings.TrimSpace(stderr) != update.ErrNeedsLinux.Error() {
|
|
t.Errorf("enable: code = %d, stderr = %q", code, stderr)
|
|
}
|
|
if disableCode != 0 || !strings.Contains(stdout, "not enabled") {
|
|
t.Errorf("disable: code = %d, stdout = %q", disableCode, stdout)
|
|
}
|
|
assertEmptyDirectory(t, workDir)
|
|
})
|
|
|
|
t.Run("no arguments is the normal server start", func(t *testing.T) {
|
|
workDir := t.TempDir()
|
|
|
|
_, stderr, code := runBinary(t, binary, workDir)
|
|
|
|
if code == 0 || !strings.Contains(stderr, "PUBLIC_URL") {
|
|
t.Errorf("code = %d, stderr = %q", code, stderr)
|
|
}
|
|
})
|
|
}
|
|
|
|
func freeTCPPort(t *testing.T) string {
|
|
t.Helper()
|
|
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer listener.Close()
|
|
return fmt.Sprint(listener.Addr().(*net.TCPAddr).Port)
|
|
}
|
|
|
|
func TestServerWritesRunningInfoBeforeServing(t *testing.T) {
|
|
if os.Getenv("RENEWCMS_TEST_SPAWN_SERVER") == "" {
|
|
t.Skip("spawns the real server, which triggers Windows firewall prompts; set RENEWCMS_TEST_SPAWN_SERVER=1 to run")
|
|
}
|
|
binary := buildServerBinary(t, "-X main.version=v9.9.9")
|
|
workDir := t.TempDir()
|
|
dataDir := filepath.Join(workDir, "data")
|
|
port := freeTCPPort(t)
|
|
command := exec.Command(binary)
|
|
command.Dir = workDir
|
|
command.Env = append(emptyEnvironment(),
|
|
"ENVIRONMENT=development", "PORT="+port, "PUBLIC_URL=http://localhost", "JWT_SECRET=test-secret-for-dispatch",
|
|
"SMTP_LOGIN=a@example.com", "SMTP_SENDER=a@example.com", "SMTP_PASSWORD=x", "SMTP_HOST=localhost", "SMTP_PORT=2525",
|
|
"DB_FILE="+filepath.Join(dataDir, "renewcms.db"), "UPLOAD_DIR="+filepath.Join(dataDir, "uploads"),
|
|
"UPDATE_FEED_URL=https://git.example")
|
|
var stderr bytes.Buffer
|
|
command.Stderr = &stderr
|
|
started := time.Now()
|
|
if err := command.Start(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() {
|
|
_ = command.Process.Kill()
|
|
_, _ = command.Process.Wait()
|
|
}()
|
|
|
|
healthy := false
|
|
for deadline := time.Now().Add(20 * time.Second); time.Now().Before(deadline); time.Sleep(50 * time.Millisecond) {
|
|
response, err := http.Get("http://127.0.0.1:" + port + "/healthz")
|
|
if err == nil {
|
|
response.Body.Close()
|
|
healthy = response.StatusCode == http.StatusOK
|
|
if healthy {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if !healthy {
|
|
t.Fatalf("the server never answered /healthz: %s", stderr.String())
|
|
}
|
|
|
|
content, err := os.ReadFile(filepath.Join(dataDir, update.RunningInfoFileName))
|
|
if err != nil {
|
|
t.Fatalf("update-running.json must exist as soon as /healthz answers: %v", err)
|
|
}
|
|
var info update.RunningInfo
|
|
if err := json.Unmarshal(content, &info); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if info.Version != "v9.9.9" || info.Pid != command.Process.Pid || info.StartedAt.Before(started.Add(-2*time.Second)) || info.StartedAt.After(time.Now()) {
|
|
t.Errorf("info = %+v, pid = %d", info, command.Process.Pid)
|
|
}
|
|
}
|