<?php
/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
 * use, copy, modify, and distribute this software in source code or binary
 * form for use in connection with the web services and APIs provided by
 * Facebook.
 *
 * As with any software that integrates with the Facebook platform, your use
 * of this software is subject to the Facebook Developer Principles and
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
 * shall be included in all copies or substantial portions of the software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

namespace FacebookAdsTest\Enum;

use FacebookAds\Enum\AbstractEnum;

class DummyEnum extends AbstractEnum {

  const NAME_1 = 'VALUE_1';
  const NAME_2 = 'VALUE_2';
}


<?php
/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
 * use, copy, modify, and distribute this software in source code or binary
 * form for use in connection with the web services and APIs provided by
 * Facebook.
 *
 * As with any software that integrates with the Facebook platform, your use
 * of this software is subject to the Facebook Developer Principles and
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
 * shall be included in all copies or substantial portions of the software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

namespace FacebookAdsTest\Enum;

use FacebookAds\Enum\EmptyEnum;
use FacebookAdsTest\AbstractUnitTestCase;

class EnumTest extends AbstractUnitTestCase {

  public function testSingleton() {
    $this->assertTrue(DummyEnum::getInstance() === DummyEnum::getInstance());
  }

  public function testAccess() {
    $this->assertEquals(DummyEnum::className(), get_class(new DummyEnum()));

    $enum = DummyEnum::getInstance();
    $copy = $enum->getArrayCopy();
    $names = $enum->getNames();
    $values = $enum->getValues();
    $values_map = $enum->getValuesMap();

    $this->assertEquals(2, count($copy));
    $this->assertArrayHasKey('NAME_1', $copy);
    $this->assertEquals(DummyEnum::NAME_1, $copy['NAME_1']);

    $this->assertEquals(2, count($names));
    $this->assertTrue(in_array('NAME_1', $names));

    $this->assertEquals(2, count($values));
    $this->assertTrue(in_array(DummyEnum::NAME_1, $values));

    $this->assertEquals(2, count($values_map));
    $this->assertArrayHasKey(DummyEnum::NAME_1, $values_map);

    $this->assertEquals(DummyEnum::NAME_1, $enum->getValueForName('NAME_1'));
  }

  public function testLazyLoading() {
    $enum = DummyEnum::getInstance();

    $this->assertTrue($enum->getArrayCopy() === $enum->getArrayCopy());
    $this->assertTrue($enum->getNames() === $enum->getNames());
    $this->assertTrue($enum->getValues() === $enum->getValues());
    $this->assertTrue($enum->getValuesMap() === $enum->getValuesMap());
  }

  public function testValidation() {
    $enum = DummyEnum::getInstance();

    $this->assertTrue($enum->isValid('NAME_1'));
    $this->assertFalse($enum->isValid('NAME_ND'));
    $this->assertTrue($enum->isValidValue(DummyEnum::NAME_1));
    $this->assertFalse($enum->isValidValue('VALUE_ND'));
  }

  public function testAssurance() {
    $enum = DummyEnum::getInstance();

    $this->assertEquals(DummyEnum::NAME_1, $enum->assureValueForName('NAME_1'));
    $this->assertWillThrow(function() use ($enum) {
      $enum->assureValueForName('NAME_ND');
    }, 'InvalidArgumentException');

    $enum->assureIsValid('NAME_1');
    $this->assertWillThrow(function() use ($enum) {
      $enum->assureIsValid('NAME_ND');
    }, 'InvalidArgumentException');

    $enum->assureIsValidValue(DummyEnum::NAME_1);
    $this->assertWillThrow(function() use ($enum) {
      $enum->assureIsValidValue('VALUE_ND');
    }, 'InvalidArgumentException');
  }

  public function testEmptyEnum() {
    $enum = EmptyEnum::getInstance();

    $this->assertEquals(array(), $enum->getArrayCopy());
    $this->assertEquals(array(), $enum->getNames());
    $this->assertEquals(array(), $enum->getValues());
    $this->assertEquals(array(), $enum->getValuesMap());
  }
}


<?php
/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
 * use, copy, modify, and distribute this software in source code or binary
 * form for use in connection with the web services and APIs provided by
 * Facebook.
 *
 * As with any software that integrates with the Facebook platform, your use
 * of this software is subject to the Facebook Developer Principles and
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
 * shall be included in all copies or substantial portions of the software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

namespace FacebookAdsTest\Exception;

use FacebookAds\Http\Exception\RequestException;

class PHPUnitRequestExceptionWrapper
  extends \PHPUnit_Framework_ExceptionWrapper {

  /**
   * @var RequestException
   */
  protected $wrappedException;

  /**
   * @param RequestException $e
   */
  public function __construct(RequestException $e) {
    $this->wrappedException = $e;
    parent::__construct($e);
  }

  /**
   * @return RequestException
   */
  public function getWrappedException() {
    return $this->wrappedException;
  }

  protected function requestToString(RequestException $e) {
    if ($e->getResponse() === null) {
      return '-';
    }

    $request = $e->getResponse()->getRequest();

    return sprintf(
      "%s %s\n%s",
      $request->getMethod(),
      $request->getUrl(),
      "    ".str_replace("\n", "\n    ", json_encode(
        $request->getFileParams()->getArrayCopy()
          + $request->getBodyParams()->getArrayCopy(),
        JSON_PRETTY_PRINT)));
  }

  /**
   * @return string
   */
  protected function blameFieldSpecsToString() {
    return implode(", ", array_map(function($value) {
      return $value[0];
    }, $this->getWrappedException()->getErrorBlameFieldSpecs()));
  }

  /**
   * @return string
   */
  public function __toString() {
    $string = \PHPUnit_Framework_TestFailure::exceptionToString($this);

    $e = $this->getWrappedException();

    $string .= "\n  Request: ".$this->requestToString($e)."\n"
      ."  HTTP status Code: ".$e->getHttpStatusCode()."\n"
      ."  Is transient: ".($e->isTransient() ? 'true' : 'false')."\n"
      ."  Code: ".$e->getCode()."\n"
      ."  Error Subcode: ".$e->getErrorSubcode()."\n"
      ."  Error User Title: ".$e->getErrorUserTitle()."\n"
      ."  Error User Message: ".$e->getErrorUserMessage()."\n";

    if ($e->getErrorBlameFieldSpecs()) {
      $string .= "  Error Blame Fields: ".$this->blameFieldSpecsToString()."\n";
    }

    if ($trace = \PHPUnit_Util_Filter::getFilteredStacktrace($this)) {
      $string .= "\n" . $trace;
    }

    if ($this->previous) {
      $string .= "\nCaused by\n" . $this->previous;
    }

    return $string;
  }
}


<?php
/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
 * use, copy, modify, and distribute this software in source code or binary
 * form for use in connection with the web services and APIs provided by
 * Facebook.
 *
 * As with any software that integrates with the Facebook platform, your use
 * of this software is subject to the Facebook Developer Principles and
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
 * shall be included in all copies or substantial portions of the software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

namespace FacebookAdsTest\Http;

use FacebookAds\ApiConfig;
use FacebookAds\Http\Adapter\AdapterInterface;
use FacebookAds\Http\Adapter\CurlAdapter;
use FacebookAds\Http\Client;
use FacebookAds\Http\Exception\EmptyResponseException;
use FacebookAds\Http\Exception\RequestException;
use FacebookAds\Http\Headers;
use FacebookAds\Http\Request;
use FacebookAds\Http\RequestInterface;
use FacebookAds\Http\Response;
use FacebookAds\Http\ResponseInterface;
use FacebookAdsTest\AbstractUnitTestCase;
use \PHPUnit_Framework_MockObject_Builder_InvocationMocker as Mock;

class ClientTest extends AbstractUnitTestCase {

  /**
   * @param mixed $response_content
   * @return Mock|AdapterInterface
   */
  protected function createMockChain($response_content) {
    $adapter = $this->createAdapterMock();
    $response = $this->createResponseMock();
    $response->method('getContent')->willReturn($response_content);
    $adapter->method('sendRequest')->willReturn($response);

    return $adapter;
  }

  public function testPrototypes() {
    $client = new Client();

    $request_proto = $this->createRequestMock();
    $request_proto->method('createClone')->willReturnSelf();
    $client->setRequestPrototype($request_proto);
    $this->assertTrue($request_proto === $client->getRequestPrototype());
    $this->assertTrue($client->createRequest() instanceof RequestInterface);

    $response_proto = $this->createResponseMock();
    $client->setResponsePrototype($response_proto);
    $this->assertTrue($response_proto === $client->getResponsePrototype());
    $this->assertTrue($client->createResponse() instanceof ResponseInterface);

    // Default initialization
    $client = new Client();
    $this->assertTrue($client->getRequestPrototype() instanceof Request);
    $this->assertTrue($client->getResponsePrototype() instanceof Response);
  }

  public function testHeaders() {
    $headers = new Headers();
    $this->assertTrue($headers instanceof \IteratorAggregate);
    $this->assertTrue($headers instanceof \Traversable);
    $this->assertTrue($headers instanceof \ArrayAccess);
    $this->assertTrue($headers instanceof \Serializable);
    $this->assertTrue($headers instanceof \Countable);

    $client = new Client();
    $client->setDefaultRequestHeaders($headers);
    $this->assertTrue($headers === $client->getDefaultRequestHeaders());

    // Default initialization
    $client = new Client();
    $headers = $client->getDefaultRequestHeaders();
    $this->assertTrue($headers instanceof Headers);
    $this->assertTrue($headers === $client->getDefaultRequestHeaders());
    $this->assertArrayHasKey('User-Agent', $headers);
    $this->assertEquals('fbbizsdk-php-v'.ApiConfig::SDKVersion, $headers['User-Agent']);
  }

  public function testDomain() {
    $client = new Client();
    $this->assertEquals(
      Client::DEFAULT_GRAPH_BASE_DOMAIN, $client->getDefaultGraphBaseDomain());

    $domain = 'not.facebook.com';
    $client->setDefaultGraphBaseDomain($domain);
    $this->assertEquals($domain, $client->getDefaultGraphBaseDomain());
  }

  public function testAdapter() {
    $client = new Client();

    $adapter = $this->createAdapterMock();
    $client->setAdapter($adapter);
    $this->assertTrue($client->getAdapter() instanceof AdapterInterface);
    $this->assertTrue($adapter === $client->getAdapter());

    // Default initialization
    $client = new Client();
    $this->assertTrue($client->getAdapter() instanceof CurlAdapter);
  }

  public function testCaBundlePath() {
    $client = new Client();
    $path = '/tmp/dont_care_if_exists';
    $client->setCaBundlePath($path);
    $this->assertEquals($path, $client->getCaBundlePath());

    // Default initialization
    $client = new Client();
    $this->assertFalse(!$client->getCaBundlePath());
    $this->assertFileExists($client->getCaBundlePath());
  }

  public function testSendRequest() {
    $client = new Client();

    // Empty response > json_decode('') = null
    $client->setAdapter($this->createMockChain(null));

    $exception_catched = false;
    try {
      $client->sendRequest($this->createRequestMock());
    } catch (EmptyResponseException $e) {
      $exception_catched = true;
    }
    $this->assertTrue($exception_catched);

    // Error payload
    $client->setAdapter($this->createMockChain(array(
      'error' => array(
        'message' => 'Stub Server Error Message',
        'type' => 'FacebookApiException',
        'code' => 1,
      ),
    )));

    $exception_catched = false;
    try {
      $client->sendRequest($this->createRequestMock());
    } catch (EmptyResponseException $e) {
      $this->fail("Catched wrong RequestException");
    } catch (RequestException $e) {
      $exception_catched = true;
    }
    $this->assertTrue($exception_catched);

    // Success
    $client->setAdapter($this->createMockChain(array(
      'id' => 4,
    )));

    $response = $client->sendRequest($this->createRequestMock());
    $this->assertTrue($response instanceof ResponseInterface);
    $content = $response->getContent();
    $this->assertArrayHasKey('id', $content);
    $this->assertEquals($content['id'], 4);
  }
}


<?php
/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
 * use, copy, modify, and distribute this software in source code or binary
 * form for use in connection with the web services and APIs provided by
 * Facebook.
 *
 * As with any software that integrates with the Facebook platform, your use
 * of this software is subject to the Facebook Developer Principles and
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
 * shall be included in all copies or substantial portions of the software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

namespace FacebookAdsTest\Http;

use FacebookAds\Http\FileParameter;
use FacebookAdsTest\AbstractUnitTestCase;

class FileParameterTest extends AbstractUnitTestCase {

  public function testGetters() {
    $path = 'PATH';
    $name = 'NAME';
    $mime_type = 'MIME_TYPE';
    $param = (new FileParameter($path))
      ->setName($name)
      ->setMimeType($mime_type);

    $this->assertEquals($param->getPath(), $path);
    $this->assertEquals($param->getName(), $name);
    $this->assertEquals($param->getMimeType(), $mime_type);
  }
}


<?php
/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
 * use, copy, modify, and distribute this software in source code or binary
 * form for use in connection with the web services and APIs provided by
 * Facebook.
 *
 * As with any software that integrates with the Facebook platform, your use
 * of this software is subject to the Facebook Developer Principles and
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
 * shall be included in all copies or substantial portions of the software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

namespace FacebookAdsTest\Http;

use FacebookAds\Http\Parameters;
use FacebookAdsTest\AbstractUnitTestCase;

class ParametersTest extends AbstractUnitTestCase {

  public function testInterfaces() {
    $parameters = new Parameters();
    $this->assertTrue($parameters instanceof \IteratorAggregate);
    $this->assertTrue($parameters instanceof \Traversable);
    $this->assertTrue($parameters instanceof \ArrayAccess);
    $this->assertTrue($parameters instanceof \Serializable);
    $this->assertTrue($parameters instanceof \Countable);
  }

  public function testEnhance() {
    $parameters = new Parameters();
    $copy = $parameters->getArrayCopy();

    $data = array(
      'foo' => 0,
      'bar' => 1,
    );

    $copy = array_merge($copy, $data);
    $parameters->enhance($data);
    $this->assertEquals($copy, $parameters->getArrayCopy());

    $data = array(
      'bar' => -1,
      'baz' => 2,
    );

    $copy = array_merge($copy, $data);
    $parameters->enhance($data);
    $this->assertEquals($copy, $parameters->getArrayCopy());
  }

  public function testExport() {
    $parameters = new Parameters();
    $parameters['scalar'] = 1;
    $parameters['non_scalar'] = array(0, '1', true);
    $data = $parameters->export();
    $this->assertArrayHasKey('scalar', $data);
    $this->assertEquals(1, $data['scalar']);
    $this->assertArrayHasKey('non_scalar', $data);
    $this->assertEquals('[0,"1",true]', $data['non_scalar']);
  }
}


<?php
/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
 * use, copy, modify, and distribute this software in source code or binary
 * form for use in connection with the web services and APIs provided by
 * Facebook.
 *
 * As with any software that integrates with the Facebook platform, your use
 * of this software is subject to the Facebook Developer Principles and
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
 * shall be included in all copies or substantial portions of the software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

namespace FacebookAdsTest\Http;

use FacebookAds\Api;
use FacebookAds\Http\Client;
use FacebookAds\Http\Headers;
use FacebookAds\Http\Parameters;
use FacebookAds\Http\Request;
use FacebookAds\Http\ResponseInterface;
use FacebookAds\Http\Util;
use FacebookAdsTest\AbstractUnitTestCase;

class RequestTest extends AbstractUnitTestCase {

  /**
   * @return Request
   */
  protected function createRequest() {
    return new Request($this->createClientMock());
  }

  public function testGetters() {
    $client = $this->createClientMock();
    $request = new Request($client);

    $this->assertTrue($request->getClient() instanceof Client);
    $this->assertTrue($client === $request->getClient());
  }

  public function testProtocol() {
    $request = $this->createRequest();
    $protocol = Request::PROTOCOL_HTTP;
    $request->setProtocol($protocol);
    $this->assertEquals($protocol, $request->getProtocol());
  }

  public function testDomain() {
    $request = $this->createRequest();
    $domain = 'not.facebook.com';
    $request->setDomain($domain);
    $this->assertEquals($domain, $request->getDomain());

    // Default initialization
    $client = $this->createClientMock();
    $client->method('getDefaultGraphBaseDomain')
      ->willReturn('not.facebook.com');
    $request = new Request($client);
    $domain = $request->getDomain();
    $this->assertRegExp('/.+\.not\.facebook\.com/', $domain);
    $this->assertEquals($domain, $request->getDomain());

    // Partially set
    $client = $this->createClientMock();
    $client->method('getDefaultGraphBaseDomain')
      ->willReturn('probably.not.facebook.com');
    $request = new Request($client);
    $request->setLastLevelDomain('most');
    $domain = $request->getDomain();
    $this->assertEquals('most.probably.not.facebook.com', $domain);
    $this->assertEquals($domain, $request->getDomain());
  }

  public function testHeaders() {
    $client = $this->createClientMock();
    $request = new Request($client);
    $headers = $this->createHeadersMock();
    $client->setDefaultRequestHeaders($headers);
    $request->setHeaders($headers);
    $headers_mirror = $request->getHeaders();
    $this->assertTrue($headers_mirror instanceof Headers);
    $this->assertTrue($headers_mirror === $request->getHeaders());

    // Default initialization
    $headers = new Headers();
    $client = $this->createClientMock();
    $client->method('getDefaultRequestHeaders')->willReturn($headers);
    $request = new Request($client);
    $headers_mirror = $request->getHeaders();
    $this->assertTrue($headers_mirror instanceof Headers);
    $this->assertFalse($headers === $request->getHeaders());
    $this->assertTrue($headers_mirror === $request->getHeaders());
  }

  public function testMethod() {
    $request = $this->createRequest();
    $method = Request::METHOD_GET;
    $request->setMethod($method);
    $this->assertEquals($method, $request->getMethod());
  }

  public function testPath() {
    $request = $this->createRequest();
    $path = '/node/edge';
    $request->setPath($path);
    $this->assertEquals($path, $request->getPath());
  }

  public function testGraphVersion() {
    $request = $this->createRequest();
    $graph_version = Api::VERSION;
    $request->setGraphVersion($graph_version);
    $this->assertEquals($graph_version, $request->getGraphVersion());
  }

  public function testQueryParams() {
    $request = $this->createRequest();
    $parameters = $this->createParametersMock();
    $request->setQueryParams($parameters);
    $this->assertTrue($request->getQueryParams() instanceof Parameters);
    $this->assertTrue($parameters === $request->getQueryParams());

    // Default initialization
    $request = $this->createRequest();
    $parameters = $request->getQueryParams();
    $this->assertTrue($parameters instanceof Parameters);
    $this->assertTrue($parameters === $request->getQueryParams());
  }

  public function testBodyParams() {
    $request = $this->createRequest();
    $parameters = $this->createParametersMock();
    $request->setBodyParams($parameters);
    $this->assertTrue($request->getBodyParams() instanceof Parameters);
    $this->assertTrue($parameters === $request->getBodyParams());

    // Default initialization
    $request = $this->createRequest();
    $parameters = $request->getBodyParams();
    $this->assertTrue($parameters instanceof Parameters);
    $this->assertTrue($parameters === $request->getBodyParams());
  }

  public function testFileParams() {
    $request = $this->createRequest();
    $parameters = $this->createParametersMock();
    $request->setFileParams($parameters);
    $this->assertTrue($request->getFileParams() instanceof Parameters);
    $this->assertTrue($parameters === $request->getFileParams());

    // Default initialization
    $request = $this->createRequest();
    $parameters = $request->getFileParams();
    $this->assertTrue($parameters instanceof Parameters);
    $this->assertTrue($parameters === $request->getFileParams());
  }

