mirror of
https://gitlab.com/TECHNOFAB/nixtest.git
synced 2025-12-12 02:00:18 +01:00
Compare commits
2 commits
22b43c9fe8
...
5741109cc9
| Author | SHA1 | Date | |
|---|---|---|---|
| 5741109cc9 | |||
| c9298b91f4 |
12 changed files with 42 additions and 43 deletions
|
|
@ -60,7 +60,7 @@ func main() {
|
||||||
SnapshotDir: appCfg.SnapshotDir,
|
SnapshotDir: appCfg.SnapshotDir,
|
||||||
UpdateSnapshots: appCfg.UpdateSnapshots,
|
UpdateSnapshots: appCfg.UpdateSnapshots,
|
||||||
SkipPattern: appCfg.SkipPattern,
|
SkipPattern: appCfg.SkipPattern,
|
||||||
PureEnv: appCfg.PureEnv,
|
ImpureEnv: appCfg.ImpureEnv,
|
||||||
}
|
}
|
||||||
testRunner, err := runner.New(runnerCfg, nixService, snapshotService)
|
testRunner, err := runner.New(runnerCfg, nixService, snapshotService)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
Usage of nixtest:
|
Usage of nixtest:
|
||||||
--junit string Path to generate JUNIT report to, leave empty to disable
|
--junit string Path to generate JUNIT report to, leave empty to disable
|
||||||
--no-color Disable coloring
|
--no-color Disable coloring
|
||||||
--pure Unset all env vars before running script tests
|
--impure Don\'t unset all env vars before running script tests
|
||||||
-s, --skip string Regular expression to skip tests (e.g., 'test-.*|.*-b')
|
-s, --skip string Regular expression to skip tests (e.g., 'test-.*|.*-b')
|
||||||
--snapshot-dir string Directory where snapshots are stored (default "./snapshots")
|
--snapshot-dir string Directory where snapshots are stored (default "./snapshots")
|
||||||
-f, --tests string Path to JSON file containing tests (required)
|
-f, --tests string Path to JSON file containing tests (required)
|
||||||
|
|
|
||||||
|
|
@ -96,10 +96,10 @@ Examples:
|
||||||
name = "script-test";
|
name = "script-test";
|
||||||
type = "script";
|
type = "script";
|
||||||
script =
|
script =
|
||||||
# there are two modes, "default"/"impure" and "pure"
|
# there are two modes, "default"/"pure" and "impure"
|
||||||
# in impure mode all env variables etc. from your current session are kept
|
# in impure mode all env variables etc. from your current session are kept
|
||||||
# and are available to the test
|
# and are available to the test (using --impure).
|
||||||
# to make it more reproducible and cleaner, use --pure to switch to pure
|
# to make it more reproducible and cleaner, the default is pure
|
||||||
# mode which will unset all env variables before running the test. That
|
# mode which will unset all env variables before running the test. That
|
||||||
# requires you to set PATH yourself then:
|
# requires you to set PATH yourself then:
|
||||||
#
|
#
|
||||||
|
|
|
||||||
2
go.mod
2
go.mod
|
|
@ -1,6 +1,6 @@
|
||||||
module gitlab.com/technofab/nixtest
|
module gitlab.com/technofab/nixtest
|
||||||
|
|
||||||
go 1.24.2
|
go 1.23.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/akedrou/textdiff v0.1.0
|
github.com/akedrou/textdiff v0.1.0
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ type AppConfig struct {
|
||||||
JunitPath string
|
JunitPath string
|
||||||
UpdateSnapshots bool
|
UpdateSnapshots bool
|
||||||
SkipPattern string
|
SkipPattern string
|
||||||
PureEnv bool
|
ImpureEnv bool
|
||||||
NoColor bool
|
NoColor bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -29,7 +29,7 @@ func Load() AppConfig {
|
||||||
flag.StringVar(&cfg.JunitPath, "junit", "", "Path to generate JUNIT report to, leave empty to disable")
|
flag.StringVar(&cfg.JunitPath, "junit", "", "Path to generate JUNIT report to, leave empty to disable")
|
||||||
flag.BoolVarP(&cfg.UpdateSnapshots, "update-snapshots", "u", false, "Update all snapshots")
|
flag.BoolVarP(&cfg.UpdateSnapshots, "update-snapshots", "u", false, "Update all snapshots")
|
||||||
flag.StringVarP(&cfg.SkipPattern, "skip", "s", "", "Regular expression to skip tests (e.g., 'test-.*|.*-b')")
|
flag.StringVarP(&cfg.SkipPattern, "skip", "s", "", "Regular expression to skip tests (e.g., 'test-.*|.*-b')")
|
||||||
flag.BoolVar(&cfg.PureEnv, "pure", false, "Unset all env vars before running script tests")
|
flag.BoolVar(&cfg.ImpureEnv, "impure", false, "Don't unset all env vars before running script tests")
|
||||||
flag.BoolVar(&cfg.NoColor, "no-color", false, "Disable coloring")
|
flag.BoolVar(&cfg.NoColor, "no-color", false, "Disable coloring")
|
||||||
helpRequested := flag.BoolP("help", "h", false, "Show this menu")
|
helpRequested := flag.BoolP("help", "h", false, "Show this menu")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ func TestLoad_CustomValues(t *testing.T) {
|
||||||
"--junit", "report.xml",
|
"--junit", "report.xml",
|
||||||
"-u",
|
"-u",
|
||||||
"--skip", "specific-test",
|
"--skip", "specific-test",
|
||||||
"--pure",
|
"--impure",
|
||||||
"--no-color",
|
"--no-color",
|
||||||
}
|
}
|
||||||
pflag.CommandLine = pflag.NewFlagSet(os.Args[0], pflag.ExitOnError) // Reset flags
|
pflag.CommandLine = pflag.NewFlagSet(os.Args[0], pflag.ExitOnError) // Reset flags
|
||||||
|
|
@ -83,7 +83,7 @@ func TestLoad_CustomValues(t *testing.T) {
|
||||||
if cfg.SkipPattern != "specific-test" {
|
if cfg.SkipPattern != "specific-test" {
|
||||||
t.Errorf("SkipPattern: got %s, want specific-test", cfg.SkipPattern)
|
t.Errorf("SkipPattern: got %s, want specific-test", cfg.SkipPattern)
|
||||||
}
|
}
|
||||||
if !cfg.PureEnv {
|
if !cfg.ImpureEnv {
|
||||||
t.Errorf("PureEnv: got %v, want true", cfg.PureEnv)
|
t.Errorf("ImpureEnv: got %v, want true", cfg.ImpureEnv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package nix
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -14,7 +15,7 @@ import (
|
||||||
type Service interface {
|
type Service interface {
|
||||||
BuildDerivation(derivation string) (string, error)
|
BuildDerivation(derivation string) (string, error)
|
||||||
BuildAndParseJSON(derivation string) (any, error)
|
BuildAndParseJSON(derivation string) (any, error)
|
||||||
BuildAndRunScript(derivation string, pureEnv bool) (exitCode int, stdout string, stderr string, err error)
|
BuildAndRunScript(derivation string, impureEnv bool) (exitCode int, stdout string, stderr string, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type DefaultService struct {
|
type DefaultService struct {
|
||||||
|
|
@ -72,21 +73,29 @@ func (s *DefaultService) BuildAndParseJSON(derivation string) (any, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildAndRunScript builds a derivation and runs it as a script
|
// BuildAndRunScript builds a derivation and runs it as a script
|
||||||
func (s *DefaultService) BuildAndRunScript(derivation string, pureEnv bool) (exitCode int, stdout string, stderr string, err error) {
|
func (s *DefaultService) BuildAndRunScript(derivation string, impureEnv bool) (exitCode int, stdout string, stderr string, err error) {
|
||||||
exitCode = -1
|
exitCode = -1
|
||||||
path, err := s.BuildDerivation(derivation)
|
path, err := s.BuildDerivation(derivation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return exitCode, "", "", err
|
return exitCode, "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// run scripts in a temporary directory
|
||||||
|
tempDir, err := os.MkdirTemp("", "nixtest-script-")
|
||||||
|
if err != nil {
|
||||||
|
return exitCode, "", "", &apperrors.ScriptExecutionError{Path: path, Err: fmt.Errorf("failed to create temporary directory: %w", err)}
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tempDir)
|
||||||
|
|
||||||
var cmdArgs []string
|
var cmdArgs []string
|
||||||
if pureEnv {
|
if impureEnv {
|
||||||
cmdArgs = append([]string{"env", "-i"}, "bash", path)
|
|
||||||
} else {
|
|
||||||
cmdArgs = []string{"bash", path}
|
cmdArgs = []string{"bash", path}
|
||||||
|
} else {
|
||||||
|
cmdArgs = append([]string{"env", "-i"}, "bash", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := s.commandExecutor(cmdArgs[0], cmdArgs[1:]...)
|
cmd := s.commandExecutor(cmdArgs[0], cmdArgs[1:]...)
|
||||||
|
cmd.Dir = tempDir
|
||||||
var outBuf, errBuf bytes.Buffer
|
var outBuf, errBuf bytes.Buffer
|
||||||
cmd.Stdout = &outBuf
|
cmd.Stdout = &outBuf
|
||||||
cmd.Stderr = &errBuf
|
cmd.Stderr = &errBuf
|
||||||
|
|
|
||||||
|
|
@ -232,7 +232,7 @@ func TestDefaultService_BuildAndRunScript(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
derivation string
|
derivation string
|
||||||
pureEnv bool
|
impureEnv bool
|
||||||
mockBuildDrvOutput string
|
mockBuildDrvOutput string
|
||||||
mockBuildDrvError string
|
mockBuildDrvError string
|
||||||
mockBuildDrvExitCode string
|
mockBuildDrvExitCode string
|
||||||
|
|
@ -252,7 +252,7 @@ func TestDefaultService_BuildAndRunScript(t *testing.T) {
|
||||||
0, "Hello", "ErrOut", false, nil, "",
|
0, "Hello", "ErrOut", false, nil, "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Success pure", "script.drv#sh", true, mockScriptPath, "", "0",
|
"Success impure", "script.drv#sh", true, mockScriptPath, "", "0",
|
||||||
"Hello", "ErrOut", "0",
|
"Hello", "ErrOut", "0",
|
||||||
0, "Hello", "ErrOut", false, nil, "",
|
0, "Hello", "ErrOut", false, nil, "",
|
||||||
},
|
},
|
||||||
|
|
@ -277,7 +277,7 @@ func TestDefaultService_BuildAndRunScript(t *testing.T) {
|
||||||
os.Setenv("MOCK_SCRIPT_STDERR", tt.mockScriptStderr)
|
os.Setenv("MOCK_SCRIPT_STDERR", tt.mockScriptStderr)
|
||||||
os.Setenv("MOCK_SCRIPT_EXIT_CODE", tt.mockScriptExitCode)
|
os.Setenv("MOCK_SCRIPT_EXIT_CODE", tt.mockScriptExitCode)
|
||||||
|
|
||||||
exitCode, stdout, stderr, err := service.BuildAndRunScript(tt.derivation, tt.pureEnv)
|
exitCode, stdout, stderr, err := service.BuildAndRunScript(tt.derivation, tt.impureEnv)
|
||||||
|
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
t.Fatalf("BuildAndRunScript() error = %v, wantErr %v", err, tt.wantErr)
|
t.Fatalf("BuildAndRunScript() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ type Config struct {
|
||||||
SnapshotDir string
|
SnapshotDir string
|
||||||
UpdateSnapshots bool
|
UpdateSnapshots bool
|
||||||
SkipPattern string
|
SkipPattern string
|
||||||
PureEnv bool
|
ImpureEnv bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(cfg Config, nixService nix.Service, snapService snapshot.Service) (*Runner, error) {
|
func New(cfg Config, nixService nix.Service, snapService snapshot.Service) (*Runner, error) {
|
||||||
|
|
@ -181,7 +181,7 @@ func (r *Runner) handleUnitTest(result *types.TestResult, spec types.TestSpec, a
|
||||||
|
|
||||||
// handleScriptTest processes script type tests
|
// handleScriptTest processes script type tests
|
||||||
func (r *Runner) handleScriptTest(result *types.TestResult, spec types.TestSpec) {
|
func (r *Runner) handleScriptTest(result *types.TestResult, spec types.TestSpec) {
|
||||||
exitCode, stdout, stderrStr, err := r.nixService.BuildAndRunScript(spec.Script, r.config.PureEnv)
|
exitCode, stdout, stderrStr, err := r.nixService.BuildAndRunScript(spec.Script, r.config.ImpureEnv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
result.Status = types.StatusError
|
result.Status = types.StatusError
|
||||||
result.ErrorMessage = fmt.Sprintf("[system] failed to run script derivation %s: %v", spec.Script, err)
|
result.ErrorMessage = fmt.Sprintf("[system] failed to run script derivation %s: %v", spec.Script, err)
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ import (
|
||||||
type mockNixService struct {
|
type mockNixService struct {
|
||||||
BuildDerivationFunc func(derivation string) (string, error)
|
BuildDerivationFunc func(derivation string) (string, error)
|
||||||
BuildAndParseJSONFunc func(derivation string) (any, error)
|
BuildAndParseJSONFunc func(derivation string) (any, error)
|
||||||
BuildAndRunScriptFunc func(derivation string, pureEnv bool) (exitCode int, stdout string, stderr string, err error)
|
BuildAndRunScriptFunc func(derivation string, impureEnv bool) (exitCode int, stdout string, stderr string, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockNixService) BuildDerivation(d string) (string, error) {
|
func (m *mockNixService) BuildDerivation(d string) (string, error) {
|
||||||
|
|
@ -253,7 +253,7 @@ func TestRunner_executeTest(t *testing.T) {
|
||||||
spec: types.TestSpec{Name: "ScriptSuccess", Type: types.TestTypeScript, Script: "script.sh"},
|
spec: types.TestSpec{Name: "ScriptSuccess", Type: types.TestTypeScript, Script: "script.sh"},
|
||||||
runnerConfig: Config{},
|
runnerConfig: Config{},
|
||||||
setupMockServices: func(t *testing.T, mNix *mockNixService, mSnap *mockSnapshotService, s types.TestSpec, c Config) {
|
setupMockServices: func(t *testing.T, mNix *mockNixService, mSnap *mockSnapshotService, s types.TestSpec, c Config) {
|
||||||
mNix.BuildAndRunScriptFunc = func(derivation string, pureEnv bool) (int, string, string, error) {
|
mNix.BuildAndRunScriptFunc = func(derivation string, impureEnv bool) (int, string, string, error) {
|
||||||
return 0, "stdout", "stderr", nil
|
return 0, "stdout", "stderr", nil
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -264,7 +264,7 @@ func TestRunner_executeTest(t *testing.T) {
|
||||||
spec: types.TestSpec{Name: "ScriptFail", Type: types.TestTypeScript, Script: "script.sh"},
|
spec: types.TestSpec{Name: "ScriptFail", Type: types.TestTypeScript, Script: "script.sh"},
|
||||||
runnerConfig: Config{},
|
runnerConfig: Config{},
|
||||||
setupMockServices: func(t *testing.T, mNix *mockNixService, mSnap *mockSnapshotService, s types.TestSpec, c Config) {
|
setupMockServices: func(t *testing.T, mNix *mockNixService, mSnap *mockSnapshotService, s types.TestSpec, c Config) {
|
||||||
mNix.BuildAndRunScriptFunc = func(derivation string, pureEnv bool) (int, string, string, error) {
|
mNix.BuildAndRunScriptFunc = func(derivation string, impureEnv bool) (int, string, string, error) {
|
||||||
return 1, "out on fail", "err on fail", nil
|
return 1, "out on fail", "err on fail", nil
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -313,7 +313,7 @@ func TestRunner_RunTests(t *testing.T) {
|
||||||
mockSnapSvc := &mockSnapshotService{}
|
mockSnapSvc := &mockSnapshotService{}
|
||||||
|
|
||||||
mockNixSvc.BuildAndParseJSONFunc = func(derivation string) (any, error) { return "parsed", nil }
|
mockNixSvc.BuildAndParseJSONFunc = func(derivation string) (any, error) { return "parsed", nil }
|
||||||
mockNixSvc.BuildAndRunScriptFunc = func(derivation string, pureEnv bool) (int, string, string, error) { return 0, "", "", nil }
|
mockNixSvc.BuildAndRunScriptFunc = func(derivation string, impureEnv bool) (int, string, string, error) { return 0, "", "", nil }
|
||||||
mockSnapSvc.StatFunc = func(name string) (os.FileInfo, error) { return mockFileInfo{}, nil }
|
mockSnapSvc.StatFunc = func(name string) (os.FileInfo, error) { return mockFileInfo{}, nil }
|
||||||
mockSnapSvc.LoadFileFunc = func(filePath string) (any, error) { return "snapshot", nil }
|
mockSnapSvc.LoadFileFunc = func(filePath string) (any, error) { return "snapshot", nil }
|
||||||
mockSnapSvc.CreateFileFunc = func(filePath string, data any) error { return nil }
|
mockSnapSvc.CreateFileFunc = func(filePath string, data any) error { return nil }
|
||||||
|
|
|
||||||
|
|
@ -35,16 +35,6 @@ function assert_file_not_contains() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function tmpdir() {
|
|
||||||
dir=$(mktemp -d)
|
|
||||||
trap "rm -rf $dir" EXIT
|
|
||||||
echo -n "$dir"
|
|
||||||
}
|
|
||||||
function tmpfile() {
|
|
||||||
file=$(mktemp)
|
|
||||||
trap "rm -f $file" EXIT
|
|
||||||
echo -n "$file"
|
|
||||||
}
|
|
||||||
function run() {
|
function run() {
|
||||||
output=$($@ 2>&1)
|
output=$($@ 2>&1)
|
||||||
exit_code=$?
|
exit_code=$?
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@
|
||||||
binary =
|
binary =
|
||||||
(ntlib.mkBinary {
|
(ntlib.mkBinary {
|
||||||
nixtests = "stub";
|
nixtests = "stub";
|
||||||
extraParams = "--pure";
|
extraParams = "--impure";
|
||||||
})
|
})
|
||||||
+ "/bin/nixtests:run";
|
+ "/bin/nixtests:run";
|
||||||
in
|
in
|
||||||
|
|
@ -45,7 +45,7 @@
|
||||||
${ntlib.helpers.path [pkgs.gnugrep]}
|
${ntlib.helpers.path [pkgs.gnugrep]}
|
||||||
${ntlib.helpers.scriptHelpers}
|
${ntlib.helpers.scriptHelpers}
|
||||||
assert_file_contains ${binary} "nixtest" "should contain nixtest"
|
assert_file_contains ${binary} "nixtest" "should contain nixtest"
|
||||||
assert_file_contains ${binary} "--pure" "should contain --pure arg"
|
assert_file_contains ${binary} "--impure" "should contain --impure arg"
|
||||||
assert_file_contains ${binary} "--tests=stub" "should contain --tests arg"
|
assert_file_contains ${binary} "--tests=stub" "should contain --tests arg"
|
||||||
|
|
||||||
run "${binary} --help"
|
run "${binary} --help"
|
||||||
|
|
@ -70,21 +70,21 @@
|
||||||
in
|
in
|
||||||
# sh
|
# sh
|
||||||
''
|
''
|
||||||
${ntlib.helpers.path [pkgs.gnugrep pkgs.mktemp]}
|
${ntlib.helpers.path [pkgs.gnugrep pkgs.mktemp pkgs.coreutils]}
|
||||||
${ntlib.helpers.scriptHelpers}
|
${ntlib.helpers.scriptHelpers}
|
||||||
|
cp -r ${./../snapshots} snapshots
|
||||||
|
|
||||||
TMPDIR=$(tmpdir)
|
|
||||||
# start without nix & env binaries to expect errors
|
# start without nix & env binaries to expect errors
|
||||||
run "${binary} --pure --junit=$TMPDIR/junit.xml"
|
run "${binary} --junit=junit.xml"
|
||||||
assert "$exit_code -eq 2" "should exit 2"
|
assert "$exit_code -eq 2" "should exit 2"
|
||||||
assert "-f $TMPDIR/junit.xml" "should create junit.xml"
|
assert "-f junit.xml" "should create junit.xml"
|
||||||
assert_contains "$output" "executable file not found" "nix should not be found in pure mode"
|
assert_contains "$output" "executable file not found" "nix should not be found in pure mode"
|
||||||
|
|
||||||
# now add required deps
|
# now add required deps
|
||||||
${ntlib.helpers.pathAdd [pkgs.nix pkgs.coreutils]}
|
${ntlib.helpers.pathAdd [pkgs.nix pkgs.coreutils]}
|
||||||
run "${binary} --pure --junit=$TMPDIR/junit2.xml"
|
run "${binary} --junit=junit2.xml"
|
||||||
assert "$exit_code -eq 2" "should exit 2"
|
assert "$exit_code -eq 2" "should exit 2"
|
||||||
assert "-f $TMPDIR/junit2.xml" "should create junit2.xml"
|
assert "-f junit2.xml" "should create junit2.xml"
|
||||||
assert_not_contains "$output" "executable file not found" "nix should now exist"
|
assert_not_contains "$output" "executable file not found" "nix should now exist"
|
||||||
assert_contains "$output" "suite-one" "should contain suite-one"
|
assert_contains "$output" "suite-one" "should contain suite-one"
|
||||||
assert_contains "$output" "8/11 (1 SKIPPED)" "should be 8/11 total"
|
assert_contains "$output" "8/11 (1 SKIPPED)" "should be 8/11 total"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue