package main import ( "context" "fmt" "net/http" "net/http/httptest" "os" "path/filepath" "testing" "cmsp/internal/downloader" "cmsp/internal/erpgo" "cmsp/internal/store" ) func TestVideoUploadUsesERPGoAndPersistsOutcome(t *testing.T) { for _, status := range []string{"succeeded", "unknown"} { t.Run(status, func(t *testing.T) { putCount := 0 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") if r.Header.Get("X-API-Key") != "fictional-key" { t.Error("missing API key") } switch { case r.Method == "GET" && r.URL.Path == "/api/v1/shopee/products/123/video": fmt.Fprint(w, `{"code":200,"data":{"shopeeId":"123","source":"huohanhan","fetchedAt":"2026-09-30T00:00:00Z","video":[]}}`) case r.Method == "PUT" && r.URL.Path == "/api/v1/shopee/products/123/video": putCount++ if r.Header.Get("Idempotency-Key") == "" { t.Error("missing idempotency key") } w.WriteHeader(202) fmt.Fprint(w, `{"code":202,"data":{"operationId":"op-1","shopeeId":"123","status":"submitted"}}`) case r.Method == "GET" && r.URL.Path == "/api/v1/shopee/products/123/video/operation": fmt.Fprintf(w, `{"code":200,"data":{"operationId":"op-1","shopeeId":"123","status":%q,"videoUrl":"https://example.invalid/video.mp4"}}`, status) default: t.Errorf("unexpected request: %s %s", r.Method, r.URL.Path) } })) defer server.Close() db, err := store.Open(":memory:") if err != nil { t.Fatal(err) } defer db.Close() if err := db.UpsertProducts([]store.Product{{ID: "internal-1", ItemID: "123", UploadStatus: store.UploadPending}}, "2026-09-30 00:00:00"); err != nil { t.Fatal(err) } a := NewApp() a.db = db a.cfg.ERPGo.BaseURL, a.cfg.ERPGo.APIKey = server.URL, "fictional-key" a.cfg.Download.VideoDir = t.TempDir() a.probe = func(context.Context, string) (downloader.ProbeResult, error) { return downloader.ProbeResult{Duration: 20, FormatName: "mp4", Width: 640, Height: 480}, nil } dir := filepath.Join(a.cfg.Download.VideoDir, "123") if err := os.MkdirAll(dir, 0700); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(dir, "video.mp4"), []byte("fictional-mp4"), 0600); err != nil { t.Fatal(err) } client, err := erpgo.NewClient(a.cfg.ERPGo, nil) if err != nil { t.Fatal(err) } a.uploadOneVideo(context.Background(), client, "internal-1", 1) p, _, err := db.GetProduct("internal-1") if err != nil { t.Fatal(err) } want := store.UploadPending if status == "succeeded" { want = store.UploadDone } if p.UploadStatus != want || putCount != 1 { t.Fatalf("status=%s put=%d", p.UploadStatus, putCount) } if status == "succeeded" { videos, err := db.ListVideos(p.ID) if err != nil || len(videos) != 1 || videos[0].Status != store.VideoStatusUploaded { t.Fatalf("videos=%+v err=%v", videos, err) } } else { a.uploadOneVideo(context.Background(), client, "internal-1", 1) if putCount != 1 { t.Fatalf("unknown outcome repeated PUT: %d", putCount) } } }) } }