Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
130 lines
3.5 KiB
Go
130 lines
3.5 KiB
Go
package shopify
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestShopifySKUSearchQueryOR(t *testing.T) {
|
|
got := shopifySKUSearchQueryOR([]string{"A", "B\"C", ""})
|
|
want := `sku:"A" OR sku:"B\"C"`
|
|
if got != want {
|
|
t.Fatalf("got %q want %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestFindProductsBySKUsBatchesOneGraphQLCall(t *testing.T) {
|
|
var calls atomic.Int32
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/admin/api/2024-10/graphql.json" {
|
|
t.Fatalf("unexpected path %s", r.URL.Path)
|
|
}
|
|
calls.Add(1)
|
|
var body struct {
|
|
Variables struct {
|
|
Q string `json:"q"`
|
|
N int `json:"n"`
|
|
} `json:"variables"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if body.Variables.N != 3 {
|
|
t.Fatalf("expected n=3 got %d", body.Variables.N)
|
|
}
|
|
if !strings.Contains(body.Variables.Q, `sku:"SKU-1"`) || !strings.Contains(body.Variables.Q, `sku:"SKU-2"`) {
|
|
t.Fatalf("query missing SKUs: %q", body.Variables.Q)
|
|
}
|
|
_, _ = io.WriteString(w, `{
|
|
"data": {
|
|
"productVariants": {
|
|
"edges": [
|
|
{"node":{"sku":"SKU-1","product":{"id":"gid://shopify/Product/101","title":"One"}}},
|
|
{"node":{"sku":"SKU-2","product":{"id":"gid://shopify/Product/102","title":"Two"}}}
|
|
]
|
|
}
|
|
}
|
|
}`)
|
|
}))
|
|
defer srv.Close()
|
|
|
|
c := NewClient("demo.myshopify.com", "tok", "2024-10", srv.Client())
|
|
c.HTTP = rewriteShopifyHost(srv, c.HTTP)
|
|
|
|
found, err := c.FindProductsBySKUs(t.Context(), []string{"SKU-1", "SKU-2", "SKU-MISSING"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if calls.Load() != 1 {
|
|
t.Fatalf("expected 1 GraphQL call, got %d", calls.Load())
|
|
}
|
|
if found["SKU-1"].ID != 101 || found["SKU-2"].ID != 102 {
|
|
t.Fatalf("unexpected map: %+v", found)
|
|
}
|
|
if _, ok := found["SKU-MISSING"]; ok {
|
|
t.Fatal("missing SKU should be absent")
|
|
}
|
|
}
|
|
|
|
func TestShopifyRESTRetries429(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, `{"errors":"throttle"}`)
|
|
return
|
|
}
|
|
_, _ = io.WriteString(w, `{"shop":{"id":1,"name":"Demo","domain":"demo.myshopify.com","currency":"USD"}}`)
|
|
}))
|
|
defer srv.Close()
|
|
|
|
c := NewClient("demo.myshopify.com", "tok", "2024-10", srv.Client())
|
|
c.HTTP = rewriteShopifyHost(srv, c.HTTP)
|
|
|
|
start := time.Now()
|
|
shop, err := c.TestConnection(t.Context())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if shop == nil || shop.Name != "Demo" {
|
|
t.Fatalf("unexpected shop %+v", shop)
|
|
}
|
|
if calls.Load() != 2 {
|
|
t.Fatalf("expected 2 attempts, got %d", calls.Load())
|
|
}
|
|
if time.Since(start) > 3*time.Second {
|
|
t.Fatal("retry waited too long")
|
|
}
|
|
}
|
|
|
|
// rewriteShopifyHost routes myshopify Admin API calls to the test server.
|
|
func rewriteShopifyHost(srv *httptest.Server, base *http.Client) *http.Client {
|
|
rt := base.Transport
|
|
if rt == nil {
|
|
rt = http.DefaultTransport
|
|
}
|
|
return &http.Client{
|
|
Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
|
|
u := *req.URL
|
|
su, _ := http.NewRequest(req.Method, srv.URL+u.Path, req.Body)
|
|
su.URL.RawQuery = u.RawQuery
|
|
su.Header = req.Header.Clone()
|
|
su = su.WithContext(req.Context())
|
|
return rt.RoundTrip(su)
|
|
}),
|
|
Timeout: base.Timeout,
|
|
}
|
|
}
|
|
|
|
type roundTripFunc func(*http.Request) (*http.Response, error)
|
|
|
|
func (f roundTripFunc) RoundTrip(r *http.Request) (*http.Response, error) { return f(r) }
|