fix(integration): execute suite commands directly in docker runner
This commit is contained in:
parent
ffc8bdba36
commit
4edbc73b64
2 changed files with 160 additions and 3 deletions
153
integration/run_integration_tests_script_test.go
Normal file
153
integration/run_integration_tests_script_test.go
Normal file
|
|
@ -0,0 +1,153 @@
|
||||||
|
package integration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRunIntegrationTestsScriptExecutesSuiteCommand(t *testing.T) {
|
||||||
|
bashPath, err := exec.LookPath("bash")
|
||||||
|
if err != nil {
|
||||||
|
t.Skip("bash not available")
|
||||||
|
}
|
||||||
|
|
||||||
|
repoRoot := repoRootFromTestFile(t)
|
||||||
|
suitesRoot := filepath.Join(repoRoot, "integration", "suites")
|
||||||
|
suiteDir, err := os.MkdirTemp(suitesRoot, "runner-script-")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("MkdirTemp() error = %v", err)
|
||||||
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
_ = os.RemoveAll(suiteDir)
|
||||||
|
})
|
||||||
|
|
||||||
|
suiteName := filepath.Base(suiteDir)
|
||||||
|
if err := os.WriteFile(
|
||||||
|
filepath.Join(suiteDir, "suite.env"),
|
||||||
|
[]byte("TEST_COMMAND='printf runner-ok'\n"),
|
||||||
|
0o644,
|
||||||
|
); err != nil {
|
||||||
|
t.Fatalf("WriteFile(suite.env) error = %v", err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(
|
||||||
|
filepath.Join(suiteDir, "docker-compose.yml"),
|
||||||
|
[]byte("services:\n fake-dependency:\n image: busybox\n"),
|
||||||
|
0o644,
|
||||||
|
); err != nil {
|
||||||
|
t.Fatalf("WriteFile(docker-compose.yml) error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
stubDir := t.TempDir()
|
||||||
|
logPath := filepath.Join(t.TempDir(), "docker.log")
|
||||||
|
if err := os.WriteFile(filepath.Join(stubDir, "docker"), []byte(`#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
log_file="${DOCKER_LOG:?}"
|
||||||
|
{
|
||||||
|
printf '%s\n' '---'
|
||||||
|
for arg in "$@"; do
|
||||||
|
printf '%s\n' "$arg"
|
||||||
|
done
|
||||||
|
} >>"$log_file"
|
||||||
|
|
||||||
|
subcommand=""
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "$arg" in
|
||||||
|
config|up|run|down)
|
||||||
|
subcommand="$arg"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
case "$subcommand" in
|
||||||
|
config)
|
||||||
|
printf '%s\n' integration-runner fake-dependency
|
||||||
|
;;
|
||||||
|
up)
|
||||||
|
;;
|
||||||
|
run)
|
||||||
|
printf '%s\n' runner-ok
|
||||||
|
;;
|
||||||
|
down)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
printf 'unexpected docker invocation: %s\n' "$*" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
`), 0o755); err != nil {
|
||||||
|
t.Fatalf("WriteFile(docker stub) error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command(bashPath, filepath.Join(repoRoot, "scripts", "run-integration-tests.sh"), suiteName)
|
||||||
|
cmd.Dir = repoRoot
|
||||||
|
cmd.Env = append(os.Environ(),
|
||||||
|
"PATH="+stubDir+string(os.PathListSeparator)+os.Getenv("PATH"),
|
||||||
|
"DOCKER_LOG="+logPath,
|
||||||
|
)
|
||||||
|
|
||||||
|
output, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("run-integration-tests.sh error = %v\noutput:\n%s", err, output)
|
||||||
|
}
|
||||||
|
if !strings.Contains(string(output), "runner-ok") {
|
||||||
|
t.Fatalf("script output did not include runner output:\n%s", output)
|
||||||
|
}
|
||||||
|
|
||||||
|
logData, err := os.ReadFile(logPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ReadFile(logPath) error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
runArgs := findLoggedDockerInvocation(t, string(logData), "run")
|
||||||
|
if strings.Contains(strings.Join(runArgs, "\n"), "\nsh\n-c\n") {
|
||||||
|
t.Fatalf("docker compose run unexpectedly wrapped TEST_COMMAND with sh -c:\n%v", runArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !containsArg(runArgs, "integration-runner") {
|
||||||
|
t.Fatalf("docker compose run args missing runner service:\n%v", runArgs)
|
||||||
|
}
|
||||||
|
if !containsArg(runArgs, "printf runner-ok") {
|
||||||
|
t.Fatalf("docker compose run args missing suite command as a single argument:\n%v", runArgs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func repoRootFromTestFile(t *testing.T) string {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Getwd() error = %v", err)
|
||||||
|
}
|
||||||
|
return filepath.Dir(wd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func findLoggedDockerInvocation(t *testing.T, logData, subcommand string) []string {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
for _, block := range strings.Split(logData, "---\n") {
|
||||||
|
block = strings.TrimSpace(block)
|
||||||
|
if block == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
args := strings.Split(block, "\n")
|
||||||
|
if containsArg(args, subcommand) {
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Fatalf("did not find docker %q invocation in log:\n%s", subcommand, logData)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func containsArg(args []string, want string) bool {
|
||||||
|
for _, arg := range args {
|
||||||
|
if arg == want {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
@ -73,10 +73,12 @@ run_suite() {
|
||||||
trap cleanup EXIT
|
trap cleanup EXIT
|
||||||
|
|
||||||
echo "==> [$suite_name] resolving services"
|
echo "==> [$suite_name] resolving services"
|
||||||
mapfile -t services < <(docker compose "${compose_args[@]}" config --services)
|
local services=()
|
||||||
|
while IFS= read -r service; do
|
||||||
|
services+=("$service")
|
||||||
|
done < <(docker compose "${compose_args[@]}" config --services)
|
||||||
|
|
||||||
local dependency_services=()
|
local dependency_services=()
|
||||||
local service
|
|
||||||
for service in "${services[@]}"; do
|
for service in "${services[@]}"; do
|
||||||
if [[ "$service" != "$runner_service" ]]; then
|
if [[ "$service" != "$runner_service" ]]; then
|
||||||
dependency_services+=("$service")
|
dependency_services+=("$service")
|
||||||
|
|
@ -89,7 +91,9 @@ run_suite() {
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "==> [$suite_name] running: $TEST_COMMAND"
|
echo "==> [$suite_name] running: $TEST_COMMAND"
|
||||||
docker compose "${compose_args[@]}" run --rm "$runner_service" sh -c "$TEST_COMMAND"
|
# integration-runner already uses `bash -lc` as its entrypoint, so pass the
|
||||||
|
# suite command as a single argument for Bash to execute directly.
|
||||||
|
docker compose "${compose_args[@]}" run --rm "$runner_service" "$TEST_COMMAND"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue