commerce_api-8.x-1.x-dev/tests/src/Kernel/Resource/CartAddResourceTest.php

tests/src/Kernel/Resource/CartAddResourceTest.php
<?php

namespace Drupal\Tests\commerce_api\Kernel\Resource;

use Drupal\commerce_api\Resource\CartAddResource;
use Drupal\commerce_order\Entity\Order;
use Drupal\commerce_price\Price;
use Drupal\commerce_product\Entity\Product;
use Drupal\commerce_product\Entity\ProductVariation;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Entity\EntityInterface;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\jsonapi\Exception\UnprocessableHttpEntityException;
use Drupal\jsonapi\JsonApiResource\JsonApiDocumentTopLevel;
use Drupal\jsonapi\JsonApiResource\ResourceObject;
use Drupal\Tests\commerce_api\Kernel\KernelTestBase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\ConflictHttpException;
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;

/**
 * Tests the CartAddResource.
 *
 * @group commerce_api
 */
final class CartAddResourceTest extends KernelTestBase {

  /**
   * Tests exception when a non-purchasable entity provided.
   */
  public function testNonPurchasableEntityType() {
    $entity = EntityTest::create(['id' => 1, 'type' => 'entity_test']);
    $entity->save();

    $request = $this->prophesize(Request::class);
    $request->getContent()->willReturn(Json::encode([
      'data' => [
        $this->createJsonapiData($entity, 1),
      ],
    ]));

    $this->expectException(BadRequestHttpException::class);
    $this->expectExceptionMessage('The provided type (entity_test--entity_test) does not match the destination resource types (product-variation--default).');

    $controller = $this->getController();
    $controller->process($request->reveal(), ['product-variation--default']);
  }

  /**
   * Tests exception when product has no stores.
   */
  public function testNoStoresException() {
    $product_variation = $this->createTestProductVariation(['stores' => []], []);

    $request = $this->prophesize(Request::class);
    $request->getContent()->willReturn(Json::encode([
      'data' => [
        $this->createJsonapiData($product_variation, 1),
      ],
    ]));

    $this->expectException(UnprocessableEntityHttpException::class);
    $this->expectExceptionMessage('The given entity is not assigned to any store.');

    $controller = $this->getController();
    $controller->process($request->reveal(), ['product-variation--default']);
  }

  /**
   * Tests exception when product's stores is not a current store.
   */
  public function testNotCurrentStoreException() {
    $additional_store1 = $this->createStore();
    $additional_store2 = $this->createStore();

    $product_variation = $this->createTestProductVariation([
      'stores' => [$additional_store2->id(), $additional_store1->id()],
    ], []);

    $request = $this->prophesize(Request::class);
    $request->getContent()->willReturn(Json::encode([
      'data' => [
        $this->createJsonapiData($product_variation, 1),
      ],
    ]));

    $this->expectException(UnprocessableEntityHttpException::class);
    $this->expectExceptionMessage("The given entity can't be purchased from the current store.");

    $controller = $this->getController();
    $controller->process($request->reveal(), ['product-variation--default']);
  }

  /**
   * Tests exception when product's stores is not a current store.
   */
  public function testAddedToCart() {
    /** @var \Drupal\commerce_product\Entity\Product $product */
    $product = Product::create([
      'type' => 'default',
      'stores' => [$this->store->id()],
    ]);
    /** @var \Drupal\commerce_product\Entity\ProductVariation $product_variation */
    $product_variation = ProductVariation::create([
      'type' => 'default',
      'title' => $this->randomString(),
      'sku' => 'JSONAPI_SKU',
      'status' => 1,
      'price' => new Price('4.00', 'USD'),
    ]);
    $product_variation->save();
    $product->addVariation($product_variation);
    $product->save();

    $request = Request::create('https://localhost/cart/add', 'POST', [], [], [], [], Json::encode([
      'data' => [
        $this->createJsonapiData($product_variation, 1),
      ],
    ]));

    $controller = $this->getController();
    $response = $controller->process($request, ['product-variation--default']);
    $this->assertInstanceOf(JsonApiDocumentTopLevel::class, $response->getResponseData());
    $this->assertCount(1, $response->getResponseData()->getData()->getIterator());
    $resource_object = $response->getResponseData()->getData()->getIterator()->offsetGet(0);
    assert($resource_object instanceof ResourceObject);
    $this->assertEquals('order-item--default', $resource_object->getTypeName());
    $purchased_entity = $resource_object->getField('purchased_entity');
    $this->assertEquals($product_variation->id(), $purchased_entity->target_id);
    $this->assertEquals(1, $resource_object->getField('quantity')->value);

    $request = Request::create('https://localhost/cart/add', 'POST', [], [], [], [], Json::encode([
      'data' => [
        $this->createJsonapiData($product_variation, 1),
      ],
    ]));
    $response = $controller->process($request, ['product-variation--default']);
    $this->assertCount(1, $response->getResponseData()->getData()->getIterator());
    $resource_object = $response->getResponseData()->getData()->getIterator()->offsetGet(0);
    $this->assertEquals(2, $resource_object->getField('quantity')->value);
  }

