Add line item acl tests
authoreileen <emcnaughton@wikimedia.org>
Fri, 28 Aug 2020 03:49:22 +0000 (15:49 +1200)
committerSeamus Lee <seamuslee001@gmail.com>
Fri, 28 Aug 2020 07:13:16 +0000 (17:13 +1000)
ext/financialacls/phpunit.xml.dist [new file with mode: 0644]
ext/financialacls/tests/phpunit/LineItemTest.php [new file with mode: 0644]
ext/financialacls/tests/phpunit/bootstrap.php [new file with mode: 0644]

diff --git a/ext/financialacls/phpunit.xml.dist b/ext/financialacls/phpunit.xml.dist
new file mode 100644 (file)
index 0000000..fc8f870
--- /dev/null
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<phpunit backupGlobals="false" backupStaticAttributes="false" colors="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" bootstrap="tests/phpunit/bootstrap.php">
+  <testsuites>
+    <testsuite name="My Test Suite">
+      <directory>./tests/phpunit</directory>
+    </testsuite>
+  </testsuites>
+  <filter>
+    <whitelist>
+      <directory suffix=".php">./</directory>
+    </whitelist>
+  </filter>
+  <listeners>
+    <listener class="Civi\Test\CiviTestListener">
+      <arguments/>
+    </listener>
+  </listeners>
+</phpunit>
diff --git a/ext/financialacls/tests/phpunit/LineItemTest.php b/ext/financialacls/tests/phpunit/LineItemTest.php
new file mode 100644 (file)
index 0000000..71eb638
--- /dev/null
@@ -0,0 +1,114 @@
+<?php
+
+use CRM_Financialacls_ExtensionUtil as E;
+use Civi\Test\HeadlessInterface;
+use Civi\Test\HookInterface;
+use Civi\Test\TransactionalInterface;
+use Civi\Api4\PriceField;
+
+/**
+ * FIXME - Add test description.
+ *
+ * Tips:
+ *  - With HookInterface, you may implement CiviCRM hooks directly in the test class.
+ *    Simply create corresponding functions (e.g. "hook_civicrm_post(...)" or similar).
+ *  - With TransactionalInterface, any data changes made by setUp() or test****() functions will
+ *    rollback automatically -- as long as you don't manipulate schema or truncate tables.
+ *    If this test needs to manipulate schema or truncate tables, then either:
+ *       a. Do all that using setupHeadless() and Civi\Test.
+ *       b. Disable TransactionalInterface, and handle all setup/teardown yourself.
+ *
+ * @group headless
+ */
+class LineItemTest extends \PHPUnit\Framework\TestCase implements HeadlessInterface, HookInterface, TransactionalInterface {
+
+  use Civi\Test\ContactTestTrait;
+  use Civi\Test\Api3TestTrait;
+
+  /**
+   * @return \Civi\Test\CiviEnvBuilder
+   * @throws \CRM_Extension_Exception_ParseException
+   */
+  public function setUpHeadless() {
+    // Civi\Test has many helpers, like install(), uninstall(), sql(), and sqlFile().
+    // See: https://docs.civicrm.org/dev/en/latest/testing/phpunit/#civitest
+    return \Civi\Test::headless()
+      ->installMe(__DIR__)
+      ->apply();
+  }
+
+  /**
+   * Test api applies permissions on line item actions (delete & get).
+   */
+  public function testLineItemApiPermissions() {
+    $contact1 = $this->individualCreate();
+    $defaultPriceFieldID = $this->getDefaultPriceFieldID();
+    $this->callAPISuccess('Order', 'create', [
+      'financial_type_id' => 'Donation',
+      'contact_id' => $contact1,
+      'line_items' => [
+        [
+          'line_item' => [
+            [
+              'financial_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'financial_type_id', 'Donation'),
+              'line_total' => 40,
+              'price_field_id' => $defaultPriceFieldID,
+              'qty' => 1,
+            ],
+            [
+              'financial_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'financial_type_id', 'Member Dues'),
+              'line_total' => 50,
+              'price_field_id' => $defaultPriceFieldID,
+              'qty' => 1,
+            ],
+          ],
+        ],
+      ],
+    ]);
+
+    $this->setPermissions([
+      'access CiviCRM',
+      'access CiviContribute',
+      'edit contributions',
+      'delete in CiviContribute',
+      'view contributions of type Donation',
+      'delete contributions of type Donation',
+    ]);
+    Civi::settings()->set('acl_financial_type', TRUE);
+    $this->createLoggedInUser();
+
+    $lineItems = $this->callAPISuccess('LineItem', 'get', ['sequential' => TRUE])['values'];
+    $this->assertCount(2, $lineItems);
+    $this->callAPISuccessGetCount('LineItem', ['check_permissions' => TRUE], 1);
+
+    $this->callAPISuccess('LineItem', 'Delete', ['check_permissions' => TRUE, 'id' => $lineItems[0]['id']]);
+    $this->callAPIFailure('LineItem', 'Delete', ['check_permissions' => TRUE, 'id' => $lineItems[1]['id']]);
+  }
+
+  /**
+   * Set ACL permissions, overwriting any existing ones.
+   *
+   * @param array $permissions
+   *   Array of permissions e.g ['access CiviCRM','access CiviContribute'],
+   */
+  protected function setPermissions($permissions) {
+    CRM_Core_Config::singleton()->userPermissionClass->permissions = $permissions;
+    if (isset(\Civi::$statics['CRM_Financial_BAO_FinancialType'])) {
+      unset(\Civi::$statics['CRM_Financial_BAO_FinancialType']);
+    }
+  }
+
+  /**
+   * @return mixed
+   * @throws \API_Exception
+   * @throws \Civi\API\Exception\UnauthorizedException
+   */
+  protected function getDefaultPriceFieldID(): int {
+    return PriceField::get()
+      ->addWhere('price_set_id:name', '=', 'default_contribution_amount')
+      ->addWhere('name', '=', 'contribution_amount')
+      ->addWhere('html_type', '=', 'Text')
+      ->addSelect('id')->execute()->first()['id'];
+  }
+
+}
diff --git a/ext/financialacls/tests/phpunit/bootstrap.php b/ext/financialacls/tests/phpunit/bootstrap.php
new file mode 100644 (file)
index 0000000..a5b4925
--- /dev/null
@@ -0,0 +1,63 @@
+<?php
+
+ini_set('memory_limit', '2G');
+ini_set('safe_mode', 0);
+// phpcs:disable
+eval(cv('php:boot --level=classloader', 'phpcode'));
+// phpcs:enable
+// Allow autoloading of PHPUnit helper classes in this extension.
+$loader = new \Composer\Autoload\ClassLoader();
+$loader->add('CRM_', __DIR__);
+$loader->add('Civi\\', __DIR__);
+$loader->add('api_', __DIR__);
+$loader->add('api\\', __DIR__);
+$loader->register();
+
+/**
+ * Call the "cv" command.
+ *
+ * @param string $cmd
+ *   The rest of the command to send.
+ * @param string $decode
+ *   Ex: 'json' or 'phpcode'.
+ * @return string
+ *   Response output (if the command executed normally).
+ * @throws \RuntimeException
+ *   If the command terminates abnormally.
+ */
+function cv($cmd, $decode = 'json') {
+  $cmd = 'cv ' . $cmd;
+  $descriptorSpec = array(0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => STDERR);
+  $oldOutput = getenv('CV_OUTPUT');
+  putenv("CV_OUTPUT=json");
+
+  // Execute `cv` in the original folder. This is a work-around for
+  // phpunit/codeception, which seem to manipulate PWD.
+  $cmd = sprintf('cd %s; %s', escapeshellarg(getenv('PWD')), $cmd);
+
+  $process = proc_open($cmd, $descriptorSpec, $pipes, __DIR__);
+  putenv("CV_OUTPUT=$oldOutput");
+  fclose($pipes[0]);
+  $result = stream_get_contents($pipes[1]);
+  fclose($pipes[1]);
+  if (proc_close($process) !== 0) {
+    throw new RuntimeException("Command failed ($cmd):\n$result");
+  }
+  switch ($decode) {
+    case 'raw':
+      return $result;
+
+    case 'phpcode':
+      // If the last output is /*PHPCODE*/, then we managed to complete execution.
+      if (substr(trim($result), 0, 12) !== "/*BEGINPHP*/" || substr(trim($result), -10) !== "/*ENDPHP*/") {
+        throw new \RuntimeException("Command failed ($cmd):\n$result");
+      }
+      return $result;
+
+    case 'json':
+      return json_decode($result, 1);
+
+    default:
+      throw new RuntimeException("Bad decoder format ($decode)");
+  }
+}