  public function testClone() {
    $request = $this->createRequest();
    $query_params = $this->createParametersMock();
    $request->setFileParams($query_params);
    $body_params = $this->createParametersMock();
    $request->setBodyParams($body_params);
    $file_params = $this->createParametersMock();
    $request->setFileParams($file_params);

    $clone = $request->createClone();
    $this->assertFalse($query_params === $clone->getQueryParams());
    $this->assertFalse($body_params === $clone->getBodyParams());
    $this->assertFalse($file_params === $clone->getFileParams());
  }

  public function testUrlCustomArgSeparator(){
      $separator = ini_get('arg_separator.output');
      ini_set('arg_separator.output', '&');
      $this->testUrl();
      ini_set('arg_separator.output', $separator);
  }

  /**
   * @depends testProtocol
   * @depends testDomain
   * @depends testGraphVersion
   * @depends testPath
   * @depends testQueryParams
   */
  public function testUrl() {
    $client = $this->createClientMock();
    $client->method('getDefaultGraphBaseDomain')
      ->willReturn(Client::DEFAULT_GRAPH_BASE_DOMAIN);
    $request = new Request($client);
    $request->setGraphVersion('2.2');
    $request->setPath('/node/edge');
    $url = $request->getUrl();
    $this->assertEquals(
      parse_url($url, PHP_URL_SCHEME).'://', $request->getProtocol());
    $this->assertEquals(parse_url($url, PHP_URL_HOST), $request->getDomain());
    $this->assertEquals(
      parse_url($url, PHP_URL_PATH),
      sprintf("/v%s%s", $request->getGraphVersion(), $request->getPath()));
    $this->assertNull(parse_url($url, PHP_URL_QUERY));

    // With query
    $params = array(
      'sdk' => 'PHP',
      'names' => 'abc,def',
    );
    $request->setQueryParams(new Parameters($params));
    $url = $request->getUrl();
    $parsed_query = Util::parseUrlQuery(parse_url($url, PHP_URL_QUERY));
    $this->assertEquals(
      $parsed_query, $request->getQueryParams()->getArrayCopy());
  }

  public function testExecute() {
    $response = $this->createResponseMock();
    $client = $this->createClientMock();
    $client->method('sendRequest')->willReturn($response);
    $request = new Request($client);
    $response_mirror = $request->execute();
    $this->assertTrue($response_mirror instanceof ResponseInterface);
    $this->assertTrue($response_mirror === $response);
  }
}


<?php
/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
 * use, copy, modify, and distribute this software in source code or binary
 * form for use in connection with the web services and APIs provided by
 * Facebook.
 *
 * As with any software that integrates with the Facebook platform, your use
 * of this software is subject to the Facebook Developer Principles and
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
 * shall be included in all copies or substantial portions of the software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

namespace FacebookAdsTest\Http;

use FacebookAds\Http\Headers;
use FacebookAds\Http\RequestInterface;
use FacebookAds\Http\Response;
use FacebookAdsTest\AbstractUnitTestCase;

class ResponseTest extends AbstractUnitTestCase {

  /**
   * @return Response
   */
  protected function createResponse() {
    return new Response();
  }

  public function testRequest() {
    $response = $this->createResponse();
    $request = $this->createRequestMock();
    $response->setRequest($request);
    $request_mirror = $response->getRequest();
    $this->assertTrue($request_mirror instanceof RequestInterface);
    $this->assertTrue($request_mirror === $request);
  }

  public function testStatusCode() {
    $response = $this->createResponse();
    $response->setStatusCode(200);
    $this->assertEquals(200, $response->getStatusCode());
  }

  public function testHeaders() {
    $response = $this->createResponse();
    $headers = $this->createHeadersMock();
    $response->setHeaders($headers);
    $headers_mirror = $response->getHeaders();
    $this->assertTrue($headers_mirror instanceof Headers);
    $this->assertTrue($headers_mirror === $response->getHeaders());

    // Default initialization
    $response = $this->createResponse();
    $headers_mirror = $response->getHeaders();
    $this->assertTrue($headers instanceof Headers);
    $this->assertTrue($headers_mirror === $response->getHeaders());
  }

  public function testBodyContent() {
    $response = $this->createResponse();
    $body = '{"data":[]}';
    $response->setBody($body);
    $this->assertEquals($body, $response->getBody());
    $this->assertEquals(array('data' => array()), $response->getContent());
  }
}


<?php
/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
 * use, copy, modify, and distribute this software in source code or binary
 * form for use in connection with the web services and APIs provided by
 * Facebook.
 *
 * As with any software that integrates with the Facebook platform, your use
 * of this software is subject to the Facebook Developer Principles and
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
 * shall be included in all copies or substantial portions of the software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

namespace FacebookAdsTest\Http\Adapter;

use FacebookAds\Http\Adapter\AdapterInterface;
use FacebookAds\Http\Client;
use FacebookAdsTest\AbstractUnitTestCase;

abstract class AbstractAdapterTest extends AbstractUnitTestCase {

  /**
   * @param Client $client
   * @return AdapterInterface
   */
  abstract protected function createAdapter($client = null);

  public function testGetters() {
    $client = new Client();
    $adapter = $this->createAdapter($client);
    $this->assertTrue($client === $adapter->getClient());
    $this->assertEquals(
      $client->getCaBundlePath(), $adapter->getCaBundlePath());
  }

  public function testOpts() {
    $adapter = $this->createAdapter();
    $opts = new \ArrayObject();
    $adapter->setOpts($opts);
    $this->assertTrue($opts === $adapter->getOpts());

    // Default initialization
    $adapter = $this->createAdapter();
    $this->assertTrue($adapter->getOpts() instanceof \ArrayObject);
  }

  abstract public function testRequest();
}


<?php
/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
 * use, copy, modify, and distribute this software in source code or binary
 * form for use in connection with the web services and APIs provided by
 * Facebook.
 *
 * As with any software that integrates with the Facebook platform, your use
 * of this software is subject to the Facebook Developer Principles and
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
 * shall be included in all copies or substantial portions of the software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

namespace FacebookAdsTest\Http\Adapter;

use FacebookAds\Exception\Exception;
use FacebookAds\Http\Adapter\Curl\CurlInterface;
use FacebookAds\Http\Adapter\CurlAdapter;
use FacebookAds\Http\Client;
use FacebookAds\Http\RequestInterface;
use FacebookAds\Http\ResponseInterface;
use \PHPUnit_Framework_MockObject_Builder_InvocationMocker as Mock;

class CurlAdapterTest extends AbstractAdapterTest {

  /**
   * @param Client $client
   * @return CurlAdapter
   */
  protected function createAdapter($client = null) {
    return new CurlAdapter($client ?: new Client(), $this->createCurlMock());
  }

  public function testGetters() {
    parent::testGetters();
    $this->assertTrue(
      $this->createAdapter()->getCurl() instanceof CurlInterface);
  }

  public function testRequest() {
    $adapter = $this->createAdapter();

    /** @var Mock|CurlInterface $curl */
    $curl = $adapter->getCurl();
    $curl->method('preparePostFileField')->willReturn('file');
    $curl->method('exec')->willReturn("HTTP/1.1 200 OK\nX-header: value");

    $headers = $this->createHeadersMock();
    $this->makeMockIterable($headers, array(
      'X-custom-header' => 'header value',
    ));

    $query = $this->createParametersMock(array(
      'param' => 'value',
    ));

    // HTTP_GET
    $body = $this->createParametersMock();
    $this->makeMockIterable($body);

    $files = $this->createParametersMock();
    $this->makeMockIterable($files);

    $request = $this->createRequestMock();
    $request->method('getMethod')->willReturn(RequestInterface::METHOD_GET);
    $request->method('getHeaders')->willReturn($headers);
    $request->method('getQueryParams')->willReturn($query);
    $request->method('getBodyParams')->willReturn($body);
    $request->method('getFileParams')->willReturn($files);
    $response = $adapter->sendRequest($request);
    $this->assertTrue($response instanceof ResponseInterface);

    // HTTP_POST with files
    $body = $this->createParametersMock();
    $this->makeMockIterable($body, array(
      'file' => './filepath',
    ));

    $files = $this->createParametersMock();
    $this->makeMockIterable($files, array(
      'field' => 'value',
    ));

    $request = $this->createRequestMock();
    $request->method('getMethod')->willReturn(RequestInterface::METHOD_POST);
    $request->method('getHeaders')->willReturn($headers);
    $request->method('getQueryParams')->willReturn($query);
    $request->method('getBodyParams')->willReturn($body);
    $request->method('getFileParams')->willReturn($files);

    $response = $adapter->sendRequest($request);
    $this->assertTrue($response instanceof ResponseInterface);

    // HTTP_DELETE
    $body = $this->createParametersMock();
    $this->makeMockIterable($body);

    $files = $this->createParametersMock();
    $this->makeMockIterable($files);

    $request = $this->createRequestMock();
    $request->method('getMethod')->willReturn(RequestInterface::METHOD_DELETE);
    $request->method('getHeaders')->willReturn($headers);
    $request->method('getQueryParams')->willReturn($query);
    $request->method('getBodyParams')->willReturn($body);
    $request->method('getFileParams')->willReturn($files);

    $response = $adapter->sendRequest($request);
    $this->assertTrue($response instanceof ResponseInterface);

    // Force Curl Error
    /** @var CurlInterface|Mock $curl */
    $curl = $adapter->getCurl();
    $curl->method('errno')->willReturn(CURLE_SSL_CONNECT_ERROR);
    $curl->method('error')->willReturn('Mocked Curl Error');
    $body = $this->createParametersMock();
    $this->makeMockIterable($body);

    $files = $this->createParametersMock();
    $this->makeMockIterable($files);

    $request = $this->createRequestMock();
    $request->method('getMethod')->willReturn(RequestInterface::METHOD_DELETE);
    $request->method('getHeaders')->willReturn($headers);
    $request->method('getQueryParams')->willReturn($query);
    $request->method('getBodyParams')->willReturn($body);
    $request->method('getFileParams')->willReturn($files);

    $e = null;
    try {
      $adapter->sendRequest($request);
    } catch (Exception $e) {}
    $this->assertTrue($e !== null);
  }
}


