You are here

class RestClientTest in Salesforce Suite 8.3

Same name in this branch
  1. 8.3 tests/src/Unit/RestClientTest.php \Drupal\Tests\salesforce\Unit\RestClientTest
  2. 8.3 modules/salesforce_encrypt/tests/src/Unit/RestClientTest.php \Drupal\Tests\salesforce_encrypt\Unit\RestClientTest
Same name and namespace in other branches
  1. 8.4 tests/src/Unit/RestClientTest.php \Drupal\Tests\salesforce\Unit\RestClientTest
  2. 5.0.x tests/src/Unit/RestClientTest.php \Drupal\Tests\salesforce\Unit\RestClientTest

@coversDefaultClass \Drupal\salesforce\Rest\RestClient @group salesforce

Hierarchy

Expanded class hierarchy of RestClientTest

File

tests/src/Unit/RestClientTest.php, line 24

Namespace

Drupal\Tests\salesforce\Unit
View source
class RestClientTest extends UnitTestCase {
  protected static $modules = [
    'salesforce',
  ];

  /**
   * Set up for each test.
   */
  public function setUp() {
    parent::setUp();
    $this->salesforce_id = '1234567890abcde';
    $this->methods = [
      'getConsumerKey',
      'getConsumerSecret',
      'getRefreshToken',
      'getAccessToken',
      'refreshToken',
      'getApiEndPoint',
      'httpRequest',
    ];
    $this->httpClient = $this
      ->getMock('\\GuzzleHttp\\Client');
    $this->configFactory = $this
      ->getMockBuilder('\\Drupal\\Core\\Config\\ConfigFactory')
      ->disableOriginalConstructor()
      ->getMock();
    $this->state = $this
      ->getMockBuilder('\\Drupal\\Core\\State\\State')
      ->disableOriginalConstructor()
      ->getMock();
    $this->cache = $this
      ->getMock('\\Drupal\\Core\\Cache\\CacheBackendInterface');
    $this->json = $this
      ->getMock(Json::CLASS);
    $this->time = $this
      ->getMock(TimeInterface::CLASS);
  }

  /**
   * @covers ::__construct
   */
  private function initClient($methods = NULL) {
    if (empty($methods)) {
      $methods = $this->methods;
    }
    $args = [
      $this->httpClient,
      $this->configFactory,
      $this->state,
      $this->cache,
      $this->json,
      $this->time,
    ];
    $this->client = $this
      ->getMock(RestClient::CLASS, $methods, $args);
    if (in_array('getApiEndPoint', $methods)) {
      $this->client
        ->expects($this
        ->any())
        ->method('getApiEndPoint')
        ->willReturn('https://example.com');
    }
    if (in_array('getAccessToken', $methods)) {
      $this->client
        ->expects($this
        ->any())
        ->method('getAccessToken')
        ->willReturn(TRUE);
    }
  }

  /**
   * @covers ::isAuthorized
   */
  public function testAuthorized() {
    $this
      ->initClient();
    $this->client
      ->expects($this
      ->at(0))
      ->method('getConsumerKey')
      ->willReturn($this
      ->randomMachineName());
    $this->client
      ->expects($this
      ->at(1))
      ->method('getConsumerSecret')
      ->willReturn($this
      ->randomMachineName());
    $this->client
      ->expects($this
      ->at(2))
      ->method('getRefreshToken')
      ->willReturn($this
      ->randomMachineName());
    $this
      ->assertTrue($this->client
      ->isAuthorized());

    // Next one will fail because mocks only return for specific invocations.
    $this
      ->assertFalse($this->client
      ->isAuthorized());
  }

  /**
   * @covers ::apiCall
   */
  public function testSimpleApiCall() {
    $this
      ->initClient();

    // Test that an apiCall returns a json-decoded value.
    $body = [
      'foo' => 'bar',
    ];
    $response = new GuzzleResponse(200, [], json_encode($body));
    $this->client
      ->expects($this
      ->any())
      ->method('httpRequest')
      ->willReturn($response);
    $result = $this->client
      ->apiCall('');
    $this
      ->assertEquals($result, $body);
  }

  /**
   * @covers ::apiCall
   * @expectedException Exception
   */
  public function testExceptionApiCall() {
    $this
      ->initClient();

    // Test that SF client throws an exception for non-200 response.
    $response = new GuzzleResponse(456);
    $this->client
      ->expects($this
      ->any())
      ->method('httpRequest')
      ->willReturn($response);
    $this->client
      ->apiCall('');
  }

