Skip to content

Commit

Permalink
Now handle more error cases. Throw malformed response exception for t…
Browse files Browse the repository at this point in the history
…imes when the json we get back is non-compliant with the documentation. Also provide helper methods on the response object to better identify the problem.
  • Loading branch information
james-turner committed Dec 14, 2014
1 parent 2aef3ae commit 55b0c1f
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 11 deletions.
9 changes: 9 additions & 0 deletions src/Google/MalformedResponseException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace Google;

class MalformedResponseException extends ReCaptchaException {
public function __construct($response, $code = 0, $previous = null){
parent::__construct("Malformed response: '$response'", $code, $previous);
}
}
13 changes: 7 additions & 6 deletions src/Google/ReCaptcha.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,16 @@ public function validate($token, $ip = null)
throw new ReCaptchaException($e->getMessage(), $e->getCode(), $e);
}
// verify json decoding.
if (false !== ($json = json_decode($response, true))) {
if (null !== ($json = json_decode($response, true))) {

if ($json['success']) {
if(array_key_exists("success", $json) && $json['success']) {
return new ReCaptchaResponse(true);
}elseif(array_key_exists("error-codes", $json) && is_array($json['error-codes'])) {
return new ReCaptchaResponse(false, $json['error-codes']);
}else{
throw new MalformedResponseException($response);
}
return new ReCaptchaResponse(false, $json['error-codes']);
}
throw new ReCaptchaException("Non-json response '$response'");
}
}

?>
}
23 changes: 23 additions & 0 deletions src/Google/ReCaptchaResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
*/
final class ReCaptchaResponse
{

const MISSING_INPUT_SECRET = "missing-input-secret";
const INVALID_INPUT_SECRET = "invalid-input-secret";
const MISSING_INPUT_RESPONSE = "missing-input-response";
const INVALID_INPUT_RESPONSE = "invalid-input-response";

private $success;
private $errors;

Expand All @@ -33,4 +39,21 @@ public function isSuccess(){
public function isFailure(){
return !$this->isSuccess();
}

public function isMissingInputSecret(){
return array_search(static::MISSING_INPUT_SECRET, $this->errors)!==false;
}

public function isMissingInputResponse(){
return array_search(static::MISSING_INPUT_RESPONSE, $this->errors)!==false;
}

public function isInvalidInputSecret(){
return array_search(static::INVALID_INPUT_SECRET, $this->errors)!==false;
}

public function isInvalidInputResponse(){
return array_search(static::INVALID_INPUT_RESPONSE, $this->errors)!==false;
}

}
73 changes: 68 additions & 5 deletions test/Google/Test/RecaptchaTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Google\HttpClientException;
use Google\ReCaptcha;
use Google\ReCaptchaException;
use Google\ReCaptchaResponse;

final class RecaptchaTest extends \PHPUnit_Framework_TestCase
Expand Down Expand Up @@ -49,8 +50,10 @@ public function testInvalidSecret()
$mockAdapter->expects($this->once())->method('get')->will($this->returnValue($this->invalidSecret()));

$recaptcha = new ReCaptcha("bad secret", $mockAdapter);
$response = $recaptcha->validate("any token");

$this->assertThat($recaptcha->validate("any token"), $this->equalTo(new ReCaptchaResponse(false, ["invalid-input-secret"])));
$this->assertThat($response->isFailure(), $this->isTrue());
$this->assertThat($response->isInvalidInputSecret(), $this->isTrue());
}

public function testMissingSecret()
Expand All @@ -59,8 +62,10 @@ public function testMissingSecret()
$mockAdapter->expects($this->once())->method('get')->will($this->returnValue($this->missingSecret()));

$recaptcha = new ReCaptcha("bad secret", $mockAdapter);
$response = $recaptcha->validate("any token");

$this->assertThat($recaptcha->validate("any token"), $this->equalTo(new ReCaptchaResponse(false, ["missing-input-secret"])));
$this->assertThat($response->isFailure(), $this->isTrue());
$this->assertThat($response->isMissingInputSecret(), $this->isTrue());
}

public function testInvalidToken()
Expand All @@ -69,8 +74,10 @@ public function testInvalidToken()
$mockAdapter->expects($this->once())->method('get')->will($this->returnValue($this->invalidToken()));

$recaptcha = new ReCaptcha("bad secret", $mockAdapter);
$response = $recaptcha->validate("any token");

$this->assertThat($recaptcha->validate("any token"), $this->equalTo(new ReCaptchaResponse(false, ["invalid-input-response"])));
$this->assertThat($response->isFailure(), $this->isTrue());
$this->assertThat($response->isInvalidInputResponse(), $this->isTrue());
}


Expand All @@ -79,9 +86,11 @@ public function testMissingToken()
$mockAdapter = $this->getMock('Google\HttpClientGetAdapter');
$mockAdapter->expects($this->once())->method('get')->will($this->returnValue($this->missingToken()));

$recaptcha = new ReCaptcha("bad secret", $mockAdapter);
$recaptcha = new ReCaptcha("some secret", $mockAdapter);

$this->assertThat($recaptcha->validate("any token"), $this->equalTo(new ReCaptchaResponse(false, ["missing-input-response"])));
$response = $recaptcha->validate("any token");
$this->assertThat($response->isFailure(), $this->isTrue());
$this->assertThat($response->isMissingInputResponse(), $this->isTrue());
}

public function testRealFailure()
Expand All @@ -92,6 +101,60 @@ public function testRealFailure()
$this->assertThat($recaptchaResponse, $this->equalTo(new ReCaptchaResponse(false, ["invalid-input-response","invalid-input-secret"])));
}

public function testInvalidJSON(){

$this->setExpectedException("Google\ReCaptchaException");

$httpAdapter = $this->getMock("Google\HttpClientGetAdapter");
$httpAdapter->expects($this->once())->method('get')->will($this->returnValue("what no json here!"));

$recaptcha = new ReCaptcha("some secret", $httpAdapter);
$recaptcha->validate("any token");

}

public function testPartialJSON(){

$this->setExpectedException("Google\ReCaptchaException");

$httpAdapter = $this->getMock("Google\HttpClientGetAdapter");
$httpAdapter->expects($this->once())->method('get')->will($this->returnValue(json_encode(["success"=>false])));

$recaptcha = new ReCaptcha("good secret", $httpAdapter);
$recaptcha->validate("any token");
}

public function testCustomHttpClientExceptionHandling(){

$httpException = new \Exception();

$httpAdapter = $this->getMock("Google\HttpClientGetAdapter");
$httpAdapter->expects($this->once())->method('get')->will($this->throwException($httpException));

try {

$recaptcha = new ReCaptcha("some secret", $httpAdapter);
$recaptcha->validate("any token");
}catch (ReCaptchaException $e){

}

$this->assertThat($e, $this->isInstanceOf("Google\ReCaptchaException"));
// must contain previously thrown exception
$this->assertThat($e->getPrevious(), $this->equalTo($httpException));
}

public function testNonExpectedJSONResponse(){

$this->setExpectedException("Google\MalformedResponseException");

$httpAdapter = $this->getMock("Google\HttpClientGetAdapter");
$httpAdapter->expects($this->once())->method('get')->will($this->returnValue(json_encode(["some other key" => "some other value"])));

$recaptcha = new ReCaptcha("some secret", $httpAdapter);
$recaptcha->validate("any token");
}


private function validUrl()
{
Expand Down

0 comments on commit 55b0c1f

Please sign in to comment.