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") } }