package ocr import ( "bytes" "context" "encoding/base64" "encoding/json" "io" "net/http" "strings" "testing" "time" "github.com/mmmy/snapgo/internal/domain" ) type roundTripFunc func(*http.Request) (*http.Response, error) func (f roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) { return f(req) } func TestAliyunRecognizeTextSignsRawPNGRequest(t *testing.T) { var requestBody []byte transport := roundTripFunc(func(r *http.Request) (*http.Response, error) { if r.URL.Host != "ocr-api.cn-hangzhou.aliyuncs.com" { t.Fatalf("unexpected host %s", r.URL.Host) } if got := r.Header.Get("x-acs-action"); got != "RecognizeGeneral" { t.Fatalf("unexpected action %q", got) } if got := r.Header.Get("x-acs-version"); got != "2021-07-07" { t.Fatalf("unexpected version %q", got) } if got := r.Header.Get("x-acs-date"); got != "2026-07-08T01:02:03Z" { t.Fatalf("unexpected date %q", got) } if got := r.Header.Get("Authorization"); !strings.Contains(got, "ACS3-HMAC-SHA256 Credential=ak") { t.Fatalf("unexpected auth header %q", got) } var err error requestBody, err = io.ReadAll(r.Body) if err != nil { t.Fatalf("read body: %v", err) } return &http.Response{ StatusCode: http.StatusOK, Header: http.Header{"Content-Type": []string{"application/json"}}, Body: io.NopCloser(bytes.NewBufferString( `{"Data":"{\"content\":\"第一行\\n第二行\"}"}`, )), }, nil }) client, err := NewClient(domain.OCRProviderAliyun, domain.OCRProviderConfig{ Endpoint: "https://ocr-api.cn-hangzhou.aliyuncs.com", AccessKeyID: "ak", AccessKeySecret: "sk", Action: "RecognizeGeneral", Version: "2021-07-07", }) if err != nil { t.Fatalf("new client: %v", err) } client.httpClient.Transport = transport client.now = func() time.Time { return time.Date(2026, 7, 8, 1, 2, 3, 0, time.UTC) } client.nonce = func() string { return "nonce" } text, err := client.RecognizeText(context.Background(), []byte("png")) if err != nil { t.Fatalf("recognize text: %v", err) } if string(requestBody) != "png" { t.Fatalf("expected raw png body, got %q", string(requestBody)) } if text != "第一行\n第二行" { t.Fatalf("unexpected OCR text %q", text) } } func TestVolcengineRecognizeTextSignsBase64JSONRequest(t *testing.T) { var requestBody map[string]string transport := roundTripFunc(func(r *http.Request) (*http.Response, error) { if got := r.URL.Query().Get("Action"); got != "OCRNormal" { t.Fatalf("unexpected action %q", got) } if got := r.URL.Query().Get("Version"); got != "2020-08-26" { t.Fatalf("unexpected version %q", got) } if got := r.Header.Get("X-Date"); got != "20260708T010203Z" { t.Fatalf("unexpected x-date %q", got) } if got := r.Header.Get("Authorization"); !strings.Contains(got, "HMAC-SHA256 Credential=ak/20260708/cn-north-1/cv/request") { t.Fatalf("unexpected auth header %q", got) } if err := json.NewDecoder(r.Body).Decode(&requestBody); err != nil { t.Fatalf("decode request: %v", err) } return &http.Response{ StatusCode: http.StatusOK, Header: http.Header{"Content-Type": []string{"application/json"}}, Body: io.NopCloser(bytes.NewBufferString( `{"ResponseMetadata":{"RequestId":"r"},"Result":{"LineTexts":["你好","世界"]}}`, )), }, nil }) client, err := NewClient(domain.OCRProviderVolcengine, domain.OCRProviderConfig{ Endpoint: "https://visual.volcengineapi.com", AccessKeyID: "ak", AccessKeySecret: "sk", Region: "cn-north-1", Service: "cv", Action: "OCRNormal", Version: "2020-08-26", }) if err != nil { t.Fatalf("new client: %v", err) } client.httpClient.Transport = transport client.now = func() time.Time { return time.Date(2026, 7, 8, 1, 2, 3, 0, time.UTC) } text, err := client.RecognizeText(context.Background(), []byte("png")) if err != nil { t.Fatalf("recognize text: %v", err) } if requestBody["image_base64"] != base64.StdEncoding.EncodeToString([]byte("png")) { t.Fatalf("expected base64 image body, got %#v", requestBody) } if text != "你好\n世界" { t.Fatalf("unexpected OCR text %q", text) } } func TestParseOCRTextSupportsWordsResult(t *testing.T) { text, err := parseOCRText([]byte(`{"data":{"words_result":[{"words":"foo"},{"words":"bar"}]}}`)) if err != nil { t.Fatalf("parse ocr text: %v", err) } if text != "foo\nbar" { t.Fatalf("unexpected OCR text %q", text) } } func TestParseOCRTextReturnsProviderError(t *testing.T) { _, err := parseOCRText([]byte(`{"ResponseMetadata":{"Error":{"Code":"BadRequest","Message":"bad image"}}}`)) if err == nil || !strings.Contains(err.Error(), "bad image") { t.Fatalf("expected provider error, got %v", err) } }