PageRenderTime 94ms CodeModel.GetById 62ms app.highlight 25ms RepoModel.GetById 1ms app.codeStats 1ms

/tests/tests.php

http://github.com/facebook/php-sdk
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}