  /**
   * Test the combine meta value.
   */
  public function testCombineAndArity() {
    /** @var \Drupal\commerce_product\Entity\Product $product */
    $product = Product::create([
      'type' => 'default',
      'stores' => [$this->store->id()],
    ]);
    /** @var \Drupal\commerce_product\Entity\ProductVariation $product_variation */
    $product_variation = ProductVariation::create([
      'type' => 'default',
      'title' => $this->randomString(),
      'sku' => 'JSONAPI_SKU',
      'status' => 1,
      'price' => new Price('4.00', 'USD'),
    ]);
    $product_variation->save();
    $product->addVariation($product_variation);
    $product->save();

    $controller = $this->getController();
    $arity0 = $this->createJsonapiData($product_variation, 2);
    $arity0['meta']['combine'] = FALSE;
    $arity0['meta']['arity'] = 0;
    $arity1 = $this->createJsonapiData($product_variation, 1);
    $arity1['meta']['combine'] = FALSE;
    $arity1['meta']['arity'] = 1;
    $request = Request::create('https://localhost/cart/add', 'POST', [], [], [], [], Json::encode([
      'data' => [
        $arity0,
        $arity1,
      ],
    ]));
    $response = $controller->process($request, ['product-variation--default']);
    $this->assertCount(2, $response->getResponseData()->getData()->getIterator());
    $resource_object = $response->getResponseData()->getData()->getIterator()->offsetGet(0);
    $this->assertEquals(2, $resource_object->getField('quantity')->value);
    $resource_object = $response->getResponseData()->getData()->getIterator()->offsetGet(1);
    $this->assertEquals(1, $resource_object->getField('quantity')->value);
  }

  /**
   * Test the returned order item has the resolved price.
   */
  public function testOrderItemHasResolvedPrice() {
    $this->installModule('commerce_price_test');
    $product_variation = $this->createTestProductVariation([
      'stores' => [$this->store->id()],
    ], [
      'sku' => 'TEST_JSONAPI_SKU',
      'status' => 1,
      'price' => new Price('4.00', 'USD'),
    ]);

    $request = Request::create('https://localhost/cart/add', 'POST', [], [], [], [], Json::encode([
      'data' => [
        $this->createJsonapiData($product_variation, 1),
      ],
    ]));

    $controller = $this->getController();
    $response = $controller->process($request, ['product-variation--default']);

    $resource_object = $response->getResponseData()->getData()->getIterator()->offsetGet(0);
    assert($resource_object instanceof ResourceObject);
    $this->assertEquals('order-item--default', $resource_object->getTypeName());
    $purchased_entity = $resource_object->getField('purchased_entity');
    $this->assertEquals($product_variation->id(), $purchased_entity->target_id);
    $this->assertEquals(1, $resource_object->getField('quantity')->value);
    $this->assertEquals(new Price('1.0', 'USD'), $resource_object->getField('unit_price')->first()->toPrice());

  }

  /**
   * Tests that purchased_entity validation occurs.
   */
  public function testPurchasedEntityValidationConstraint() {
    $this->installModule('commerce_order_test');
    $product_variation = $this->createTestProductVariation([
      'stores' => [$this->store->id()],
    ], [
      'sku' => 'TEST_JSONAPI_SKU',
      'status' => 1,
      'price' => new Price('4.00', 'USD'),
    ]);

    $request = Request::create('https://localhost/cart/add', 'POST', [], [], [], [], Json::encode([
      'data' => [
        $this->createJsonapiData($product_variation, 1),
      ],
    ]));
    $controller = $this->getController();

    try {
      $controller->process($request, ['product-variation--default']);
      $this->fail(sprintf('Expected %s to be thrown', UnprocessableHttpEntityException::class));
    }
    catch (UnprocessableHttpEntityException $exception) {
      $violations = $exception->getViolations();
      $this->assertEquals(1, $violations->count());
      $this->assertEquals(['purchased_entity'], $violations->getFieldNames());
    }
  }

