package service import ( "context" "net/http" "net/http/httptest" "os" "path/filepath" "strings" "testing" "fashionapi/internal/pkg/storage" ) // TestFetchImagesCleansUpOnFailure 验证「单图失败=整任务失败」时,本批已成功上传的对象被回滚删除, // 不会在存储里留下孤儿文件。 func TestFetchImagesCleansUpOnFailure(t *testing.T) { dir := t.TempDir() local := storage.NewLocalUploader(dir, "/uploads") s := &IngestService{uploader: local, local: local, httpClient: &http.Client{}} srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if strings.Contains(r.URL.Path, "bad") { w.WriteHeader(500) return } w.Header().Set("Content-Type", "image/jpeg") _, _ = w.Write([]byte("fakeimagebytes")) })) defer srv.Close() urls := []string{srv.URL + "/ok1.jpg", srv.URL + "/ok2.jpg", srv.URL + "/bad.jpg"} _, out, keys, failed := s.fetchImages(context.Background(), urls, "runway") if !failed { t.Fatalf("expected failed=true when one image errors") } if len(out) != 2 || len(keys) != 2 { t.Fatalf("expected 2 successful uploads, got out=%d keys=%d", len(out), len(keys)) } for _, k := range keys { if _, err := os.Stat(filepath.Join(dir, filepath.FromSlash(k))); err != nil { t.Fatalf("uploaded file missing before cleanup: %s", k) } } s.cleanupUploads(context.Background(), keys) for _, k := range keys { if _, err := os.Stat(filepath.Join(dir, filepath.FromSlash(k))); !os.IsNotExist(err) { t.Fatalf("orphan file not cleaned up: %s", k) } } }