package backup import ( "archive/tar" "compress/gzip" "context" "io" "log/slog" "os" "path/filepath" "slices" "testing" "github.com/cdnutter/canon/internal/store" ) func TestCreateListPrune(t *testing.T) { data := t.TempDir() st, err := store.Open(data) if err != nil { t.Fatal(err) } defer st.Close() images := filepath.Join(data, "images") os.MkdirAll(images, 0o755) os.WriteFile(filepath.Join(images, "abc.jpg"), []byte("jpeg"), 0o644) os.WriteFile(filepath.Join(images, ".upload-123"), []byte("partial"), 0o644) m := New(filepath.Join(data, "backups"), images, st, slog.New(slog.DiscardHandler)) ctx := context.Background() manual, err := m.Create(ctx, true) if err != nil { t.Fatal(err) } if got := archiveNames(t, filepath.Join(m.Dir(), manual.Name)); !slices.Equal(got, []string{"canon.db", "images/abc.jpg"}) { t.Fatalf("archive holds %v", got) } for range 3 { if _, err := m.Create(ctx, false); err != nil { t.Fatal(err) } } if err := m.prune(2); err != nil { t.Fatal(err) } list, _ := m.List() var scheduled, manuals int for _, b := range list { if b.Manual { manuals++ } else { scheduled++ } } if scheduled != 2 || manuals != 1 { t.Fatalf("after prune: %d scheduled, %d manual", scheduled, manuals) } if _, err := m.Path("../canon.db"); err == nil { t.Fatal("path traversal accepted") } if err := m.Delete(manual.Name); err != nil { t.Fatal(err) } } func archiveNames(t *testing.T, path string) []string { t.Helper() f, err := os.Open(path) if err != nil { t.Fatal(err) } defer f.Close() gz, err := gzip.NewReader(f) if err != nil { t.Fatal(err) } tr := tar.NewReader(gz) var names []string for { h, err := tr.Next() if err == io.EOF { return names } if err != nil { t.Fatal(err) } names = append(names, h.Name) } }