  /**
   * Tests the order version mismatch exception.
   */
  public function testOrderVersionMismatchException() {
    /** @var \Drupal\commerce_product\Entity\Product $product */
    $product = Product::create([
      'type' => 'default',
      'stores' => [$this->store->id()],
    ]);
    /** @var \Drupal\commerce_product\Entity\ProductVariation $product_variation */
    $product_variation = ProductVariation::create([
      'type' => 'default',
      'title' => $this->randomString(),
      'sku' => 'JSONAPI_SKU',
      'status' => 1,
      'price' => new Price('4.00', 'USD'),
    ]);
    $product_variation->save();
    $product->addVariation($product_variation);
    $product->save();

    $request = Request::create('https://localhost/cart/add', 'POST', [], [], [], [], Json::encode([
      'data' => [
        $this->createJsonapiData($product_variation, 1),
      ],
    ]));
    $controller = $this->getController();

    $controller->process($request, ['product-variation--default']);
    $order = Order::load(1);
    $this->assertCount(1, $order->getItems());
    $order->setVersion(1);
    try {
      $controller->process($request, ['product-variation--default']);
      $this->fail(sprintf('Expected %s to be thrown', ConflictHttpException::class));
    }
    catch (ConflictHttpException $exception) {
      $order = $this->reloadEntity($order);
      $this->assertCount(1, $order->getItems());
      $this->assertEquals($exception->getMessage(), 'Attempted to save order 1 with version 1. Current version is 2.');
    }
  }

  /**
   * Tests that defining invalid meta field values will throw an error.
   */
  public function testMetaContainsInvalidFieldValue() {
    /** @var \Drupal\commerce_product\Entity\Product $product */
    $product = Product::create([
      'type' => 'default',
      'stores' => [$this->store->id()],
    ]);
    /** @var \Drupal\commerce_product\Entity\ProductVariation $product_variation */
    $product_variation = ProductVariation::create([
      'type' => 'default',
      'sku' => 'JSONAPI_SKU',
      'status' => 1,
      'price' => new Price('4.00', 'USD'),
    ]);
    $product_variation->save();
    $product->addVariation($product_variation);
    $product->save();

    $request = Request::create('https://localhost/cart/add', 'POST', [], [], [], [], Json::encode([
      'data' => [
        // 'field_invalid' does not exist on the order item entity:
        $this->createJsonapiData($product_variation, 1, [
          'field_invalid' => 1,
        ]),
      ],
    ]));
    $controller = $this->getController();

    try {
      $controller->process($request, ['product-variation--default']);
      $this->fail(sprintf('Expected %s to be thrown', UnprocessableEntityHttpException::class));
    }
    catch (UnprocessableEntityHttpException $exception) {
      $message = $exception->getMessage();
      $this->assertEquals("The given meta fields 'field_invalid' are not valid entity fields.", $message);
    }
  }

  /**
   * Tests that defining valid meta field values will NOT throw an error.
   */
  public function testMetaContainsValidFieldValue() {
    /** @var \Drupal\commerce_product\Entity\Product $product */
    $product = Product::create([
      'type' => 'default',
      'stores' => [$this->store->id()],
    ]);
    /** @var \Drupal\commerce_product\Entity\ProductVariation $product_variation */
    $product_variation = ProductVariation::create([
      'type' => 'default',
      'title' => $this->randomString(),
      'sku' => 'JSONAPI_SKU',
      'status' => 1,
      'price' => new Price('4.00', 'USD'),
    ]);
    $product_variation->save();
    $product->addVariation($product_variation);
    $product->save();

    // Create field storage for the valid text field:
    FieldStorageConfig::create([
      'field_name' => 'field_text',
      'entity_type' => 'commerce_order_item',
      'type' => 'text',
    ])->save();
    // Create the field instance for the valid text field:
    FieldConfig::create([
      'field_name' => 'field_text',
      'entity_type' => 'commerce_order_item',
      'bundle' => 'default',
    ])->save();

    $request = Request::create('https://localhost/cart/add', 'POST', [], [], [], [], Json::encode([
      'data' => [
        $this->createJsonapiData($product_variation, 1, [
          'field_text' => 'This is a test',
        ]),
      ],
    ]));
    $controller = $this->getController();
    $response = $controller->process($request, ['product-variation--default']);
    $resource_object = $response->getResponseData()->getData()->getIterator()->offsetGet(0);
    $textValue = $resource_object->getField('field_text')->value;
    $this->assertEquals('This is a test', $textValue);
  }

