ZDDC/zddc/internal/handler/zipwrite_rt_test.go
ZDDC fcb8fc6cf1 feat(server): edit-in-place for the .zddc.zip config bundle, with in-zip history
A zip is random-access (unlike a streamed .tgz), so a member can be rewritten
in place. ServeZipWrite (handler/zipwrite.go) handles PUT (write/create a
member) and DELETE (remove) inside the .zddc.zip bundle: read the whole archive,
snapshot the prior member into an in-zip .history/<member>/<ts> + append a
log.jsonl audit line, mutate, then write a fresh zip and atomically rename over
the original (serialized on one mutex). After a write the policy cache is
invalidated so .zddc policy members take effect immediately, and the apps.Bundle
mtime-reload picks up tool-HTML edits.

Gated to active admins and to the .zddc.zip bundle only (dispatch's bundle gate
already 404s everyone else; content zips — transmittal/WORM packages — stay
read-only and 405). Writing into the in-zip .history/ is refused (append-only).

Also fixes a read collision: a .zddc member INSIDE a zip (e.g. a policy member,
URL ".../.zddc.zip/<dir>/.zddc") was being grabbed by the raw-.zddc-view handler
and 500ing; that handler now excludes ".zip/" paths so the zip intercept serves
the member.

Tests: writer round-trip (incl. wildcard member); dispatch create+overwrite,
policy-takes-effect, in-zip history audit, read-back, non-admin 404, content-zip
405.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 14:28:06 -05:00

31 lines
806 B
Go

package handler
import (
"path/filepath"
"testing"
)
func TestZipWriteRoundTrip(t *testing.T) {
zp := filepath.Join(t.TempDir(), ".zddc.zip")
if err := writeZipAtomic(zp, map[string][]byte{"a.txt": []byte("v1")}, []string{"a.txt"}); err != nil {
t.Fatalf("seed: %v", err)
}
m, ord, err := readZipMembers(zp)
if err != nil {
t.Fatalf("read1: %v", err)
}
addMember(m, &ord, "*/.zddc", []byte("hello-wildcard"))
if err := writeZipAtomic(zp, m, ord); err != nil {
t.Fatalf("write2: %v", err)
}
m2, _, err := readZipMembers(zp)
if err != nil {
t.Fatalf("read2: %v", err)
}
if got := string(m2["*/.zddc"]); got != "hello-wildcard" {
t.Errorf("wildcard member = %q, want hello-wildcard", got)
}
if got := string(m2["a.txt"]); got != "v1" {
t.Errorf("a.txt = %q, want v1", got)
}
}