1
0
forked from sneak/dcf
Files
dcf/pkg/dcf/dcf_test.go
clawbot ed95e66f7d bring the repo up to org standards
Adopt the standard tooling: a Makefile of thin shims over a full
scripts-to-rule-them-all `script/` set, the canonical `.golangci.yml`
vendored byte-identical from `sneak/prompts`, docker-only linting via
`Dockerfile.lint`, a `Dockerfile` and Gitea workflow that gate every
push, and prettier/editorconfig/dockerignore config.

`make build` pointed at a `cmd/dcfinfo` that is not in the tree and
could never have succeeded; this repo is a library, so `build` is now
the compile check over every package.

Clear the 57 findings the canonical linter config reports on `pkg/dcf`.
Two were real: `findDCFMountPoints` checked a never-assigned `erro`
instead of the error from `findAllMountPoints`, discarding it, and
`privatePath` was computed twice so the first computation was dead.
Mountpoint selection otherwise behaves exactly as before; the defects
that survive are filed as issue #6, not fixed here.

Rename `DCFStore` to `Store` and `DCFObject` to `Object` (with its
`DCFStoreRoot` field to `StoreRoot`), which revive's stutter rule
requires and which the `fs.FS` rework in issue #4 will build on.

Replace the placeholder test with tests over the exported surface.
The filesystem walk stays uncovered: it is reachable only through
`GetDCFStores`, which needs real mounted media.

README keeps its content, reorganised into the required sections and
gaining Entrypoints.

(closes #1)
2026-08-30 11:36:58 +00:00

178 lines
3.9 KiB
Go

package dcf_test
import (
"crypto/sha256"
"encoding/hex"
"os"
"path/filepath"
"strings"
"testing"
"git.eeqj.de/sneak/dcf/pkg/dcf"
)
// newStore builds a store with two images and one video of known sizes,
// so the count and size accessors have something deterministic to
// report.
func newStore(root string) *dcf.Store {
return &dcf.Store{
RootDirectory: root,
Images: []*dcf.Image{
{Object: dcf.Object{
StoreRoot: root,
Path: "DCIM/100MSDCF/DSC00001.ARW",
Size: 2000,
Extension: ".ARW",
}},
{Object: dcf.Object{
StoreRoot: root,
Path: "DCIM/100MSDCF/DSC00002.JPG",
Size: 500,
Extension: ".JPG",
}},
},
Videos: []*dcf.Video{
{Object: dcf.Object{
StoreRoot: root,
Path: "PRIVATE/M4ROOT/CLIP/C0001.MP4",
Size: 9000,
Extension: ".MP4",
}},
},
}
}
func TestObjectFullFilePath(t *testing.T) {
t.Parallel()
object := dcf.Object{
StoreRoot: filepath.Join("/mnt", "card"),
Path: filepath.Join("DCIM", "100MSDCF", "DSC00001.ARW"),
}
want := filepath.Join("/mnt", "card", "DCIM", "100MSDCF", "DSC00001.ARW")
if got := object.FullFilePath(); got != want {
t.Fatalf("FullFilePath() = %q, want %q", got, want)
}
}
func TestStoreCounts(t *testing.T) {
t.Parallel()
store := newStore("/mnt/card")
if got := store.ImagesCount(); got != 2 {
t.Errorf("ImagesCount() = %d, want 2", got)
}
if got := store.VideosCount(); got != 1 {
t.Errorf("VideosCount() = %d, want 1", got)
}
}
func TestStoreSizes(t *testing.T) {
t.Parallel()
store := newStore("/mnt/card")
if got := store.TotalImageSize(); got != 2500 {
t.Errorf("TotalImageSize() = %d, want 2500", got)
}
if got := store.TotalVideoSize(); got != 9000 {
t.Errorf("TotalVideoSize() = %d, want 9000", got)
}
if got := store.TotalSize(); got != 11500 {
t.Errorf("TotalSize() = %d, want 11500", got)
}
}
func TestEmptyStoreSizesAreZero(t *testing.T) {
t.Parallel()
store := dcf.Store{RootDirectory: "/mnt/empty"}
if got := store.TotalSize(); got != 0 {
t.Fatalf("TotalSize() = %d, want 0", got)
}
}
func TestStoreStringReportsHumanizedTotal(t *testing.T) {
t.Parallel()
got := newStore("/mnt/card").String()
for _, want := range []string{"/mnt/card", "Images: 2", "Videos: 1", "12 kB"} {
if !strings.Contains(got, want) {
t.Errorf("String() = %q, want it to contain %q", got, want)
}
}
}
func TestImageAndVideoString(t *testing.T) {
t.Parallel()
image := dcf.Image{Object: dcf.Object{
Path: "DCIM/100MSDCF/DSC00001.ARW",
Size: 2000,
Extension: ".ARW",
}}
want := "Image{Path: DCIM/100MSDCF/DSC00001.ARW, Size: 2000, " +
"Extension: .ARW}"
if got := image.String(); got != want {
t.Errorf("Image.String() = %q, want %q", got, want)
}
video := dcf.Video{Object: dcf.Object{
Path: "PRIVATE/M4ROOT/CLIP/C0001.MP4",
Size: 9000,
Extension: ".MP4",
}}
want = "Video{Path: PRIVATE/M4ROOT/CLIP/C0001.MP4, Size: 9000, " +
"Extension: .MP4}"
if got := video.String(); got != want {
t.Errorf("Video.String() = %q, want %q", got, want)
}
}
func TestObjectHashMatchesSHA256OfFile(t *testing.T) {
t.Parallel()
root := t.TempDir()
contents := []byte("DSC00001.ARW contents")
err := os.MkdirAll(filepath.Join(root, "DCIM"), 0o750)
if err != nil {
t.Fatalf("MkdirAll: %v", err)
}
err = os.WriteFile(filepath.Join(root, "DCIM", "a.arw"), contents, 0o600)
if err != nil {
t.Fatalf("WriteFile: %v", err)
}
object := dcf.Object{StoreRoot: root, Path: filepath.Join("DCIM", "a.arw")}
got, err := object.Hash()
if err != nil {
t.Fatalf("Hash() returned error: %v", err)
}
sum := sha256.Sum256(contents)
if want := hex.EncodeToString(sum[:]); got != want {
t.Fatalf("Hash() = %q, want %q", got, want)
}
}
func TestObjectHashOnMissingFileReturnsError(t *testing.T) {
t.Parallel()
object := dcf.Object{StoreRoot: t.TempDir(), Path: "absent.jpg"}
_, err := object.Hash()
if err == nil {
t.Fatal("Hash() on a missing file returned no error")
}
}