*/ namespace Httpful\Test; require(dirname(dirname(dirname(__FILE__))) . '/bootstrap.php'); \Httpful\Bootstrap::init(); use Httpful\Httpful; use Httpful\Request; use Httpful\Mime; use Httpful\Http; use Httpful\Response; class HttpfulTest extends \PHPUnit_Framework_TestCase { const TEST_SERVER = '127.0.0.1:8008'; const TEST_URL = 'http://127.0.0.1:8008'; const TEST_URL_400 = 'http://127.0.0.1:8008/400'; const SAMPLE_JSON_HEADER = "HTTP/1.1 200 OK Content-Type: application/json Connection: keep-alive Transfer-Encoding: chunked\r\n"; const SAMPLE_JSON_RESPONSE = '{"key":"value","object":{"key":"value"},"array":[1,2,3,4]}'; const SAMPLE_CSV_HEADER = "HTTP/1.1 200 OK Content-Type: text/csv Connection: keep-alive Transfer-Encoding: chunked\r\n"; const SAMPLE_CSV_RESPONSE = "Key1,Key2 Value1,Value2 \"40.0\",\"Forty\""; const SAMPLE_XML_RESPONSE = '2a stringTRUE'; const SAMPLE_XML_HEADER = "HTTP/1.1 200 OK Content-Type: application/xml Connection: keep-alive Transfer-Encoding: chunked\r\n"; const SAMPLE_VENDOR_HEADER = "HTTP/1.1 200 OK Content-Type: application/vnd.nategood.message+xml Connection: keep-alive Transfer-Encoding: chunked\r\n"; const SAMPLE_VENDOR_TYPE = "application/vnd.nategood.message+xml"; const SAMPLE_MULTI_HEADER = "HTTP/1.1 200 OK Content-Type: application/json Connection: keep-alive Transfer-Encoding: chunked X-My-Header:Value1 X-My-Header:Value2\r\n"; function testInit() { $r = Request::init(); // Did we get a 'Request' object? $this->assertEquals('Httpful\Request', get_class($r)); } function testMethods() { $valid_methods = ['get', 'post', 'delete', 'put', 'options', 'head']; $url = 'http://example.com/'; foreach ($valid_methods as $method) { $r = call_user_func(['Httpful\Request', $method], $url); $this->assertEquals('Httpful\Request', get_class($r)); $this->assertEquals(strtoupper($method), $r->method); } } function testDefaults() { // Our current defaults are as follows $r = Request::init(); $this->assertEquals(Http::GET, $r->method); $this->assertFalse($r->strict_ssl); } function testShortMime() { // Valid short ones $this->assertEquals(Mime::JSON, Mime::getFullMime('json')); $this->assertEquals(Mime::XML, Mime::getFullMime('xml')); $this->assertEquals(Mime::HTML, Mime::getFullMime('html')); $this->assertEquals(Mime::CSV, Mime::getFullMime('csv')); // Valid long ones $this->assertEquals(Mime::JSON, Mime::getFullMime(Mime::JSON)); $this->assertEquals(Mime::XML, Mime::getFullMime(Mime::XML)); $this->assertEquals(Mime::HTML, Mime::getFullMime(Mime::HTML)); $this->assertEquals(Mime::CSV, Mime::getFullMime(Mime::CSV)); // No false positives $this->assertNotEquals(Mime::XML, Mime::getFullMime(Mime::HTML)); $this->assertNotEquals(Mime::JSON, Mime::getFullMime(Mime::XML)); $this->assertNotEquals(Mime::HTML, Mime::getFullMime(Mime::JSON)); $this->assertNotEquals(Mime::XML, Mime::getFullMime(Mime::CSV)); } function testSettingStrictSsl() { $r = Request::init() ->withStrictSsl(); $this->assertTrue($r->strict_ssl); $r = Request::init() ->withoutStrictSsl(); $this->assertFalse($r->strict_ssl); } function testSendsAndExpectsType() { $r = Request::init() ->sendsAndExpectsType(Mime::JSON); $this->assertEquals(Mime::JSON, $r->expected_type); $this->assertEquals(Mime::JSON, $r->content_type); $r = Request::init() ->sendsAndExpectsType('html'); $this->assertEquals(Mime::HTML, $r->expected_type); $this->assertEquals(Mime::HTML, $r->content_type); $r = Request::init() ->sendsAndExpectsType('form'); $this->assertEquals(Mime::FORM, $r->expected_type); $this->assertEquals(Mime::FORM, $r->content_type); $r = Request::init() ->sendsAndExpectsType('application/x-www-form-urlencoded'); $this->assertEquals(Mime::FORM, $r->expected_type); $this->assertEquals(Mime::FORM, $r->content_type); $r = Request::init() ->sendsAndExpectsType(Mime::CSV); $this->assertEquals(Mime::CSV, $r->expected_type); $this->assertEquals(Mime::CSV, $r->content_type); } function testIni() { // Test setting defaults/templates // Create the template $template = Request::init() ->method(Http::POST) ->withStrictSsl() ->expectsType(Mime::HTML) ->sendsType(Mime::FORM); Request::ini($template); $r = Request::init(); $this->assertTrue($r->strict_ssl); $this->assertEquals(Http::POST, $r->method); $this->assertEquals(Mime::HTML, $r->expected_type); $this->assertEquals(Mime::FORM, $r->content_type); // Test the default accessor as well $this->assertTrue(Request::d('strict_ssl')); $this->assertEquals(Http::POST, Request::d('method')); $this->assertEquals(Mime::HTML, Request::d('expected_type')); $this->assertEquals(Mime::FORM, Request::d('content_type')); Request::resetIni(); } function testAccept() { $r = Request::get('http://example.com/') ->expectsType(Mime::JSON); $this->assertEquals(Mime::JSON, $r->expected_type); $r->_curlPrep(); $this->assertContains('application/json', $r->raw_headers); } function testCustomAccept() { $accept = 'application/api-1.0+json'; $r = Request::get('http://example.com/') ->addHeader('Accept', $accept); $r->_curlPrep(); $this->assertContains($accept, $r->raw_headers); $this->assertEquals($accept, $r->headers['Accept']); } function testUserAgent() { $r = Request::get('http://example.com/') ->withUserAgent('ACME/1.2.3'); $this->assertArrayHasKey('User-Agent', $r->headers); $r->_curlPrep(); $this->assertContains('User-Agent: ACME/1.2.3', $r->raw_headers); $this->assertNotContains('User-Agent: HttpFul/1.0', $r->raw_headers); $r = Request::get('http://example.com/') ->withUserAgent(''); $this->assertArrayHasKey('User-Agent', $r->headers); $r->_curlPrep(); $this->assertContains('User-Agent:', $r->raw_headers); $this->assertNotContains('User-Agent: HttpFul/1.0', $r->raw_headers); } function testAuthSetup() { $username = 'nathan'; $password = 'opensesame'; $r = Request::get('http://example.com/') ->authenticateWith($username, $password); $this->assertEquals($username, $r->username); $this->assertEquals($password, $r->password); $this->assertTrue($r->hasBasicAuth()); } function testDigestAuthSetup() { $username = 'nathan'; $password = 'opensesame'; $r = Request::get('http://example.com/') ->authenticateWithDigest($username, $password); $this->assertEquals($username, $r->username); $this->assertEquals($password, $r->password); $this->assertTrue($r->hasDigestAuth()); } function testJsonResponseParse() { $req = Request::init()->sendsAndExpects(Mime::JSON); $response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req); $this->assertEquals("value", $response->body->key); $this->assertEquals("value", $response->body->object->key); $this->assertInternalType('array', $response->body->array); $this->assertEquals(1, $response->body->array[0]); } function testXMLResponseParse() { $req = Request::init()->sendsAndExpects(Mime::XML); $response = new Response(self::SAMPLE_XML_RESPONSE, self::SAMPLE_XML_HEADER, $req); $sxe = $response->body; $this->assertEquals("object", gettype($sxe)); $this->assertEquals("SimpleXMLElement", get_class($sxe)); $bools = $sxe->xpath('/stdClass/boolProp'); list( , $bool ) = each($bools); $this->assertEquals("TRUE", (string) $bool); $ints = $sxe->xpath('/stdClass/arrayProp/array/k1/myClass/intProp'); list( , $int ) = each($ints); $this->assertEquals("2", (string) $int); $strings = $sxe->xpath('/stdClass/stringProp'); list( , $string ) = each($strings); $this->assertEquals("a string", (string) $string); } function testCsvResponseParse() { $req = Request::init()->sendsAndExpects(Mime::CSV); $response = new Response(self::SAMPLE_CSV_RESPONSE, self::SAMPLE_CSV_HEADER, $req); $this->assertEquals("Key1", $response->body[0][0]); $this->assertEquals("Value1", $response->body[1][0]); $this->assertInternalType('string', $response->body[2][0]); $this->assertEquals("40.0", $response->body[2][0]); } function testParsingContentTypeCharset() { $req = Request::init()->sendsAndExpects(Mime::JSON); // $response = new Response(SAMPLE_JSON_RESPONSE, "", $req); // // Check default content type of iso-8859-1 $response = new Response(self::SAMPLE_JSON_RESPONSE, "HTTP/1.1 200 OK Content-Type: text/plain; charset=utf-8\r\n", $req); $this->assertInstanceOf('Httpful\Response\Headers', $response->headers); $this->assertEquals($response->headers['Content-Type'], 'text/plain; charset=utf-8'); $this->assertEquals($response->content_type, 'text/plain'); $this->assertEquals($response->charset, 'utf-8'); } function testEmptyResponseParse() { $req = Request::init()->sendsAndExpects(Mime::JSON); $response = new Response("", self::SAMPLE_JSON_HEADER, $req); $this->assertEquals(null, $response->body); $reqXml = Request::init()->sendsAndExpects(Mime::XML); $responseXml = new Response("", self::SAMPLE_XML_HEADER, $reqXml); $this->assertEquals(null, $responseXml->body); } function testNoAutoParse() { $req = Request::init()->sendsAndExpects(Mime::JSON)->withoutAutoParsing(); $response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req); $this->assertInternalType('string', $response->body); $req = Request::init()->sendsAndExpects(Mime::JSON)->withAutoParsing(); $response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req); $this->assertInternalType('object', $response->body); } function testParseHeaders() { $req = Request::init()->sendsAndExpects(Mime::JSON); $response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req); $this->assertEquals('application/json', $response->headers['Content-Type']); } function testRawHeaders() { $req = Request::init()->sendsAndExpects(Mime::JSON); $response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req); $this->assertContains('Content-Type: application/json', $response->raw_headers); } function testHasErrors() { $req = Request::init()->sendsAndExpects(Mime::JSON); $response = new Response('', "HTTP/1.1 100 Continue\r\n", $req); $this->assertFalse($response->hasErrors()); $response = new Response('', "HTTP/1.1 200 OK\r\n", $req); $this->assertFalse($response->hasErrors()); $response = new Response('', "HTTP/1.1 300 Multiple Choices\r\n", $req); $this->assertFalse($response->hasErrors()); $response = new Response('', "HTTP/1.1 400 Bad Request\r\n", $req); $this->assertTrue($response->hasErrors()); $response = new Response('', "HTTP/1.1 500 Internal Server Error\r\n", $req); $this->assertTrue($response->hasErrors()); } function test_parseCode() { $req = Request::init()->sendsAndExpects(Mime::JSON); $response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req); $code = $response->_parseCode("HTTP/1.1 406 Not Acceptable\r\n"); $this->assertEquals(406, $code); } function testToString() { $req = Request::init()->sendsAndExpects(Mime::JSON); $response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req); $this->assertEquals(self::SAMPLE_JSON_RESPONSE, (string)$response); } function test_parseHeaders() { $parse_headers = Response\Headers::fromString(self::SAMPLE_JSON_HEADER); $this->assertCount(3, $parse_headers); $this->assertEquals('application/json', $parse_headers['Content-Type']); $this->assertTrue(isset($parse_headers['Connection'])); } function testMultiHeaders() { $req = Request::init(); $response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_MULTI_HEADER, $req); $parse_headers = $response->_parseHeaders(self::SAMPLE_MULTI_HEADER); $this->assertEquals('Value1,Value2', $parse_headers['X-My-Header']); } function testDetectContentType() { $req = Request::init(); $response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req); $this->assertEquals('application/json', $response->headers['Content-Type']); } function testMissingBodyContentType() { $body = 'A string'; $request = Request::post(HttpfulTest::TEST_URL, $body)->_curlPrep(); $this->assertEquals($body, $request->serialized_payload); } function testParentType() { // Parent type $request = Request::init()->sendsAndExpects(Mime::XML); $response = new Response('Nathan', self::SAMPLE_VENDOR_HEADER, $request); $this->assertEquals("application/xml", $response->parent_type); $this->assertEquals(self::SAMPLE_VENDOR_TYPE, $response->content_type); $this->assertTrue($response->is_mime_vendor_specific); // Make sure we still parsed as if it were plain old XML $this->assertEquals("Nathan", $response->body->name->__toString()); } function testMissingContentType() { // Parent type $request = Request::init()->sendsAndExpects(Mime::XML); $response = new Response( 'Nathan', "HTTP/1.1 200 OK Connection: keep-alive Transfer-Encoding: chunked\r\n", $request ); $this->assertEquals("", $response->content_type); } function testCustomMimeRegistering() { // Register new mime type handler for "application/vnd.nategood.message+xml" Httpful::register(self::SAMPLE_VENDOR_TYPE, new DemoMimeHandler()); $this->assertTrue(Httpful::hasParserRegistered(self::SAMPLE_VENDOR_TYPE)); $request = Request::init(); $response = new Response('Nathan', self::SAMPLE_VENDOR_HEADER, $request); $this->assertEquals(self::SAMPLE_VENDOR_TYPE, $response->content_type); $this->assertEquals('custom parse', $response->body); } public function testShorthandMimeDefinition() { $r = Request::init()->expects('json'); $this->assertEquals(Mime::JSON, $r->expected_type); $r = Request::init()->expectsJson(); $this->assertEquals(Mime::JSON, $r->expected_type); } public function testOverrideXmlHandler() { // Lazy test... $prev = \Httpful\Httpful::get(\Httpful\Mime::XML); $this->assertEquals($prev, new \Httpful\Handlers\XmlHandler()); $conf = ['namespace' => 'http://example.com']; \Httpful\Httpful::register(\Httpful\Mime::XML, new \Httpful\Handlers\XmlHandler($conf)); $new = \Httpful\Httpful::get(\Httpful\Mime::XML); $this->assertNotEquals($prev, $new); } } class DemoMimeHandler extends \Httpful\Handlers\MimeHandlerAdapter { public function parse($body) { return 'custom parse'; } }