<?php
/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
 * use, copy, modify, and distribute this software in source code or binary
 * form for use in connection with the web services and APIs provided by
 * Facebook.
 *
 * As with any software that integrates with the Facebook platform, your use
 * of this software is subject to the Facebook Developer Principles and
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
 * shall be included in all copies or substantial portions of the software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

namespace FacebookAdsTest\Http\Exception;

use FacebookAds\Http\Exception\RequestException;
use FacebookAds\Http\Response;
use FacebookAdsTest\AbstractUnitTestCase;

class RequestExceptionTest extends AbstractUnitTestCase {

  public function testGetters() {
    $data = array(
      'error' => array(
        'message' => 'Stub Server Error Message',
        'type' => 'FacebookApiException',
        'code' => 1,
        'error_data' => array(
          'blame_field_specs' => array(
            array('mandatory_field'),
            array('field_with_malformed_value'),
          ),
        ),
        'error_subcode' => 1,
        'is_transient' => false,
        'error_user_title' => 'Just a test',
        'error_user_msg' => 'Let\'s avoid alarmism',
      ),
    );
    $status_code = 400;
    $response = new Response();
    $response->setBody(json_encode($data));
    $response->setStatusCode($status_code);
    $e = new RequestException($response);
    $this->assertEquals($status_code, $e->getHttpStatusCode());
    $this->assertEquals(
      $data['error']['error_subcode'], $e->getErrorSubcode());
    $this->assertEquals(
      $data['error']['error_user_title'], $e->getErrorUserTitle());
    $this->assertEquals(
      $data['error']['error_user_msg'], $e->getErrorUserMessage());
    $this->assertEquals(
      $data['error']['error_data']['blame_field_specs'],
      $e->getErrorBlameFieldSpecs());
  }

  public function testGetFacebookTraceIdReturnsValueFromResponseErrorData() {
    $data = array(
      'error' => array(
        'fbtrace_id' => 'abc123',
      ),
    );

    $response = new Response();
    $response->setBody(json_encode($data));
    $e = new RequestException($response);

    $this->assertSame('abc123', $e->getFacebookTraceId());
  }

  public function testGetErrorBlameFieldSpecsReturnsNullWithNullErrorData() {
    $data = array(
      'error' => array(
          'error_data' => null,
      ),
    );

    $response = new Response();
    $response->setBody(json_encode($data));
    $e = new RequestException($response);

    $this->assertNull($e->getErrorBlameFieldSpecs());
  }

  /**
   * @return array
   */
  public function createConcreteProvider() {
    return array(
      array(array('type' => 'OAuthException'), 'AuthorizationException'),
      array(array('code' => 1), 'ServerException'),
      array(array('code' => 4), 'ThrottleException'),
      array(array('code' => 506), 'ClientException'),
      array(array('code' => 10), 'PermissionException'),
      array(array(), 'RequestException'),
    );
  }

  /**
   * @param array $data
   * @param string $expected_class
   * @dataProvider createConcreteProvider
   */
  public function testCreateConcrete($data, $expected_class) {
    $response = new Response();
    $response->setBody(json_encode(array('error' => $data)));
    $response->setStatusCode(400);
    $e = RequestException::create($response);
    $fqn = '\FacebookAds\Http\Exception\\'.$expected_class;
    $this->assertTrue(is_a($e, $fqn));
  }
}


<?php
/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
 * use, copy, modify, and distribute this software in source code or binary
 * form for use in connection with the web services and APIs provided by
 * Facebook.
 *
 * As with any software that integrates with the Facebook platform, your use
 * of this software is subject to the Facebook Developer Principles and
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
 * shall be included in all copies or substantial portions of the software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

namespace FacebookAdsTest\Logger;

use FacebookAds\Logger\LoggerInterface;
use FacebookAdsTest\AbstractUnitTestCase;

abstract class AbstractLoggerTest extends AbstractUnitTestCase {

  /**
   * @var string
   */
  const VALUE_LOG_LEVEL = '<LEVEL>';

  /**
   * @var string
   */
  const VALUE_LOG_MESSAGE = '<MESSAGE>';

  /**
   * @return LoggerInterface
   */
  abstract protected function createLogger();
}


<?php
/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
 * use, copy, modify, and distribute this software in source code or binary
 * form for use in connection with the web services and APIs provided by
 * Facebook.
 *
 * As with any software that integrates with the Facebook platform, your use
 * of this software is subject to the Facebook Developer Principles and
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
 * shall be included in all copies or substantial portions of the software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

namespace FacebookAdsTest\Logger;

use FacebookAds\Http\RequestInterface;
use FacebookAds\Logger\CurlLogger;
use FacebookAds\Logger\CurlLogger\JsonAwareParameters;

class CurlLoggerTest extends AbstractLoggerTest {

  /**
   * @var resource
   */
  protected $handle;

  public function setup(): void {
    $this->handle = fopen('php://temp', 'w+');
  }

  public function tearDown(): void {
    fclose($this->handle);
  }

  /**
   * @return resource
   */
  protected function getHandle() {
    return $this->handle;
  }

  /**
   * @return CurlLogger
   */
  protected function createLogger() {
    return new CurlLogger($this->getHandle());
  }

  protected function createRequestMock() {
    $query = $this->createParametersMock();
    $query->method('export')->willReturn(array(
      'appsecret_proof' => '<APPSECRET_PROOF>',
      'access_token' => '<ACCESS_TOKEN>',
      'query_field' => 'query_value',
    ));

    $body = $this->createParametersMock();
    $body->method('export')->willReturn(array(
      'body_field' => 'body_value',
    ));

    $files = $this->createParametersMock();
    $files->method('export')->willReturn(array(
      'file_field' => 'filepath',
    ));

    $request = parent::createRequestMock();
    $request->method('getQueryParams')->willReturn($query);
    $request->method('getBodyParams')->willReturn($body);
    $request->method('getFileParams')->willReturn($files);

    return $request;
  }

  public function testLog() {
    $this->createLogger()->log(
      static::VALUE_LOG_LEVEL, static::VALUE_LOG_MESSAGE);
  }

  /**
   * @return array
   */
  public function logRequestProvider() {
    return array(
      array(RequestInterface::METHOD_GET),
      array(RequestInterface::METHOD_POST),
      array(RequestInterface::METHOD_PUT),
      array(RequestInterface::METHOD_DELETE),
    );
  }

  /**
   * @dataProvider logRequestProvider
   * @param string $http_method
   */
  public function testLogRequest($http_method) {
    $request = $this->createRequestMock();
    $request->method('getMethod')->willReturn($http_method);

    $logger = $this->createLogger();
    $logger->logRequest(static::VALUE_LOG_LEVEL, $request);
  }

  public function testLogResponse() {
    $this->createLogger()->logResponse(
      static::VALUE_LOG_LEVEL, $this->createResponseMock());
  }

  public function testJsonPrettyPrint() {
    $logger = $this->createLogger();
    $this->assertFalse($logger->isJsonPrettyPrint());
    $logger->setJsonPrettyPrint(true);
    $this->assertTrue($logger->isJsonPrettyPrint());

    $query = new JsonAwareParameters(array(
      'json_field' => array_fill(0, 3, 'json_value'),
    ));
    $body = $files = $this->createParametersMock();

    $request = parent::createRequestMock();
    $request->method('getQueryParams')->willReturn($query);
    $request->method('getBodyParams')->willReturn($body);
    $request->method('getFileParams')->willReturn($files);

    $logger->logRequest(static::VALUE_LOG_LEVEL, $request);

    $logger->setJsonPrettyPrint(false);
    $this->assertFalse($logger->isJsonPrettyPrint());
  }
}


<?php
/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
 * use, copy, modify, and distribute this software in source code or binary
 * form for use in connection with the web services and APIs provided by
 * Facebook.
 *
 * As with any software that integrates with the Facebook platform, your use
 * of this software is subject to the Facebook Developer Principles and
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
 * shall be included in all copies or substantial portions of the software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

namespace FacebookAdsTest\Logger;

use FacebookAds\Logger\NullLogger;

class NullLoggerTest extends AbstractLoggerTest {

  protected function createLogger() {
    return new NullLogger();
  }

  public function testLog() {
    $this->createLogger()->log(
      static::VALUE_LOG_LEVEL, static::VALUE_LOG_MESSAGE);
  }

  public function testLogRequest() {
    $this->createLogger()->logRequest(
      static::VALUE_LOG_LEVEL, $this->createRequestMock());
  }

  public function testLogResponse() {
    $this->createLogger()->logResponse(
      static::VALUE_LOG_LEVEL, $this->createResponseMock());
  }
}


<?php
/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
 * use, copy, modify, and distribute this software in source code or binary
 * form for use in connection with the web services and APIs provided by
 * Facebook.
 *
 * As with any software that integrates with the Facebook platform, your use
 * of this software is subject to the Facebook Developer Principles and
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
 * shall be included in all copies or substantial portions of the software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

namespace FacebookAdsTest\Logger\CurlLogger;

use FacebookAds\Logger\CurlLogger\JsonAwareParameters;
use FacebookAdsTest\AbstractUnitTestCase;

class JsonAwareParametersTest extends AbstractUnitTestCase {
  use JsonAwareTestTrait;

  /**
   * @dataProvider parameterProvider
   * @param mixed $param_data
   */
  public function testExtract($param_data) {
    foreach ((new JsonAwareParameters($param_data))->export() as $param) {
      $this->assertTrue(is_scalar($param) || is_null($param));
    }
  }
}


