mirror of
https://github.com/samuelncui/acp.git
synced 2026-11-08 04:21:16 +00:00
Reshape the public API around one push engine and restore theaf05f05csurface as a compatibility shell over it. - NewStream takes caller-owned items (Source/Targets), reports every accepted item through one results callback, and Wait returns the run's terminal error. Run, BatchSource and the item callbacks are gone. - The engine owns the result path: WithResultBuffer is the depth of the result queue, WithResultBatch is how many results one delivery carries, and WithResultFlushInterval is how long a successful result may wait. A result that carries any error is delivered immediately as its own batch. - Restore theaf05f05cAPI byte-compatibly in the same package: New, Copyer, Wait/WaitErr, AccurateJob, WildcardJob, the report events and the JSON report round-trip through encoding/json alone. - One descriptor serves a whole item; the stored hash is read through the descriptor that reads the content and the computed hash is published through it before it closes. - Make every handoff escapable by a hard stop: the read buffer, a chunk send, an event send and the result buffer. - Fix the recorded sweep findings: report indent and round-trip, cmd/acp exit status, acp-rewrite final paths, mmap EOF/close/slice, disk-usage reservations, ErrTargetIO identity, the fatal-panic safety net, option validation, and the FreeBSD/Windows build paths. - Cover the contract with semantic tests, add Makefile check/race/vet/cross targets, and document the withdrawn v0.1.0/v0.2.0 migration in README.
56 lines
1.6 KiB
Go
56 lines
1.6 KiB
Go
package acp
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
func TestCleanupRemovesTargetAfterMetadataFailure(t *testing.T) {
|
|
// Use a dangling symlink so metadata restoration fails while removal remains possible.
|
|
root := t.TempDir()
|
|
sourcePath := writeSourceFile(t, root, "source", []byte("fixture"))
|
|
info, err := os.Stat(sourcePath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
stat, err := newStat(sourcePath, info)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
target := filepath.Join(root, "target")
|
|
if err := os.Symlink(filepath.Join(root, "missing"), target); err != nil {
|
|
t.Skipf("create test symlink: %v", err)
|
|
}
|
|
|
|
// The results stage must discard the failed target before publishing its final result.
|
|
copyer := newTestStream(t)
|
|
item := newFixtureItem(sourcePath, target)
|
|
fixture := newStreamFixture(item)
|
|
copyer.onResults = fixture.onResults
|
|
job := &baseJob{
|
|
copyer: copyer,
|
|
item: item,
|
|
path: sourcePath,
|
|
stat: stat,
|
|
targets: []string{target},
|
|
successTargets: []string{target},
|
|
}
|
|
copyed := make(chan *baseJob, 1)
|
|
copyed <- job
|
|
close(copyed)
|
|
runResults(copyer, copyed)
|
|
|
|
if _, err := os.Lstat(target); !errors.Is(err, os.ErrNotExist) {
|
|
t.Fatalf("stat failed target error = %v, want %v", err, os.ErrNotExist)
|
|
}
|
|
result, terminalErr := item.terminal(t)
|
|
if terminalErr != nil {
|
|
t.Fatalf("a metadata failure is a target outcome, not an item failure: %v", terminalErr)
|
|
}
|
|
if len(result.Targets) != 1 || !errors.Is(result.Targets[0].Err, os.ErrNotExist) {
|
|
t.Fatalf("target outcome = %#v, want %v", result.Targets, os.ErrNotExist)
|
|
}
|
|
}
|