108 lines
3.2 KiB
Go
108 lines
3.2 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"sneak.berlin/go/webhooker/internal/config"
|
|
)
|
|
|
|
// dotEnvKey is a throwaway variable name these tests write and read,
|
|
// so they cannot disturb real configuration.
|
|
const dotEnvKey = "WEBHOOKER_TEST_DISPATCH_VALUE"
|
|
|
|
// writeDotEnvInWorkingDir puts contents in a .env file in a fresh
|
|
// temporary directory and moves the process there.
|
|
//
|
|
// The callers are deliberately not parallel and must stay that way:
|
|
// t.Chdir moves the whole process. Go releases parallel tests only
|
|
// after every sequential test in the package has finished, so nothing
|
|
// else runs while these do.
|
|
func writeDotEnvInWorkingDir(t *testing.T, contents string) {
|
|
t.Helper()
|
|
|
|
dir := t.TempDir()
|
|
require.NoError(t, os.WriteFile(
|
|
filepath.Join(dir, config.DotEnvPath),
|
|
[]byte(contents), 0o600,
|
|
))
|
|
t.Chdir(dir)
|
|
}
|
|
|
|
// TestDispatch_MalformedDotEnvRefuses pins the second half of the
|
|
// defect. godotenv applies nothing at all when a file will not parse,
|
|
// so one mistyped line used to revert every variable in it to its
|
|
// default and start the server anyway, with no log line naming the
|
|
// file. The refusal has to arrive before any subcommand runs, which
|
|
// is why `help` — the one subcommand that touches nothing — is still
|
|
// refused here.
|
|
//
|
|
//nolint:paralleltest // t.Chdir moves the whole process.
|
|
func TestDispatch_MalformedDotEnvRefuses(t *testing.T) {
|
|
writeDotEnvInWorkingDir(t, "PORT 19615\n")
|
|
|
|
var stdout, stderr bytes.Buffer
|
|
|
|
code := dispatch(
|
|
[]string{helpCommand}, strings.NewReader(""), &stdout, &stderr,
|
|
)
|
|
|
|
require.Equal(t, 1, code, "a broken .env must exit non-zero")
|
|
assert.Contains(
|
|
t, stderr.String(), config.DotEnvPath,
|
|
"the refusal must name the file",
|
|
)
|
|
assert.Empty(
|
|
t, stdout.String(),
|
|
"the subcommand must not have run",
|
|
)
|
|
}
|
|
|
|
// TestDispatch_LoadsDotEnvBeforeSubcommands pins the ordering the
|
|
// godotenv/autoload import used to provide for free. It ran in an
|
|
// init(), so .env was in the environment before anything read it —
|
|
// including config.DataDir, which both the DATA_DIR lock and resetpw
|
|
// call outside the fx graph. Loading any later would let a .env that
|
|
// sets DATA_DIR lock one directory while the config opened databases
|
|
// in another.
|
|
func TestDispatch_LoadsDotEnvBeforeSubcommands(t *testing.T) {
|
|
t.Setenv(dotEnvKey, "placeholder")
|
|
require.NoError(t, os.Unsetenv(dotEnvKey))
|
|
|
|
writeDotEnvInWorkingDir(t, dotEnvKey+"=from-dot-env\n")
|
|
|
|
var stdout, stderr bytes.Buffer
|
|
|
|
code := dispatch(
|
|
[]string{helpCommand}, strings.NewReader(""), &stdout, &stderr,
|
|
)
|
|
|
|
require.Equal(t, 0, code)
|
|
assert.Equal(
|
|
t, "from-dot-env", os.Getenv(dotEnvKey),
|
|
"the file must be applied before the subcommand runs",
|
|
)
|
|
}
|
|
|
|
// TestDispatch_MissingDotEnvIsFine pins the case most deployments are
|
|
// in: no .env at all, which must stay a normal start.
|
|
//
|
|
//nolint:paralleltest // t.Chdir moves the whole process.
|
|
func TestDispatch_MissingDotEnvIsFine(t *testing.T) {
|
|
t.Chdir(t.TempDir())
|
|
|
|
var stdout, stderr bytes.Buffer
|
|
|
|
code := dispatch(
|
|
[]string{helpCommand}, strings.NewReader(""), &stdout, &stderr,
|
|
)
|
|
|
|
require.Equal(t, 0, code)
|
|
assert.Empty(t, stderr.String())
|
|
}
|