<?php
/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
 * use, copy, modify, and distribute this software in source code or binary
 * form for use in connection with the web services and APIs provided by
 * Facebook.
 *
 * As with any software that integrates with the Facebook platform, your use
 * of this software is subject to the Facebook Developer Principles and
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
 * shall be included in all copies or substantial portions of the software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

namespace FacebookAdsTest\Logger\CurlLogger;

use FacebookAds\Logger\CurlLogger\JsonNode;

trait JsonAwareTestTrait {

  /**
   * @return array
   */
  public function parameterProvider() {
    $base = array(
      'appsecret_proof' => '<APPSECRET_PROOF>',
      'access_token' => '<ACCESS_TOKEN>',
      'query_field' => 'query_value',
    );

    return array(
      array(
        $base + array(
          'json_field' => array(
            'query_field' => 'query_value',
          ),
        )
      ),
      array(
        $base + array(
          'json_field' => array(
            'query_field' => 'query_value',
            'query_field_1' => 'query_value_1',
            'query_field_2' => 'query_value_2',
          ),
        )
      ),
      array(
        $base + array(
          'json_field' => array(
            'query_field' => str_repeat('x', JsonNode::EXPLOSION_THRESHOLD + 1),
          ),
        ),
      ),
      array(
        $base + array(
          'json_field' => array(),
        ),
      ),
      array(
        $base + array(
          'json_field' => array(
            'vector_value_1',
            'vector_value_2',
            'vector_value_3',
          ),
        ),
      ),
      array(
        array(),
      ),
      array(
        new \StdClass(),
      )
    );
  }
}


<?php
/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
 * use, copy, modify, and distribute this software in source code or binary
 * form for use in connection with the web services and APIs provided by
 * Facebook.
 *
 * As with any software that integrates with the Facebook platform, your use
 * of this software is subject to the Facebook Developer Principles and
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
 * shall be included in all copies or substantial portions of the software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

namespace FacebookAdsTest\Logger\CurlLogger;

use FacebookAds\Logger\CurlLogger\JsonNode;
use FacebookAdsTest\AbstractUnitTestCase;

class JsonNodeTest extends AbstractUnitTestCase {
  use JsonAwareTestTrait;

  /**
   * @dataProvider parameterProvider
   * @param mixed $param_data
   */
  public function testEncoding($param_data) {
    JsonNode::factory($param_data)->encode();
  }

  public function testInvalidType() {
    $this->expectException(\InvalidArgumentException::class);
    JsonNode::factory(fopen('php://memory', 'r'))->encode();
  }

  /**
   * Test the sanity check for behaviour on empty children list
   * which should never happen as this is protected method
   */
  public function testGetLastChildKey() {
    $object = JsonNode::factory(array());
    $method = new \ReflectionMethod($object, 'getLastChildKey');
    $method->setAccessible(true);

    $this->assertNull($method->invoke($object));

    $method->setAccessible(false);
  }
}


<?php
/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
 * use, copy, modify, and distribute this software in source code or binary
 * form for use in connection with the web services and APIs provided by
 * Facebook.
 *
 * As with any software that integrates with the Facebook platform, your use
 * of this software is subject to the Facebook Developer Principles and
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
 * shall be included in all copies or substantial portions of the software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

namespace FacebookAdsTest\Object;

use FacebookAds\Object\AbstractCrudObject;
use FacebookAdsTest\Config\SkippableFeatureTestInterface;

abstract class AbstractAsyncJobTestCase extends AbstractCrudObjectTestCase
  implements SkippableFeatureTestInterface {

  /**
   * @return array
   */
  public function skipIfAny() {
    return array('no_async_jobs');
  }

  /**
   * @param AbstractCrudObject $job
   * @param int $timeout
   * @param int $interval
   */
  protected function waitTillJobComplete(
    AbstractCrudObject $job, $timeout = 60, $interval = 2) {

    $end = time() + $timeout;
    do {
      if ($job->getSelf()->isComplete()) {
        return;
      }
      sleep($interval);
    } while (time() <= $end);

    $this->markTestSkipped(
      "Async Job timed out. Timeout: {$timeout}, Interval: {$interval}");
  }
}


<?php
/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
 * use, copy, modify, and distribute this software in source code or binary
 * form for use in connection with the web services and APIs provided by
 * Facebook.
 *
 * As with any software that integrates with the Facebook platform, your use
 * of this software is subject to the Facebook Developer Principles and
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
 * shall be included in all copies or substantial portions of the software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

namespace FacebookAdsTest\Object;

use FacebookAdsTest\Object\EmptyCrudObject;
use FacebookAds\Object\AbstractCrudObject;

class AbstractCrudObjectTest extends AbstractCrudObjectTestCase {

  public function testCreationWithNull() {
    $this->checkCreationSuccessWithID(null);
  }

  public function testCreationWithNegativeId() {
    $this->checkCreationFailureWithID(-1234123);
  }

  public function testCreationWithPositiveId() {
    $this->checkCreationSuccessWithID(100007309086878);
  }

  public function testCreationWithCorrectStringId() {
    $this->checkCreationSuccessWithID('100007309086878');
  }

  public function testCreationWithIncorrectNegativeStringId() {
    $this->checkCreationFailureWithID('-1234123');
  }

  public function testCreationWithIncorrectStringId() {
    $this->checkCreationFailureWithID('1.234e10');
  }

  public function testCreationSuccessWithAct_() {
    $this->checkCreationSuccessWithID('act_12341521352312');
  }

  public function testCreationFailureWithAct_() {
    $this->checkCreationFailureWithID('act_1.1234e10');
  }

  public function testCreationWithOverFlowInt() {
    $this->checkCreationFailureWithID(PHP_INT_MAX+1);
  }
  private function checkCreationSuccessWithID($id) {
    new EmptyCrudObject($id);
  }

  private function checkCreationFailureWithID($id) {
    $this->assertInvalidArgumentException($id);
  }

  /**
   * @param mixed $id
   */
  public function assertInvalidArgumentException(mixed $id) {
    $has_throw_exception = false;
    try {
      new EmptyCrudObject($id);
    } catch (\InvalidArgumentException $e) {
      $has_throw_exception = true;
    }
    $this->assertTrue($has_throw_exception);
  }

}


<?php
/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
 * use, copy, modify, and distribute this software in source code or binary
 * form for use in connection with the web services and APIs provided by
 * Facebook.
 *
 * As with any software that integrates with the Facebook platform, your use
 * of this software is subject to the Facebook Developer Principles and
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
 * shall be included in all copies or substantial portions of the software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

namespace FacebookAdsTest\Object;

use FacebookAds\Cursor;
use FacebookAds\Object\AbstractCrudObject;
use FacebookAds\Object\AbstractObject;
use FacebookAds\Object\AbstractArchivableCrudObject;
use FacebookAds\Object\AdLabel;
use FacebookAds\Object\CanRedownloadInterface;
use FacebookAds\Object\Fields\AbstractArchivableCrudObjectFields;
use FacebookAds\Object\Fields\AdLabelFields;
use FacebookAds\Object\Traits\AdLabelAwareCrudObjectTrait;
use FacebookAdsTest\AbstractIntegrationTestCase;

abstract class AbstractCrudObjectTestCase extends AbstractIntegrationTestCase {

  /**
   * @param AbstractCrudObject $subject
   * @param array $params
   */
  public function assertCanCreate(
    AbstractCrudObject $subject, array $params = array()) {

    if ($subject instanceof CanRedownloadInterface) {
      $params[CanRedownloadInterface::PARAM_REDOWNLOAD] = true;
    }

    $this->assertEmpty($subject->{AbstractCrudObject::FIELD_ID});
    $subject->create($params);
    $this->assertNotEmpty($subject->{AbstractCrudObject::FIELD_ID});

    /** @var AbstractCrudObject $subject */
    if ($subject instanceof CanRedownloadInterface) {
      $non_null_count = 0;
      foreach ($subject->getData() as $key => $value) {
        if ($key !== AbstractCrudObject::FIELD_ID && $value !== null) {
          ++$non_null_count;
          // Normalize assert function behaviour with non-redownloadable objects
          $subject->{$key} = null;
        }
      }

      $this->assertGreaterThan(0, $non_null_count);
    }
  }

  /**
   * @param AbstractCrudObject $object
   * @return AbstractCrudObject
   */
  protected function getEmptyClone(AbstractCrudObject $object) {
    $fqn = get_class($object);
    return new $fqn(
      $object->{AbstractCrudObject::FIELD_ID},
      $object->getParentId());
  }

  /**
   * @param AbstractCrudObject $subject
   */
  public function assertCannotCreate(AbstractCrudObject $subject) {
    $has_throw_exception = false;
    try {
      $subject->create();
    } catch (\Exception $e) {
      $has_throw_exception = true;
    }
    $this->assertTrue($has_throw_exception);
  }

  /**
   * @param AbstractCrudObject $subject
   */
  public function assertCanRead(AbstractCrudObject $subject) {
    $this->assertNotEmpty($subject->{AbstractCrudObject::FIELD_ID});
    $mirror = $this->getEmptyClone($subject);
    $mirror->read();
    $this->assertEquals(
      $subject->{AbstractCrudObject::FIELD_ID},
      $mirror->{AbstractCrudObject::FIELD_ID});
  }

  /**
   * @param AbstractCrudObject $subject
   * @param string $connection_name
   * @param array $fields
   * @param array $params
   * @return mixed
   */
  protected function fetchConnection(
    AbstractCrudObject $subject,
    $connection_name,
    array $fields = array(),
    array $params = array()) {

    $this->assertNotEmpty($subject->{AbstractCrudObject::FIELD_ID});
    $mirror = $this->getEmptyClone($subject);
    return call_user_func(array($mirror, $connection_name), $fields, $params);
  }

  /**
   * @param AbstractCrudObject $subject
   * @param string $connection_name
   * @param array $fields
   * @param array $params
   */
  public function assertCanFetchConnection(
    AbstractCrudObject $subject,
    $connection_name,
    array $fields = array(),
    array $params = array()) {

    $result = $this->fetchConnection(
      $subject, $connection_name, $fields, $params);

    $this->assertTrue(
      $result instanceof Cursor
      || $result instanceof AbstractObject);
  }

