salesforce-8.x-4.x-dev/tests/src/Unit/RestClientTest.php

tests/src/Unit/RestClientTest.php
<?php

namespace Drupal\Tests\salesforce\Unit;

use Drupal\Component\Datetime\TimeInterface;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Config\ConfigFactory;
use Drupal\Core\State\State;
use Drupal\salesforce\Entity\SalesforceAuthConfig;
use Drupal\salesforce\Rest\RestClient;
use Drupal\salesforce\Rest\RestResponse;
use Drupal\salesforce\Rest\RestResponseDescribe;
use Drupal\salesforce\Rest\RestResponseResources;
use Drupal\salesforce\SalesforceAuthProviderInterface;
use Drupal\salesforce\SalesforceAuthProviderPluginManager;
use Drupal\salesforce\SelectQuery;
use Drupal\salesforce\SelectQueryResult;
use Drupal\salesforce\SFID;
use Drupal\salesforce\SObject;
use Drupal\Tests\UnitTestCase;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Psr7\Request as GuzzleRequest;
use GuzzleHttp\Psr7\Response as GuzzleResponse;
use OAuth\OAuth2\Token\TokenInterface;

/**
 * @coversDefaultClass \Drupal\salesforce\Rest\RestClient
 * @group salesforce
 */
class RestClientTest extends UnitTestCase {

  /**
   * Required modules.
   *
   * @var array
   */
  protected static $modules = ['salesforce'];

  protected $authConfig;

  protected $authMan;

  protected $authProvider;

  protected $authToken;

  protected $cache;

  protected $client;

  protected $configFactory;

  protected $httpClient;

  protected $json;

  protected $methods;

  protected $salesforce_id;

  protected $state;

  protected $time;

  /**
   * Set up for each test.
   */
  public function setUp(): void {
    parent::setUp();
    $this->salesforce_id = '1234567890abcde';
    $this->methods = [
      'httpRequest',
    ];

    $this->httpClient = $this->getMockBuilder(Client::CLASS)->getMock();
    $this->configFactory =
      $this->getMockBuilder(ConfigFactory::CLASS)
        ->disableOriginalConstructor()
        ->getMock();
    $this->state =
      $this->getMockBuilder(State::CLASS)
        ->disableOriginalConstructor()
        ->getMock();
    $this->cache = $this->getMockBuilder(CacheBackendInterface::CLASS)
      ->getMock();
    $this->json = $this->getMockBuilder(Json::CLASS)->getMock();
    $this->time = $this->getMockBuilder(TimeInterface::CLASS)->getMock();
    $this->authToken = $this->getMockBuilder(TokenInterface::CLASS)->getMock();
    $this->authProvider = $this->getMockBuilder(SalesforceAuthProviderInterface::CLASS)
      ->disableOriginalConstructor()
      ->getMock();
    $this->authProvider->expects($this->any())
      ->method('getApiEndpoint')
      ->willReturn('https://example.com');
    $this->authConfig =
      $this->getMockBuilder(SalesforceAuthConfig::CLASS)
        ->disableOriginalConstructor()
        ->getMock();

    $this->authMan =
      $this->getMockBuilder(SalesforceAuthProviderPluginManager::CLASS)
        ->disableOriginalConstructor()
        ->getMock();
    $this->authMan->expects($this->any())
      ->method('getToken')
      ->willReturn($this->authToken);
    $this->authMan->expects($this->any())
      ->method('getProvider')
      ->willReturn($this->authProvider);
    $this->authMan->expects($this->any())
      ->method('getConfig')
      ->willReturn($this->authConfig);
    $this->authMan->expects($this->any())
      ->method('refreshToken')
      ->willReturn($this->authToken);
  }

  /**
   * @covers ::__construct
   */
  private function initClient($methods = NULL) {
    if (empty($methods)) {
      $methods = $this->methods;
    }
    $methods[] = 'getShortTermCacheLifetime';
    $methods[] = 'getLongTermCacheLifetime';

    $args = [
      $this->httpClient,
      $this->configFactory,
      $this->state,
      $this->cache,
      $this->json,
      $this->time,
      $this->authMan,
    ];

    $this->client = $this
      ->getMockBuilder(RestClient::CLASS)
      ->onlyMethods($methods)
      ->setConstructorArgs($args)
      ->getMock();

    $this->client->expects($this->any())
      ->method('getShortTermCacheLifetime')
      ->willReturn(0);

    $this->client->expects($this->any())
      ->method('getLongTermCacheLifetime')
      ->willReturn(0);
  }

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

    // Test that an apiCall returns a json-decoded value.
    $body = ['foo' => 'bar'];
    $response = new GuzzleResponse(200, ['Content-Type' => 'application/json'], json_encode($body));

    $this->client->expects($this->any())
      ->method('httpRequest')
      ->willReturn($response);

