Centralize managed state targets

This commit is contained in:
2026-05-31 03:17:40 +00:00
parent 4bd5b19025
commit 61a40ab656
10 changed files with 116 additions and 43 deletions

View File

@@ -181,22 +181,10 @@ func (b *Backend) DeleteManagedBundle(ctx context.Context, bundlePath string, ma
if err := ctx.Err(); err != nil {
return err
}
if err := storage.ValidatePrefix(bundlePath); err != nil {
return err
}
targets := make([]string, 0, len(managedOutputPaths)+1)
for _, outputPath := range managedOutputPaths {
target, err := storage.Join(bundlePath, outputPath)
if err != nil {
return err
}
targets = append(targets, target)
}
statePath, err := storage.StatePath(bundlePath)
targets, err := storage.ManagedBundleTargets(bundlePath, managedOutputPaths)
if err != nil {
return err
}
targets = append(targets, statePath)
for _, target := range targets {
if _, ok := b.dirs[target]; ok {

View File

@@ -116,21 +116,28 @@ func TestBackendManagedDeletion(t *testing.T) {
backend := New()
mustWrite(t, backend, "bundle/report.html", "html")
mustWrite(t, backend, "bundle/keep.txt", "keep")
mustWrite(t, backend, "bundle/.distributor.json", "{}")
statePath, err := storage.StatePath("bundle")
if err != nil {
t.Fatalf("StatePath() error = %v", err)
}
mustWrite(t, backend, statePath, "{}")
err := backend.DeleteManagedBundle(context.Background(), "bundle", []string{"report.html"}, storage.DeleteOptions{PruneEmptyDirs: true})
err = backend.DeleteManagedBundle(context.Background(), "bundle", []string{"report.html"}, storage.DeleteOptions{PruneEmptyDirs: true})
if err != nil {
t.Fatalf("DeleteManagedBundle() error = %v", err)
}
if _, err := backend.Stat(context.Background(), "bundle/report.html"); !storage.IsNotFound(err) {
t.Fatalf("managed output stat error = %v, want not found", err)
}
if _, err := backend.Stat(context.Background(), "bundle/.distributor.json"); !storage.IsNotFound(err) {
if _, err := backend.Stat(context.Background(), statePath); !storage.IsNotFound(err) {
t.Fatalf("state stat error = %v, want not found", err)
}
if _, err := backend.Stat(context.Background(), "bundle/keep.txt"); err != nil {
t.Fatalf("unlisted file stat error = %v", err)
}
if err := backend.DeleteManagedBundle(context.Background(), "bundle", []string{""}, storage.DeleteOptions{}); !storage.IsInvalidPath(err) {
t.Fatalf("DeleteManagedBundle invalid output error = %v, want invalid path", err)
}
}
func TestBackendHasAnyAndWalkStop(t *testing.T) {

View File

@@ -6,7 +6,7 @@ import (
"strings"
)
const stateFileName = ".distributor.json"
const StateFileName = ".distributor.json"
func ValidatePath(value string) error {
if value == "" {
@@ -37,9 +37,29 @@ func Join(base, child string) (string, error) {
func StatePath(bundlePath string) (string, error) {
if bundlePath == "" {
return stateFileName, nil
return StateFileName, nil
}
return Join(bundlePath, stateFileName)
return Join(bundlePath, StateFileName)
}
func ManagedBundleTargets(bundlePath string, managedOutputPaths []string) ([]string, error) {
if err := ValidatePrefix(bundlePath); err != nil {
return nil, err
}
targets := make([]string, 0, len(managedOutputPaths)+1)
for _, outputPath := range managedOutputPaths {
target, err := Join(bundlePath, outputPath)
if err != nil {
return nil, err
}
targets = append(targets, target)
}
statePath, err := StatePath(bundlePath)
if err != nil {
return nil, err
}
targets = append(targets, statePath)
return targets, nil
}
func SortEntries(entries []Entry) {

View File

@@ -49,6 +49,67 @@ func TestValidatePrefixAllowsRoot(t *testing.T) {
}
}
func TestStatePath(t *testing.T) {
tests := map[string]string{
"": StateFileName,
"bundle": "bundle/" + StateFileName,
}
for bundlePath, want := range tests {
t.Run(bundlePath, func(t *testing.T) {
got, err := StatePath(bundlePath)
if err != nil {
t.Fatalf("StatePath(%q) error = %v", bundlePath, err)
}
if got != want {
t.Fatalf("StatePath(%q) = %q, want %q", bundlePath, got, want)
}
})
}
}
func TestManagedBundleTargets(t *testing.T) {
tests := []struct {
name string
bundlePath string
outputs []string
want []string
}{
{
name: "root",
outputs: []string{"report.html", "assets/style.css"},
want: []string{"report.html", "assets/style.css", StateFileName},
},
{
name: "nested",
bundlePath: "daily",
outputs: []string{"report.html"},
want: []string{"daily/report.html", "daily/" + StateFileName},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ManagedBundleTargets(tt.bundlePath, tt.outputs)
if err != nil {
t.Fatalf("ManagedBundleTargets() error = %v", err)
}
if len(got) != len(tt.want) {
t.Fatalf("targets = %v, want %v", got, tt.want)
}
for i := range got {
if got[i] != tt.want[i] {
t.Fatalf("targets = %v, want %v", got, tt.want)
}
}
})
}
}
func TestManagedBundleTargetsRejectsInvalidOutputPath(t *testing.T) {
if _, err := ManagedBundleTargets("bundle", []string{"../outside"}); !IsInvalidPath(err) {
t.Fatalf("ManagedBundleTargets() error = %v, want invalid path", err)
}
}
func TestListSortsEntries(t *testing.T) {
backend := walkBackend{
entries: []Entry{