/tests/tests.php
PHP | 940 lines | 771 code | 106 blank | 63 comment | 3 complexity | 79565ad7bdae96bbe5c7dd11f7002196 MD5 | raw file
1<?php 2/** 3 * Copyright 2011 Facebook, Inc. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); you may 6 * not use this file except in compliance with the License. You may obtain 7 * a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 * License for the specific language governing permissions and limitations 15 * under the License. 16 */ 17 18class PHPSDKTestCase extends PHPUnit_Framework_TestCase { 19 const APP_ID = '117743971608120'; 20 const SECRET = '943716006e74d9b9283d4d5d8ab93204'; 21 22 const MIGRATED_APP_ID = '174236045938435'; 23 const MIGRATED_SECRET = '0073dce2d95c4a5c2922d1827ea0cca6'; 24 25 private static $kExpiredAccessToken = '206492729383450|2.N4RKywNPuHAey7CK56_wmg__.3600.1304560800.1-214707|6Q14AfpYi_XJB26aRQumouzJiGA'; 26 private static $kValidSignedRequest = '1sxR88U4SW9m6QnSxwCEw_CObqsllXhnpP5j2pxD97c.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImV4cGlyZXMiOjEyODEwNTI4MDAsIm9hdXRoX3Rva2VuIjoiMTE3NzQzOTcxNjA4MTIwfDIuVlNUUWpub3hYVVNYd1RzcDB1U2g5d19fLjg2NDAwLjEyODEwNTI4MDAtMTY3Nzg0NjM4NXx4NURORHBtcy1nMUM0dUJHQVYzSVdRX2pYV0kuIiwidXNlcl9pZCI6IjE2Nzc4NDYzODUifQ'; 27 private static $kNonTosedSignedRequest = 'c0Ih6vYvauDwncv0n0pndr0hP0mvZaJPQDPt6Z43O0k.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiJ9'; 28 private static $kSignedRequestWithBogusSignature = '1sxR32U4SW9m6QnSxwCEw_CObqsllXhnpP5j2pxD97c.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImV4cGlyZXMiOjEyODEwNTI4MDAsIm9hdXRoX3Rva2VuIjoiMTE3NzQzOTcxNjA4MTIwfDIuVlNUUWpub3hYVVNYd1RzcDB1U2g5d19fLjg2NDAwLjEyODEwNTI4MDAtMTY3Nzg0NjM4NXx4NURORHBtcy1nMUM0dUJHQVYzSVdRX2pYV0kuIiwidXNlcl9pZCI6IjE2Nzc4NDYzODUifQ'; 29 30 public function testConstructor() { 31 $facebook = new TransientFacebook(array( 32 'appId' => self::APP_ID, 33 'secret' => self::SECRET, 34 )); 35 $this->assertEquals($facebook->getAppId(), self::APP_ID, 36 'Expect the App ID to be set.'); 37 $this->assertEquals($facebook->getAppSecret(), self::SECRET, 38 'Expect the API secret to be set.'); 39 } 40 41 public function testConstructorWithFileUpload() { 42 $facebook = new TransientFacebook(array( 43 'appId' => self::APP_ID, 44 'secret' => self::SECRET, 45 'fileUpload' => true, 46 )); 47 $this->assertEquals($facebook->getAppId(), self::APP_ID, 48 'Expect the App ID to be set.'); 49 $this->assertEquals($facebook->getAppSecret(), self::SECRET, 50 'Expect the API secret to be set.'); 51 $this->assertTrue($facebook->getFileUploadSupport(), 52 'Expect file upload support to be on.'); 53 // alias (depricated) for getFileUploadSupport -- test until removed 54 $this->assertTrue($facebook->useFileUploadSupport(), 55 'Expect file upload support to be on.'); 56 } 57 58 public function testSetAppId() { 59 $facebook = new TransientFacebook(array( 60 'appId' => self::APP_ID, 61 'secret' => self::SECRET, 62 )); 63 $facebook->setAppId('dummy'); 64 $this->assertEquals($facebook->getAppId(), 'dummy', 65 'Expect the App ID to be dummy.'); 66 } 67 68 public function testSetAPISecret() { 69 $facebook = new TransientFacebook(array( 70 'appId' => self::APP_ID, 71 'secret' => self::SECRET, 72 )); 73 $facebook->setApiSecret('dummy'); 74 $this->assertEquals($facebook->getApiSecret(), 'dummy', 75 'Expect the API secret to be dummy.'); 76 } 77 78 public function testSetAPPSecret() { 79 $facebook = new TransientFacebook(array( 80 'appId' => self::APP_ID, 81 'secret' => self::SECRET, 82 )); 83 $facebook->setAppSecret('dummy'); 84 $this->assertEquals($facebook->getAppSecret(), 'dummy', 85 'Expect the API secret to be dummy.'); 86 } 87 88 public function testSetAccessToken() { 89 $facebook = new TransientFacebook(array( 90 'appId' => self::APP_ID, 91 'secret' => self::SECRET, 92 )); 93 94 $facebook->setAccessToken('saltydog'); 95 $this->assertEquals($facebook->getAccessToken(), 'saltydog', 96 'Expect installed access token to remain \'saltydog\''); 97 } 98 99 public function testSetFileUploadSupport() { 100 $facebook = new TransientFacebook(array( 101 'appId' => self::APP_ID, 102 'secret' => self::SECRET, 103 )); 104 $this->assertFalse($facebook->getFileUploadSupport(), 105 'Expect file upload support to be off.'); 106 // alias for getFileUploadSupport (depricated), testing until removed 107 $this->assertFalse($facebook->useFileUploadSupport(), 108 'Expect file upload support to be off.'); 109 $facebook->setFileUploadSupport(true); 110 $this->assertTrue($facebook->getFileUploadSupport(), 111 'Expect file upload support to be on.'); 112 // alias for getFileUploadSupport (depricated), testing until removed 113 $this->assertTrue($facebook->useFileUploadSupport(), 114 'Expect file upload support to be on.'); 115 } 116 117 public function testGetCurrentURL() { 118 $facebook = new FBGetCurrentURLFacebook(array( 119 'appId' => self::APP_ID, 120 'secret' => self::SECRET, 121 )); 122 123 // fake the HPHP $_SERVER globals 124 $_SERVER['HTTP_HOST'] = 'www.test.com'; 125 $_SERVER['REQUEST_URI'] = '/unit-tests.php?one=one&two=two&three=three'; 126 $current_url = $facebook->publicGetCurrentUrl(); 127 $this->assertEquals( 128 'http://www.test.com/unit-tests.php?one=one&two=two&three=three', 129 $current_url, 130 'getCurrentUrl function is changing the current URL'); 131 132 // ensure structure of valueless GET params is retained (sometimes 133 // an = sign was present, and sometimes it was not) 134 // first test when equal signs are present 135 $_SERVER['HTTP_HOST'] = 'www.test.com'; 136 $_SERVER['REQUEST_URI'] = '/unit-tests.php?one=&two=&three='; 137 $current_url = $facebook->publicGetCurrentUrl(); 138 $this->assertEquals( 139 'http://www.test.com/unit-tests.php?one=&two=&three=', 140 $current_url, 141 'getCurrentUrl function is changing the current URL'); 142 143 // now confirm that 144 $_SERVER['HTTP_HOST'] = 'www.test.com'; 145 $_SERVER['REQUEST_URI'] = '/unit-tests.php?one&two&three'; 146 $current_url = $facebook->publicGetCurrentUrl(); 147 $this->assertEquals( 148 'http://www.test.com/unit-tests.php?one&two&three', 149 $current_url, 150 'getCurrentUrl function is changing the current URL'); 151 } 152 153 public function testGetLoginURL() { 154 $facebook = new Facebook(array( 155 'appId' => self::APP_ID, 156 'secret' => self::SECRET, 157 )); 158 159 // fake the HPHP $_SERVER globals 160 $_SERVER['HTTP_HOST'] = 'www.test.com'; 161 $_SERVER['REQUEST_URI'] = '/unit-tests.php'; 162 $login_url = parse_url($facebook->getLoginUrl()); 163 $this->assertEquals($login_url['scheme'], 'https'); 164 $this->assertEquals($login_url['host'], 'www.facebook.com'); 165 $this->assertEquals($login_url['path'], '/dialog/oauth'); 166 $expected_login_params = 167 array('client_id' => self::APP_ID, 168 'redirect_uri' => 'http://www.test.com/unit-tests.php'); 169 170 $query_map = array(); 171 parse_str($login_url['query'], $query_map); 172 $this->assertIsSubset($expected_login_params, $query_map); 173 // we don't know what the state is, but we know it's an md5 and should 174 // be 32 characters long. 175 $this->assertEquals(strlen($query_map['state']), $num_characters = 32); 176 } 177 178 public function testGetLoginURLWithExtraParams() { 179 $facebook = new Facebook(array( 180 'appId' => self::APP_ID, 181 'secret' => self::SECRET, 182 )); 183 184 // fake the HPHP $_SERVER globals 185 $_SERVER['HTTP_HOST'] = 'www.test.com'; 186 $_SERVER['REQUEST_URI'] = '/unit-tests.php'; 187 $extra_params = array('scope' => 'email, sms', 188 'nonsense' => 'nonsense'); 189 $login_url = parse_url($facebook->getLoginUrl($extra_params)); 190 $this->assertEquals($login_url['scheme'], 'https'); 191 $this->assertEquals($login_url['host'], 'www.facebook.com'); 192 $this->assertEquals($login_url['path'], '/dialog/oauth'); 193 $expected_login_params = 194 array_merge( 195 array('client_id' => self::APP_ID, 196 'redirect_uri' => 'http://www.test.com/unit-tests.php'), 197 $extra_params); 198 $query_map = array(); 199 parse_str($login_url['query'], $query_map); 200 $this->assertIsSubset($expected_login_params, $query_map); 201 // we don't know what the state is, but we know it's an md5 and should 202 // be 32 characters long. 203 $this->assertEquals(strlen($query_map['state']), $num_characters = 32); 204 } 205 206 public function testGetLoginURLWithScopeParamsAsArray() { 207 $facebook = new Facebook(array( 208 'appId' => self::APP_ID, 209 'secret' => self::SECRET, 210 )); 211 212 // fake the HPHP $_SERVER globals 213 $_SERVER['HTTP_HOST'] = 'www.test.com'; 214 $_SERVER['REQUEST_URI'] = '/unit-tests.php'; 215 $scope_params_as_array = array('email','sms','read_stream'); 216 $extra_params = array('scope' => $scope_params_as_array, 217 'nonsense' => 'nonsense'); 218 $login_url = parse_url($facebook->getLoginUrl($extra_params)); 219 $this->assertEquals($login_url['scheme'], 'https'); 220 $this->assertEquals($login_url['host'], 'www.facebook.com'); 221 $this->assertEquals($login_url['path'], '/dialog/oauth'); 222 // expect api to flatten array params to comma separated list 223 // should do the same here before asserting to make sure API is behaving 224 // correctly; 225 $extra_params['scope'] = implode(',', $scope_params_as_array); 226 $expected_login_params = 227 array_merge( 228 array('client_id' => self::APP_ID, 229 'redirect_uri' => 'http://www.test.com/unit-tests.php'), 230 $extra_params); 231 $query_map = array(); 232 parse_str($login_url['query'], $query_map); 233 $this->assertIsSubset($expected_login_params, $query_map); 234 // we don't know what the state is, but we know it's an md5 and should 235 // be 32 characters long. 236 $this->assertEquals(strlen($query_map['state']), $num_characters = 32); 237 } 238 239 public function testGetCodeWithValidCSRFState() { 240 $facebook = new FBCode(array( 241 'appId' => self::APP_ID, 242 'secret' => self::SECRET, 243 )); 244 245 $facebook->setCSRFStateToken(); 246 $code = $_REQUEST['code'] = $this->generateMD5HashOfRandomValue(); 247 $_REQUEST['state'] = $facebook->getCSRFStateToken(); 248 $this->assertEquals($code, 249 $facebook->publicGetCode(), 250 'Expect code to be pulled from $_REQUEST[\'code\']'); 251 } 252 253 public function testGetCodeWithInvalidCSRFState() { 254 $facebook = new FBCode(array( 255 'appId' => self::APP_ID, 256 'secret' => self::SECRET, 257 )); 258 259 $facebook->setCSRFStateToken(); 260 $code = $_REQUEST['code'] = $this->generateMD5HashOfRandomValue(); 261 $_REQUEST['state'] = $facebook->getCSRFStateToken().'forgery!!!'; 262 $this->assertFalse($facebook->publicGetCode(), 263 'Expect getCode to fail, CSRF state should not match.'); 264 } 265 266 public function testGetCodeWithMissingCSRFState() { 267 $facebook = new FBCode(array( 268 'appId' => self::APP_ID, 269 'secret' => self::SECRET, 270 )); 271 272 $code = $_REQUEST['code'] = $this->generateMD5HashOfRandomValue(); 273 // intentionally don't set CSRF token at all 274 $this->assertFalse($facebook->publicGetCode(), 275 'Expect getCode to fail, CSRF state not sent back.'); 276 277 } 278 279 public function testGetUserFromSignedRequest() { 280 $facebook = new TransientFacebook(array( 281 'appId' => self::APP_ID, 282 'secret' => self::SECRET, 283 )); 284 285 $_REQUEST['signed_request'] = self::$kValidSignedRequest; 286 $this->assertEquals('1677846385', $facebook->getUser(), 287 'Failed to get user ID from a valid signed request.'); 288 } 289 290 public function testGetSignedRequestFromCookie() { 291 $facebook = new FBGetSignedRequestCookieFacebook(array( 292 'appId' => self::APP_ID, 293 'secret' => self::SECRET, 294 )); 295 296 $_COOKIE[$facebook->publicGetSignedRequestCookieName()] = 297 self::$kValidSignedRequest; 298 $this->assertNotNull($facebook->publicGetSignedRequest()); 299 $this->assertEquals('1677846385', $facebook->getUser(), 300 'Failed to get user ID from a valid signed request.'); 301 } 302 303 public function testGetSignedRequestWithIncorrectSignature() { 304 $facebook = new FBGetSignedRequestCookieFacebook(array( 305 'appId' => self::APP_ID, 306 'secret' => self::SECRET, 307 )); 308 309 $_COOKIE[$facebook->publicGetSignedRequestCookieName()] = 310 self::$kSignedRequestWithBogusSignature; 311 $this->assertNull($facebook->publicGetSignedRequest()); 312 } 313 314 public function testNonUserAccessToken() { 315 $facebook = new FBAccessToken(array( 316 'appId' => self::APP_ID, 317 'secret' => self::SECRET, 318 )); 319 320 // no cookies, and no request params, so no user or code, 321 // so no user access token (even with cookie support) 322 $this->assertEquals($facebook->publicGetApplicationAccessToken(), 323 $facebook->getAccessToken(), 324 'Access token should be that for logged out users.'); 325 } 326 327 public function testAPIForLoggedOutUsers() { 328 $facebook = new TransientFacebook(array( 329 'appId' => self::APP_ID, 330 'secret' => self::SECRET, 331 )); 332 $response = $facebook->api(array( 333 'method' => 'fql.query', 334 'query' => 'SELECT name FROM user WHERE uid=4', 335 )); 336 $this->assertEquals(count($response), 1, 337 'Expect one row back.'); 338 $this->assertEquals($response[0]['name'], 'Mark Zuckerberg', 339 'Expect the name back.'); 340 } 341 342 public function testAPIWithBogusAccessToken() { 343 $facebook = new TransientFacebook(array( 344 'appId' => self::APP_ID, 345 'secret' => self::SECRET, 346 )); 347 348 $facebook->setAccessToken('this-is-not-really-an-access-token'); 349 // if we don't set an access token and there's no way to 350 // get one, then the FQL query below works beautifully, handing 351 // over Zuck's public data. But if you specify a bogus access 352 // token as I have right here, then the FQL query should fail. 353 // We could return just Zuck's public data, but that wouldn't 354 // advertise the issue that the access token is at worst broken 355 // and at best expired. 356 try { 357 $response = $facebook->api(array( 358 'method' => 'fql.query', 359 'query' => 'SELECT name FROM profile WHERE id=4', 360 )); 361 $this->fail('Should not get here.'); 362 } catch(FacebookApiException $e) { 363 $result = $e->getResult(); 364 $this->assertTrue(is_array($result), 'expect a result object'); 365 $this->assertEquals('190', $result['error_code'], 'expect code'); 366 } 367 } 368 369 public function testAPIGraphPublicData() { 370 $facebook = new TransientFacebook(array( 371 'appId' => self::APP_ID, 372 'secret' => self::SECRET, 373 )); 374 375 $response = $facebook->api('/jerry'); 376 $this->assertEquals( 377 $response['id'], '214707', 'should get expected id.'); 378 } 379 380 public function testGraphAPIWithBogusAccessToken() { 381 $facebook = new TransientFacebook(array( 382 'appId' => self::APP_ID, 383 'secret' => self::SECRET, 384 )); 385 386 $facebook->setAccessToken('this-is-not-really-an-access-token'); 387 try { 388 $response = $facebook->api('/me'); 389 $this->fail('Should not get here.'); 390 } catch(FacebookApiException $e) { 391 // means the server got the access token and didn't like it 392 $msg = 'OAuthException: Invalid OAuth access token.'; 393 $this->assertEquals($msg, (string) $e, 394 'Expect the invalid OAuth token message.'); 395 } 396 } 397 398 public function testGraphAPIWithExpiredAccessToken() { 399 $facebook = new TransientFacebook(array( 400 'appId' => self::APP_ID, 401 'secret' => self::SECRET, 402 )); 403 404 $facebook->setAccessToken(self::$kExpiredAccessToken); 405 try { 406 $response = $facebook->api('/me'); 407 $this->fail('Should not get here.'); 408 } catch(FacebookApiException $e) { 409 // means the server got the access token and didn't like it 410 $error_msg_start = 'OAuthException: Error validating access token:'; 411 $this->assertTrue(strpos((string) $e, $error_msg_start) === 0, 412 'Expect the token validation error message.'); 413 } 414 } 415 416 public function testGraphAPIMethod() { 417 $facebook = new TransientFacebook(array( 418 'appId' => self::APP_ID, 419 'secret' => self::SECRET, 420 )); 421 422 try { 423 // naitik being bold about deleting his entire record.... 424 // let's hope this never actually passes. 425 $response = $facebook->api('/naitik', $method = 'DELETE'); 426 $this->fail('Should not get here.'); 427 } catch(FacebookApiException $e) { 428 // ProfileDelete means the server understood the DELETE 429 $msg = 430 'OAuthException: (#200) User cannot access this application'; 431 $this->assertEquals($msg, (string) $e, 432 'Expect the invalid session message.'); 433 } 434 } 435 436 public function testGraphAPIOAuthSpecError() { 437 $facebook = new TransientFacebook(array( 438 'appId' => self::MIGRATED_APP_ID, 439 'secret' => self::MIGRATED_SECRET, 440 )); 441 442 try { 443 $response = $facebook->api('/me', array( 444 'client_id' => self::MIGRATED_APP_ID)); 445 446 $this->fail('Should not get here.'); 447 } catch(FacebookApiException $e) { 448 // means the server got the access token 449 $msg = 'invalid_request: An active access token must be used '. 450 'to query information about the current user.'; 451 $this->assertEquals($msg, (string) $e, 452 'Expect the invalid session message.'); 453 } 454 } 455 456 public function testGraphAPIMethodOAuthSpecError() { 457 $facebook = new TransientFacebook(array( 458 'appId' => self::MIGRATED_APP_ID, 459 'secret' => self::MIGRATED_SECRET, 460 )); 461 462 try { 463 $response = $facebook->api('/daaku.shah', 'DELETE', array( 464 'client_id' => self::MIGRATED_APP_ID)); 465 $this->fail('Should not get here.'); 466 } catch(FacebookApiException $e) { 467 $this->assertEquals(strpos($e, 'invalid_request'), 0); 468 } 469 } 470 471 public function testCurlFailure() { 472 $facebook = new TransientFacebook(array( 473 'appId' => self::APP_ID, 474 'secret' => self::SECRET, 475 )); 476 477 if (!defined('CURLOPT_TIMEOUT_MS')) { 478 // can't test it if we don't have millisecond timeouts 479 return; 480 } 481 482 $exception = null; 483 try { 484 // we dont expect facebook will ever return in 1ms 485 Facebook::$CURL_OPTS[CURLOPT_TIMEOUT_MS] = 50; 486 $facebook->api('/naitik'); 487 } catch(FacebookApiException $e) { 488 $exception = $e; 489 } 490 unset(Facebook::$CURL_OPTS[CURLOPT_TIMEOUT_MS]); 491 if (!$exception) { 492 $this->fail('no exception was thrown on timeout.'); 493 } 494 495 $this->assertEquals( 496 CURLE_OPERATION_TIMEOUTED, $exception->getCode(), 'expect timeout'); 497 $this->assertEquals('CurlException', $exception->getType(), 'expect type'); 498 } 499 500 public function testGraphAPIWithOnlyParams() { 501 $facebook = new TransientFacebook(array( 502 'appId' => self::APP_ID, 503 'secret' => self::SECRET, 504 )); 505 506 $response = $facebook->api('/jerry'); 507 $this->assertTrue(isset($response['id']), 508 'User ID should be public.'); 509 $this->assertTrue(isset($response['name']), 510 'User\'s name should be public.'); 511 $this->assertTrue(isset($response['first_name']), 512 'User\'s first name should be public.'); 513 $this->assertTrue(isset($response['last_name']), 514 'User\'s last name should be public.'); 515 $this->assertFalse(isset($response['work']), 516 'User\'s work history should only be available with '. 517 'a valid access token.'); 518 $this->assertFalse(isset($response['education']), 519 'User\'s education history should only be '. 520 'available with a valid access token.'); 521 $this->assertFalse(isset($response['verified']), 522 'User\'s verification status should only be '. 523 'available with a valid access token.'); 524 } 525 526 public function testLoginURLDefaults() { 527 $_SERVER['HTTP_HOST'] = 'fbrell.com'; 528 $_SERVER['REQUEST_URI'] = '/examples'; 529 $facebook = new TransientFacebook(array( 530 'appId' => self::APP_ID, 531 'secret' => self::SECRET, 532 )); 533 $encodedUrl = rawurlencode('http://fbrell.com/examples'); 534 $this->assertNotNull(strpos($facebook->getLoginUrl(), $encodedUrl), 535 'Expect the current url to exist.'); 536 } 537 538 public function testLoginURLDefaultsDropStateQueryParam() { 539 $_SERVER['HTTP_HOST'] = 'fbrell.com'; 540 $_SERVER['REQUEST_URI'] = '/examples?state=xx42xx'; 541 $facebook = new TransientFacebook(array( 542 'appId' => self::APP_ID, 543 'secret' => self::SECRET, 544 )); 545 $expectEncodedUrl = rawurlencode('http://fbrell.com/examples'); 546 $this->assertTrue(strpos($facebook->getLoginUrl(), $expectEncodedUrl) > -1, 547 'Expect the current url to exist.'); 548 $this->assertFalse(strpos($facebook->getLoginUrl(), 'xx42xx'), 549 'Expect the session param to be dropped.'); 550 } 551 552 public function testLoginURLDefaultsDropCodeQueryParam() { 553 $_SERVER['HTTP_HOST'] = 'fbrell.com'; 554 $_SERVER['REQUEST_URI'] = '/examples?code=xx42xx'; 555 $facebook = new TransientFacebook(array( 556 'appId' => self::APP_ID, 557 'secret' => self::SECRET, 558 )); 559 $expectEncodedUrl = rawurlencode('http://fbrell.com/examples'); 560 $this->assertTrue(strpos($facebook->getLoginUrl(), $expectEncodedUrl) > -1, 561 'Expect the current url to exist.'); 562 $this->assertFalse(strpos($facebook->getLoginUrl(), 'xx42xx'), 563 'Expect the session param to be dropped.'); 564 } 565 566 public function testLoginURLDefaultsDropSignedRequestParamButNotOthers() { 567 $_SERVER['HTTP_HOST'] = 'fbrell.com'; 568 $_SERVER['REQUEST_URI'] = 569 '/examples?signed_request=xx42xx&do_not_drop=xx43xx'; 570 $facebook = new TransientFacebook(array( 571 'appId' => self::APP_ID, 572 'secret' => self::SECRET, 573 )); 574 $expectEncodedUrl = rawurlencode('http://fbrell.com/examples'); 575 $this->assertFalse(strpos($facebook->getLoginUrl(), 'xx42xx'), 576 'Expect the session param to be dropped.'); 577 $this->assertTrue(strpos($facebook->getLoginUrl(), 'xx43xx') > -1, 578 'Expect the do_not_drop param to exist.'); 579 } 580 581 public function testLoginURLCustomNext() { 582 $_SERVER['HTTP_HOST'] = 'fbrell.com'; 583 $_SERVER['REQUEST_URI'] = '/examples'; 584 $facebook = new TransientFacebook(array( 585 'appId' => self::APP_ID, 586 'secret' => self::SECRET, 587 )); 588 $next = 'http://fbrell.com/custom'; 589 $loginUrl = $facebook->getLoginUrl(array( 590 'redirect_uri' => $next, 591 'cancel_url' => $next 592 )); 593 $currentEncodedUrl = rawurlencode('http://fbrell.com/examples'); 594 $expectedEncodedUrl = rawurlencode($next); 595 $this->assertNotNull(strpos($loginUrl, $expectedEncodedUrl), 596 'Expect the custom url to exist.'); 597 $this->assertFalse(strpos($loginUrl, $currentEncodedUrl), 598 'Expect the current url to not exist.'); 599 } 600 601 public function testLogoutURLDefaults() { 602 $_SERVER['HTTP_HOST'] = 'fbrell.com'; 603 $_SERVER['REQUEST_URI'] = '/examples'; 604 $facebook = new TransientFacebook(array( 605 'appId' => self::APP_ID, 606 'secret' => self::SECRET, 607 )); 608 $encodedUrl = rawurlencode('http://fbrell.com/examples'); 609 $this->assertNotNull(strpos($facebook->getLogoutUrl(), $encodedUrl), 610 'Expect the current url to exist.'); 611 } 612 613 public function testLoginStatusURLDefaults() { 614 $_SERVER['HTTP_HOST'] = 'fbrell.com'; 615 $_SERVER['REQUEST_URI'] = '/examples'; 616 $facebook = new TransientFacebook(array( 617 'appId' => self::APP_ID, 618 'secret' => self::SECRET, 619 )); 620 $encodedUrl = rawurlencode('http://fbrell.com/examples'); 621 $this->assertNotNull(strpos($facebook->getLoginStatusUrl(), $encodedUrl), 622 'Expect the current url to exist.'); 623 } 624 625 public function testLoginStatusURLCustom() { 626 $_SERVER['HTTP_HOST'] = 'fbrell.com'; 627 $_SERVER['REQUEST_URI'] = '/examples'; 628 $facebook = new TransientFacebook(array( 629 'appId' => self::APP_ID, 630 'secret' => self::SECRET, 631 )); 632 $encodedUrl1 = rawurlencode('http://fbrell.com/examples'); 633 $okUrl = 'http://fbrell.com/here1'; 634 $encodedUrl2 = rawurlencode($okUrl); 635 $loginStatusUrl = $facebook->getLoginStatusUrl(array( 636 'ok_session' => $okUrl, 637 )); 638 $this->assertNotNull(strpos($loginStatusUrl, $encodedUrl1), 639 'Expect the current url to exist.'); 640 $this->assertNotNull(strpos($loginStatusUrl, $encodedUrl2), 641 'Expect the custom url to exist.'); 642 } 643 644 public function testNonDefaultPort() { 645 $_SERVER['HTTP_HOST'] = 'fbrell.com:8080'; 646 $_SERVER['REQUEST_URI'] = '/examples'; 647 $facebook = new TransientFacebook(array( 648 'appId' => self::APP_ID, 649 'secret' => self::SECRET, 650 )); 651 $encodedUrl = rawurlencode('http://fbrell.com:8080/examples'); 652 $this->assertNotNull(strpos($facebook->getLoginUrl(), $encodedUrl), 653 'Expect the current url to exist.'); 654 } 655 656 public function testSecureCurrentUrl() { 657 $_SERVER['HTTP_HOST'] = 'fbrell.com'; 658 $_SERVER['REQUEST_URI'] = '/examples'; 659 $_SERVER['HTTPS'] = 'on'; 660 $facebook = new TransientFacebook(array( 661 'appId' => self::APP_ID, 662 'secret' => self::SECRET, 663 )); 664 $encodedUrl = rawurlencode('https://fbrell.com/examples'); 665 $this->assertNotNull(strpos($facebook->getLoginUrl(), $encodedUrl), 666 'Expect the current url to exist.'); 667 } 668 669 public function testSecureCurrentUrlWithNonDefaultPort() { 670 $_SERVER['HTTP_HOST'] = 'fbrell.com:8080'; 671 $_SERVER['REQUEST_URI'] = '/examples'; 672 $_SERVER['HTTPS'] = 'on'; 673 $facebook = new TransientFacebook(array( 674 'appId' => self::APP_ID, 675 'secret' => self::SECRET, 676 )); 677 $encodedUrl = rawurlencode('https://fbrell.com:8080/examples'); 678 $this->assertNotNull(strpos($facebook->getLoginUrl(), $encodedUrl), 679 'Expect the current url to exist.'); 680 } 681 682 public function testAppSecretCall() { 683 $facebook = new TransientFacebook(array( 684 'appId' => self::APP_ID, 685 'secret' => self::SECRET, 686 )); 687 688 try { 689 $response = $facebook->api('/' . self::APP_ID . '/insights'); 690 $this->fail('Desktop applications need a user token for insights.'); 691 } catch (FacebookApiException $e) { 692 // this test is failing as the graph call is returning the wrong 693 // error message 694 $this->assertTrue(strpos($e->getMessage(), 695 'Requires session when calling from a desktop app') !== false, 696 'Incorrect exception type thrown when trying to gain ' . 697 'insights for desktop app without a user access token.'); 698 } catch (Exception $e) { 699 $this->fail('Incorrect exception type thrown when trying to gain ' . 700 'insights for desktop app without a user access token.'); 701 } 702 } 703 704 public function testBase64UrlEncode() { 705 $input = 'Facebook rocks'; 706 $output = 'RmFjZWJvb2sgcm9ja3M'; 707 708 $this->assertEquals(FBPublic::publicBase64UrlDecode($output), $input); 709 } 710 711 public function testSignedToken() { 712 $facebook = new FBPublic(array( 713 'appId' => self::APP_ID, 714 'secret' => self::SECRET 715 )); 716 $payload = $facebook->publicParseSignedRequest(self::$kValidSignedRequest); 717 $this->assertNotNull($payload, 'Expected token to parse'); 718 $this->assertEquals($facebook->getSignedRequest(), null); 719 $_REQUEST['signed_request'] = self::$kValidSignedRequest; 720 $this->assertEquals($facebook->getSignedRequest(), $payload); 721 } 722 723 public function testNonTossedSignedtoken() { 724 $facebook = new FBPublic(array( 725 'appId' => self::APP_ID, 726 'secret' => self::SECRET 727 )); 728 $payload = $facebook->publicParseSignedRequest( 729 self::$kNonTosedSignedRequest); 730 $this->assertNotNull($payload, 'Expected token to parse'); 731 $this->assertNull($facebook->getSignedRequest()); 732 $_REQUEST['signed_request'] = self::$kNonTosedSignedRequest; 733 $this->assertEquals($facebook->getSignedRequest(), 734 array('algorithm' => 'HMAC-SHA256')); 735 } 736 737 public function testBundledCACert() { 738 $facebook = new TransientFacebook(array( 739 'appId' => self::APP_ID, 740 'secret' => self::SECRET 741 )); 742 743 // use the bundled cert from the start 744 Facebook::$CURL_OPTS[CURLOPT_CAINFO] = 745 dirname(__FILE__) . '/../src/fb_ca_chain_bundle.crt'; 746 $response = $facebook->api('/naitik'); 747 748 unset(Facebook::$CURL_OPTS[CURLOPT_CAINFO]); 749 $this->assertEquals( 750 $response['id'], '5526183', 'should get expected id.'); 751 } 752 753 public function testVideoUpload() { 754 $facebook = new FBRecordURL(array( 755 'appId' => self::APP_ID, 756 'secret' => self::SECRET 757 )); 758 759 $facebook->api(array('method' => 'video.upload')); 760 $this->assertContains('//api-video.', $facebook->getRequestedURL(), 761 'video.upload should go against api-video'); 762 } 763 764 public function testGetUserAndAccessTokenFromSession() { 765 $facebook = new PersistentFBPublic(array( 766 'appId' => self::APP_ID, 767 'secret' => self::SECRET 768 )); 769 770 $facebook->publicSetPersistentData('access_token', 771 self::$kExpiredAccessToken); 772 $facebook->publicSetPersistentData('user_id', 12345); 773 $this->assertEquals(self::$kExpiredAccessToken, 774 $facebook->getAccessToken(), 775 'Get access token from persistent store.'); 776 $this->assertEquals('12345', 777 $facebook->getUser(), 778 'Get user id from persistent store.'); 779 } 780 781 public function testGetUserAndAccessTokenFromSignedRequestNotSession() { 782 $facebook = new PersistentFBPublic(array( 783 'appId' => self::APP_ID, 784 'secret' => self::SECRET 785 )); 786 787 $_REQUEST['signed_request'] = self::$kValidSignedRequest; 788 $facebook->publicSetPersistentData('user_id', 41572); 789 $facebook->publicSetPersistentData('access_token', 790 self::$kExpiredAccessToken); 791 $this->assertNotEquals('41572', $facebook->getUser(), 792 'Got user from session instead of signed request.'); 793 $this->assertEquals('1677846385', $facebook->getUser(), 794 'Failed to get correct user ID from signed request.'); 795 $this->assertNotEquals( 796 self::$kExpiredAccessToken, 797 $facebook->getAccessToken(), 798 'Got access token from session instead of signed request.'); 799 $this->assertNotEmpty( 800 $facebook->getAccessToken(), 801 'Failed to extract an access token from the signed request.'); 802 } 803 804 public function testGetUserWithoutCodeOrSignedRequestOrSession() { 805 $facebook = new PersistentFBPublic(array( 806 'appId' => self::APP_ID, 807 'secret' => self::SECRET 808 )); 809 810 // deliberately leave $_REQUEST and _$SESSION empty 811 $this->assertEmpty($_REQUEST, 812 'GET, POST, and COOKIE params exist even though '. 813 'they should. Test cannot succeed unless all of '. 814 '$_REQUEST is empty.'); 815 $this->assertEmpty($_SESSION, 816 'Session is carrying state and should not be.'); 817 $this->assertEmpty($facebook->getUser(), 818 'Got a user id, even without a signed request, '. 819 'access token, or session variable.'); 820 $this->assertEmpty($_SESSION, 821 'Session superglobal incorrectly populated by getUser.'); 822 } 823 824 protected function generateMD5HashOfRandomValue() { 825 return md5(uniqid(mt_rand(), true)); 826 } 827 828 protected function setUp() { 829 parent::setUp(); 830 } 831 832 protected function tearDown() { 833 $this->clearSuperGlobals(); 834 parent::tearDown(); 835 } 836 837 protected function clearSuperGlobals() { 838 unset($_SERVER['HTTPS']); 839 unset($_SERVER['HTTP_HOST']); 840 unset($_SERVER['REQUEST_URI']); 841 $_SESSION = array(); 842 $_COOKIE = array(); 843 $_REQUEST = array(); 844 $_POST = array(); 845 $_GET = array(); 846 if (session_id()) { 847 session_destroy(); 848 } 849 } 850 851 /** 852 * Checks that the correct args are a subset of the returned obj 853 * @param array $correct The correct array values 854 * @param array $actual The values in practice 855 * @param string $message to be shown on failure 856 */ 857 protected function assertIsSubset($correct, $actual, $msg='') { 858 foreach ($correct as $key => $value) { 859 $actual_value = $actual[$key]; 860 $newMsg = (strlen($msg) ? ($msg.' ') : '').'Key: '.$key; 861 $this->assertEquals($value, $actual_value, $newMsg); 862 } 863 } 864} 865 866class TransientFacebook extends BaseFacebook { 867 protected function setPersistentData($key, $value) {} 868 protected function getPersistentData($key, $default = false) { 869 return $default; 870 } 871 protected function clearPersistentData($key) {} 872 protected function clearAllPersistentData() {} 873} 874 875class FBRecordURL extends TransientFacebook { 876 private $url; 877 878 protected function _oauthRequest($url, $params) { 879 $this->url = $url; 880 } 881 882 public function getRequestedURL() { 883 return $this->url; 884 } 885} 886 887class FBPublic extends TransientFacebook { 888 public static function publicBase64UrlDecode($input) { 889 return self::base64UrlDecode($input); 890 } 891 public function publicParseSignedRequest($input) { 892 return $this->parseSignedRequest($input); 893 } 894} 895 896class PersistentFBPublic extends Facebook { 897 public function publicParseSignedRequest($input) { 898 return $this->parseSignedRequest($input); 899 } 900 901 public function publicSetPersistentData($key, $value) { 902 $this->setPersistentData($key, $value); 903 } 904} 905 906class FBCode extends Facebook { 907 public function publicGetCode() { 908 return $this->getCode(); 909 } 910 911 public function setCSRFStateToken() { 912 $this->establishCSRFTokenState(); 913 } 914 915 public function getCSRFStateToken() { 916 return $this->getPersistentData('state'); 917 } 918} 919 920class FBAccessToken extends TransientFacebook { 921 public function publicGetApplicationAccessToken() { 922 return $this->getApplicationAccessToken(); 923 } 924} 925 926class FBGetCurrentURLFacebook extends TransientFacebook { 927 public function publicGetCurrentUrl() { 928 return $this->getCurrentUrl(); 929 } 930} 931 932class FBGetSignedRequestCookieFacebook extends TransientFacebook { 933 public function publicGetSignedRequest() { 934 return $this->getSignedRequest(); 935 } 936 937 public function publicGetSignedRequestCookieName() { 938 return $this->getSignedRequestCookieName(); 939 } 940}