GPLv3's "How to Apply These Terms" asks for a notice in each source file; this is the modern two-line SPDX form of it rather than the full paragraph. 82 files, including tests. The blank line after the header is load-bearing. In Go a comment block immediately preceding `package X` becomes the package doc comment, so without the separator the SPDX lines would be absorbed into the doc for the eleven packages whose doc.go (or main.go) opens with one, and `go doc` would print them. Verified it doesn't.
108 lines
2.7 KiB
Go
108 lines
2.7 KiB
Go
// SPDX-FileCopyrightText: 2026 LINUXexpert-org
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package recovery
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// freePort asks the OS for an unused TCP port by binding to :0 and
|
|
// immediately releasing it. There's a small window where something else
|
|
// could grab it before the fake server binds, but that's an acceptable,
|
|
// standard tradeoff for tests.
|
|
func freePort(t *testing.T) int {
|
|
t.Helper()
|
|
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer ln.Close()
|
|
return ln.Addr().(*net.TCPAddr).Port
|
|
}
|
|
|
|
func helperProcessEnv(port int, extra ...string) []string {
|
|
env := []string{
|
|
"STALWART_MIGRATOR_TEST_HELPER=1",
|
|
fmt.Sprintf("STALWART_MIGRATOR_TEST_PORT=%d", port),
|
|
}
|
|
return append(env, extra...)
|
|
}
|
|
|
|
func testBinaryPath(t *testing.T) string {
|
|
t.Helper()
|
|
self, err := os.Executable()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return self
|
|
}
|
|
|
|
func TestProcessStartWaitHealthyStop(t *testing.T) {
|
|
port := freePort(t)
|
|
configPath := filepath.Join(t.TempDir(), "config.json")
|
|
os.WriteFile(configPath, []byte("{}"), 0o644)
|
|
|
|
proc := &Process{}
|
|
err := proc.Start(context.Background(), ProcessOptions{
|
|
BinaryPath: testBinaryPath(t),
|
|
ConfigPath: configPath,
|
|
ExtraEnv: helperProcessEnv(port),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Start: %v", err)
|
|
}
|
|
|
|
url := fmt.Sprintf("http://127.0.0.1:%d/", port)
|
|
if err := WaitForHealthy(context.Background(), nil, url, 5*time.Second); err != nil {
|
|
t.Fatalf("WaitForHealthy: %v", err)
|
|
}
|
|
|
|
if err := proc.Stop(5 * time.Second); err != nil {
|
|
t.Fatalf("Stop: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestProcessStopEscalatesToSIGKILL(t *testing.T) {
|
|
port := freePort(t)
|
|
configPath := filepath.Join(t.TempDir(), "config.json")
|
|
os.WriteFile(configPath, []byte("{}"), 0o644)
|
|
|
|
proc := &Process{}
|
|
err := proc.Start(context.Background(), ProcessOptions{
|
|
BinaryPath: testBinaryPath(t),
|
|
ConfigPath: configPath,
|
|
ExtraEnv: helperProcessEnv(port, "STALWART_MIGRATOR_TEST_IGNORE_SIGTERM=1"),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Start: %v", err)
|
|
}
|
|
|
|
url := fmt.Sprintf("http://127.0.0.1:%d/", port)
|
|
if err := WaitForHealthy(context.Background(), nil, url, 5*time.Second); err != nil {
|
|
t.Fatalf("WaitForHealthy: %v", err)
|
|
}
|
|
|
|
err = proc.Stop(500 * time.Millisecond)
|
|
if err == nil {
|
|
t.Fatal("Stop should report an error when it had to escalate to SIGKILL")
|
|
}
|
|
}
|
|
|
|
func TestWaitForHealthyTimesOut(t *testing.T) {
|
|
// Nothing listens on this port.
|
|
port := freePort(t)
|
|
url := fmt.Sprintf("http://127.0.0.1:%d/", port)
|
|
|
|
err := WaitForHealthy(context.Background(), nil, url, 500*time.Millisecond)
|
|
if err == nil {
|
|
t.Fatal("WaitForHealthy should time out when nothing is listening")
|
|
}
|
|
}
|