  /**
   * @covers ::apiCall
   */
  public function testReauthApiCall() {
    $this
      ->initClient();

    // Test that apiCall does auto-re-auth after 401 response.
    $response_401 = new GuzzleResponse(401);
    $response_200 = new GuzzleResponse(200);

    // First httpRequest() is position 4.
    // @TODO this is extremely brittle, exposes complexity in underlying client. Refactor this.
    $this->client
      ->expects($this
      ->at(3))
      ->method('httpRequest')
      ->willReturn($response_401);
    $this->client
      ->expects($this
      ->at(4))
      ->method('httpRequest')
      ->willReturn($response_200);
    $this->client
      ->apiCall('');
  }

  /**
   * @covers ::objects
   */
  public function testObjects() {
    $this
      ->initClient(array_merge($this->methods, [
      'apiCall',
    ]));
    $objects = [
      'sobjects' => [
        'Test' => [
          'name' => 'Test',
          'updateable' => TRUE,
        ],
        'NonUpdateable' => [
          'name' => 'NonUpdateable',
          'updateable' => FALSE,
        ],
      ],
    ];
    $cache = (object) [
      'created' => time(),
      'data' => $objects,
    ];
    unset($cache->data['sobjects']['NonUpdateable']);
    $this->cache
      ->expects($this
      ->at(0))
      ->method('get')
      ->willReturn($cache);
    $this->cache
      ->expects($this
      ->at(1))
      ->method('get')
      ->willReturn(FALSE);
    $this->client
      ->expects($this
      ->once())
      ->method('apiCall')
      ->willReturn($objects);

    // First call, from cache:
    $this
      ->assertEquals($cache->data['sobjects'], $this->client
      ->objects());

    // Second call, from apiCall()
    $this
      ->assertEquals($cache->data['sobjects'], $this->client
      ->objects());
  }

  /**
   * @covers ::query
   */
  public function testQuery() {
    $this
      ->initClient(array_merge($this->methods, [
      'apiCall',
    ]));
    $rawQueryResult = [
      'totalSize' => 1,
      'done' => TRUE,
      'records' => [
        0 => [
          'attributes' => [
            'type' => 'Foo',
            'url' => 'Bar',
          ],
          'Id' => $this->salesforce_id,
        ],
      ],
    ];
    $this->client
      ->expects($this
      ->once())
      ->method('apiCall')
      ->willReturn($rawQueryResult);

    // @TODO this doesn't seem like a very good test.
    $this
      ->assertEquals(new SelectQueryResult($rawQueryResult), $this->client
      ->query(new SelectQuery("")));
  }

  /**
   * @covers ::objectDescribe
   *
   * @expectedException Exception
   */
  public function testObjectDescribe() {
    $this
      ->initClient(array_merge($this->methods, [
      'apiCall',
    ]));
    $name = $this
      ->randomMachineName();

    // @TODO this is fugly, do we need a refactor on RestResponse?
    $restResponse = new RestResponse(new GuzzleResponse('200', [], json_encode([
      'name' => $name,
      'fields' => [
        [
          'name' => $this
            ->randomMachineName(),
          'label' => 'Foo Bar',
          $this
            ->randomMachineName() => $this
            ->randomMachineName(),
          $this
            ->randomMachineName() => [
            $this
              ->randomMachineName() => $this
              ->randomMachineName(),
            $this
              ->randomMachineName() => $this
              ->randomMachineName(),
          ],
        ],
        [
          'name' => $this
            ->randomMachineName(),
        ],
      ],
    ])));
    $this->client
      ->expects($this
      ->once())
      ->method('apiCall')
      ->willReturn($restResponse);

    // Test that we hit "apiCall" and get expected result:
    $result = $this->client
      ->objectDescribe($name);
    $expected = new RestResponseDescribe($restResponse);
    $this
      ->assertEquals($expected, $result);

    // Test that cache gets set correctly:
    $this->cache
      ->expects($this
      ->any())
      ->method('get')
      ->willReturn((object) [
      'data' => $expected,
      'created' => time(),
    ]);

    // Test that we hit cache when we call again.
    // (Otherwise, we'll blow the "once" condition)
    $this
      ->assertEquals($expected, $this->client
      ->objectDescribe($name));

    // @TODO what happens when we provide a name for non-existent SF table?
    // 404 exception?
    // Test that we throw an exception if name is not provided.
    $this->client
      ->objectDescribe('');
  }