  /**
   * Tests that manually overriding a base field value will not work.
   */
  public function testMetaContainsInvalidBaseFieldValue() {
    /** @var \Drupal\commerce_product\Entity\Product $product */
    $product = Product::create([
      'type' => 'default',
      'stores' => [$this->store->id()],
    ]);
    /** @var \Drupal\commerce_product\Entity\ProductVariation $product_variation */
    $product_variation = ProductVariation::create([
      'type' => 'default',
      'sku' => 'JSONAPI_SKU',
      'status' => 1,
      'price' => new Price('4.00', 'USD'),
    ]);
    $product_variation->save();
    $product->addVariation($product_variation);
    $product->save();

    $request = Request::create('https://localhost/cart/add', 'POST', [], [], [], [], Json::encode([
      'data' => [
        // Try manually overriding the price of the order item:
        $this->createJsonapiData($product_variation, 1, [
          'overridden_unit_price' => TRUE,
          'unit_price' => [
            'number' => 400.00,
            'currency_code' => 'USD',
          ],
        ]),
      ],
    ]));
    $controller = $this->getController();

    try {
      $controller->process($request, ['product-variation--default']);
      $this->fail(sprintf('Expected %s to be thrown', UnprocessableEntityHttpException::class));
    }
    catch (UnprocessableEntityHttpException $exception) {
      $message = $exception->getMessage();
      $this->assertEquals("You are not allowed to change base field values.", $message);
    }
  }

  /**
   * Tests if the meta fields are validated correctly.
   */
  public function testOrderItemValidation() {
    /** @var \Drupal\commerce_product\Entity\Product $product */
    $product = Product::create([
      'type' => 'default',
      'stores' => [$this->store->id()],
    ]);
    /** @var \Drupal\commerce_product\Entity\ProductVariation $product_variation */
    $product_variation = ProductVariation::create([
      'type' => 'default',
      'sku' => 'JSONAPI_SKU',
      'status' => 1,
      'price' => new Price('4.00', 'USD'),
    ]);
    $product_variation->save();
    $product->addVariation($product_variation);
    $product->save();

    // Create field storage for a text field:
    FieldStorageConfig::create([
      'field_name' => 'field_bool',
      'entity_type' => 'commerce_order_item',
      'type' => 'boolean',
    ])->save();
    // Create the field instance for a text field:
    FieldConfig::create([
      'field_name' => 'field_bool',
      'entity_type' => 'commerce_order_item',
      'bundle' => 'default',
    ])->save();

    $request = Request::create('https://localhost/cart/add', 'POST', [], [], [], [], Json::encode([
      'data' => [
        // Try providing an invalid field value and see if it throws an error:
        $this->createJsonapiData($product_variation, 1, [
          'field_bool' => 'Invalid value',
        ]),
      ],
    ]));
    $controller = $this->getController();

    try {
      $controller->process($request, ['product-variation--default']);
      $this->fail(sprintf('Expected %s to be thrown', UnprocessableHttpEntityException::class));
    }
    catch (UnprocessableHttpEntityException $exception) {
      $violations = $exception->getViolations();
      $this->assertEquals(1, $violations->count());
      $this->assertEquals(['field_bool'], $violations->getFieldNames());
    }
  }

  /**
   * Creates data array for the JSON:API document.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The entity.
   * @param int $quantity
   *   The quantity.
   * @param array $field_values
   *   Optional field data.
   *
   * @return array
   *   The data array.
   */
  private function createJsonapiData(EntityInterface $entity, $quantity, array $field_values = []) {
    $resource_type = $this->container->get('jsonapi.resource_type.repository')->get(
      $entity->getEntityTypeId(),
      $entity->bundle()
    );
    assert($resource_type !== NULL);
    return [
      'type' => $resource_type->getTypeName(),
      'id' => $entity->uuid(),
      'meta' => [
        'quantity' => $quantity,
      ] + $field_values,
    ];
  }

  /**
   * Gets the controller to test.
   *
   * @return \Drupal\commerce_api\Resource\CartAddResource
   *   The controller.
   *
   * @throws \Exception
   */
  protected function getController() {
    $controller = new CartAddResource(
      $this->container->get('commerce_cart.cart_provider'),
      $this->container->get('commerce_cart.cart_manager'),
      $this->container->get('commerce_api.jsonapi_controller_shim'),
      $this->container->get('commerce_order.chain_order_type_resolver'),
      $this->container->get('commerce_store.current_store'),
      $this->container->get('commerce_price.chain_price_resolver'),
      $this->container->get('entity.repository'),
      $this->container->get('current_user'),
      $this->container->get('renderer'),
      $this->container->get('database')
    );
    $controller->setResourceResponseFactory($this->container->get('jsonapi_resources.resource_response_factory'));
    $controller->setResourceTypeRepository($this->container->get('jsonapi.resource_type.repository'));
    $controller->setEntityTypeManager($this->container->get('entity_type.manager'));
    $controller->setEntityAccessChecker($this->container->get('jsonapi_resources.entity_access_checker'));
    return $controller;
  }

}

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

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