  /**
   * @param AbstractCrudObject $subject
   * @param string $connection_name
   * @param array $fields
   * @param array $params
   */
  public function assertCanFetchConnectionAsArray(
    AbstractCrudObject $subject,
    $connection_name,
    array $fields = array(),
    array $params = array()) {

    $result = $this->fetchConnection(
      $subject, $connection_name, $fields, $params);

    $this->assertTrue(is_array($result));

    foreach ($result as $object) {
      if (!$object instanceof AbstractObject) {
        $this->fail("Not an instance of AbstractObject");
      }
    }
  }

  /**
   * @param AbstractCrudObject $object
   * @param array $diff
   * @return array
   */
  private function extractDiffFromObject(
    AbstractCrudObject $object,
    array $diff) {

    $sub_diff = array();
    foreach ($object->getData() as $key => $val) {
      if (array_key_exists($key, $diff)) {
        $sub_diff[$key] = $val;
      }
    }

    return $sub_diff + array_combine(
      array_keys($diff),
      array_fill(0, count($diff), null));
  }

  /**
   * @param AbstractCrudObject $subject
   * @param array $diff Data to be changed in the object
   */
  public function assertCanUpdate(
    AbstractCrudObject $subject,
    array $diff) {

    $this->assertNotEmpty($subject->{AbstractCrudObject::FIELD_ID});
    $mirror = $this->getEmptyClone($subject);
    $mirror->read();
    $mirror->setData($diff);
    $mirror->update();
    $this->assertNotEmpty($subject->id);
    $mirror2 = $this->getEmptyClone($subject);
    $mirror2->read(array_keys($diff));
    $this->assertEquals(
      $this->extractDiffFromObject($mirror, $diff),
      $this->extractDiffFromObject($mirror2, $diff));

    $subject->read(array_keys($diff));
  }

  /**
   * @param AbstractCrudObject $subject
   */
  public function assertCannotUpdate(AbstractCrudObject $subject) {
    $has_throw_exception = false;
    try {
      $subject->update();
    } catch (\Exception $e) {
      $has_throw_exception = true;
    }
    $this->assertTrue($has_throw_exception);
  }

  /**
   * @param AbstractArchivableCrudObject $subject
   * @param string $status
   */
  private function assertArchivableCrudObjectStatus(
    AbstractArchivableCrudObject $subject, $status) {

    $fields = array(
      AbstractArchivableCrudObjectFields::CONFIGURED_STATUS,
      AbstractArchivableCrudObjectFields::EFFECTIVE_STATUS,
    );

    $mirror = $this->getEmptyClone($subject);
    $mirror->read($fields);

    foreach ($fields as $field) {
      $this->assertEquals($mirror->{$field}, $status);
      $subject->{$field} = $status;
    }
  }

  /**
   * @param AbstractCrudObject $subject
   */
  public function assertCanDelete(AbstractCrudObject $subject) {
    $this->assertNotEmpty($subject->{AbstractCrudObject::FIELD_ID});
    $subject->deleteSelf();
    if ($subject instanceof AbstractArchivableCrudObject) {
      $this->assertArchivableCrudObjectStatus(
        $subject, AbstractArchivableCrudObject::STATUS_DELETED);
    }
  }

  /**
   * @param AbstractCrudObject $subject
   */
  public function assertCannotDelete(AbstractCrudObject $subject) {
    $has_throw_exception = false;
    try {
      $subject->deleteSelf();
    } catch (\Exception $e) {
      $has_throw_exception = true;
    }
    $this->assertTrue($has_throw_exception);
  }

  /**
   * @param AbstractArchivableCrudObject $subject
   */
  public function assertCanArchive(AbstractArchivableCrudObject $subject) {
    $this->assertNotEmpty($subject->{AbstractCrudObject::FIELD_ID});
    $subject->archiveSelf();
    if ($subject instanceof AbstractArchivableCrudObject) {
      $this->assertArchivableCrudObjectStatus(
        $subject, AbstractArchivableCrudObject::STATUS_ARCHIVED);
    }
  }

  /**
   * @param array $response_content
   */
  public function assertSuccessResponse(array $response_content) {
    $this->assertTrue(is_array($response_content));
    $this->assertArrayHasKey('success', $response_content);
    $this->assertTrue($response_content['success']);
  }

  /**
   * @param AdLabelAwareCrudObjectTrait|AbstractCrudObject $object
   */
  public function assertCanBeLabeled(AbstractCrudObject $object) {
    $label = new AdLabel(null, $this->getConfig()->accountId);
    $label->{AdLabelFields::NAME} = $this->getConfig()->testRunId;
    $label->create();

    /** @var AdLabelAwareCrudObjectTrait|AbstractCrudObject $mirror */
    $mirror = $this->getEmptyClone($object);
    $mirror->addAdLabels(array($label->{AdLabelFields::ID}));

    $mirror = $this->getEmptyClone($object);
    $mirror->read(array('adlabels'));
    $this->assertCount(1, $mirror->{'adlabels'});

    $mirror = $this->getEmptyClone($object);
    $mirror->removeAdLabels(array($label->{AdLabelFields::ID}));
    $mirror->read(array('adlabels'));
    $this->assertNull($mirror->{'adlabels'});

    $label->deleteSelf();
  }
}


<?php
/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
 * use, copy, modify, and distribute this software in source code or binary
 * form for use in connection with the web services and APIs provided by
 * Facebook.
 *
 * As with any software that integrates with the Facebook platform, your use
 * of this software is subject to the Facebook Developer Principles and
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
 * shall be included in all copies or substantial portions of the software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

namespace FacebookAdsTest\Object;

use FacebookAds\Object\AdAccountGroup;
use FacebookAds\Object\Fields\AdAccountGroupFields;

class AdAccountGroupTest extends AbstractCrudObjectTestCase {

  public function testCrudAccess() {
    $group = new AdAccountGroup();
    $group->{AdAccountGroupFields::NAME} = $this->getConfig()->testRunId;
    $this->assertCanCreate($group);
    $this->assertCanRead($group);
    $this->assertCanUpdate($group, array(
      AdAccountGroupFields::NAME => $this->getConfig()->testRunId.' updated'));

    $this->assertCanFetchConnectionAsArray($group, 'getUsers');
    $this->assertCanFetchConnectionAsArray($group, 'getAdAccounts');

    $this->assertCanDelete($group);
  }
}


<?php
/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
 * use, copy, modify, and distribute this software in source code or binary
 * form for use in connection with the web services and APIs provided by
 * Facebook.
 *
 * As with any software that integrates with the Facebook platform, your use
 * of this software is subject to the Facebook Developer Principles and
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
 * shall be included in all copies or substantial portions of the software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

namespace FacebookAdsTest\Object;

use FacebookAds\Object\AdAccount;
use FacebookAds\Object\AdCreative;
use FacebookAds\Object\AdLabel;
use FacebookAds\Object\Fields\AdAccountFields;
use FacebookAds\Object\Fields\AdCreativeFields;
use FacebookAds\Object\Fields\AdLabelFields;
use FacebookAds\Object\Values\AdAccountRoles;
use FacebookAds\Object\Fields\TargetingSpecsFields;
use FacebookAds\Object\TargetingSpecs;
use FacebookAds\Object\Values\AdFormats;
use FacebookAds\Object\Values\OptimizationGoals;

class AdAccountTest extends AbstractCrudObjectTestCase {

  /**
   * @var AdLabel
   */
  protected $adLabel;

  public function setup() {
    parent::setup();

    $this->adLabel = new AdLabel(null, $this->getConfig()->accountId);
    $this->adLabel->{AdLabelFields::NAME} = $this->getConfig()->testRunId;
    $this->adLabel->create();
  }

  public function tearDown() {
    if ($this->adLabel !== null) {
      $this->adLabel->deleteSelf();
      $this->adLabel = null;
    }

    parent::tearDown();
  }

  public function testCrud() {
    $account = new AdAccount($this->getConfig()->accountId);

    $this->assertCanRead($account);
    $name = $account->read(array(AdAccountFields::NAME))
      ->{AdAccountFields::NAME};
    $this->assertCanUpdate(
      $account,
      array(AdAccountFields::NAME => $this->getConfig()->testRunId));

    // Restore original account name
    $account->{AdAccountFields::NAME} = $name;
    $account->save();

    $this->assertCannotDelete($account);

    $targeting = new TargetingSpecs();
    $targeting->setData(array(
      TargetingSpecsFields::GEO_LOCATIONS =>
        array('countries' => array('US', 'JP')),
      TargetingSpecsFields::GENDERS => array(1),
      TargetingSpecsFields::AGE_MIN => 20,
      TargetingSpecsFields::AGE_MAX => 24,
    ));

    $creative = (new AdCreative())->setData(array(
      AdCreativeFields::TITLE => 'My Test Creative',
      AdCreativeFields::BODY => 'My Test Ad Creative Body',
      AdCreativeFields::OBJECT_URL => 'https://www.facebook.com/facebook',
    ));

    $targeting_params = array(
      'targeting_spec' => $targeting->exportData(),
    );

    $label_params = array(
      'ad_label_ids' => array(
        $this->adLabel->{AdLabelFields::ID},
      ),
    );

    $this->assertCanFetchConnection($account, 'getActivities');
    $this->assertCanFetchConnection($account, 'getAdUsers');
    $this->assertCanFetchConnection($account, 'getCampaigns');
    $this->assertCanFetchConnection($account, 'getAdSets');
    $this->assertCanFetchConnection($account, 'getAds');
    $this->assertCanFetchConnection($account, 'getAdCreatives');
    $this->assertCanFetchConnection($account, 'getAdImages');
    $this->assertCanFetchConnection($account, 'getAdsPixels');
    $this->assertCanFetchConnection($account, 'getAdVideos');
    $this->assertCanFetchConnection($account, 'getBroadCategoryTargeting');
    $this->assertCanFetchConnection($account, 'getCustomAudiences');
    $this->assertCanFetchConnection($account, 'getConversionPixels');
    $this->assertCanFetchConnection($account, 'getPartnerCategories');
    $this->assertCanFetchConnection($account, 'getRateCards');
    $this->assertCanFetchConnection(
      $account,
      'getReachEstimate',
      array(),
      array_merge(
        $targeting_params,
        array(
          'optimize_for' => OptimizationGoals::OFFSITE_CONVERSIONS,
        )));

    if (!$this->shouldSkipTest('no_reach_and_frequency')) {
      $this->assertCanFetchConnection($account, 'getReachFrequencyPredictions');
    }

    $this->assertCanFetchConnection(
      $account, 'getTargetingDescription', array(), $targeting_params);

    $this->assertCanFetchConnection($account, 'getTransactions');
    $this->assertCanFetchConnection($account, 'getAdPreviews', array(), array(
      'ad_format' => AdFormats::DESKTOP_FEED_STANDARD,
      'creative' => $creative->exportData(),
    ));
    $this->assertCanFetchConnection($account, 'getInsights');
    $this->assertCanFetchConnection($account, 'getInsightsAsync');
    $this->assertCanFetchConnection($account, 'getAgencies');
    $this->assertCanFetchConnection($account, 'getMinimumBudgets');
    $this->assertCanFetchConnection($account, 'getAdLabels');
    $this->assertCanFetchConnection(
      $account, 'getCampaignsByLabel', array(), $label_params);
    $this->assertCanFetchConnection(
      $account, 'getAdSetsByLabel', array(), $label_params);
    $this->assertCanFetchConnection(
      $account, 'getAdsByLabel', array(), $label_params);
    $this->assertCanFetchConnection(
      $account, 'getAdCreativesByLabel', array(), $label_params);

    if (!$this->getSkippableFeaturesManager()
      ->isSkipKey('no_business_manager')) {

      $account->grantAgencyAcccess(
        $this->getConfig()->secondaryBusinessId,
        array(AdAccountRoles::GENERAL_USER));

      $account->revokeAgencyAccess($this->getConfig()->secondaryBusinessId);
    }
  }
}


<?php
/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
 * use, copy, modify, and distribute this software in source code or binary
 * form for use in connection with the web services and APIs provided by
 * Facebook.
 *
 * As with any software that integrates with the Facebook platform, your use
 * of this software is subject to the Facebook Developer Principles and
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
 * shall be included in all copies or substantial portions of the software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

namespace FacebookAdsTest\Object;

use FacebookAds\Object\AdConversionPixel;
use FacebookAds\Object\Fields\AdConversionPixelFields;

class AdConversionPixelTest extends AbstractCrudObjectTestCase {

  public function testCrud() {
    $conversionPixel = new AdConversionPixel(
      null, $this->getConfig()->accountId);
    $conversionPixel->{AdConversionPixelFields::NAME}
      = $this->getConfig()->testRunId;
    $conversionPixel->{AdConversionPixelFields::TAG} = 'checkout';

    $this->assertCanCreate($conversionPixel);
    $this->assertCanRead($conversionPixel);
    $this->assertCanUpdate(
      $conversionPixel,
      array(
        AdConversionPixelFields::TAG => 'add_to_cart',
      ));
    $this->assertCanDelete($conversionPixel);
  }
}


<?php
/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
 * use, copy, modify, and distribute this software in source code or binary
 * form for use in connection with the web services and APIs provided by
 * Facebook.
 *
 * As with any software that integrates with the Facebook platform, your use
 * of this software is subject to the Facebook Developer Principles and
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
 * shall be included in all copies or substantial portions of the software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

namespace FacebookAdsTest\Object;

use FacebookAds\Object\AdCreative;
use FacebookAds\Object\AdImage;
use FacebookAds\Object\ObjectStorySpec;
use FacebookAds\Object\ObjectStory\LinkData;
use FacebookAds\Object\ObjectStory\AttachmentData;
use FacebookAds\Object\Fields\AdCreativeFields;
use FacebookAds\Object\Fields\AdImageFields;
use FacebookAds\Object\Fields\ObjectStorySpecFields;
use FacebookAds\Object\Fields\ObjectStory\LinkDataFields;
use FacebookAds\Object\Fields\ObjectStory\AttachmentDataFields;

class AdCreativeTest extends AbstractCrudObjectTestCase {

  public function testCrud() {
    $creative = new AdCreative(null, $this->getConfig()->accountId);
    $creative->{AdCreativeFields::TITLE} = 'My Test Ad';
    $creative->{AdCreativeFields::NAME} = 'My Test Ad';
    $creative->{AdCreativeFields::BODY} = 'My Test Ad Body';
    $creative->{AdCreativeFields::OBJECT_ID} = $this->getConfig()->pageId;
    $this->assertCanCreate($creative);
    $this->assertCanRead($creative);
    $this->assertCanUpdate(
      $creative,
      array(
        AdCreativeFields::NAME => 'My Test Ad '. $this->getConfig()->testRunId,
      ));
    $this->assertCanBeLabeled($creative);
    $this->assertCanDelete($creative);
  }

  public function testMultiProductObjectSpec() {
    // Create a new AdCreative
    $creative = new AdCreative(null, $this->getConfig()->accountId);
    $creative->{AdCreativeFields::NAME} = 'Multi Product Ad Creative';

    // Create a new ObjectStorySpec to create an unpublished post
    $story = new ObjectStorySpec();
    $story->{ObjectStorySpecFields::PAGE_ID} = $this->getConfig()->pageId;

    // Create LinkData object representing data for a link page post
    $link = new LinkData();
    $link->{LinkDataFields::LINK} = $this->getConfig()->appUrl;
    $link->{LinkDataFields::CAPTION} = 'My Caption';

    // Upload a test image to use in Attachments
    $adImage = new AdImage(null, $this->getConfig()->accountId);
    $adImage->{AdImageFields::FILENAME} = $this->getConfig()->testImagePath;
    $adImage->save();

    // Create 3 products as this will be a multi-product ad
    $product1 = (new AttachmentData())->setData(array(
      AttachmentDataFields::LINK => $this->getConfig()->appUrl.'p1',
      AttachmentDataFields::IMAGE_HASH => $adImage->hash,
      AttachmentDataFields::NAME => 'Product 1',
      AttachmentDataFields::DESCRIPTION => '$100',
    ));

    $product2 = (new AttachmentData())->setData(array(
      AttachmentDataFields::LINK => $this->getConfig()->appUrl.'p2',
      AttachmentDataFields::IMAGE_HASH => $adImage->hash,
      AttachmentDataFields::NAME => 'Product 2',
      AttachmentDataFields::DESCRIPTION => '$200',
    ));

    $product3 = (new AttachmentData())->setData(array(
      AttachmentDataFields::LINK => $this->getConfig()->appUrl.'p3',
      AttachmentDataFields::IMAGE_HASH => $adImage->hash,
      AttachmentDataFields::NAME => 'Product 3',
      AttachmentDataFields::DESCRIPTION => '$300',
    ));

    // Add the products into the child attachments
    $link->{LinkDataFields::CHILD_ATTACHMENTS} = array(
      $product1,
      $product2,
      $product3,
    );

    $story->{ObjectStorySpecFields::LINK_DATA} = $link;
    $creative->{AdCreativeFields::OBJECT_STORY_SPEC} = $story;

    $this->assertCanCreate($creative);
    $this->assertCanDelete($creative);
  }
}


<?php
/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
 * use, copy, modify, and distribute this software in source code or binary
 * form for use in connection with the web services and APIs provided by
 * Facebook.
 *
 * As with any software that integrates with the Facebook platform, your use
 * of this software is subject to the Facebook Developer Principles and
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
 * shall be included in all copies or substantial portions of the software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

namespace FacebookAdsTest\Object;

use FacebookAds\Object\AdImage;
use FacebookAds\Object\Fields\AdImageFields;

class AdImageTest extends AbstractCrudObjectTestCase {

  public function testCrud() {
    $image = new AdImage(null, $this->getConfig()->accountId);
    $image->{AdImageFields::FILENAME} = $this->getConfig()->testImagePath;
    $this->assertCanCreate($image);
    $this->assertCanRead($image);
    $this->assertCannotUpdate($image);
    $this->assertCanDelete($image);
  }

  /**
   * @expectedException \Exception
   */
  public function testZipFileInNormalCreate() {
    $image = new AdImage(null, $this->getConfig()->accountId);
    $image->{AdImageFields::FILENAME}
      = $this->getConfig()->testZippedImagesPath;
    $image->create();
  }

  public function testBulkZipUpload() {
    $images = AdImage::createFromZip(
       $this->getConfig()->testZippedImagesPath,
       $this->getConfig()->accountId);
    $this->assertTrue(is_array($images));

    foreach ($images as $image) {
      $this->assertCanDelete($image);
    }
  }
}


<?php
/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
 * use, copy, modify, and distribute this software in source code or binary
 * form for use in connection with the web services and APIs provided by
 * Facebook.
 *
 * As with any software that integrates with the Facebook platform, your use
 * of this software is subject to the Facebook Developer Principles and
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
 * shall be included in all copies or substantial portions of the software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

namespace FacebookAdsTest\Object;

use FacebookAds\Object\AdLabel;
use FacebookAds\Object\Fields\AdLabelFields;

class AdLabelTest extends AbstractCrudObjectTestCase {

  public function testCrudAccess() {
    $label = new AdLabel(null, $this->getConfig()->accountId);
    $label->{AdLabelFields::NAME} = $this->getConfig()->testRunId;

    $this->assertCanCreate($label);
    $this->assertCanRead($label);
    $this->assertCanUpdate(
      $label,
      array(AdLabelFields::NAME => $this->getConfig()->testRunId . ' updated'));

    $this->assertCanDelete($label);
  }
}


<?php
/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
 * use, copy, modify, and distribute this software in source code or binary
 * form for use in connection with the web services and APIs provided by
 * Facebook.
 *
 * As with any software that integrates with the Facebook platform, your use
 * of this software is subject to the Facebook Developer Principles and
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
 * shall be included in all copies or substantial portions of the software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

namespace FacebookAdsTest\Object;

use FacebookAds\Object\AdImage;
use FacebookAds\Object\AdCreative;
use FacebookAds\Object\Ad;
use FacebookAds\Object\AdSet;
use FacebookAds\Object\Campaign;
use FacebookAds\Object\AdAccount;
use FacebookAds\Object\Fields\AdImageFields;
use FacebookAds\Object\TargetingSpecs;
use FacebookAds\Object\Fields\AdPreviewFields;
use FacebookAds\Object\Fields\AdCreativeFields;
use FacebookAds\Object\Fields\AdFields;
use FacebookAds\Object\Fields\AdSetFields;
use FacebookAds\Object\Fields\CampaignFields;
use FacebookAds\Object\Fields\TargetingSpecsFields;
use FacebookAds\Object\Values\AdFormats;
use FacebookAds\Object\Values\BillingEvents;
use FacebookAds\Object\Values\OptimizationGoals;
use FacebookAdsTest\Config\SkippableFeatureTestInterface;

class AdPreviewTest extends AbstractCrudObjectTestCase
  implements SkippableFeatureTestInterface {

  /**
   * @var Campaign
   */
  protected $campaign;

  /**
   * @var AdSet
   */
  protected $adSet;

  /**
   * @var Ad
   */
  protected $ad;

  /**
   * @var AdImage
   */
  protected $adImage;

  /**
   * @var AdCreative
   */
  protected $adCreative;

  /**
   * @return array
   */
  public function skipIfAny() {
    return array('no_payment_method');
  }

  public function setup() {
    parent::setup();

    $targeting = new TargetingSpecs();
    $targeting->{TargetingSpecsFields::GEO_LOCATIONS}
      = array('countries' => array('US'));

    $this->campaign = new Campaign(null, $this->getConfig()->accountId);
    $this->campaign->{CampaignFields::NAME} = $this->getConfig()->testRunId;
    $this->campaign->create();

    $this->adSet = new AdSet(null, $this->getConfig()->accountId);
    $this->adSet->{AdSetFields::CAMPAIGN_ID}
      = (int) $this->campaign->{AdSetFields::ID};
    $this->adSet->{AdSetFields::NAME} = $this->getConfig()->testRunId;
    $this->adSet->{AdSetFields::DAILY_BUDGET} = '150';
    $this->adSet->{AdSetFields::START_TIME}
      = (new \DateTime("+1 week"))->format(\DateTime::ISO8601);
    $this->adSet->{AdSetFields::END_TIME}
      = (new \DateTime("+2 week"))->format(\DateTime::ISO8601);
    $this->adSet->{AdFields::TARGETING} = $targeting;
    $this->adSet->{AdSetFields::OPTIMIZATION_GOAL} = OptimizationGoals::REACH;
    $this->adSet->{AdSetFields::BILLING_EVENT} = BillingEvents::IMPRESSIONS;
    $this->adSet->{AdSetFields::BID_AMOUNT} = 2;
    $this->adSet->create(array(
      AdSet::STATUS_PARAM_NAME => AdSet::STATUS_PAUSED,
    ));

    $this->adImage = new AdImage(null, $this->getConfig()->accountId);
    $this->adImage->{AdImageFields::FILENAME}
      = $this->getConfig()->testImagePath;
    $this->adImage->save();

    $this->adCreative = new AdCreative(null, $this->getConfig()->accountId);
    $this->adCreative->{AdCreativeFields::TITLE} = 'My Test Ad';
    $this->adCreative->{AdCreativeFields::BODY} = 'My Test Ad Body';
    $this->adCreative->{AdCreativeFields::OBJECT_ID}
      = $this->getConfig()->pageId;
    $this->adCreative->{AdCreativeFields::IMAGE_HASH}
      = $this->adImage->{AdImageFields::HASH};
    $this->adCreative->create();

    $this->ad = new Ad(null, $this->getConfig()->accountId);
    $this->ad->{AdFields::NAME} = $this->getConfig()->testRunId;
    $this->ad->{AdFields::ADSET_ID}
      = (int) $this->adSet->{AdSetFields::ID};
    $this->ad->{AdFields::CREATIVE}
      = array('creative_id' => $this->adCreative->{AdCreativeFields::ID});
    $this->ad->create(array(
      Ad::STATUS_PARAM_NAME => Ad::STATUS_PAUSED,
    ));
  }

  public function tearDown() {

    if ($this->ad) {
      $this->ad->deleteSelf();
      $this->ad = null;
    }

    if ($this->adSet) {
      $this->adSet->deleteSelf();
      $this->adSet = null;
    }

    if ($this->campaign) {
      $this->campaign->deleteSelf();
      $this->campaign = null;
    }

    if ($this->adCreative) {
      $this->adCreative->deleteSelf();
      $this->adCreative = null;
    }

    if ($this->adImage) {
      $this->adImage->deleteSelf();
      $this->adImage = null;
    }

    parent::tearDown();
  }

  public function testAdPreviews() {
    // Preview with actual creative
    $previews = $this->adCreative->getAdPreviews(
      array(),
      array(
        AdPreviewFields::AD_FORMAT => AdFormats::RIGHT_COLUMN_STANDARD
      )
    );
    $this->assertNotEquals(0, $previews->count());
    $preview = $previews->offsetGet(0);
    $this->assertRegExp(
      '/iframe/',
      $preview->{AdPreviewFields::BODY}
    );

    // Preview with actual adgroup
    $previews = $this->ad->getAdPreviews(
      array(),
      array(
        AdPreviewFields::AD_FORMAT => AdFormats::RIGHT_COLUMN_STANDARD
      )
    );
    $this->assertNotEquals(0, $previews->count());
    $preview = $previews->offsetGet(0);
    $this->assertRegExp(
      '/iframe/',
      $preview->{AdPreviewFields::BODY}
    );

    // Preview with creative specs
    $account = new AdAccount($this->getConfig()->accountId);
    $previews = $account->getAdPreviews(
      array(),
      array(
        AdPreviewFields::CREATIVE => array(
          AdCreativeFields::BODY => 'Testing the creative preview',
          AdCreativeFields::OBJECT_ID => $this->getConfig()->pageId,
        ),
        AdPreviewFields::AD_FORMAT => AdFormats::RIGHT_COLUMN_STANDARD,
      )
    );
    $this->assertNotEquals(0, $previews->count());
    $preview = $previews->offsetGet(0);
    $this->assertRegExp(
      '/iframe/',
      $preview->{AdPreviewFields::BODY}
    );
  }
}


<?php
/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
 * use, copy, modify, and distribute this software in source code or binary
 * form for use in connection with the web services and APIs provided by
 * Facebook.
 *
 * As with any software that integrates with the Facebook platform, your use
 * of this software is subject to the Facebook Developer Principles and
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
 * shall be included in all copies or substantial portions of the software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

namespace FacebookAdsTest\Object;

use FacebookAds\Object\Campaign;
use FacebookAds\Object\AdSet;
use FacebookAds\Object\Fields\CampaignFields;
use FacebookAds\Object\Fields\AdSetFields;
use FacebookAds\Object\Fields\TargetingSpecsFields;
use FacebookAds\Object\TargetingSpecs;
use FacebookAds\Object\Values\BillingEvents;
use FacebookAds\Object\Values\OptimizationGoals;

class AdSetTest extends AbstractCrudObjectTestCase {

  /**
   * @var Campaign
   */
  protected $campaign;

  public function setup() {
    parent::setup();
    $this->campaign = new Campaign(null, $this->getConfig()->accountId);
    $this->campaign->{CampaignFields::NAME}
      = $this->getConfig()->testRunId;
    $this->campaign->create();
  }

  public function tearDown() {
    $this->campaign->deleteSelf();
    $this->campaign = null;
    parent::tearDown();
  }

  public function testCrud() {
    $targeting = new TargetingSpecs();
    $targeting->{TargetingSpecsFields::GEO_LOCATIONS}
      = array('countries' => array('US'));

    $set = new AdSet(null, $this->getConfig()->accountId);
    $set->{AdSetFields::CAMPAIGN_ID}
      = $this->campaign->{CampaignFields::ID};
    $set->{AdSetFields::NAME} = $this->getConfig()->testRunId;
    $set->{AdSetFields::OPTIMIZATION_GOAL} = OptimizationGoals::REACH;
    $set->{AdSetFields::BILLING_EVENT} = BillingEvents::IMPRESSIONS;
    $set->{AdSetFields::BID_AMOUNT} = 2;
    $set->{AdSetFields::DAILY_BUDGET} = '150';
    $set->{AdSetFields::TARGETING} = $targeting;
    $set->{AdSetFields::START_TIME}
      = (new \DateTime("+1 week"))->format(\DateTime::ISO8601);
    $set->{AdSetFields::END_TIME}
      = (new \DateTime("+2 week"))->format(\DateTime::ISO8601);

    $this->assertCanCreate($set, array(
      AdSet::STATUS_PARAM_NAME => AdSet::STATUS_PAUSED,
    ));
    $this->assertCanRead($set);
    $this->assertCanUpdate($set, array(
      AdSetFields::NAME => $this->getConfig()->testRunId.' updated',
    ));
    $this->assertCanFetchConnection($set, 'getAds');
    $this->assertCanFetchConnection($set, 'getAdCreatives');
    $this->assertCanFetchConnection($set, 'getInsights');
    $this->assertCanFetchConnection($set, 'getInsightsAsync');

    $this->assertCanBeLabeled($set);
    $this->assertCanArchive($set);

    $this->assertCanDelete($set);
  }
}