  /**
   * @covers ::objectCreate
   */
  public function testObjectCreate() {
    $this
      ->initClient(array_merge($this->methods, [
      'apiCall',
    ]));
    $restResponse = new RestResponse(new GuzzleResponse('200', [], json_encode([
      'id' => $this->salesforce_id,
    ])));
    $sfid = new SFID($this->salesforce_id);
    $this->client
      ->expects($this
      ->once())
      ->method('apiCall')
      ->willReturn($restResponse);

    // @TODO this doesn't seem like a very good test.
    $this
      ->assertEquals($sfid, $this->client
      ->objectCreate('', []));
  }

  /**
   * @covers ::objectUpsert
   */
  public function testObjectUpsert() {
    $this
      ->initClient(array_merge($this->methods, [
      'apiCall',
      'objectReadbyExternalId',
    ]));
    $createResponse = new RestResponse(new GuzzleResponse('200', [], json_encode([
      'id' => $this->salesforce_id,
    ])));
    $updateResponse = new RestResponse(new GuzzleResponse('204', [], ''));
    $sfid = new SFID($this->salesforce_id);
    $sobject = new SObject([
      'id' => $this->salesforce_id,
      'attributes' => [
        'type' => 'dummy',
      ],
    ]);
    $this->client
      ->expects($this
      ->at(0))
      ->method('apiCall')
      ->willReturn($createResponse);
    $this->client
      ->expects($this
      ->at(1))
      ->method('apiCall')
      ->willReturn($updateResponse);
    $this->client
      ->expects($this
      ->once())
      ->method('objectReadbyExternalId')
      ->willReturn($sobject);

    // Ensure both upsert-create and upsert-update return the same value.
    $this
      ->assertEquals($sfid, $this->client
      ->objectUpsert('', '', '', []));
    $this
      ->assertEquals($sfid, $this->client
      ->objectUpsert('', '', '', []));
  }

  /**
   * @covers ::objectUpdate
   */
  public function testObjectUpdate() {
    $this
      ->initClient(array_merge($this->methods, [
      'apiCall',
    ]));
    $this->client
      ->expects($this
      ->once())
      ->method('apiCall')
      ->willReturn(NULL);
    $this
      ->assertNull($this->client
      ->objectUpdate('', '', []));
  }

  /**
   * @covers ::objectRead
   */
  public function testObjectRead() {
    $this
      ->initClient(array_merge($this->methods, [
      'apiCall',
    ]));
    $rawData = [
      'id' => $this->salesforce_id,
      'attributes' => [
        'type' => 'dummy',
      ],
    ];
    $this->client
      ->expects($this
      ->once())
      ->method('apiCall')
      ->willReturn($rawData);
    $this
      ->assertEquals(new SObject($rawData), $this->client
      ->objectRead('', ''));
  }

  /**
   * @covers ::objectReadbyExternalId
   */
  public function testObjectReadbyExternalId() {
    $this
      ->initClient(array_merge($this->methods, [
      'apiCall',
    ]));
    $rawData = [
      'id' => $this->salesforce_id,
      'attributes' => [
        'type' => 'dummy',
      ],
    ];
    $this->client
      ->expects($this
      ->once())
      ->method('apiCall')
      ->willReturn($rawData);
    $this
      ->assertEquals(new SObject($rawData), $this->client
      ->objectReadByExternalId('', '', ''));
  }

  /**
   * @covers ::objectDelete
   *
   * @expectedException \GuzzleHttp\Exception\RequestException
   */
  public function testObjectDelete() {
    $this
      ->initClient(array_merge($this->methods, [
      'apiCall',
    ]));

    // 3 tests for objectDelete:
    // 1. test that a successful delete returns null
    // 2. test that a 404 response gets eaten
    // 3. test that any other error response percolates.
    $this->client
      ->expects($this
      ->exactly(3))
      ->method('apiCall');
    $this->client
      ->expects($this
      ->at(0))
      ->method('apiCall')
      ->willReturn(NULL);
    $exception404 = new RequestException('', new GuzzleRequest('GET', ''), new GuzzleResponse(404, [], ''));
    $this->client
      ->expects($this
      ->at(1))
      ->method('apiCall')
      ->will($this
      ->throwException($exception404));

    // Test the objectDelete throws any other exception.
    $exceptionOther = new RequestException('', new GuzzleRequest('GET', ''), new GuzzleResponse(456, [], ''));
    $this->client
      ->expects($this
      ->at(2))
      ->method('apiCall')
      ->will($this
      ->throwException($exceptionOther));
    $this
      ->assertNull($this->client
      ->objectDelete('', ''));
    $this
      ->assertNull($this->client
      ->objectDelete('', ''));
    $this->client
      ->objectDelete('', '');
  }