    $result = $this->client->apiCall('');
    $this->assertEquals($result, $body);
  }

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

    // Test that an apiCall returns a CSV string value.
    $body = <<<EOT
    "Id","Name"
    "005R0000000UyrWIAS","Johnny B. Goode"
    "005R0000000GiwjIAC","Prince Rogers Nelson"
    "005R0000000GiwoIAC","Robert Allen Zimmerman"
    EOT;

    $response = new GuzzleResponse(200, ['Content-Type' => 'text/csv'], $body);

    $this->client->expects($this->any())
      ->method('httpRequest')
      ->willReturn($response);

    $result = $this->client->apiCall('');
    $this->assertEquals($result, $body);
  }

  /**
   * @covers ::apiCall
   */
  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->expectException(\Exception::class);
    $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);

    // @todo this is extremely brittle, exposes complexity in underlying client. Refactor this.
    $this->client->expects($this->exactly(2))
      ->method('httpRequest')
      ->willReturnOnConsecutiveCalls(
        $response_401,
        $response_200
      );

    $this->client->apiCall('');
  }

  /**
   * @covers ::objects
   */
  public function testObjectsFromCache() {
    $this->initClient(array_merge($this->methods, ['apiCall']));
    $objects = [
      'sobjects' => [
        'Test' => [
          'name' => 'Test',
          'updateable' => TRUE,
        ],
      ],
    ];
    $cache = (object) [
      'created' => time(),
      'data' => $objects,
    ];

    $this->cache->expects($this->once())
      ->method('get')
      ->willReturn($cache);

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

  }

  /**
   * @covers ::objects
   */
  public function testObjectsFromApiCall() {
    $this->initClient(array_merge($this->methods, ['apiCall']));
    $objects = [
      'sobjects' => [
        'Test' => [
          'name' => 'Test',
          'updateable' => TRUE,
        ],
      ],
    ];
    $this->cache->expects($this->once())
      ->method('get')
      ->willReturn(FALSE);
    $this->client->expects($this->once())
      ->method('apiCall')
      ->willReturn($objects);

    // Get objects from apiCall()
    $this->assertEquals($objects['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
   */
  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', ['Content-Type' => 'application/json'], 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->expectException(\Exception::class);
    $this->client->objectDescribe('');
  }

  /**
   * @covers ::objectCreate
   */
  public function testObjectCreate() {
    $this->initClient(array_merge($this->methods, ['apiCall']));
    $restResponse = new RestResponse(
      new GuzzleResponse('200', ['Content-Type' => 'application/json'], 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', ['Content-Type' => 'application/json'], 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->exactly(2))
      ->method('apiCall')
      ->willReturnOnConsecutiveCalls(
        $createResponse,
        $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
   *
   * 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.
   */
  public function testObjectDeleteSuccess() {
    $this->initClient(array_merge($this->methods, [
      'apiCall',
    ]));

    $this->client->expects($this->once())
      ->method('apiCall')
      ->willReturn(NULL);

    $this->assertNull($this->client->objectDelete('', ''));
  }

  /**
   * @covers ::objectDelete
   */
  public function testObjectDelete404() {
    $this->initClient(array_merge($this->methods, [
      'apiCall',
    ]));

    $exception404 = new RequestException('', new GuzzleRequest('GET', ''), new GuzzleResponse(404, [], ''));
    $this->client->expects($this->once())
      ->method('apiCall')
      ->will($this->throwException($exception404));

    $this->assertNull($this->client->objectDelete('', ''));
  }

  /**
   * @covers ::objectDelete
   */
  public function testObjectDeleteException() {
    $this->initClient(array_merge($this->methods, [
      'apiCall',
    ]));

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

  /**
   * @covers ::listResources
   */
  public function testListResources() {
    $this->initClient(array_merge($this->methods, [
      'apiCall',
    ]));
    $restResponse = new RestResponse(new GuzzleResponse('204', ['Content-Type' => 'application/json'], 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
   */
  public function testGetRecordTypesAll() {
    $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]),
      ],
    ];

    $this->cache->expects($this->once())
      ->method('get')
      ->willReturn(FALSE);
    $this->client->expects($this->once())
      ->method('query')
      ->willReturn(new SelectQueryResult($rawQueryResult));

    $this->assertEquals($recordTypes, $this->client->getRecordTypes());
  }

  /**
   * @covers ::getRecordTypes
   */
  public function testGetRecordTypesSingle() {
    $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->once())
      ->method('get')
      ->willReturn($cache);

    $this->assertEquals($recordTypes[$sObjectType], $this->client->getRecordTypes($sObjectType));
  }

  /**
   * @covers ::getRecordTypes
   */
  public function testGetRecordTypesNonexistent() {
    $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->once())
      ->method('get')
      ->willReturn($cache);

    $this->assertFalse($this->client->getRecordTypes('fail'));
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc