Files
mesh-catalog/modules/brother/cmd/brother/file_test.go
T

195 lines
6.1 KiB
Go

package main
import (
"bytes"
"fmt"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"testing"
"time"
)
var scanName = regexp.MustCompile(`^scan-\d{4}-\d{2}-\d{2}-\d{6}(-\d+)?\.pdf$`)
// 1440/AC5: a valid one-page PDF holding the JPEG as DCTDecode, at the image's real size.
func TestPDFHoldsTheJPEGAtItsRealSize(t *testing.T) {
jpg := fixture(t, "scan.jpg")
pdf, err := PDF(jpg, 200)
if err != nil {
t.Fatal(err)
}
s := string(pdf)
if !strings.HasPrefix(s, "%PDF-1.4") || !strings.HasSuffix(s, "%%EOF\n") {
t.Fatalf("not a PDF: starts %q ends %q", s[:8], s[len(s)-6:])
}
if !bytes.Contains(pdf, jpg) {
t.Error("the JPEG is not embedded as it is")
}
for _, want := range []string{"/Filter /DCTDecode", "/Width 1696", "/Height 2203", "/ColorSpace /DeviceRGB", "/Count 1"} {
if !strings.Contains(s, want) {
t.Errorf("the PDF lacks %s", want)
}
}
// 1696 x 2203 px at 200 dpi is 610.56 x 793.08 pt, never the 300 dpi that was asked.
if !strings.Contains(s, "/MediaBox [0 0 610.56 793.08]") {
t.Errorf("page size wrong:\n%s", regexp.MustCompile(`/MediaBox[^\]]*\]`).FindString(s))
}
if n := strings.Count(s, "/Type /Page "); n != 1 {
t.Errorf("%d pages", n)
}
// Every xref entry points at its object.
at := strings.LastIndex(s, "startxref\n")
xref, _ := strconv.Atoi(strings.Fields(s[at+len("startxref\n"):])[0])
lines := strings.Split(s[xref:], "\n")
for i := 3; i <= 7 && i < len(lines); i++ {
off, err := strconv.Atoi(lines[i][:10])
if err != nil || !strings.HasPrefix(s[off:], fmt.Sprintf("%d 0 obj", i-2)) {
t.Errorf("xref entry %d (%q) does not point at object %d", i-2, lines[i], i-2)
}
}
}
// 1440/AC2: the page size follows the density it is given.
func TestPDFPageSizeFollowsTheDensity(t *testing.T) {
jpg := fixture(t, "scan.jpg")
a, _ := PDF(jpg, 200)
b, _ := PDF(jpg, 300)
if bytes.Equal(a, b) {
t.Fatal("200 and 300 dpi give the same page")
}
if !strings.Contains(string(b), "/MediaBox [0 0 407.04 528.72]") {
t.Errorf("300 dpi page wrong")
}
if _, err := PDF(jpg, 0); err == nil {
t.Error("0 dpi accepted")
}
}
// 1440/AC5: exactly one file, named by local time, no temporary left, its bytes the PDF.
func TestFileWritesOneNamedFileAndLeavesNoTemporary(t *testing.T) {
folder := t.TempDir()
at := time.Date(2026, 10, 20, 19, 5, 9, 0, time.Local)
path, err := File(folder, []byte("%PDF-x"), at)
if err != nil {
t.Fatal(err)
}
if want := filepath.Join(folder, "scan-2026-10-20-190509.pdf"); path != want {
t.Fatalf("path = %s, want %s", path, want)
}
entries, _ := os.ReadDir(folder)
if len(entries) != 1 || !scanName.MatchString(entries[0].Name()) {
t.Fatalf("folder holds %v, want the one scan", entries)
}
if got, _ := os.ReadFile(path); string(got) != "%PDF-x" {
t.Fatalf("file holds %q", got)
}
}
// 1440/AC5: a name taken gets -2, -3; what was there is never overwritten.
func TestFileNeverOverwritesAndSuffixes(t *testing.T) {
folder := t.TempDir()
at := time.Date(2026, 10, 20, 19, 5, 9, 0, time.Local)
first, _ := File(folder, []byte("one"), at)
second, err := File(folder, []byte("two"), at)
if err != nil {
t.Fatal(err)
}
third, _ := File(folder, []byte("three"), at)
for p, want := range map[string]string{
first: "one", second: "two", third: "three",
} {
if got, _ := os.ReadFile(p); string(got) != want {
t.Errorf("%s holds %q, want %q", p, got, want)
}
}
if filepath.Base(second) != "scan-2026-10-20-190509-2.pdf" || filepath.Base(third) != "scan-2026-10-20-190509-3.pdf" {
t.Errorf("names %s and %s", filepath.Base(second), filepath.Base(third))
}
}
// 1440/AC11: a folder that is not there is made, 0755, and the scan is filed in it.
func TestFileMakesAMissingFolder(t *testing.T) {
folder := filepath.Join(t.TempDir(), "a", "scans")
path, err := File(folder, []byte("x"), time.Now())
if err != nil {
t.Fatal(err)
}
info, err := os.Stat(folder)
if err != nil || info.Mode().Perm() != 0o755 {
t.Fatalf("folder: %v, mode %v, want 0755", err, info)
}
if _, err := os.Stat(path); err != nil {
t.Fatal(err)
}
}
// 1440/AC11: a file in the folder that the module did not write is left as it was by every tool.
func TestFileLeavesForeignFilesAlone(t *testing.T) {
folder := t.TempDir()
old := time.Date(2020, 1, 2, 3, 4, 5, 0, time.UTC)
foreign := map[string]string{"notes.txt": "mine", ".scan-theirs.tmp": "half", "scan-2020-01-01-000000.pdf": "someone else's"}
for n, c := range foreign {
p := filepath.Join(folder, n)
os.WriteFile(p, []byte(c), 0o600)
os.Chtimes(p, old, old)
}
if _, err := File(folder, []byte("x"), time.Now()); err != nil {
t.Fatal(err)
}
if _, err := List(folder, 10); err != nil {
t.Fatal(err)
}
for n, c := range foreign {
p := filepath.Join(folder, n)
got, err := os.ReadFile(p)
info, _ := os.Stat(p)
if err != nil || string(got) != c || !info.ModTime().Equal(old) || info.Mode().Perm() != 0o600 {
t.Errorf("%s changed: %q %v", n, got, err)
}
}
}
// 1440/AC7: a relative folder writes nothing.
func TestFileRefusesARelativeFolder(t *testing.T) {
dir := t.TempDir()
t.Chdir(dir)
if _, err := File("scans", []byte("x"), time.Now()); err == nil {
t.Fatal("a relative folder was accepted")
}
if entries, _ := os.ReadDir(dir); len(entries) != 0 {
t.Fatalf("wrote %v", entries)
}
}
// 1440/AC8: scan_list: newest first, only scan PDFs, limited.
func TestListIsNewestFirstAndLimited(t *testing.T) {
folder := t.TempDir()
base := time.Date(2026, 10, 1, 12, 0, 0, 0, time.UTC)
for i, n := range []string{"scan-a.pdf", "scan-b.pdf", "scan-c.pdf", "other.pdf", "scan-d.txt"} {
p := filepath.Join(folder, n)
os.WriteFile(p, []byte("x"), 0o644)
at := base.Add(time.Duration(i) * time.Hour)
os.Chtimes(p, at, at)
}
got, err := List(folder, 20)
if err != nil {
t.Fatal(err)
}
names := []string{}
for _, e := range got {
names = append(names, filepath.Base(e.Path))
}
if strings.Join(names, ",") != "scan-c.pdf,scan-b.pdf,scan-a.pdf" {
t.Fatalf("list = %v", names)
}
if got, _ = List(folder, 2); len(got) != 2 || filepath.Base(got[0].Path) != "scan-c.pdf" {
t.Fatalf("limit 2 = %v", got)
}
if got, err = List(filepath.Join(folder, "none"), 5); err != nil || len(got) != 0 {
t.Fatalf("missing folder = %v, %v", got, err)
}
}