mirror of
https://github.com/OwO-Network/DeepLX.git
synced 2026-09-10 12:00:58 +00:00
The translate package had no tests. These are fully hermetic: callOneshot is exercised against httptest, so go test ./... stays offline and fast. Covers lang resolution (incl. the EN/PT target-vs-source asymmetry), TranslateByDeepLX preflight validation (404/400/413, pre-network only), callOneshot request shaping and gzip/deflate/br decoding, newInstanceID UUIDv4 shape, and the oneshotRequest JSON contract.
152 lines
4.6 KiB
Go
152 lines
4.6 KiB
Go
package translate
|
|
|
|
import (
|
|
"bytes"
|
|
"compress/flate"
|
|
"compress/gzip"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/andybalholm/brotli"
|
|
"github.com/imroc/req/v3"
|
|
)
|
|
|
|
// useHermeticClient pre-seeds the process-wide client cache for the empty
|
|
// proxy key so callOneshot reuses it instead of building the real
|
|
// ImpersonateChrome client, whose creation also fires a background warmup
|
|
// GET to www.deepl.com. The client mirrors production where it matters for
|
|
// this layer: it pins Accept-Encoding so Go's transport does not auto-
|
|
// decompress, which is exactly why callOneshot decompresses by hand.
|
|
func useHermeticClient(t *testing.T) {
|
|
t.Helper()
|
|
c := req.C().
|
|
SetTimeout(5*time.Second).
|
|
SetCommonHeader("Accept-Encoding", "gzip, deflate, br")
|
|
oneshotClients.Store("", c)
|
|
}
|
|
|
|
func TestCallOneshotRequestShaping(t *testing.T) {
|
|
useHermeticClient(t)
|
|
|
|
var gotHeader http.Header
|
|
var gotBody []byte
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
gotHeader = r.Header.Clone()
|
|
gotBody, _ = io.ReadAll(r.Body)
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_, _ = w.Write([]byte(`{"translations":[{"text":"hallo","detected_source_language":"EN"}]}`))
|
|
}))
|
|
defer srv.Close()
|
|
|
|
body := []byte(`{"text":["hello"],"target_lang":"de"}`)
|
|
|
|
t.Run("anonymous sends Authorization None and extension headers", func(t *testing.T) {
|
|
res, status, err := callOneshot(srv.URL, body, "", "")
|
|
if err != nil {
|
|
t.Fatalf("callOneshot error: %v", err)
|
|
}
|
|
if status != http.StatusOK {
|
|
t.Fatalf("status = %d, want 200", status)
|
|
}
|
|
if got := res.Get("translations.0.text").String(); got != "hallo" {
|
|
t.Errorf("parsed body wrong, translations.0.text = %q", got)
|
|
}
|
|
if h := gotHeader.Get("Authorization"); h != "None" {
|
|
t.Errorf("Authorization = %q, want %q", h, "None")
|
|
}
|
|
if h := gotHeader.Get("Content-Type"); h != "application/json" {
|
|
t.Errorf("Content-Type = %q, want application/json", h)
|
|
}
|
|
if h := gotHeader.Get("Origin"); h != "chrome-extension://"+chromeExtensionID {
|
|
t.Errorf("Origin = %q, want chrome-extension://%s", h, chromeExtensionID)
|
|
}
|
|
for k, want := range map[string]string{
|
|
"Sec-Fetch-Site": "cross-site",
|
|
"Sec-Fetch-Mode": "cors",
|
|
"Sec-Fetch-Dest": "empty",
|
|
} {
|
|
if h := gotHeader.Get(k); h != want {
|
|
t.Errorf("%s = %q, want %q", k, h, want)
|
|
}
|
|
}
|
|
if !bytes.Equal(gotBody, body) {
|
|
t.Errorf("forwarded body = %q, want %q", gotBody, body)
|
|
}
|
|
})
|
|
|
|
t.Run("bearer token used when session provided", func(t *testing.T) {
|
|
if _, _, err := callOneshot(srv.URL, body, "tok123", ""); err != nil {
|
|
t.Fatalf("callOneshot error: %v", err)
|
|
}
|
|
if h := gotHeader.Get("Authorization"); h != "Bearer tok123" {
|
|
t.Errorf("Authorization = %q, want %q", h, "Bearer tok123")
|
|
}
|
|
})
|
|
}
|
|
|
|
// TestCallOneshotDecompression covers the three hand-rolled decode paths
|
|
// (gzip/deflate/br) plus identity. This code exists only because the client
|
|
// pins Accept-Encoding, and it was previously untested — a decode regression
|
|
// would surface to users as a blind "Translation failed".
|
|
func TestCallOneshotDecompression(t *testing.T) {
|
|
useHermeticClient(t)
|
|
|
|
const payload = `{"translations":[{"text":"decoded"}]}`
|
|
|
|
cases := []struct {
|
|
name string
|
|
encoding string
|
|
compress func([]byte) []byte
|
|
}{
|
|
{"identity", "", func(b []byte) []byte { return b }},
|
|
{"gzip", "gzip", func(b []byte) []byte {
|
|
var buf bytes.Buffer
|
|
w := gzip.NewWriter(&buf)
|
|
_, _ = w.Write(b)
|
|
_ = w.Close()
|
|
return buf.Bytes()
|
|
}},
|
|
{"deflate", "deflate", func(b []byte) []byte {
|
|
var buf bytes.Buffer
|
|
w, _ := flate.NewWriter(&buf, flate.DefaultCompression)
|
|
_, _ = w.Write(b)
|
|
_ = w.Close()
|
|
return buf.Bytes()
|
|
}},
|
|
{"brotli", "br", func(b []byte) []byte {
|
|
var buf bytes.Buffer
|
|
w := brotli.NewWriter(&buf)
|
|
_, _ = w.Write(b)
|
|
_ = w.Close()
|
|
return buf.Bytes()
|
|
}},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if tc.encoding != "" {
|
|
w.Header().Set("Content-Encoding", tc.encoding)
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_, _ = w.Write(tc.compress([]byte(payload)))
|
|
}))
|
|
defer srv.Close()
|
|
|
|
res, status, err := callOneshot(srv.URL, []byte(`{}`), "", "")
|
|
if err != nil {
|
|
t.Fatalf("callOneshot(%s) error: %v", tc.name, err)
|
|
}
|
|
if status != http.StatusOK {
|
|
t.Fatalf("status = %d, want 200", status)
|
|
}
|
|
if got := res.Get("translations.0.text").String(); got != "decoded" {
|
|
t.Errorf("decoded text = %q, want %q (encoding %q)", got, "decoded", tc.encoding)
|
|
}
|
|
})
|
|
}
|
|
}
|