package woocommerce import ( "encoding/json" "io" "net/http" "net/http/httptest" "strings" "sync/atomic" "testing" "time" "github.com/google/uuid" ) func TestListProductsBySKUsOneRequest(t *testing.T) { var calls atomic.Int32 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/wp-json/wc/v3/products" { t.Fatalf("path %s", r.URL.Path) } calls.Add(1) sku := r.URL.Query().Get("sku") if !strings.Contains(sku, "A1") || !strings.Contains(sku, "B2") { t.Fatalf("sku query %q", sku) } _, _ = io.WriteString(w, `[{"id":11,"sku":"A1","name":"A"},{"id":22,"sku":"B2","name":"B"}]`) })) defer srv.Close() c := NewClient(srv.URL, "ck", "cs", srv.Client()) found, err := c.ListProductsBySKUs(t.Context(), []string{"A1", "B2", "MISSING"}) if err != nil { t.Fatal(err) } if calls.Load() != 1 { t.Fatalf("calls=%d", calls.Load()) } if found["A1"].ID != 11 || found["B2"].ID != 22 { t.Fatalf("%+v", found) } if _, ok := found["MISSING"]; ok { t.Fatal("unexpected missing hit") } } func TestPushProductsBatchesSKULookup(t *testing.T) { var skuCalls, batchCalls atomic.Int32 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch { case r.Method == http.MethodGet && r.URL.Path == "/wp-json/wc/v3/products": skuCalls.Add(1) _, _ = io.WriteString(w, `[{"id":99,"sku":"EXISTING","name":"Exist"}]`) case r.Method == http.MethodPost && r.URL.Path == "/wp-json/wc/v3/products/batch": batchCalls.Add(1) var req BatchRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { t.Fatal(err) } if len(req.Create) == 1 && req.Create[0].SKU == "NEW" { _, _ = io.WriteString(w, `{"create":[{"id":100,"sku":"NEW"}],"update":[]}`) return } if len(req.Update) == 1 && req.Update[0].SKU == "EXISTING" { _, _ = io.WriteString(w, `{"create":[],"update":[{"id":99,"sku":"EXISTING"}]}`) return } t.Fatalf("unexpected batch %+v", req) default: t.Fatalf("unexpected %s %s", r.Method, r.URL.Path) } })) defer srv.Close() c := NewClient(srv.URL, "ck", "cs", srv.Client()) svc := &Service{} idExisting := uuid.MustParse("11111111-1111-1111-1111-111111111111") idNew := uuid.MustParse("22222222-2222-2222-2222-222222222222") pidExisting := "EXISTING" pidNew := "NEW" name := "Product" opt := SyncOptions{ ProductIDs: map[string]int{}, MatchStrategy: "sku", BatchSize: 25, } rows := []syncProductRow{ {ID: idExisting, ProductID: &pidExisting, Name: &name, MappedData: []byte(`{"sku":"EXISTING","price":"1"}`)}, {ID: idNew, ProductID: &pidNew, Name: &name, MappedData: []byte(`{"sku":"NEW","price":"2"}`)}, } summary, err := svc.pushProducts(t.Context(), c, uuid.Nil, &opt, rows) if err != nil { t.Fatal(err) } if skuCalls.Load() != 1 { t.Fatalf("expected 1 SKU lookup, got %d", skuCalls.Load()) } if batchCalls.Load() != 2 { t.Fatalf("expected create+update batches, got %d", batchCalls.Load()) } if summary.Created != 1 || summary.Updated != 1 || summary.Failed != 0 { t.Fatalf("summary=%+v", summary) } if opt.ProductIDs[idExisting.String()] != 99 || opt.ProductIDs[idNew.String()] != 100 { t.Fatalf("product ids=%v", opt.ProductIDs) } } func TestWooCommerceRetries429(t *testing.T) { var calls atomic.Int32 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { n := calls.Add(1) if n == 1 { w.Header().Set("Retry-After", "0") w.WriteHeader(http.StatusTooManyRequests) _, _ = io.WriteString(w, `{"code":"woocommerce_rest_cannot_view"}`) return } _, _ = io.WriteString(w, `[]`) })) defer srv.Close() c := NewClient(srv.URL, "ck", "cs", srv.Client()) start := time.Now() if err := c.TestConnection(t.Context()); err != nil { t.Fatal(err) } if calls.Load() != 2 { t.Fatalf("calls=%d", calls.Load()) } if time.Since(start) > 3*time.Second { t.Fatal("retry waited too long") } }