Files
jbergner ad54651558
release-tag / release-image (push) Successful in 2m13s
v9.3.3
2026-08-31 22:34:50 +02:00

142 lines
4.5 KiB
Go

package stacks
import (
"context"
"os"
"path/filepath"
"strings"
"testing"
)
func TestPathRejectsTraversal(t *testing.T) {
s, err := New(t.TempDir())
if err != nil {
t.Fatal(err)
}
for _, name := range []string{"../x", "/tmp/x", "a/b", ""} {
if _, err := s.path(name); err == nil {
t.Fatalf("expected %q to be rejected", name)
}
}
if _, err := s.path("my-stack_1.2"); err != nil {
t.Fatalf("valid name rejected: %v", err)
}
}
func TestSaveStagesEnvAndSecretsBeforeComposeValidation(t *testing.T) {
root := t.TempDir()
bin := t.TempDir()
docker := filepath.Join(bin, "docker")
script := `#!/bin/sh
set -eu
[ -f .env ]
[ -f secrets/api_key ]
[ "$(cat secrets/api_key)" = "supersecret" ]
exit 0
`
if err := os.WriteFile(docker, []byte(script), 0755); err != nil {
t.Fatal(err)
}
t.Setenv("PATH", bin+string(os.PathListSeparator)+os.Getenv("PATH"))
s, err := New(root)
if err != nil {
t.Fatal(err)
}
in := SaveInput{Compose: "services:\n app:\n image: nginx:alpine\n env_file: .env\nsecrets:\n api_key:\n file: ./secrets/api_key\n", Env: "A=B\n", Secrets: []SecretFile{{Name: "api_key", Content: "supersecret"}}}
if err := s.Save(context.Background(), "demo", in); err != nil {
t.Fatalf("save: %v", err)
}
if _, err := os.Stat(filepath.Join(root, "demo", "compose.yaml")); err != nil {
t.Fatal(err)
}
if b, err := os.ReadFile(filepath.Join(root, "demo", "secrets", "api_key")); err != nil || string(b) != "supersecret" {
t.Fatalf("secret not committed: %q %v", string(b), err)
}
}
func TestSaveReportsDockerExecErrorWhenComposeIsSilent(t *testing.T) {
root := t.TempDir()
bin := t.TempDir()
docker := filepath.Join(bin, "docker")
if err := os.WriteFile(docker, []byte("#!/bin/sh\nexit 7\n"), 0755); err != nil {
t.Fatal(err)
}
t.Setenv("PATH", bin+string(os.PathListSeparator)+os.Getenv("PATH"))
s, _ := New(root)
err := s.Save(context.Background(), "demo", SaveInput{Compose: "services:\n app:\n image: nginx\n"})
if err == nil || !strings.Contains(err.Error(), "exit status 7") {
t.Fatalf("expected useful exit error, got %v", err)
}
}
func TestDescriptorDigestPrefersDescriptor(t *testing.T) {
v := map[string]any{"Descriptor": map[string]any{"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}, "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"}
got := descriptorDigest(v)
if got != "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" {
t.Fatalf("got %q", got)
}
}
func TestDeletePreservesUnmanagedStackDataByDefault(t *testing.T) {
root := t.TempDir()
s, err := New(root)
if err != nil {
t.Fatal(err)
}
dir := filepath.Join(root, "demo")
if err := os.MkdirAll(filepath.Join(dir, "data"), 0750); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(dir, "compose.yaml"), []byte("services: {}\n"), 0640); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(dir, "data", "important.db"), []byte("keep"), 0640); err != nil {
t.Fatal(err)
}
if err := s.Delete(context.Background(), "demo", false, false); err != nil {
t.Fatal(err)
}
if _, err := os.Stat(filepath.Join(dir, "compose.yaml")); !os.IsNotExist(err) {
t.Fatalf("managed compose file should be removed, got %v", err)
}
if b, err := os.ReadFile(filepath.Join(dir, "data", "important.db")); err != nil || string(b) != "keep" {
t.Fatalf("unmanaged bind-mount data was damaged: %q %v", b, err)
}
}
func TestSafeDockerPositionalRejectsOptionLikeValues(t *testing.T) {
for _, v := range []string{"--help", "-f", "bad\nvalue"} {
if _, err := safeDockerPositional(v, "target"); err == nil {
t.Fatalf("expected %q to be rejected", v)
}
}
if got, err := safeDockerPositional("sha256:abc", "target"); err != nil || got != "sha256:abc" {
t.Fatalf("valid target rejected: %q %v", got, err)
}
}
func TestExecRejectsOptionLikeServiceName(t *testing.T) {
s, err := New(t.TempDir())
if err != nil {
t.Fatal(err)
}
if _, err := s.Exec(context.Background(), "demo", ExecInput{Service: "--index", Command: "id"}); err == nil {
t.Fatal("expected invalid service name to be rejected before invoking Docker")
}
}
func TestStackPathRejectsSymlinkDirectory(t *testing.T) {
root := t.TempDir()
outside := t.TempDir()
if err := os.Symlink(outside, filepath.Join(root, "demo")); err != nil {
t.Skipf("symlinks unavailable: %v", err)
}
s, err := New(root)
if err != nil {
t.Fatal(err)
}
if _, err := s.path("demo"); err == nil {
t.Fatal("expected symlink stack directory to be rejected")
}
}