  /**
   * @covers ::listResources
   */
  public function testListResources() {
    $this
      ->initClient(array_merge($this->methods, [
      'apiCall',
    ]));
    $restResponse = new RestResponse(new GuzzleResponse('204', [], json_encode([
      'foo' => 'bar',
      'zee' => 'bang',
    ])));
    $this->client
      ->expects($this
      ->once())
      ->method('apiCall')
      ->willReturn($restResponse);
    $this
      ->assertEquals(new RestResponseResources($restResponse), $this->client
      ->listResources());
  }

  /**
   * @covers ::getRecordTypes
   *
   * @expectedException Exception
   */
  public function testGetRecordTypes() {
    $this
      ->initClient(array_merge($this->methods, [
      'query',
    ]));
    $sObjectType = $this
      ->randomMachineName();
    $developerName = $this
      ->randomMachineName();
    $rawQueryResult = [
      'totalSize' => 1,
      'done' => TRUE,
      'records' => [
        0 => [
          'attributes' => [
            'type' => 'Foo',
            'url' => 'Bar',
          ],
          'SobjectType' => $sObjectType,
          'DeveloperName' => $developerName,
          'Id' => $this->salesforce_id,
        ],
      ],
    ];
    $recordTypes = [
      $sObjectType => [
        $developerName => new SObject($rawQueryResult['records'][0]),
      ],
    ];
    $cache = (object) [
      'created' => time(),
      'data' => $recordTypes,
    ];
    $this->cache
      ->expects($this
      ->at(1))
      ->method('get')
      ->willReturn(FALSE);
    $this->cache
      ->expects($this
      ->at(2))
      ->method('get')
      ->willReturn($cache);
    $this->cache
      ->expects($this
      ->at(3))
      ->method('get')
      ->willReturn($cache);
    $this->client
      ->expects($this
      ->once())
      ->method('query')
      ->willReturn(new SelectQueryResult($rawQueryResult));
    $this
      ->assertEquals($recordTypes, $this->client
      ->getRecordTypes());
    $this
      ->assertEquals($recordTypes[$sObjectType], $this->client
      ->getRecordTypes($sObjectType));
    $this->client
      ->getRecordTypes('fail');
  }

}

Members

Namesort descending Modifiers Type Description Overrides
PhpunitCompatibilityTrait::getMock Deprecated public function Returns a mock object for the specified class using the available method.
PhpunitCompatibilityTrait::setExpectedException Deprecated public function Compatibility layer for PHPUnit 6 to support PHPUnit 4 code.
RestClientTest::$modules protected static property
RestClientTest::initClient private function @covers ::__construct
RestClientTest::setUp public function Set up for each test. Overrides UnitTestCase::setUp
RestClientTest::testAuthorized public function @covers ::isAuthorized
RestClientTest::testExceptionApiCall public function @covers ::apiCall @expectedException Exception
RestClientTest::testGetRecordTypes public function @covers ::getRecordTypes
RestClientTest::testListResources public function @covers ::listResources
RestClientTest::testObjectCreate public function @covers ::objectCreate
RestClientTest::testObjectDelete public function @covers ::objectDelete
RestClientTest::testObjectDescribe public function @covers ::objectDescribe
RestClientTest::testObjectRead public function @covers ::objectRead
RestClientTest::testObjectReadbyExternalId public function @covers ::objectReadbyExternalId
RestClientTest::testObjects public function @covers ::objects
RestClientTest::testObjectUpdate public function @covers ::objectUpdate
RestClientTest::testObjectUpsert public function @covers ::objectUpsert
RestClientTest::testQuery public function @covers ::query
RestClientTest::testReauthApiCall public function @covers ::apiCall
RestClientTest::testSimpleApiCall public function @covers ::apiCall
UnitTestCase::$randomGenerator protected property The random generator.
UnitTestCase::$root protected property The app root. 1
UnitTestCase::assertArrayEquals protected function Asserts if two arrays are equal by sorting them first.
UnitTestCase::getBlockMockWithMachineName Deprecated protected function Mocks a block with a block plugin. 1
UnitTestCase::getClassResolverStub protected function Returns a stub class resolver.
UnitTestCase::getConfigFactoryStub public function Returns a stub config factory that behaves according to the passed array.
UnitTestCase::getConfigStorageStub public function Returns a stub config storage that returns the supplied configuration.
UnitTestCase::getContainerWithCacheTagsInvalidator protected function Sets up a container with a cache tags invalidator.
UnitTestCase::getRandomGenerator protected function Gets the random generator for the utility methods.
UnitTestCase::getStringTranslationStub public function Returns a stub translation manager that just returns the passed string.
UnitTestCase::randomMachineName public function Generates a unique random string containing letters and numbers.