You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

146 lines
5.1 KiB

package main
import "testing"
// TestParseVkCaptchaError_NewFormatWithoutCaptchaSid reproduces issue #182.
//
// VK started returning a captcha-required error (error_code 14) at the
// calls.getAnonymousToken step in a NEW shape that only carries:
//
// error_code, error_msg, is_enabled_captcha, redirect_uri
//
// and notably NO captcha_sid and NO captcha_img. The previous parser
// rejected this payload with "missing captcha_sid in captcha error data"
// and returned nil, so the auth loop never reached the captcha solver and
// just fell through to the next client_id.
//
// After the fix, ParseVkCaptchaError must return a non-nil *VkCaptchaError
// with RedirectURI + SessionToken populated so both the auto solver and the
// manual (browser) solver can run.
func TestParseVkCaptchaError_NewFormatWithoutCaptchaSid(t *testing.T) {
t.Parallel()
// Shape taken verbatim from the issue #182 log (session_token truncated
// for readability but structurally identical to the real payload).
errData := map[string]interface{}{
"error_code": float64(14),
"error_msg": "Captcha need",
"is_enabled_captcha": true,
"redirect_uri": "https://id.vk.ru/not_robot_captcha?domain=vk.com&session_token=eyJhbGciOiJBMjU2R0NNS1ci.payload.signature&variant=popup&blank=1",
"captcha_stid": "some-stid", // unrelated field, must be ignored
}
got := ParseVkCaptchaError(errData)
if got == nil {
t.Fatal("expected non-nil VkCaptchaError for new captcha format, got nil")
}
if got.ErrorCode != 14 {
t.Fatalf("ErrorCode = %d, want 14", got.ErrorCode)
}
if got.ErrorMsg != "Captcha need" {
t.Fatalf("ErrorMsg = %q, want %q", got.ErrorMsg, "Captcha need")
}
if got.RedirectURI == "" {
t.Fatal("RedirectURI is empty, expected the not_robot_captcha URL")
}
if got.SessionToken == "" {
t.Fatal("SessionToken is empty, expected it to be parsed from redirect_uri query")
}
if got.SessionToken != "eyJhbGciOiJBMjU2R0NNS1ci.payload.signature" {
t.Fatalf("SessionToken = %q, want the session_token query value", got.SessionToken)
}
// captcha_sid / captcha_img are absent in the new format and must default
// to empty strings (NOT cause the parser to bail out).
if got.CaptchaSid != "" {
t.Fatalf("CaptchaSid = %q, want empty (not present in new format)", got.CaptchaSid)
}
if got.CaptchaImg != "" {
t.Fatalf("CaptchaImg = %q, want empty (not present in new format)", got.CaptchaImg)
}
if !got.IsCaptchaError() {
t.Fatal("IsCaptchaError() = false, want true (new flow is solvable via redirect_uri + session_token)")
}
}
// TestParseVkCaptchaError_LegacyFormatStillWorks ensures the legacy captcha
// payload (captcha_sid + captcha_img, no redirect_uri) still parses so the
// manual HTTP captcha path keeps working.
func TestParseVkCaptchaError_LegacyFormatStillWorks(t *testing.T) {
t.Parallel()
errData := map[string]interface{}{
"error_code": float64(14),
"error_msg": "Captcha need",
"captcha_sid": "1234567890",
"captcha_img": "https://api.vk.com/captcha.php?sid=1234567890&s=1",
}
got := ParseVkCaptchaError(errData)
if got == nil {
t.Fatal("expected non-nil VkCaptchaError for legacy captcha format, got nil")
}
if got.CaptchaSid != "1234567890" {
t.Fatalf("CaptchaSid = %q, want 1234567890", got.CaptchaSid)
}
if got.CaptchaImg == "" {
t.Fatal("CaptchaImg is empty, expected the captcha image URL")
}
if got.RedirectURI != "" {
t.Fatalf("RedirectURI = %q, want empty in legacy flow", got.RedirectURI)
}
}
// TestParseVkCaptchaError_NumericCaptchaSid covers the case where VK returns
// captcha_sid as a JSON number (unmarshalled into float64).
func TestParseVkCaptchaError_NumericCaptchaSid(t *testing.T) {
t.Parallel()
errData := map[string]interface{}{
"error_code": float64(14),
"error_msg": "Captcha need",
"captcha_sid": float64(9876543210),
"captcha_img": "https://api.vk.com/captcha.php?sid=9876543210",
}
got := ParseVkCaptchaError(errData)
if got == nil {
t.Fatal("expected non-nil VkCaptchaError, got nil")
}
if got.CaptchaSid != "9876543210" {
t.Fatalf("CaptchaSid = %q, want 9876543210", got.CaptchaSid)
}
}
// TestParseVkCaptchaError_UnsolvableReturnsNil verifies that when none of the
// solvable fields are present, the parser returns nil so the caller surfaces
// the raw VK error instead of pretending it can solve a captcha it cannot.
func TestParseVkCaptchaError_UnsolvableReturnsNil(t *testing.T) {
t.Parallel()
errData := map[string]interface{}{
"error_code": float64(14),
"error_msg": "Captcha need",
// no redirect_uri, no captcha_sid, no captcha_img
}
if got := ParseVkCaptchaError(errData); got != nil {
t.Fatalf("expected nil for unsolvable captcha payload, got %+v", got)
}
}
// TestParseVkCaptchaError_MissingErrorCodeReturnsNil verifies the parser still
// rejects payloads that don't even identify as a VK API error object.
func TestParseVkCaptchaError_MissingErrorCodeReturnsNil(t *testing.T) {
t.Parallel()
errData := map[string]interface{}{
"error_msg": "Captcha need",
"redirect_uri": "https://id.vk.ru/not_robot_captcha?session_token=x",
}
if got := ParseVkCaptchaError(errData); got != nil {
t.Fatalf("expected nil when error_code is missing, got %+v", got)
}
}