package storage import ( "os" "path/filepath" "testing" ) func TestEnsureLayoutCreatesCanonicalDirectories(t *testing.T) { root := t.TempDir() layout := New(root) if err := layout.Ensure(); err != nil { t.Fatalf("Ensure() error = %v", err) } for _, rel := range []string{"events", "live", "logs", "artifacts", "cache", "workspaces", "secrets", "inbox", "active", "schedules"} { info, err := os.Stat(filepath.Join(root, rel)) if err != nil { t.Fatalf("stat %s: %v", rel, err) } if !info.IsDir() { t.Fatalf("%s is not a directory", rel) } } } func TestEnsureFailsWhenRootIsFile(t *testing.T) { root := filepath.Join(t.TempDir(), "file") if err := os.WriteFile(root, []byte("x"), 0o644); err != nil { t.Fatal(err) } if err := New(root).Ensure(); err == nil { t.Fatal("Ensure under file root accepted") } } func TestSecretPathAcceptsCleanSegments(t *testing.T) { layout := New("/data/ci") got, err := layout.SecretPath("bugabinga.net", "deploy", "HCLOUD_TOKEN") if err != nil { t.Fatalf("SecretPath() error = %v", err) } want := filepath.Join("/data/ci", "secrets", "bugabinga.net", "deploy", "HCLOUD_TOKEN") if got != want { t.Fatalf("SecretPath() = %q, want %q", got, want) } } func TestSecretPathRejectsTraversal(t *testing.T) { layout := New("/data/ci") cases := [][3]string{ {"../repo", "job", "TOKEN"}, {"repo", "../job", "TOKEN"}, {"repo", "job", "../TOKEN"}, {"repo/slash", "job", "TOKEN"}, {"repo", "job/slash", "TOKEN"}, {"repo", "job", ""}, } for _, tc := range cases { if _, err := layout.SecretPath(tc[0], tc[1], tc[2]); err == nil { t.Fatalf("SecretPath(%q, %q, %q) accepted invalid segment", tc[0], tc[1], tc[2]) } } }