Make run identities collision-resistant and outputs exclusive

This commit is contained in:
2026-07-18 14:21:26 +00:00
parent a39eea7ed6
commit 2111e01142
9 changed files with 367 additions and 75 deletions

View File

@@ -1,8 +1,10 @@
package debugbundle
import (
"bytes"
"os"
"path/filepath"
"strings"
"testing"
"time"
@@ -11,17 +13,16 @@ import (
"gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline"
)
const testBundleRunID = "run-42-00000000000000000000000000000001"
func TestAllocateCreatesRestrictiveSummaryAndTrace(t *testing.T) {
parent := t.TempDir()
fixed := time.Unix(0, 42).UTC()
previous := utcNow
utcNow = func() time.Time { return fixed }
defer func() { utcNow = previous }()
bundle, err := Allocate(parent)
bundle, err := Allocate(parent, testBundleRunID, fixed)
if err != nil {
t.Fatal(err)
}
if bundle.RunID() != "run-42" || bundle.SummaryRoot() != filepath.Join(bundle.Path(), "summary") || bundle.TraceRoot() != filepath.Join(bundle.Path(), "trace") {
if bundle.RunID() != testBundleRunID || bundle.CreatedAt() != fixed || bundle.SummaryRoot() != filepath.Join(bundle.Path(), "summary") || bundle.TraceRoot() != filepath.Join(bundle.Path(), "trace") {
t.Fatalf("bundle=%#v", bundle)
}
for _, path := range []string{bundle.Path(), bundle.SummaryRoot(), bundle.TraceRoot()} {
@@ -44,45 +45,42 @@ func TestAllocateCreatesRestrictiveSummaryAndTrace(t *testing.T) {
t.Fatalf("file mode=%#o", info.Mode().Perm())
}
}
func TestAllocateRetriesAndDoesNotDeleteBundle(t *testing.T) {
func TestAllocateRejectsExistingBundleWithoutChangingIt(t *testing.T) {
parent := t.TempDir()
fixed := time.Unix(0, 9).UTC()
previous := utcNow
defer func() { utcNow = previous }()
calls := 0
utcNow = func() time.Time { calls++; return fixed.Add(time.Duration(calls-1) * time.Nanosecond) }
if err := os.Mkdir(filepath.Join(parent, "run-9"), 0o700); err != nil {
bundlePath := filepath.Join(parent, testBundleRunID)
if err := os.Mkdir(bundlePath, 0o700); err != nil {
t.Fatal(err)
}
bundle, err := Allocate(parent)
if err != nil {
sentinelPath := filepath.Join(bundlePath, "sentinel")
sentinel := []byte("existing bundle")
if err := os.WriteFile(sentinelPath, sentinel, 0o600); err != nil {
t.Fatal(err)
}
if bundle.RunID() != "run-10" {
t.Fatalf("run id=%q", bundle.RunID())
if _, err := Allocate(parent, testBundleRunID, time.Unix(0, 42)); err == nil || !strings.Contains(err.Error(), "already exists") {
t.Fatalf("Allocate() error = %v, want collision", err)
}
if _, err := os.Stat(bundle.Path()); err != nil {
t.Fatal(err)
if got, err := os.ReadFile(sentinelPath); err != nil || !bytes.Equal(got, sentinel) {
t.Fatalf("sentinel = %q, %v", got, err)
}
}
func TestAllocateReturnsErrorAfterRunIDCollisionsAreExhausted(t *testing.T) {
parent := t.TempDir()
fixed := time.Unix(0, 99).UTC()
if err := os.Mkdir(filepath.Join(parent, "run-99"), 0o700); err != nil {
t.Fatal(err)
}
previous := utcNow
utcNow = func() time.Time { return fixed }
defer func() { utcNow = previous }()
if _, err := Allocate(parent); err == nil {
t.Fatal("Allocate succeeded after exhausting run ID collisions")
func TestAllocateRejectsUnsafeRunIDsBeforeCreatingParent(t *testing.T) {
for _, runID := range []string{"", ".", "..", "../escape", `..\\escape`, "/absolute", " trailing "} {
t.Run(runID, func(t *testing.T) {
parent := filepath.Join(t.TempDir(), "debug")
if _, err := Allocate(parent, runID, time.Time{}); err == nil {
t.Fatalf("Allocate(%q) succeeded", runID)
}
if _, err := os.Stat(parent); !os.IsNotExist(err) {
t.Fatalf("debug parent exists or stat failed: %v", err)
}
})
}
}
func TestSummaryWriterWritesEverySummaryArtifact(t *testing.T) {
bundle, err := Allocate(t.TempDir())
bundle, err := Allocate(t.TempDir(), testBundleRunID, time.Unix(0, 42))
if err != nil {
t.Fatal(err)
}
@@ -140,7 +138,7 @@ func TestSummaryWriterWritesEverySummaryArtifact(t *testing.T) {
}
}
func TestSummaryWriterConfinesArtifacts(t *testing.T) {
bundle, err := Allocate(t.TempDir())
bundle, err := Allocate(t.TempDir(), testBundleRunID, time.Unix(0, 42))
if err != nil {
t.Fatal(err)
}