1<?php 2 3namespace Drupal\Tests\migrate\Kernel; 4 5/** 6 * Tests migration high water property. 7 * 8 * @group migrate 9 */ 10class HighWaterTest extends MigrateTestBase { 11 12 /** 13 * {@inheritdoc} 14 */ 15 protected static $modules = [ 16 'system', 17 'user', 18 'node', 19 'migrate', 20 'migrate_high_water_test', 21 'field', 22 ]; 23 24 /** 25 * {@inheritdoc} 26 */ 27 protected function setUp(): void { 28 parent::setUp(); 29 // Create source test table. 30 $this->sourceDatabase->schema()->createTable('high_water_node', [ 31 'fields' => [ 32 'id' => [ 33 'description' => 'Serial', 34 'type' => 'serial', 35 'unsigned' => TRUE, 36 'not null' => TRUE, 37 ], 38 'changed' => [ 39 'description' => 'Highwater', 40 'type' => 'int', 41 'unsigned' => TRUE, 42 ], 43 'title' => [ 44 'description' => 'Title', 45 'type' => 'varchar', 46 'length' => 128, 47 'not null' => TRUE, 48 'default' => '', 49 ], 50 ], 51 'primary key' => [ 52 'id', 53 ], 54 'description' => 'Contains nodes to import', 55 ]); 56 57 // Add 3 items to source table. 58 $this->sourceDatabase->insert('high_water_node') 59 ->fields([ 60 'title', 61 'changed', 62 ]) 63 ->values([ 64 'title' => 'Item 1', 65 'changed' => 1, 66 ]) 67 ->values([ 68 'title' => 'Item 2', 69 'changed' => 2, 70 ]) 71 ->values([ 72 'title' => 'Item 3', 73 'changed' => 3, 74 ]) 75 ->execute(); 76 77 $this->installEntitySchema('node'); 78 $this->installEntitySchema('user'); 79 $this->installSchema('node', 'node_access'); 80 81 $this->executeMigration('high_water_test'); 82 } 83 84 /** 85 * Tests high water property of SqlBase. 86 */ 87 public function testHighWater() { 88 // Assert all of the nodes have been imported. 89 $this->assertNodeExists('Item 1'); 90 $this->assertNodeExists('Item 2'); 91 $this->assertNodeExists('Item 3'); 92 93 // Update Item 1 setting its high_water_property to value that is below 94 // current high water mark. 95 $this->sourceDatabase->update('high_water_node') 96 ->fields([ 97 'title' => 'Item 1 updated', 98 'changed' => 2, 99 ]) 100 ->condition('title', 'Item 1') 101 ->execute(); 102 103 // Update Item 2 setting its high_water_property to value equal to 104 // current high water mark. 105 $this->sourceDatabase->update('high_water_node') 106 ->fields([ 107 'title' => 'Item 2 updated', 108 'changed' => 3, 109 ]) 110 ->condition('title', 'Item 2') 111 ->execute(); 112 113 // Update Item 3 setting its high_water_property to value that is above 114 // current high water mark. 115 $this->sourceDatabase->update('high_water_node') 116 ->fields([ 117 'title' => 'Item 3 updated', 118 'changed' => 4, 119 ]) 120 ->condition('title', 'Item 3') 121 ->execute(); 122 123 // Execute migration again. 124 $this->executeMigration('high_water_test'); 125 126 // Item with lower highwater should not be updated. 127 $this->assertNodeExists('Item 1'); 128 $this->assertNodeDoesNotExist('Item 1 updated'); 129 130 // Item with equal highwater should not be updated. 131 $this->assertNodeExists('Item 2'); 132 $this->assertNodeDoesNotExist('Item 2 updated'); 133 134 // Item with greater highwater should be updated. 135 $this->assertNodeExists('Item 3 updated'); 136 $this->assertNodeDoesNotExist('Item 3'); 137 } 138 139 /** 140 * Tests that the high water value can be 0. 141 */ 142 public function testZeroHighwater() { 143 // Assert all of the nodes have been imported. 144 $this->assertNodeExists('Item 1'); 145 $this->assertNodeExists('Item 2'); 146 $this->assertNodeExists('Item 3'); 147 $migration = $this->container->get('plugin.manager.migration')->CreateInstance('high_water_test', []); 148 $source = $migration->getSourcePlugin(); 149 $source->rewind(); 150 $count = 0; 151 while ($source->valid()) { 152 $count++; 153 $source->next(); 154 } 155 156 // Expect no rows as everything is below the high water mark. 157 $this->assertSame(0, $count); 158 159 // Test resetting the high water mark to 0. 160 $this->container->get('keyvalue')->get('migrate:high_water')->set('high_water_test', 0); 161 $migration = $this->container->get('plugin.manager.migration')->CreateInstance('high_water_test', []); 162 $source = $migration->getSourcePlugin(); 163 $source->rewind(); 164 $count = 0; 165 while ($source->valid()) { 166 $count++; 167 $source->next(); 168 } 169 $this->assertSame(3, $count); 170 } 171 172 /** 173 * Tests that deleting the high water value causes all rows to be reimported. 174 */ 175 public function testNullHighwater() { 176 // Assert all of the nodes have been imported. 177 $this->assertNodeExists('Item 1'); 178 $this->assertNodeExists('Item 2'); 179 $this->assertNodeExists('Item 3'); 180 $migration = $this->container->get('plugin.manager.migration')->CreateInstance('high_water_test', []); 181 $source = $migration->getSourcePlugin(); 182 $source->rewind(); 183 $count = 0; 184 while ($source->valid()) { 185 $count++; 186 $source->next(); 187 } 188 189 // Expect no rows as everything is below the high water mark. 190 $this->assertSame(0, $count); 191 192 // Test resetting the high water mark. 193 $this->container->get('keyvalue')->get('migrate:high_water')->delete('high_water_test'); 194 $migration = $this->container->get('plugin.manager.migration')->CreateInstance('high_water_test', []); 195 $source = $migration->getSourcePlugin(); 196 $source->rewind(); 197 $count = 0; 198 while ($source->valid()) { 199 $count++; 200 $source->next(); 201 } 202 $this->assertSame(3, $count); 203 } 204 205 /** 206 * Tests high water property of SqlBase when rows marked for update. 207 */ 208 public function testHighWaterUpdate() { 209 // Assert all of the nodes have been imported. 210 $this->assertNodeExists('Item 1'); 211 $this->assertNodeExists('Item 2'); 212 $this->assertNodeExists('Item 3'); 213 214 // Update Item 1 setting its high_water_property to value that is below 215 // current high water mark. 216 $this->sourceDatabase->update('high_water_node') 217 ->fields([ 218 'title' => 'Item 1 updated', 219 'changed' => 2, 220 ]) 221 ->condition('title', 'Item 1') 222 ->execute(); 223 224 // Update Item 2 setting its high_water_property to value equal to 225 // current high water mark. 226 $this->sourceDatabase->update('high_water_node') 227 ->fields([ 228 'title' => 'Item 2 updated', 229 'changed' => 3, 230 ]) 231 ->condition('title', 'Item 2') 232 ->execute(); 233 234 // Update Item 3 setting its high_water_property to value that is above 235 // current high water mark. 236 $this->sourceDatabase->update('high_water_node') 237 ->fields([ 238 'title' => 'Item 3 updated', 239 'changed' => 4, 240 ]) 241 ->condition('title', 'Item 3') 242 ->execute(); 243 244 // Set all rows as needing an update. 245 $id_map = $this->getMigration('high_water_test')->getIdMap(); 246 $id_map->prepareUpdate(); 247 248 $this->executeMigration('high_water_test'); 249 250 // Item with lower highwater should be updated. 251 $this->assertNodeExists('Item 1 updated'); 252 $this->assertNodeDoesNotExist('Item 1'); 253 254 // Item with equal highwater should be updated. 255 $this->assertNodeExists('Item 2 updated'); 256 $this->assertNodeDoesNotExist('Item 2'); 257 258 // Item with greater highwater should be updated. 259 $this->assertNodeExists('Item 3 updated'); 260 $this->assertNodeDoesNotExist('Item 3'); 261 } 262 263 /** 264 * Assert that node with given title exists. 265 * 266 * @param string $title 267 * Title of the node. 268 */ 269 protected function assertNodeExists($title) { 270 self::assertTrue($this->nodeExists($title)); 271 } 272 273 /** 274 * Assert that node with given title does not exist. 275 * 276 * @param string $title 277 * Title of the node. 278 */ 279 protected function assertNodeDoesNotExist($title) { 280 self::assertFalse($this->nodeExists($title)); 281 } 282 283 /** 284 * Checks if node with given title exists. 285 * 286 * @param string $title 287 * Title of the node. 288 * 289 * @return bool 290 */ 291 protected function nodeExists($title) { 292 $query = \Drupal::entityQuery('node')->accessCheck(FALSE); 293 $result = $query 294 ->condition('title', $title) 295 ->range(0, 1) 296 ->execute(); 297 298 return !empty($result); 299 } 300 301} 302