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.
105 lines
3.2 KiB
Go
105 lines
3.2 KiB
Go
// SPDX-FileCopyrightText: 2026 LINUXexpert-org
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package validate
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"strconv"
|
|
"strings"
|
|
"syscall"
|
|
"testing"
|
|
)
|
|
|
|
// TestMain lets this test binary also act as a fake Stalwart binary,
|
|
// mirroring internal/recovery's own TestMain - see that package's doc
|
|
// comment for why (the standard os/exec "helper process" technique). Beyond
|
|
// plain reachability, it also speaks just enough JMAP to serve
|
|
// stalwartapi.Client.AccountSnapshot (x:Account/query, x:Account/get,
|
|
// session discovery, Mailbox/get) for a single fixed fake account
|
|
// "[email protected]", so BootCheck's content-integrity comparison can be
|
|
// exercised against a real subprocess rather than mocked in-process. The
|
|
// mailbox message count it reports is configurable via
|
|
// STALWART_MIGRATOR_TEST_MAILBOX_COUNT (default 42), so tests can produce
|
|
// both a matching and a mismatching post-migration snapshot.
|
|
func TestMain(m *testing.M) {
|
|
if os.Getenv("STALWART_MIGRATOR_TEST_HELPER") == "1" {
|
|
runFakeStalwartServer()
|
|
return
|
|
}
|
|
os.Exit(m.Run())
|
|
}
|
|
|
|
func runFakeStalwartServer() {
|
|
port := os.Getenv("STALWART_MIGRATOR_TEST_PORT")
|
|
messageCount := 42
|
|
if v := os.Getenv("STALWART_MIGRATOR_TEST_MAILBOX_COUNT"); v != "" {
|
|
if n, err := strconv.Atoi(v); err == nil {
|
|
messageCount = n
|
|
}
|
|
}
|
|
|
|
ln, err := net.Listen("tcp", "127.0.0.1:"+port)
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, "fake stalwart: listen:", err)
|
|
os.Exit(1)
|
|
}
|
|
srv := &http.Server{Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
switch {
|
|
case r.Method == http.MethodGet && r.URL.Path == "/.well-known/jmap":
|
|
user, _, _ := r.BasicAuth()
|
|
if strings.Contains(user, "%") {
|
|
json.NewEncoder(w).Encode(map[string]any{
|
|
"apiUrl": "http://127.0.0.1:" + port + "/api",
|
|
"primaryAccounts": map[string]string{"urn:ietf:params:jmap:mail": "mail-alice"},
|
|
})
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusOK)
|
|
case r.Method == http.MethodPost && r.URL.Path == "/api":
|
|
var body map[string]any
|
|
json.NewDecoder(r.Body).Decode(&body)
|
|
methodCalls, _ := body["methodCalls"].([]any)
|
|
if len(methodCalls) == 0 {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
return
|
|
}
|
|
call := methodCalls[0].([]any)
|
|
name := call[0].(string)
|
|
switch name {
|
|
case "x:Account/query":
|
|
json.NewEncoder(w).Encode(map[string]any{"methodResponses": []any{
|
|
[]any{"x:Account/query", map[string]any{"ids": []string{"a1"}}, "q"},
|
|
}})
|
|
case "x:Account/get":
|
|
json.NewEncoder(w).Encode(map[string]any{"methodResponses": []any{
|
|
[]any{"x:Account/get", map[string]any{"list": []map[string]any{
|
|
{"id": "a1", "name": "[email protected]", "domainId": "example.com"},
|
|
}}, "g"},
|
|
}})
|
|
case "Mailbox/get":
|
|
json.NewEncoder(w).Encode(map[string]any{"methodResponses": []any{
|
|
[]any{"Mailbox/get", map[string]any{"list": []map[string]any{
|
|
{"name": "Inbox", "totalEmails": messageCount},
|
|
}}, "m"},
|
|
}})
|
|
default:
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
}
|
|
default:
|
|
w.WriteHeader(http.StatusOK)
|
|
}
|
|
})}
|
|
go srv.Serve(ln)
|
|
|
|
sigCh := make(chan os.Signal, 1)
|
|
signal.Notify(sigCh, syscall.SIGTERM)
|
|
<-sigCh
|
|
os.Exit(0)
|
|
}
|