check / check (push) Waiting to run
cli.Main starts the server only when given no arguments. check-cidr CIDR and prepare-data-dir DATA_DIR run as before. Any other command line, such as a subcommand with no value or two values, or a misspelt subcommand, gets a one-line usage message naming both forms on stderr and exit code 2. It used to start the server, which then held the port. Model: opus-5-5
220 lines
6.4 KiB
Go
220 lines
6.4 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, writing
|
|
// nothing, for a CIDR the server takes in its TRUSTED_PROXIES, and 1,
|
|
// with an error naming TRUSTED_PROXIES and the CIDR, for one it
|
|
// refuses, and does not start the server.
|
|
func TestMainRunsCheckCIDR(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var stderr bytes.Buffer
|
|
|
|
code := cli.Main(&stderr, "netwatch", "test",
|
|
[]string{"check-cidr", "10.0.0.0/8"}, failIfServerStarts(t))
|
|
if code != 0 || stderr.Len() != 0 {
|
|
t.Errorf("check-cidr 10.0.0.0/8: exit %d, stderr %q; want exit 0 and no output",
|
|
code, stderr.String())
|
|
}
|
|
|
|
stderr.Reset()
|
|
|
|
code = cli.Main(&stderr, "netwatch", "test",
|
|
[]string{"check-cidr", "1.2.3/32"}, failIfServerStarts(t))
|
|
|
|
want := `TRUSTED_PROXIES "1.2.3/32": `
|
|
if code != 1 || !strings.HasPrefix(stderr.String(), want) {
|
|
t.Errorf("check-cidr 1.2.3/32: exit %d, stderr %q; want exit 1 and %q...",
|
|
code, stderr.String(), want)
|
|
}
|
|
}
|
|
|
|
// TestMainRunsPrepareDataDir: Main given prepare-data-dir runs the
|
|
// prepare-data-dir subcommand for the netwatch user, and does not start
|
|
// the server. The test image has no netwatch user, so that subcommand
|
|
// exits 1 with the error from looking the user up;
|
|
// TestPrepareDataDirRefusesPathOutsideVolume checks the path.
|
|
func TestMainRunsPrepareDataDir(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var stderr bytes.Buffer
|
|
|
|
code := cli.Main(&stderr, "netwatch", "test",
|
|
[]string{"prepare-data-dir", "/etc"}, failIfServerStarts(t))
|
|
|
|
want := user.UnknownUserError("netwatch").Error() + "\n"
|
|
if code != 1 || stderr.String() != want {
|
|
t.Errorf("prepare-data-dir /etc: exit %d, stderr %q; want exit 1 and %q",
|
|
code, stderr.String(), want)
|
|
}
|
|
}
|
|
|
|
// TestMainRunsServerWithNoArguments: Main given no arguments starts the
|
|
// server with the name and version it was given, writes nothing and
|
|
// returns 0.
|
|
func TestMainRunsServerWithNoArguments(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var (
|
|
stderr bytes.Buffer
|
|
gotAppname, gotVersion string
|
|
)
|
|
|
|
code := cli.Main(&stderr, "netwatch", "test", []string{},
|
|
func(appname, version string) {
|
|
gotAppname, gotVersion = appname, version
|
|
})
|
|
if code != 0 || stderr.Len() != 0 {
|
|
t.Errorf("exit %d, stderr %q; want exit 0 and no output",
|
|
code, stderr.String())
|
|
}
|
|
|
|
if gotAppname != "netwatch" || gotVersion != "test" {
|
|
t.Errorf("server started with %q %q; want %q %q",
|
|
gotAppname, gotVersion, "netwatch", "test")
|
|
}
|
|
}
|
|
|
|
// TestMainRefusesOtherCommandLines: Main given a subcommand with too
|
|
// few or too many values, or a misspelt subcommand, writes the usage
|
|
// message to stderr, returns 2 and does not start the server.
|
|
func TestMainRefusesOtherCommandLines(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
want := "usage: netwatch-server " +
|
|
"[check-cidr CIDR | prepare-data-dir DATA_DIR]\n"
|
|
|
|
for _, commandLine := range []string{
|
|
"netwatch-server check-cidr",
|
|
"netwatch-server check-cidr 192.0.2.0/24 198.51.100.0/24",
|
|
"netwatch-server prepare-data-dir",
|
|
"netwatch-server prepare-data-dir /data/reports /data/other",
|
|
"netwatch-server check-cdir 192.0.2.0/24",
|
|
} {
|
|
var stderr bytes.Buffer
|
|
|
|
// Main takes the command line after the program's name.
|
|
args := strings.Fields(commandLine)[1:]
|
|
|
|
code := cli.Main(&stderr, "netwatch", "test", args, failIfServerStarts(t))
|
|
if code != 2 || stderr.String() != want {
|
|
t.Errorf("%q: exit %d, stderr %q; want exit 2 and %q",
|
|
commandLine, code, stderr.String(), want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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")
|
|
}
|
|
}
|