4 * Class CRM_Upgrade_SnapshotTest
7 class CRM_Upgrade_SnapshotTest
extends CiviUnitTestCase
{
9 protected function setUp(): void
{
11 CRM_Upgrade_Snapshot
::$pageSize = 10;
12 CRM_Upgrade_Snapshot
::$cleanupAfter = 4;
15 public function testTableNames_good() {
16 $this->assertEquals('snap_civicrm_v5_45_stuff', CRM_Upgrade_Snapshot
::createTableName('civicrm', '5.45', 'stuff'));
17 $this->assertEquals('snap_civicrm_v5_50_stuffy_things', CRM_Upgrade_Snapshot
::createTableName('civicrm', '5.50', 'stuffy_things'));
18 $this->assertEquals('snap_oauth_client_v12_34_ext_things', CRM_Upgrade_Snapshot
::createTableName('oauth_client', '12.34', 'ext_things'));
19 $this->assertEquals('snap_oauth_client_v0_1234_ext_things', CRM_Upgrade_Snapshot
::createTableName('oauth_client', '0.1234', 'ext_things'));
22 public function testTableNames_bad() {
24 CRM_Upgrade_Snapshot
::createTableName('civicrm', '5.45', 'ab&cd');
25 $this->fail('Accepted invalid name');
27 catch (CRM_Core_Exception
$e) {
28 $this->assertRegExp('/Malformed snapshot name/', $e->getMessage());
31 CRM_Upgrade_Snapshot
::createTableName('civicrm', '5.45', 'loremipsumdolorsitametconsecteturadipiscingelitseddoeiusmod');
32 $this->fail('Accepted excessive name');
34 catch (CRM_Core_Exception
$e) {
35 $this->assertRegExp('/Snapshot name is too long/', $e->getMessage());
40 * This example creates a snapshot based on particular sliver of data (ie
41 * the "display_name" and "sort_name" for "Individual" records). It ensures that:
43 * 1. Some columns are copied - while other columns are not (based on `select()`).
44 * 2. Some rows are copied - while other rows are not (based on `where()`).
45 * 3. Multiple pages of data are copied.
47 public function testContent(): void
{
48 for ($i = 0; $i < 15; $i++
) {
49 $this->individualCreate([], $i);
50 $this->organizationCreate([], $i);
53 $this->runAll(CRM_Upgrade_Snapshot
::createTasks('civicrm', '5.45', 'names', CRM_Utils_SQL_Select
::from('civicrm_contact')
54 ->select('id, display_name, sort_name, modified_date')
55 ->where('contact_type = "Individual"')
57 $this->assertTrue(CRM_Core_DAO
::checkTableExists('snap_civicrm_v5_45_names'));
58 $this->assertSameSchema('civicrm_contact.display_name', 'snap_civicrm_v5_45_names.display_name');
59 $this->assertSameSchema('civicrm_contact.sort_name', 'snap_civicrm_v5_45_names.sort_name');
60 $this->assertSameSchema('civicrm_contact.modified_date', 'snap_civicrm_v5_45_names.modified_date');
61 $this->assertTrue(CRM_Core_BAO_SchemaHandler
::checkIfFieldExists('civicrm_contact', 'legal_name'));
62 $this->assertFalse(CRM_Core_BAO_SchemaHandler
::checkIfFieldExists('snap_civicrm_v5_45_names', 'legal_name'));
64 $liveContacts = CRM_Core_DAO
::singleValueQuery('SELECT count(*) FROM civicrm_contact');
65 $liveIndividuals = CRM_Core_DAO
::singleValueQuery('SELECT count(*) FROM civicrm_contact WHERE contact_type = "Individual"');
66 $snapCount = CRM_Core_DAO
::singleValueQuery('SELECT count(*) FROM snap_civicrm_v5_45_names');
67 $this->assertEquals($liveIndividuals, $snapCount, 'The snapshot should have as many records as live table.');
68 $this->assertTrue($liveContacts > $liveIndividuals);
69 $this->assertGreaterThan(CRM_Upgrade_Snapshot
::$pageSize, $snapCount, "There should be more than 1 page of data in the snapshot. Found $snapCount records.");
71 CRM_Core_DAO
::executeQuery('DROP TABLE snap_civicrm_v5_45_names');
75 * This example creates multiple snapshots (attributed to different versions, v5.45 and v5.50),
76 * and it ensures that they can be cleaned-up by future upgrades (eg v5.52 and v5.58).
78 public function testBasicLifecycle(): void
{
79 for ($i = 0; $i < 15; $i++
) {
80 $this->individualCreate([], $i);
81 $this->organizationCreate([], $i);
83 $this->eventCreate([]);
84 $this->eventCreate([]);
86 $this->runAll(CRM_Upgrade_Snapshot
::createTasks('civicrm', '5.45', 'names', CRM_Utils_SQL_Select
::from('civicrm_contact')
87 ->select('id, display_name, sort_name')
88 ->where('contact_type = "Individual"')
90 $this->assertTrue(CRM_Core_DAO
::checkTableExists('snap_civicrm_v5_45_names'));
91 $this->assertSameSchema('civicrm_contact.display_name', 'snap_civicrm_v5_45_names.display_name');
92 $this->assertGreaterThan(1, CRM_Core_DAO
::singleValueQuery('SELECT count(*) FROM snap_civicrm_v5_45_names'));
94 $this->runAll(CRM_Upgrade_Snapshot
::createTasks('civicrm', '5.50', 'dates', CRM_Utils_SQL_Select
::from('civicrm_event')
95 ->select('id, start_date, end_date, registration_start_date, registration_end_date')
97 $this->assertTrue(CRM_Core_DAO
::checkTableExists('snap_civicrm_v5_50_dates'));
98 $this->assertSameSchema('civicrm_event.start_date', 'snap_civicrm_v5_50_dates.start_date');
99 $this->assertGreaterThan(1, CRM_Core_DAO
::singleValueQuery('SELECT count(*) FROM snap_civicrm_v5_50_dates'));
101 CRM_Upgrade_Snapshot
::cleanupTask(NULL, 'civicrm', '5.52', 6);
102 $this->assertFalse(CRM_Core_DAO
::checkTableExists('snap_civicrm_v5_45_names'));
103 $this->assertTrue(CRM_Core_DAO
::checkTableExists('snap_civicrm_v5_50_dates'));
105 CRM_Upgrade_Snapshot
::cleanupTask(NULL, 'civicrm', '5.58', 6);
106 $this->assertFalse(CRM_Core_DAO
::checkTableExists('snap_civicrm_v5_45_names'));
107 $this->assertFalse(CRM_Core_DAO
::checkTableExists('snap_civicrm_v5_50_dates'));
111 * Assert that two columns have the same schema.
113 * @param string $expectField
114 * ex: "table_1.column_1"
115 * @param string $actualField
116 * ex: "table_2.column_2"
118 protected function assertSameSchema(string $expectField, string $actualField): void
{
119 [$expectTable, $expectColumn] = explode('.', $expectField);
120 [$actualTable, $actualColumn] = explode('.', $actualField);
122 $expectDao = CRM_Core_DAO
::executeQuery("SHOW COLUMNS FROM {$expectTable} LIKE %1", [
123 1 => [$expectColumn, 'String'],
127 $actualDao = CRM_Core_DAO
::executeQuery("SHOW COLUMNS FROM {$actualTable} LIKE %1", [
128 1 => [$actualColumn, 'String'],
132 foreach (['Type', 'Null'] as $fieldProp) {
133 $this->assertEquals($expectDao->{$fieldProp}, $actualDao->{$fieldProp}, "The fields $expectField and $actualField should have the same schema.");
137 protected function runAll(iterable
$tasks): void
{
138 $queue = Civi
::queue('snaptest', ['type' => 'Memory']);
139 foreach ($tasks as $task) {
140 $queue->createItem($task);
142 $r = new CRM_Queue_Runner([
144 'errorMode' => CRM_Queue_Runner
::ERROR_ABORT
,