package deepseek import ( "encoding/base64" "encoding/json" "errors" "net/http" "net/url" "testing" ) type cookieJarStub struct { cookies []*http.Cookie } func (j cookieJarStub) SetCookies(*url.URL, []*http.Cookie) {} func (j cookieJarStub) Cookies(*url.URL) []*http.Cookie { return j.cookies } type errorDeviceIDProvider struct { err error } func (p errorDeviceIDProvider) Get() (string, error) { return "", p.err } func TestDeviceIDFromJar(t *testing.T) { tests := []struct { name string jar http.CookieJar want string wantErr string }{ {name: "nil jar", wantErr: "cookie jar is nil"}, {name: "missing cookie", jar: cookieJarStub{}, wantErr: "thumbcache cookie not found"}, {name: "empty cookie", jar: cookieJarStub{cookies: []*http.Cookie{{Name: ".thumbcache_test"}}}, wantErr: "thumbcache cookie is empty"}, { name: "matching cookie", jar: cookieJarStub{cookies: []*http.Cookie{ {Name: "other", Value: "ignored"}, {Name: ".thumbcache_test", Value: "device-cookie"}, }}, want: base64.StdEncoding.EncodeToString([]byte("device-cookie")), }, } client := NewClient() for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := client.DeviceIDFromJar(tt.jar) if tt.wantErr != "" { if err == nil || err.Error() != tt.wantErr { t.Fatalf("DeviceIDFromJar() error = %v, want %q", err, tt.wantErr) } return } if err != nil { t.Fatalf("DeviceIDFromJar() error = %v", err) } if got != tt.want { t.Fatalf("DeviceIDFromJar() = %q, want %q", got, tt.want) } }) } } func TestLogin(t *testing.T) { client := testAPI(func(r *http.Request) (*http.Response, error) { if r.URL.Path != "/users/login" { t.Errorf("path = %q, want /users/login", r.URL.Path) } var body LoginReq if err := json.NewDecoder(r.Body).Decode(&body); err != nil { t.Errorf("decode request: %v", err) } if body.DeviceID != "device-id" || body.Email != "user@example.test" || body.Password != "password" || body.OS != "ios" { t.Errorf("request body = %#v", body) } return testHTTPResponse(`{"code":0,"data":{"biz_code":0,"biz_data":{"user":{"id":"user-id","token":"api-token","email":"user@example.test"}}}}`), nil }).SetDeviceIDProvider(StaticDeviceIDProvider("device-id")) if err := client.Login("user@example.test", "password"); err != nil { t.Fatalf("Login() error = %v", err) } if client.token != "api-token" { t.Fatalf("token = %q, want api-token", client.token) } } func TestLoginUsesRandomProviderByDefault(t *testing.T) { client := testAPI(func(r *http.Request) (*http.Response, error) { var body LoginReq if err := json.NewDecoder(r.Body).Decode(&body); err != nil { t.Errorf("decode request: %v", err) } raw, err := base64.StdEncoding.DecodeString(body.DeviceID) if err != nil { t.Errorf("decode device ID: %v", err) } else if len(raw) != 64 { t.Errorf("device ID has %d decoded bytes, want 64", len(raw)) } return testHTTPResponse(`{"code":0,"data":{"biz_code":0,"biz_data":{"user":{"token":"api-token"}}}}`), nil }) if err := client.Login("user@example.test", "password"); err != nil { t.Fatalf("Login() error = %v", err) } } func TestLoginReturnsProviderErrorWithoutRequest(t *testing.T) { wantErr := errors.New("device ID unavailable") requests := 0 client := testAPI(func(*http.Request) (*http.Response, error) { requests++ return testHTTPResponse(`{}`), nil }).SetDeviceIDProvider(errorDeviceIDProvider{err: wantErr}) err := client.Login("user@example.test", "password") if !errors.Is(err, wantErr) { t.Fatalf("Login() error = %v, want %v", err, wantErr) } if requests != 0 { t.Fatalf("transport received %d requests, want 0", requests) } }