check / check (push) Waiting to run
cmd/netwatch-server/main.go parsed the command line, ran the check-cidr and prepare-data-dir subcommands and built the fx application itself; the Go style guide puts all of that in internal/ or pkg/. The new package internal/cli does it, with the same arguments, messages and exit codes. main() is now os.Exit(cli.Main(Appname, Version, os.Args[1:], cli.RunServer)): Main takes the function that runs the server, so its tests call it with one that fails them. Appname and Version stay in main, where the build sets Version. Each subcommand writes its error to a writer it is given, and prepare-data-dir also takes the volume and the user's name, so its own tests give it a temporary directory and the user they run as. Model: opus-5-5
146 lines
4.0 KiB
Go
146 lines
4.0 KiB
Go
package cli_test
|
|
|
|
import (
|
|
"bytes"
|
|
"os"
|
|
"os/user"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
|
|
"sneak.berlin/go/netwatch/internal/cli"
|
|
"sneak.berlin/go/netwatch/internal/reportbuf"
|
|
)
|
|
|
|
// TestMainRunsCheckCIDR: Main given check-cidr returns 0 for a CIDR the
|
|
// server takes in its TRUSTED_PROXIES and 1 for one it refuses, and
|
|
// does not start the server.
|
|
func TestMainRunsCheckCIDR(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
for cidr, want := range map[string]int{"10.0.0.0/8": 0, "1.2.3/32": 1} {
|
|
code := cli.Main("netwatch", "test", []string{"check-cidr", cidr},
|
|
failIfServerStarts(t))
|
|
if code != want {
|
|
t.Errorf("check-cidr %q: exit %d, want %d", cidr, code, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestMainRunsPrepareDataDir: Main given prepare-data-dir returns 1 for
|
|
// a DATA_DIR outside /data, and does not start the server. The test
|
|
// image has no netwatch user, so there the 1 comes from looking that
|
|
// user up; TestPrepareDataDirRefusesPathOutsideVolume checks the path.
|
|
func TestMainRunsPrepareDataDir(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
code := cli.Main("netwatch", "test", []string{"prepare-data-dir", "/etc"},
|
|
failIfServerStarts(t))
|
|
if code != 1 {
|
|
t.Errorf("prepare-data-dir /etc: exit %d, want 1", code)
|
|
}
|
|
}
|
|
|
|
// TestCheckCIDRTakesCIDR: check-cidr exits 0, writing nothing, for a
|
|
// CIDR the server takes in its TRUSTED_PROXIES.
|
|
func TestCheckCIDRTakesCIDR(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
for _, cidr := range []string{"10.0.0.0/8", "192.0.2.1/32", "2001:db8::/32"} {
|
|
var stderr bytes.Buffer
|
|
|
|
code := cli.CheckCIDR(&stderr, cidr)
|
|
if code != 0 || stderr.Len() != 0 {
|
|
t.Errorf("%q: exit %d, stderr %q; want exit 0 and no output",
|
|
cidr, code, stderr.String())
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestCheckCIDRRefusesOtherInput: check-cidr exits 1, with an error
|
|
// naming TRUSTED_PROXIES and the input, for anything the server would
|
|
// refuse in its TRUSTED_PROXIES, such as 1.2.3/32, which nginx would
|
|
// look up as a hostname.
|
|
func TestCheckCIDRRefusesOtherInput(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
for _, cidr := range []string{
|
|
"1.2.3/32", "example.com/32", "10.0.0.0/33", "10.0.0.1", "",
|
|
} {
|
|
var stderr bytes.Buffer
|
|
|
|
code := cli.CheckCIDR(&stderr, cidr)
|
|
|
|
want := "TRUSTED_PROXIES " + strconv.Quote(cidr) + ": "
|
|
if code != 1 || !strings.HasPrefix(stderr.String(), want) {
|
|
t.Errorf("%q: exit %d, stderr %q; want exit 1 and %q...",
|
|
cidr, code, stderr.String(), want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestPrepareDataDirTakesPathInVolume: prepare-data-dir exits 0,
|
|
// writing nothing, for a DATA_DIR below the volume, and creates it.
|
|
func TestPrepareDataDirTakesPathInVolume(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
volume := t.TempDir()
|
|
dataDir := filepath.Join(volume, "reports")
|
|
|
|
var stderr bytes.Buffer
|
|
|
|
code := cli.PrepareDataDir(&stderr, volume, dataDir, currentUsername(t))
|
|
if code != 0 || stderr.Len() != 0 {
|
|
t.Errorf("exit %d, stderr %q; want exit 0 and no output",
|
|
code, stderr.String())
|
|
}
|
|
|
|
info, err := os.Stat(dataDir)
|
|
if err != nil || !info.IsDir() {
|
|
t.Errorf("DATA_DIR not created: %v", err)
|
|
}
|
|
}
|
|
|
|
// TestPrepareDataDirRefusesPathOutsideVolume: prepare-data-dir exits
|
|
// 1, with an error naming DATA_DIR and its value, for a DATA_DIR
|
|
// outside the volume.
|
|
func TestPrepareDataDirRefusesPathOutsideVolume(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
volume := t.TempDir()
|
|
|
|
var stderr bytes.Buffer
|
|
|
|
code := cli.PrepareDataDir(&stderr, volume, "/etc", currentUsername(t))
|
|
|
|
want := "DATA_DIR '/etc': " + reportbuf.ErrDataDirOutsideVolume.Error() + "\n"
|
|
if code != 1 || stderr.String() != want {
|
|
t.Errorf("exit %d, stderr %q; want exit 1 and %q",
|
|
code, stderr.String(), want)
|
|
}
|
|
}
|
|
|
|
// currentUsername is the name of the user the test runs as, the only
|
|
// owner a test not run as root can give files to.
|
|
func currentUsername(t *testing.T) string {
|
|
t.Helper()
|
|
|
|
current, err := user.Current()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
return current.Username
|
|
}
|
|
|
|
// failIfServerStarts is what the tests give Main in place of
|
|
// cli.RunServer: it fails the test instead of starting the server.
|
|
func failIfServerStarts(t *testing.T) func(appname, version string) {
|
|
t.Helper()
|
|
|
|
return func(string, string) {
|
|
t.Error("Main started the server")
|
|
}
|
|
}
|