PageRenderTime 53ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/codes-php/phpjakarta/tests/functional/WindowsAzure/Table/TableServiceFunctionalTest.php

http://bukuphpjs.codeplex.com
PHP | 1975 lines | 1380 code | 195 blank | 400 comment | 222 complexity | 45bfb747e3f26ec0d597f9d27c702176 MD5 | raw file
Possible License(s): Apache-2.0, MIT, LGPL-2.1
  1. <?php
  2. /**
  3. * LICENSE: Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. * http://www.apache.org/licenses/LICENSE-2.0
  7. *
  8. * Unless required by applicable law or agreed to in writing, software
  9. * distributed under the License is distributed on an "AS IS" BASIS,
  10. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. * See the License for the specific language governing permissions and
  12. * limitations under the License.
  13. *
  14. * PHP version 5
  15. *
  16. * @category Microsoft
  17. * @package Tests\Functional\WindowsAzure\Table
  18. * @author Azure PHP SDK <azurephpsdk@microsoft.com>
  19. * @copyright 2012 Microsoft Corporation
  20. * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
  21. * @link https://github.com/windowsazure/azure-sdk-for-php
  22. */
  23. namespace Tests\Functional\WindowsAzure\Table;
  24. use Tests\Framework\TestResources;
  25. use Tests\Functional\WindowsAzure\Table\Enums\ConcurType;
  26. use Tests\Functional\WindowsAzure\Table\Enums\MutatePivot;
  27. use Tests\Functional\WindowsAzure\Table\Enums\OpType;
  28. use Tests\Functional\WindowsAzure\Table\Models\BatchWorkerConfig;
  29. use Tests\Functional\WindowsAzure\Table\Models\FakeTableInfoEntry;
  30. use WindowsAzure\Common\Internal\Utilities;
  31. use WindowsAzure\Common\ServiceException;
  32. use WindowsAzure\Table\Models\BatchError;
  33. use WindowsAzure\Table\Models\BatchOperations;
  34. use WindowsAzure\Table\Models\DeleteEntityOptions;
  35. use WindowsAzure\Table\Models\EdmType;
  36. use WindowsAzure\Table\Models\Entity;
  37. use WindowsAzure\Table\Models\InsertEntityResult;
  38. use WindowsAzure\Table\Models\Property;
  39. use WindowsAzure\Table\Models\QueryEntitiesOptions;
  40. use WindowsAzure\Table\Models\QueryTablesOptions;
  41. use WindowsAzure\Table\Models\TableServiceOptions;
  42. use WindowsAzure\Table\Models\UpdateEntityResult;
  43. class TableServiceFunctionalTest extends FunctionalTestBase
  44. {
  45. /**
  46. * @covers WindowsAzure\Table\TableRestProxy::getServiceProperties
  47. * @covers WindowsAzure\Table\TableRestProxy::setServiceProperties
  48. */
  49. public function testGetServicePropertiesNoOptions()
  50. {
  51. $serviceProperties = TableServiceFunctionalTestData::getDefaultServiceProperties();
  52. $shouldReturn = false;
  53. try {
  54. $this->restProxy->setServiceProperties($serviceProperties);
  55. $this->assertFalse($this->isEmulated(), 'Should succeed when not running in emulator');
  56. } catch (ServiceException $e) {
  57. // Expect failure in emulator, as v1.6 doesn't support this method
  58. if ($this->isEmulated()) {
  59. $this->assertEquals(TestResources::STATUS_BAD_REQUEST, $e->getCode(), 'getCode');
  60. $shouldReturn = true;
  61. } else {
  62. throw $e;
  63. }
  64. }
  65. if($shouldReturn) {
  66. return;
  67. }
  68. $this->getServicePropertiesWorker(null);
  69. }
  70. /**
  71. * @covers WindowsAzure\Table\TableRestProxy::getServiceProperties
  72. * @covers WindowsAzure\Table\TableRestProxy::setServiceProperties
  73. */
  74. public function testGetServiceProperties()
  75. {
  76. $serviceProperties = TableServiceFunctionalTestData::getDefaultServiceProperties();
  77. try {
  78. $this->restProxy->setServiceProperties($serviceProperties);
  79. $this->assertFalse($this->isEmulated(), 'Should succeed when not running in emulator');
  80. } catch (ServiceException $e) {
  81. // Expect failure in emulator, as v1.6 doesn't support this method
  82. if ($this->isEmulated()) {
  83. $this->assertEquals(TestResources::STATUS_BAD_REQUEST, $e->getCode(), 'getCode');
  84. } else {
  85. throw $e;
  86. }
  87. }
  88. }
  89. /**
  90. * @covers WindowsAzure\Table\TableRestProxy::getServiceProperties
  91. */
  92. private function getServicePropertiesWorker($options)
  93. {
  94. self::println( 'Trying $options: ' . self::tmptostring($options));
  95. $effOptions = (is_null($options) ? new TableServiceOptions() : $options);
  96. try {
  97. $ret = (is_null($options) ? $this->restProxy->getServiceProperties() : $this->restProxy->getServiceProperties($effOptions));
  98. $this->assertFalse($this->isEmulated(), 'Should succeed when not running in emulator');
  99. $this->verifyServicePropertiesWorker($ret, null);
  100. } catch (ServiceException $e) {
  101. if ($this->isEmulated()) {
  102. // Expect failure in emulator, as v1.6 doesn't support this method
  103. $this->assertEquals(TestResources::STATUS_BAD_REQUEST, $e->getCode(), 'getCode');
  104. } else {
  105. $this->assertEquals(TestResources::STATUS_INTERNAL_SERVER_ERROR, $e->getCode(), 'getCode');
  106. }
  107. }
  108. }
  109. private function verifyServicePropertiesWorker($ret, $serviceProperties)
  110. {
  111. if (is_null($serviceProperties)) {
  112. $serviceProperties = TableServiceFunctionalTestData::getDefaultServiceProperties();
  113. }
  114. $sp = $ret->getValue();
  115. $this->assertNotNull($sp, 'getValue should be non-null');
  116. $l = $sp->getLogging();
  117. $this->assertNotNull($l, 'getValue()->getLogging() should be non-null');
  118. $this->assertEquals($serviceProperties->getLogging()->getVersion(), $l->getVersion(), 'getValue()->getLogging()->getVersion');
  119. $this->assertEquals($serviceProperties->getLogging()->getDelete(), $l->getDelete(), 'getValue()->getLogging()->getDelete');
  120. $this->assertEquals($serviceProperties->getLogging()->getRead(), $l->getRead(), 'getValue()->getLogging()->getRead');
  121. $this->assertEquals($serviceProperties->getLogging()->getWrite(), $l->getWrite(), 'getValue()->getLogging()->getWrite');
  122. $r = $l->getRetentionPolicy();
  123. $this->assertNotNull($r, 'getValue()->getLogging()->getRetentionPolicy should be non-null');
  124. $this->assertEquals($serviceProperties->getLogging()->getRetentionPolicy()->getDays(), $r->getDays(), 'getValue()->getLogging()->getRetentionPolicy()->getDays');
  125. $m = $sp->getMetrics();
  126. $this->assertNotNull($m, 'getValue()->getMetrics() should be non-null');
  127. $this->assertEquals($serviceProperties->getMetrics()->getVersion(), $m->getVersion(), 'getValue()->getMetrics()->getVersion');
  128. $this->assertEquals($serviceProperties->getMetrics()->getEnabled(), $m->getEnabled(), 'getValue()->getMetrics()->getEnabled');
  129. $this->assertEquals($serviceProperties->getMetrics()->getIncludeAPIs(), $m->getIncludeAPIs(), 'getValue()->getMetrics()->getIncludeAPIs');
  130. $r = $m->getRetentionPolicy();
  131. $this->assertNotNull($r, 'getValue()->getMetrics()->getRetentionPolicy should be non-null');
  132. $this->assertEquals($serviceProperties->getMetrics()->getRetentionPolicy()->getDays(), $r->getDays(), 'getValue()->getMetrics()->getRetentionPolicy()->getDays');
  133. }
  134. /**
  135. * @covers WindowsAzure\Table\TableRestProxy::getServiceProperties
  136. * @covers WindowsAzure\Table\TableRestProxy::setServiceProperties
  137. */
  138. public function testSetServicePropertiesNoOptions()
  139. {
  140. $serviceProperties = TableServiceFunctionalTestData::getDefaultServiceProperties();
  141. $this->setServicePropertiesWorker($serviceProperties, null);
  142. }
  143. /**
  144. * @covers WindowsAzure\Table\TableRestProxy::getServiceProperties
  145. * @covers WindowsAzure\Table\TableRestProxy::setServiceProperties
  146. */
  147. public function testSetServiceProperties()
  148. {
  149. $interestingServiceProperties = TableServiceFunctionalTestData::getInterestingServiceProperties();
  150. foreach($interestingServiceProperties as $serviceProperties) {
  151. $options = new TableServiceOptions();
  152. $this->setServicePropertiesWorker($serviceProperties, $options);
  153. }
  154. if (!$this->isEmulated()) {
  155. $serviceProperties = TableServiceFunctionalTestData::getDefaultServiceProperties();
  156. $this->restProxy->setServiceProperties($serviceProperties);
  157. }
  158. }
  159. /**
  160. * @covers WindowsAzure\Table\TableRestProxy::getServiceProperties
  161. * @covers WindowsAzure\Table\TableRestProxy::setServiceProperties
  162. */
  163. private function setServicePropertiesWorker($serviceProperties, $options)
  164. {
  165. try {
  166. if (is_null($options)) {
  167. $this->restProxy->setServiceProperties($serviceProperties);
  168. } else {
  169. $this->restProxy->setServiceProperties($serviceProperties, $options);
  170. }
  171. $this->assertFalse($this->isEmulated(), 'Should succeed when not running in emulator');
  172. $ret = (is_null($options) ? $this->restProxy->getServiceProperties() : $this->restProxy->getServiceProperties($options));
  173. $this->verifyServicePropertiesWorker($ret, $serviceProperties);
  174. } catch (ServiceException $e) {
  175. if ($this->isEmulated()) {
  176. $this->assertEquals(TestResources::STATUS_BAD_REQUEST, $e->getCode(), 'getCode');
  177. } else {
  178. throw $e;
  179. }
  180. }
  181. }
  182. /**
  183. * @covers WindowsAzure\Table\TableRestProxy::queryTables
  184. */
  185. public function testQueryTablesNoOptions()
  186. {
  187. $this->queryTablesWorker(null);
  188. }
  189. /**
  190. * @covers WindowsAzure\Table\TableRestProxy::queryTables
  191. */
  192. public function testQueryTables()
  193. {
  194. $interestingqueryTablesOptions = TableServiceFunctionalTestData::getInterestingQueryTablesOptions($this->isEmulated());
  195. foreach($interestingqueryTablesOptions as $options) {
  196. $this->queryTablesWorker($options);
  197. }
  198. }
  199. /**
  200. * @covers WindowsAzure\Table\TableRestProxy::queryTables
  201. */
  202. private function queryTablesWorker($options)
  203. {
  204. try {
  205. $ret = (is_null($options) ? $this->restProxy->queryTables() : $this->restProxy->queryTables($options));
  206. if (is_null($options)) {
  207. $options = new QueryTablesOptions();
  208. }
  209. if ((!is_null($options->getTop()) && $options->getTop() <= 0)) {
  210. if ($this->isEmulated()) {
  211. $this->assertEquals(0, count($ret->getTables()), "should be no tables");
  212. } else {
  213. $this->fail('Expect non-positive Top in $options to throw');
  214. }
  215. }
  216. $this->verifyqueryTablesWorker($ret, $options);
  217. } catch (ServiceException $e) {
  218. if ((!is_null($options->getTop()) && $options->getTop() <= 0) && !$this->isEmulated()) {
  219. $this->assertEquals(TestResources::STATUS_BAD_REQUEST, $e->getCode(), 'getCode');
  220. } else {
  221. throw $e;
  222. }
  223. }
  224. }
  225. private function verifyqueryTablesWorker($ret, $options)
  226. {
  227. $this->assertNotNull($ret->getTables(), 'getTables');
  228. $effectivePrefix = $options->getPrefix();
  229. if (is_null($effectivePrefix)) {
  230. $effectivePrefix = '';
  231. }
  232. $expectedFilter = $options->getFilter();
  233. if (TableServiceFunctionalTestUtils::isEqNotInTopLevel($expectedFilter)) {
  234. // This seems wrong, but appears to be a bug in the $service itself.
  235. // So working around the limitation.
  236. $expectedFilter = TableServiceFunctionalTestUtils::cloneRemoveEqNotInTopLevel($expectedFilter);
  237. }
  238. $expectedData = array();
  239. foreach(TableServiceFunctionalTestData::$testTableNames as $s) {
  240. if (substr($s, 0, strlen($effectivePrefix)) == $effectivePrefix) {
  241. $fte = new FakeTableInfoEntry();
  242. $fte->TableName = $s;
  243. array_push($expectedData, $fte);
  244. }
  245. }
  246. if (!is_null($options->getNextTableName())) {
  247. $tmpExpectedData = array();
  248. $foundNext = false;
  249. foreach($expectedData as $s) {
  250. if ($s == $options->getNextTableName()) {
  251. $foundNext = true;
  252. }
  253. if (!$foundNext) {
  254. continue;
  255. }
  256. if (substr($s, 0, strlen($effectivePrefix)) == $effectivePrefix) {
  257. $fte = new FakeTableInfoEntry();
  258. $fte->TableName = $s;
  259. array_push($expectedData, $fte);
  260. }
  261. }
  262. $expectedData = $tmpExpectedData;
  263. }
  264. $expectedData = TableServiceFunctionalTestUtils::filterList($expectedFilter, $expectedData);
  265. $effectiveTop = (is_null($options->getTop()) ? 100000 : $options->getTop());
  266. $expectedCount = min($effectiveTop, count($expectedData));
  267. $tables = $ret->getTables();
  268. for ($i = 0; $i < $expectedCount; $i++) {
  269. $expected = $expectedData[$i]->TableName;
  270. // Assume there are other tables. Make sure the expected ones are there.
  271. $foundNext = false;
  272. foreach($tables as $actual) {
  273. if ($expected == $actual) {
  274. $foundNext = true;
  275. break;
  276. }
  277. }
  278. $this->assertTrue($foundNext, $expected . ' should be in getTables');
  279. }
  280. }
  281. /**
  282. * @covers WindowsAzure\Table\TableRestProxy::createTable
  283. * @covers WindowsAzure\Table\TableRestProxy::deleteTable
  284. * @covers WindowsAzure\Table\TableRestProxy::queryTables
  285. */
  286. public function testCreateTableNoOptions()
  287. {
  288. $this->createTableWorker(null);
  289. }
  290. /**
  291. * @covers WindowsAzure\Table\TableRestProxy::createTable
  292. * @covers WindowsAzure\Table\TableRestProxy::deleteTable
  293. * @covers WindowsAzure\Table\TableRestProxy::queryTables
  294. */
  295. public function testCreateTable()
  296. {
  297. $options = new TableServiceOptions();
  298. $this->createTableWorker($options);
  299. }
  300. /**
  301. * @covers WindowsAzure\Table\TableRestProxy::createTable
  302. * @covers WindowsAzure\Table\TableRestProxy::deleteTable
  303. * @covers WindowsAzure\Table\TableRestProxy::queryTables
  304. */
  305. private function createTableWorker($options)
  306. {
  307. $table = TableServiceFunctionalTestData::getInterestingTableName();
  308. $created = false;
  309. // Make sure that the list of all applicable Tables is correctly updated.
  310. $qto = new QueryTablesOptions();
  311. if (!$this->isEmulated()) {
  312. // The emulator has problems with some queries,
  313. // but full Azure allow this to be more efficient:
  314. $qto->setPrefix(TableServiceFunctionalTestData::$testUniqueId);
  315. }
  316. $qsStart = $this->restProxy->queryTables($qto);
  317. if (is_null($options)) {
  318. $this->restProxy->createTable($table);
  319. } else {
  320. $this->restProxy->createTable($table, $options);
  321. }
  322. $created = true;
  323. if (is_null($options)) {
  324. $options = new TableServiceOptions();
  325. }
  326. // Make sure that the list of all applicable Tables is correctly updated.
  327. $qs = $this->restProxy->queryTables($qto);
  328. if ($created) {
  329. $this->restProxy->deleteTable($table);
  330. }
  331. $this->assertEquals(count($qsStart->getTables()) + 1, count($qs->getTables()), 'After adding one, with Prefix=(\'' . TableServiceFunctionalTestData::$testUniqueId . '\'), then count(Tables)');
  332. }
  333. /**
  334. * @covers WindowsAzure\Table\TableRestProxy::createTable
  335. * @covers WindowsAzure\Table\TableRestProxy::deleteTable
  336. * @covers WindowsAzure\Table\TableRestProxy::queryTables
  337. */
  338. public function testDeleteTableNoOptions()
  339. {
  340. $this->deleteTableWorker(null);
  341. }
  342. /**
  343. * @covers WindowsAzure\Table\TableRestProxy::createTable
  344. * @covers WindowsAzure\Table\TableRestProxy::deleteTable
  345. * @covers WindowsAzure\Table\TableRestProxy::queryTables
  346. */
  347. public function testDeleteTable()
  348. {
  349. $options = new TableServiceOptions();
  350. $this->deleteTableWorker($options);
  351. }
  352. /**
  353. * @covers WindowsAzure\Table\TableRestProxy::createTable
  354. * @covers WindowsAzure\Table\TableRestProxy::deleteTable
  355. * @covers WindowsAzure\Table\TableRestProxy::queryTables
  356. */
  357. private function deleteTableWorker($options)
  358. {
  359. $Table = TableServiceFunctionalTestData::getInterestingTableName();
  360. // Make sure that the list of all applicable Tables is correctly updated.
  361. $qto = new QueryTablesOptions();
  362. if (!$this->isEmulated()) {
  363. // The emulator has problems with some queries,
  364. // but full Azure allow this to be more efficient:
  365. $qto->setPrefix(TableServiceFunctionalTestData::$testUniqueId);
  366. }
  367. $qsStart = $this->restProxy->queryTables($qto);
  368. // Make sure there is something to delete.
  369. $this->restProxy->createTable($Table);
  370. // Make sure that the list of all applicable Tables is correctly updated.
  371. $qs = $this->restProxy->queryTables($qto);
  372. $this->assertEquals(count($qsStart->getTables()) + 1, count($qs->getTables()), 'After adding one, with Prefix=(\'' . TableServiceFunctionalTestData::$testUniqueId . '\'), then count Tables');
  373. $deleted = false;
  374. if (is_null($options)) {
  375. $this->restProxy->deleteTable($Table);
  376. } else {
  377. $this->restProxy->deleteTable($Table, $options);
  378. }
  379. $deleted = true;
  380. if (is_null($options)) {
  381. $options = new TableServiceOptions();
  382. }
  383. // Make sure that the list of all applicable Tables is correctly updated.
  384. $qs = $this->restProxy->queryTables($qto);
  385. if (!$deleted) {
  386. $this->println('Test didn\'t delete the $Table, so try again more simply');
  387. // Try again. If it doesn't work, not much else to try.
  388. $this->restProxy->deleteTable($Table);
  389. }
  390. $this->assertEquals(count($qsStart->getTables()), count($qs->getTables()),'After adding then deleting one, with Prefix=(\'' . TableServiceFunctionalTestData::$testUniqueId . '\'), then count(Tables)');
  391. }
  392. /**
  393. * @covers WindowsAzure\Table\TableRestProxy::createTable
  394. * @covers WindowsAzure\Table\TableRestProxy::deleteTable
  395. * @covers WindowsAzure\Table\TableRestProxy::getTable
  396. */
  397. public function testGetTableNoOptions()
  398. {
  399. $this->getTableWorker(null);
  400. }
  401. /**
  402. * @covers WindowsAzure\Table\TableRestProxy::createTable
  403. * @covers WindowsAzure\Table\TableRestProxy::deleteTable
  404. * @covers WindowsAzure\Table\TableRestProxy::getTable
  405. */
  406. public function testGetTable()
  407. {
  408. $options = new TableServiceOptions();
  409. $this->getTableWorker($options);
  410. }
  411. /**
  412. * @covers WindowsAzure\Table\TableRestProxy::createTable
  413. * @covers WindowsAzure\Table\TableRestProxy::deleteTable
  414. * @covers WindowsAzure\Table\TableRestProxy::getTable
  415. */
  416. private function getTableWorker($options)
  417. {
  418. $table = TableServiceFunctionalTestData::getInterestingTableName();
  419. $created = false;
  420. $this->restProxy->createTable($table);
  421. $created = true;
  422. $ret = (is_null($options) ? $this->restProxy->getTable($table) : $this->restProxy->getTable($table, $options));
  423. if (is_null($options)) {
  424. $options = new TableServiceOptions();
  425. }
  426. $this->verifygetTableWorker($ret, $table);
  427. if ($created) {
  428. $this->restProxy->deleteTable($table);
  429. }
  430. }
  431. private function verifygetTableWorker($ret, $tableName)
  432. {
  433. $this->assertNotNull($ret, 'getTableEntry');
  434. $this->assertEquals($tableName, $ret->getName(), 'getTableEntry->Name');
  435. }
  436. /**
  437. * @covers WindowsAzure\Table\TableRestProxy::getEntity
  438. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  439. */
  440. public function testGetEntity()
  441. {
  442. $ents = TableServiceFunctionalTestData::getInterestingEntities();
  443. foreach($ents as $ent) {
  444. $options = new TableServiceOptions();
  445. $this->getEntityWorker($ent, true, $options);
  446. }
  447. }
  448. /**
  449. * @covers WindowsAzure\Table\TableRestProxy::getEntity
  450. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  451. */
  452. private function getEntityWorker($ent, $isGood, $options)
  453. {
  454. $table = $this->getCleanTable();
  455. try {
  456. // Upload the entity.
  457. $this->restProxy->insertEntity($table, $ent);
  458. $qer = (is_null($options) ? $this->restProxy->getEntity($table, $ent->getPartitionKey(), $ent->getRowKey()) : $this->restProxy->getEntity($table, $ent->getPartitionKey(), $ent->getRowKey(), $options));
  459. if (is_null($options)) {
  460. $options = new TableServiceOptions();
  461. }
  462. $this->assertNotNull($qer->getEntity(), 'getEntity()');
  463. $this->verifygetEntityWorker($ent, $qer->getEntity());
  464. } catch (ServiceException $e) {
  465. if (!$isGood) {
  466. $this->assertEquals(TestResources::STATUS_BAD_REQUEST, $e->getCode(), 'getCode');
  467. } else if (is_null($ent->getPartitionKey()) || is_null($ent->getRowKey())) {
  468. $this->assertEquals(TestResources::STATUS_BAD_REQUEST, $e->getCode(), 'getCode');
  469. } else {
  470. throw $e;
  471. }
  472. }
  473. $this->clearTable($table);
  474. }
  475. private function verifygetEntityWorker($ent, $entReturned)
  476. {
  477. $expectedProps = array();
  478. foreach($ent->getProperties() as $pname => $actualProp) {
  479. if (is_null($actualProp) || !is_null($actualProp->getValue())) {
  480. $cloneProp = null;
  481. if (!is_null($actualProp)) {
  482. $cloneProp = new Property();
  483. $cloneProp->setEdmType($actualProp->getEdmType());
  484. $cloneProp->setValue($actualProp->getValue());
  485. }
  486. $expectedProps[$pname] = $cloneProp;
  487. }
  488. }
  489. // Compare the entities to make sure they match.
  490. $this->assertEquals($ent->getPartitionKey(), $entReturned->getPartitionKey(), 'getPartitionKey');
  491. $this->assertEquals($ent->getRowKey(), $entReturned->getRowKey(), 'getRowKey');
  492. $this->assertNotNull($entReturned->getETag(), 'getETag');
  493. if (!is_null($ent->getETag())) {
  494. $this->assertEquals($ent->getETag(), $entReturned->getETag(), 'getETag');
  495. }
  496. $this->assertNotNull($entReturned->getTimestamp(), 'getTimestamp');
  497. if (is_null($ent->getTimestamp())) {
  498. // This property will come back, so need to account for it.
  499. $expectedProps['Timestamp'] = null;
  500. } else {
  501. $this->assertEquals($ent->getTimestamp(), $entReturned->getTimestamp(), 'getTimestamp');
  502. }
  503. $this->assertNotNull($ent->getProperties(), 'getProperties');
  504. $nullCount = 0;
  505. foreach($entReturned->getProperties() as $pname => $actualProp) {
  506. if (is_null($actualProp->getValue())) {
  507. $nullCount++;
  508. }
  509. }
  510. // Need to skip null values from the count.
  511. $this->assertEquals(count($expectedProps) + $nullCount, count($entReturned->getProperties()), 'getProperties()');
  512. foreach($entReturned->getProperties() as $pname => $actualProp) {
  513. $this->println($actualProp->getEdmType() . ':' . (is_null($actualProp->getValue()) ? 'NULL' :
  514. ($actualProp->getValue() instanceof \DateTime ? "date" : $actualProp->getValue())));
  515. }
  516. foreach($entReturned->getProperties() as $pname => $actualProp) {
  517. $expectedProp = Utilities::tryGetValue($expectedProps, $pname, null);
  518. $this->assertNotNull($actualProp, 'getProperties[\'' . $pname . '\']');
  519. if (!is_null($expectedProp)) {
  520. $this->compareProperties($pname, $actualProp, $expectedProp);
  521. }
  522. $this->assertEquals($entReturned->getProperty($pname), $actualProp, 'getProperty(\'' . $pname . '\')');
  523. $this->assertEquals($entReturned->getPropertyValue($pname), $actualProp->getValue(), 'getPropertyValue(\'' . $pname . '\')');
  524. }
  525. }
  526. /**
  527. * @covers WindowsAzure\Table\TableRestProxy::deleteEntity
  528. * @covers WindowsAzure\Table\TableRestProxy::getEntity
  529. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  530. */
  531. public function testDeleteEntity()
  532. {
  533. $ents = TableServiceFunctionalTestData::getSimpleEntities(3);
  534. for ($useETag = 0; $useETag <= 2; $useETag++) {
  535. foreach($ents as $ent) {
  536. $options = new DeleteEntityOptions();
  537. $this->deleteEntityWorker($ent, $useETag, $options);
  538. }
  539. }
  540. }
  541. /**
  542. * @covers WindowsAzure\Table\TableRestProxy::deleteEntity
  543. * @covers WindowsAzure\Table\TableRestProxy::getEntity
  544. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  545. */
  546. private function deleteEntityWorker($ent, $useETag, $options)
  547. {
  548. $table = $this->getCleanTable();
  549. try {
  550. // Upload the entity.
  551. $ier = $this->restProxy->insertEntity($table, $ent);
  552. if ($useETag == 1) {
  553. $options->setETag($ier->getEntity()->getETag());
  554. } else if ($useETag == 2) {
  555. $options->setETag('W/"datetime\'2012-03-05T21%3A46%3A25->5385467Z\'"');
  556. }
  557. $this->restProxy->deleteEntity($table, $ent->getPartitionKey(), $ent->getRowKey(), $options);
  558. if ($useETag == 2) {
  559. $this->fail('Expect bad etag throws');
  560. }
  561. // Check that the entity really is gone
  562. $gotError = false;
  563. try {
  564. $this->restProxy->getEntity($table, $ent->getPartitionKey(), $ent->getRowKey());
  565. } catch (ServiceException $e2) {
  566. $gotError = ($e2->getCode() == TestResources::STATUS_NOT_FOUND);
  567. }
  568. $this->assertTrue($gotError, 'Expect error when entity is deleted');
  569. } catch (ServiceException $e) {
  570. if ($useETag == 2) {
  571. $this->assertEquals(TestResources::STATUS_PRECONDITION_FAILED, $e->getCode(), 'getCode');
  572. } else {
  573. throw $e;
  574. }
  575. }
  576. $this->clearTable($table);
  577. }
  578. /**
  579. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  580. * @covers WindowsAzure\Table\TableRestProxy::queryEntities
  581. */
  582. public function testInsertEntity()
  583. {
  584. $ents = TableServiceFunctionalTestData::getInterestingEntities();
  585. foreach($ents as $ent) {
  586. $options = new TableServiceOptions();
  587. $this->insertEntityWorker($ent, true, $options);
  588. }
  589. }
  590. /**
  591. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  592. * @covers WindowsAzure\Table\TableRestProxy::queryEntities
  593. */
  594. public function testInsertBadEntity()
  595. {
  596. $ents = TableServiceFunctionalTestData::getInterestingBadEntities();
  597. foreach($ents as $ent) {
  598. $options = new TableServiceOptions();
  599. try {
  600. $this->insertEntityWorker($ent, true, $options);
  601. $this->fail('this call should fail');
  602. } catch (\InvalidArgumentException $e) {
  603. $this->assertEquals(0, $e->getCode(), 'getCode');
  604. $this->assertTrue(true, 'got expected exception');
  605. }
  606. }
  607. }
  608. /**
  609. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  610. * @covers WindowsAzure\Table\TableRestProxy::queryEntities
  611. */
  612. public function testInsertEntityBoolean()
  613. {
  614. foreach(TableServiceFunctionalTestData::getInterestingGoodBooleans() as $o) {
  615. $ent = new Entity();
  616. $ent->setPartitionKey(TableServiceFunctionalTestData::getNewKey());
  617. $ent->setRowKey(TableServiceFunctionalTestData::getNewKey());
  618. $ent->addProperty('BOOLEAN', EdmType::BOOLEAN, $o);
  619. $this->insertEntityWorker($ent, true, null, $o);
  620. }
  621. }
  622. /**
  623. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  624. * @covers WindowsAzure\Table\TableRestProxy::queryEntities
  625. */
  626. public function testInsertEntityBooleanNegative()
  627. {
  628. foreach(TableServiceFunctionalTestData::getInterestingBadBooleans() as $o) {
  629. $ent = new Entity();
  630. $ent->setPartitionKey(TableServiceFunctionalTestData::getNewKey());
  631. $ent->setRowKey(TableServiceFunctionalTestData::getNewKey());
  632. try {
  633. $ent->addProperty('BOOLEAN', EdmType::BOOLEAN, $o);
  634. $this->fail('Should get an exception when trying to parse this value');
  635. $this->insertEntityWorker($ent, false, null, $o);
  636. } catch (\Exception $e) {
  637. $this->assertEquals(0, $e->getCode(), 'getCode');
  638. $this->assertTrue(true, 'got expected exception');
  639. }
  640. }
  641. }
  642. /**
  643. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  644. * @covers WindowsAzure\Table\TableRestProxy::queryEntities
  645. */
  646. public function testInsertEntityDate()
  647. {
  648. foreach(TableServiceFunctionalTestData::getInterestingGoodDates() as $o) {
  649. $ent = new Entity();
  650. $ent->setPartitionKey(TableServiceFunctionalTestData::getNewKey());
  651. $ent->setRowKey(TableServiceFunctionalTestData::getNewKey());
  652. $ent->addProperty('DATETIME', EdmType::DATETIME, $o);
  653. $this->insertEntityWorker($ent, true, null, $o);
  654. }
  655. }
  656. /**
  657. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  658. * @covers WindowsAzure\Table\TableRestProxy::queryEntities
  659. */
  660. public function testInsertEntityDateNegative()
  661. {
  662. foreach(TableServiceFunctionalTestData::getInterestingBadDates() as $o) {
  663. $ent = new Entity();
  664. $ent->setPartitionKey(TableServiceFunctionalTestData::getNewKey());
  665. $ent->setRowKey(TableServiceFunctionalTestData::getNewKey());
  666. try {
  667. $ent->addProperty('DATETIME', EdmType::DATETIME, $o);
  668. $this->fail('Should get an exception when trying to parse this value');
  669. $this->insertEntityWorker($ent, false, null, $o);
  670. } catch (\Exception $e) {
  671. $this->assertEquals(0, $e->getCode(), 'getCode');
  672. $this->assertTrue(true, 'got expected exception');
  673. }
  674. }
  675. }
  676. /**
  677. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  678. * @covers WindowsAzure\Table\TableRestProxy::queryEntities
  679. */
  680. public function testInsertEntityDouble()
  681. {
  682. foreach(TableServiceFunctionalTestData::getInterestingGoodDoubles() as $o) {
  683. $ent = new Entity();
  684. $ent->setPartitionKey(TableServiceFunctionalTestData::getNewKey());
  685. $ent->setRowKey(TableServiceFunctionalTestData::getNewKey());
  686. $ent->addProperty('DOUBLE', EdmType::DOUBLE, $o);
  687. $this->insertEntityWorker($ent, true, null, $o);
  688. }
  689. }
  690. /**
  691. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  692. * @covers WindowsAzure\Table\TableRestProxy::queryEntities
  693. */
  694. public function testInsertEntityDoubleNegative()
  695. {
  696. foreach(TableServiceFunctionalTestData::getInterestingBadDoubles() as $o) {
  697. $ent = new Entity();
  698. $ent->setPartitionKey(TableServiceFunctionalTestData::getNewKey());
  699. $ent->setRowKey(TableServiceFunctionalTestData::getNewKey());
  700. try {
  701. $ent->addProperty('DOUBLE', EdmType::DOUBLE, $o);
  702. $this->fail('Should get an exception when trying to parse this value');
  703. $this->insertEntityWorker($ent, false, null, $o);
  704. } catch (\Exception $e) {
  705. $this->assertEquals(0, $e->getCode(), 'getCode');
  706. $this->assertTrue(true, 'got expected exception');
  707. }
  708. }
  709. }
  710. /**
  711. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  712. * @covers WindowsAzure\Table\TableRestProxy::queryEntities
  713. */
  714. public function testInsertEntityGuid()
  715. {
  716. foreach(TableServiceFunctionalTestData::getInterestingGoodGuids() as $o) {
  717. $ent = new Entity();
  718. $ent->setPartitionKey(TableServiceFunctionalTestData::getNewKey());
  719. $ent->setRowKey(TableServiceFunctionalTestData::getNewKey());
  720. $ent->addProperty('GUID', EdmType::GUID, $o);
  721. $this->insertEntityWorker($ent, true, null, $o);
  722. }
  723. }
  724. /**
  725. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  726. * @covers WindowsAzure\Table\TableRestProxy::queryEntities
  727. */
  728. public function testInsertEntityGuidNegative()
  729. {
  730. foreach(TableServiceFunctionalTestData::getInterestingBadGuids() as $o) {
  731. $ent = new Entity();
  732. $ent->setPartitionKey(TableServiceFunctionalTestData::getNewKey());
  733. $ent->setRowKey(TableServiceFunctionalTestData::getNewKey());
  734. try {
  735. $ent->addProperty('GUID', EdmType::GUID, $o);
  736. $this->fail('Should get an exception when trying to parse this value');
  737. $this->insertEntityWorker($ent, false, null, $o);
  738. } catch (\Exception $e) {
  739. $this->assertEquals(0, $e->getCode(), 'getCode');
  740. $this->assertTrue(true, 'got expected exception');
  741. }
  742. }
  743. }
  744. /**
  745. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  746. * @covers WindowsAzure\Table\TableRestProxy::queryEntities
  747. */
  748. public function testInsertEntityInt()
  749. {
  750. foreach(TableServiceFunctionalTestData::getInterestingGoodInts() as $o) {
  751. $ent = new Entity();
  752. $ent->setPartitionKey(TableServiceFunctionalTestData::getNewKey());
  753. $ent->setRowKey(TableServiceFunctionalTestData::getNewKey());
  754. $ent->addProperty('INT32', EdmType::INT32, $o);
  755. $this->insertEntityWorker($ent, true, null, $o);
  756. }
  757. }
  758. /**
  759. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  760. * @covers WindowsAzure\Table\TableRestProxy::queryEntities
  761. */
  762. public function testInsertEntityIntNegative()
  763. {
  764. foreach(TableServiceFunctionalTestData::getInterestingBadInts() as $o) {
  765. $ent = new Entity();
  766. $ent->setPartitionKey(TableServiceFunctionalTestData::getNewKey());
  767. $ent->setRowKey(TableServiceFunctionalTestData::getNewKey());
  768. try {
  769. $ent->addProperty('INT32', EdmType::INT32, $o);
  770. $this->fail('Should get an exception when trying to parse this value');
  771. $this->insertEntityWorker($ent, false, null, $o);
  772. } catch (\Exception $e) {
  773. $this->assertEquals(0, $e->getCode(), 'getCode');
  774. $this->assertTrue(true, 'got expected exception');
  775. }
  776. }
  777. }
  778. /**
  779. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  780. * @covers WindowsAzure\Table\TableRestProxy::queryEntities
  781. */
  782. public function testInsertEntityLong()
  783. {
  784. foreach(TableServiceFunctionalTestData::getInterestingGoodLongs() as $o) {
  785. $ent = new Entity();
  786. $ent->setPartitionKey(TableServiceFunctionalTestData::getNewKey());
  787. $ent->setRowKey(TableServiceFunctionalTestData::getNewKey());
  788. $ent->addProperty('INT64', EdmType::INT64, $o);
  789. $this->insertEntityWorker($ent, true, null, $o);
  790. }
  791. }
  792. /**
  793. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  794. * @covers WindowsAzure\Table\TableRestProxy::queryEntities
  795. */
  796. public function testInsertEntityLongNegative()
  797. {
  798. foreach(TableServiceFunctionalTestData::getInterestingBadLongs() as $o) {
  799. $ent = new Entity();
  800. $ent->setPartitionKey(TableServiceFunctionalTestData::getNewKey());
  801. $ent->setRowKey(TableServiceFunctionalTestData::getNewKey());
  802. try {
  803. $ent->addProperty('INT64', EdmType::INT64, $o);
  804. $this->fail('Should get an exception when trying to parse this value');
  805. $this->insertEntityWorker($ent, false, null, $o);
  806. } catch (\Exception $e) {
  807. $this->assertEquals(0, $e->getCode(), 'getCode');
  808. $this->assertTrue(true, 'got expected exception');
  809. }
  810. }
  811. }
  812. /**
  813. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  814. * @covers WindowsAzure\Table\TableRestProxy::queryEntities
  815. */
  816. public function testInsertEntityBinary()
  817. {
  818. foreach(TableServiceFunctionalTestData::getInterestingGoodBinaries() as $o) {
  819. $ent = new Entity();
  820. $ent->setPartitionKey(TableServiceFunctionalTestData::getNewKey());
  821. $ent->setRowKey(TableServiceFunctionalTestData::getNewKey());
  822. $ent->addProperty('BINARY', EdmType::BINARY, $o);
  823. $this->insertEntityWorker($ent, true, null, $o);
  824. }
  825. }
  826. /**
  827. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  828. * @covers WindowsAzure\Table\TableRestProxy::queryEntities
  829. */
  830. public function testInsertEntityBinaryNegative()
  831. {
  832. foreach(TableServiceFunctionalTestData::getInterestingBadBinaries() as $o) {
  833. $ent = new Entity();
  834. $ent->setPartitionKey(TableServiceFunctionalTestData::getNewKey());
  835. $ent->setRowKey(TableServiceFunctionalTestData::getNewKey());
  836. try {
  837. $ent->addProperty('BINARY', EdmType::BINARY, $o);
  838. $this->fail('Should get an exception when trying to parse this value');
  839. $this->insertEntityWorker($ent, false, null, $o);
  840. } catch (\Exception $e) {
  841. $this->assertEquals(0, $e->getCode(), 'getCode');
  842. $this->assertTrue(true, 'got expected exception');
  843. }
  844. }
  845. }
  846. /**
  847. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  848. * @covers WindowsAzure\Table\TableRestProxy::queryEntities
  849. */
  850. public function testInsertEntityString()
  851. {
  852. foreach(TableServiceFunctionalTestData::getInterestingGoodStrings() as $o) {
  853. $ent = new Entity();
  854. $ent->setPartitionKey(TableServiceFunctionalTestData::getNewKey());
  855. $ent->setRowKey(TableServiceFunctionalTestData::getNewKey());
  856. $ent->addProperty('STRING', EdmType::STRING, $o);
  857. $this->insertEntityWorker($ent, true, null, $o);
  858. }
  859. }
  860. /**
  861. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  862. * @covers WindowsAzure\Table\TableRestProxy::queryEntities
  863. */
  864. private function insertEntityWorker($ent, $isGood, $options, $specialValue = null)
  865. {
  866. $table = $this->getCleanTable();
  867. try {
  868. $ret = (is_null($options) ? $this->restProxy->insertEntity($table, $ent) : $this->restProxy->insertEntity($table, $ent, $options));
  869. if (is_null($options)) {
  870. $options = new TableServiceOptions();
  871. }
  872. // Check that the message matches
  873. $this->assertNotNull($ret->getEntity(), 'getEntity()');
  874. $this->verifyinsertEntityWorker($ent, $ret->getEntity());
  875. if (is_null($ent->getPartitionKey()) || is_null($ent->getRowKey())) {
  876. $this->fail('Expect missing keys throw');
  877. }
  878. if (!$isGood) {
  879. $this->fail('Expect bad values to throw: ' . self::tmptostring($specialValue));
  880. }
  881. // Check that the message matches
  882. $qer = $this->restProxy->queryEntities($table);
  883. $this->assertNotNull($qer->getEntities(), 'getEntities()');
  884. $this->assertEquals(1, count($qer->getEntities()), 'getEntities() count');
  885. $entReturned = $qer->getEntities();
  886. $entReturned = $entReturned[0];
  887. $this->assertNotNull($entReturned, 'getEntities()[0]');
  888. $this->verifyinsertEntityWorker($ent, $entReturned);
  889. } catch (ServiceException $e) {
  890. if (!$isGood) {
  891. $this->assertEquals(TestResources::STATUS_BAD_REQUEST, $e->getCode(), 'getCode');
  892. } else if (is_null($ent->getPartitionKey()) || is_null($ent->getRowKey())) {
  893. $this->assertEquals(TestResources::STATUS_BAD_REQUEST, $e->getCode(), 'getCode');
  894. } else {
  895. throw $e;
  896. }
  897. }
  898. $this->clearTable($table);
  899. }
  900. /**
  901. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  902. * @covers WindowsAzure\Table\TableRestProxy::queryEntities
  903. * @covers WindowsAzure\Table\TableRestProxy::updateEntity
  904. */
  905. public function testUpdateEntity()
  906. {
  907. $ents = TableServiceFunctionalTestData::getSimpleEntities(2);
  908. foreach(MutatePivot::values() as $mutatePivot) {
  909. foreach($ents as $initialEnt) {
  910. $options = new TableServiceOptions();
  911. $ent = TableServiceFunctionalTestUtils::cloneEntity($initialEnt);
  912. TableServiceFunctionalTestUtils::mutateEntity($ent, $mutatePivot);
  913. $this->updateEntityWorker($initialEnt, $ent, $options);
  914. }
  915. }
  916. }
  917. /**
  918. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  919. * @covers WindowsAzure\Table\TableRestProxy::queryEntities
  920. * @covers WindowsAzure\Table\TableRestProxy::updateEntity
  921. */
  922. private function updateEntityWorker($initialEnt, $ent, $options)
  923. {
  924. $table = $this->getCleanTable();
  925. // Upload the entity.
  926. $this->restProxy->insertEntity($table, $initialEnt);
  927. if (is_null($options)) {
  928. $this->restProxy->updateEntity($table, $ent);
  929. } else {
  930. $this->restProxy->updateEntity($table, $ent, $options);
  931. }
  932. if (is_null($options)) {
  933. $options = new TableServiceOptions();
  934. }
  935. // Check that the message matches
  936. $qer = $this->restProxy->queryEntities($table);
  937. $this->assertNotNull($qer->getEntities(), 'getEntities()');
  938. $this->assertEquals(1, count($qer->getEntities()), 'getEntities()');
  939. $entReturned = $qer->getEntities();
  940. $entReturned = $entReturned[0];
  941. $this->assertNotNull($entReturned, 'getEntities()[0]');
  942. $this->verifyinsertEntityWorker($ent, $entReturned);
  943. $this->clearTable($table);
  944. }
  945. /**
  946. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  947. * @covers WindowsAzure\Table\TableRestProxy::mergeEntity
  948. * @covers WindowsAzure\Table\TableRestProxy::queryEntities
  949. */
  950. public function testMergeEntity()
  951. {
  952. $ents = TableServiceFunctionalTestData::getSimpleEntities(2);
  953. foreach(MutatePivot::values() as $mutatePivot) {
  954. foreach($ents as $initialEnt) {
  955. $options = new TableServiceOptions();
  956. $ent = TableServiceFunctionalTestUtils::cloneEntity($initialEnt);
  957. TableServiceFunctionalTestUtils::mutateEntity($ent, $mutatePivot);
  958. $this->mergeEntityWorker($initialEnt, $ent, $options);
  959. }
  960. }
  961. }
  962. /**
  963. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  964. * @covers WindowsAzure\Table\TableRestProxy::mergeEntity
  965. * @covers WindowsAzure\Table\TableRestProxy::queryEntities
  966. */
  967. private function mergeEntityWorker($initialEnt, $ent, $options)
  968. {
  969. $table = $this->getCleanTable();
  970. // Upload the entity.
  971. $this->restProxy->insertEntity($table, $initialEnt);
  972. if (is_null($options)) {
  973. $this->restProxy->mergeEntity($table, $ent);
  974. } else {
  975. $this->restProxy->mergeEntity($table, $ent, $options);
  976. }
  977. if (is_null($options)) {
  978. $options = new TableServiceOptions();
  979. }
  980. // Check that the message matches
  981. $qer = $this->restProxy->queryEntities($table);
  982. $this->assertNotNull($qer->getEntities(), 'getEntities()');
  983. $this->assertEquals(1, count($qer->getEntities()), 'getEntities() count');
  984. $entReturned = $qer->getEntities();
  985. $entReturned = $entReturned[0];
  986. $this->assertNotNull($entReturned, 'getEntities()[0]');
  987. $this->verifymergeEntityWorker($initialEnt, $ent, $entReturned);
  988. $this->clearTable($table);
  989. }
  990. /**
  991. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  992. * @covers WindowsAzure\Table\TableRestProxy::insertOrReplaceEntity
  993. * @covers WindowsAzure\Table\TableRestProxy::queryEntities
  994. */
  995. public function testInsertOrReplaceEntity()
  996. {
  997. $ents = TableServiceFunctionalTestData::getSimpleEntities(2);
  998. foreach(MutatePivot::values() as $mutatePivot) {
  999. foreach($ents as $initialEnt) {
  1000. $options = new TableServiceOptions();
  1001. $ent = TableServiceFunctionalTestUtils::cloneEntity($initialEnt);
  1002. TableServiceFunctionalTestUtils::mutateEntity($ent, $mutatePivot);
  1003. try {
  1004. $this->insertOrReplaceEntityWorker($initialEnt, $ent, $options);
  1005. $this->assertFalse($this->isEmulated(), 'Should succeed when not running in emulator');
  1006. } catch (ServiceException $e) {
  1007. // Expect failure in emulator, as v1.6 doesn't support this method
  1008. if ($this->isEmulated()) {
  1009. $this->assertEquals(TestResources::STATUS_BAD_REQUEST, $e->getCode(), 'getCode');
  1010. } else {
  1011. throw $e;
  1012. }
  1013. }
  1014. }
  1015. }
  1016. }
  1017. /**
  1018. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  1019. * @covers WindowsAzure\Table\TableRestProxy::insertOrReplaceEntity
  1020. * @covers WindowsAzure\Table\TableRestProxy::queryEntities
  1021. */
  1022. private function insertOrReplaceEntityWorker($initialEnt, $ent, $options)
  1023. {
  1024. $table = $this->getCleanTable();
  1025. // Upload the entity.
  1026. $this->restProxy->insertEntity($table, $initialEnt);
  1027. if (is_null($options)) {
  1028. $this->restProxy->insertOrReplaceEntity($table, $ent);
  1029. } else {
  1030. $this->restProxy->insertOrReplaceEntity($table, $ent, $options);
  1031. }
  1032. if (is_null($options)) {
  1033. $options = new TableServiceOptions();
  1034. }
  1035. // Check that the message matches
  1036. $qer = $this->restProxy->queryEntities($table);
  1037. $this->assertNotNull($qer->getEntities(), 'getEntities()');
  1038. $this->assertEquals(1, count($qer->getEntities()), 'getEntities() count');
  1039. $entReturned = $qer->getEntities();
  1040. $entReturned = $entReturned[0];
  1041. $this->assertNotNull($entReturned, 'getEntities()[0]');
  1042. $this->verifyinsertEntityWorker($ent, $entReturned);
  1043. $this->clearTable($table);
  1044. }
  1045. /**
  1046. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  1047. * @covers WindowsAzure\Table\TableRestProxy::insertOrMergeEntity
  1048. * @covers WindowsAzure\Table\TableRestProxy::queryEntities
  1049. */
  1050. public function testInsertOrMergeEntity()
  1051. {
  1052. $ents = TableServiceFunctionalTestData::getSimpleEntities(2);
  1053. foreach(MutatePivot::values() as $mutatePivot) {
  1054. foreach($ents as $initialEnt) {
  1055. $options = new TableServiceOptions();
  1056. $ent = TableServiceFunctionalTestUtils::cloneEntity($initialEnt);
  1057. TableServiceFunctionalTestUtils::mutateEntity($ent, $mutatePivot);
  1058. try {
  1059. $this->insertOrMergeEntityWorker($initialEnt, $ent, $options);
  1060. $this->assertFalse($this->isEmulated(), 'Should succeed when not running in emulator');
  1061. } catch (ServiceException $e) {
  1062. // Expect failure in emulator, as v1.6 doesn't support this method
  1063. if ($this->isEmulated()) {
  1064. $this->assertEquals(TestResources::STATUS_BAD_REQUEST, $e->getCode(), 'getCode');
  1065. } else {
  1066. throw $e;
  1067. }
  1068. }
  1069. }
  1070. }
  1071. }
  1072. /**
  1073. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  1074. * @covers WindowsAzure\Table\TableRestProxy::insertOrMergeEntity
  1075. * @covers WindowsAzure\Table\TableRestProxy::queryEntities
  1076. */
  1077. private function insertOrMergeEntityWorker($initialEnt, $ent, $options)
  1078. {
  1079. $table = $this->getCleanTable();
  1080. // Upload the entity.
  1081. $this->restProxy->insertEntity($table, $initialEnt);
  1082. if (is_null($options)) {
  1083. $this->restProxy->insertOrMergeEntity($table, $ent);
  1084. } else {
  1085. $this->restProxy->insertOrMergeEntity($table, $ent, $options);
  1086. }
  1087. if (is_null($options)) {
  1088. $options = new TableServiceOptions();
  1089. }
  1090. // Check that the message matches
  1091. $qer = $this->restProxy->queryEntities($table);
  1092. $this->assertNotNull($qer->getEntities(), 'getEntities()');
  1093. $this->assertEquals(1, count($qer->getEntities()), 'getEntities() count');
  1094. $entReturned = $qer->getEntities();
  1095. $entReturned = $entReturned[0];
  1096. $this->assertNotNull($entReturned, 'getEntities()[0]');
  1097. $this->verifymergeEntityWorker($initialEnt, $ent, $entReturned);
  1098. $this->clearTable($table);
  1099. }
  1100. /**
  1101. * @covers WindowsAzure\Table\TableRestProxy::deleteEntity
  1102. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  1103. * @covers WindowsAzure\Table\TableRestProxy::insertOrMergeEntity
  1104. * @covers WindowsAzure\Table\TableRestProxy::insertOrReplaceEntity
  1105. * @covers WindowsAzure\Table\TableRestProxy::mergeEntity
  1106. * @covers WindowsAzure\Table\TableRestProxy::updateEntity
  1107. */
  1108. public function testCRUDdeleteEntity()
  1109. {
  1110. foreach(ConcurType::values() as $concurType) {
  1111. foreach(MutatePivot::values() as $mutatePivot) {
  1112. for ($i = 0; $i <= 1; $i++) {
  1113. foreach(TableServiceFunctionalTestData::getSimpleEntities(2) as $ent) {
  1114. $options = ($i == 0 ? null : new TableServiceOptions());
  1115. $this->crudWorker(OpType::DELETE_ENTITY, $concurType, $mutatePivot, $ent, $options);
  1116. }
  1117. }
  1118. }
  1119. }
  1120. }
  1121. /*
  1122. /**
  1123. * @covers WindowsAzure\Table\TableRestProxy::deleteEntity
  1124. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  1125. * @covers WindowsAzure\Table\TableRestProxy::insertOrMergeEntity
  1126. * @covers WindowsAzure\Table\TableRestProxy::insertOrReplaceEntity
  1127. * @covers WindowsAzure\Table\TableRestProxy::mergeEntity
  1128. * @covers WindowsAzure\Table\TableRestProxy::updateEntity
  1129. */
  1130. public function testCRUDinsertEntity()
  1131. {
  1132. foreach(ConcurType::values() as $concurType) {
  1133. foreach(MutatePivot::values() as $mutatePivot) {
  1134. for ($i = 0; $i <= 1; $i++) {
  1135. foreach(TableServiceFunctionalTestData::getSimpleEntities(2) as $ent) {
  1136. $options = ($i == 0 ? null : new TableServiceOptions());
  1137. $this->crudWorker(OpType::INSERT_ENTITY, $concurType, $mutatePivot, $ent, $options);
  1138. }
  1139. }
  1140. }
  1141. }
  1142. }
  1143. /**
  1144. * @covers WindowsAzure\Table\TableRestProxy::deleteEntity
  1145. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  1146. * @covers WindowsAzure\Table\TableRestProxy::insertOrMergeEntity
  1147. * @covers WindowsAzure\Table\TableRestProxy::insertOrReplaceEntity
  1148. * @covers WindowsAzure\Table\TableRestProxy::mergeEntity
  1149. * @covers WindowsAzure\Table\TableRestProxy::updateEntity
  1150. */
  1151. public function testCRUDinsertOrMergeEntity()
  1152. {
  1153. $this->skipIfEmulated();
  1154. foreach(ConcurType::values() as $concurType) {
  1155. foreach(MutatePivot::values() as $mutatePivot) {
  1156. for ($i = 0; $i <= 1; $i++) {
  1157. foreach(TableServiceFunctionalTestData::getSimpleEntities(2) as $ent) {
  1158. $options = ($i == 0 ? null : new TableServiceOptions());
  1159. $this->crudWorker(OpType::INSERT_OR_MERGE_ENTITY, $concurType, $mutatePivot, $ent, $options);
  1160. }
  1161. }
  1162. }
  1163. }
  1164. }
  1165. /*
  1166. /**
  1167. * @covers WindowsAzure\Table\TableRestProxy::deleteEntity
  1168. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  1169. * @covers WindowsAzure\Table\TableRestProxy::insertOrMergeEntity
  1170. * @covers WindowsAzure\Table\TableRestProxy::insertOrReplaceEntity
  1171. * @covers WindowsAzure\Table\TableRestProxy::mergeEntity
  1172. * @covers WindowsAzure\Table\TableRestProxy::updateEntity
  1173. */
  1174. public function testCRUDinsertOrReplaceEntity()
  1175. {
  1176. $this->skipIfEmulated();
  1177. foreach(ConcurType::values() as $concurType) {
  1178. foreach(MutatePivot::values() as $mutatePivot) {
  1179. for ($i = 0; $i <= 1; $i++) {
  1180. foreach(TableServiceFunctionalTestData::getSimpleEntities(2) as $ent) {
  1181. $options = ($i == 0 ? null : new TableServiceOptions());
  1182. $this->crudWorker(OpType::INSERT_OR_REPLACE_ENTITY, $concurType, $mutatePivot, $ent, $options);
  1183. }
  1184. }
  1185. }
  1186. }
  1187. }
  1188. /**
  1189. * @covers WindowsAzure\Table\TableRestProxy::deleteEntity
  1190. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  1191. * @covers WindowsAzure\Table\TableRestProxy::insertOrMergeEntity
  1192. * @covers WindowsAzure\Table\TableRestProxy::insertOrReplaceEntity
  1193. * @covers WindowsAzure\Table\TableRestProxy::mergeEntity
  1194. * @covers WindowsAzure\Table\TableRestProxy::updateEntity
  1195. */
  1196. public function testCRUDmergeEntity()
  1197. {
  1198. foreach(ConcurType::values() as $concurType) {
  1199. foreach(MutatePivot::values() as $mutatePivot) {
  1200. for ($i = 0; $i <= 1; $i++) {
  1201. foreach(TableServiceFunctionalTestData::getSimpleEntities(2) as $ent) {
  1202. $options = ($i == 0 ? null : new TableServiceOptions());
  1203. $this->crudWorker(OpType::MERGE_ENTITY, $concurType, $mutatePivot, $ent, $options);
  1204. }
  1205. }
  1206. }
  1207. }
  1208. }
  1209. /**
  1210. * @covers WindowsAzure\Table\TableRestProxy::deleteEntity
  1211. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  1212. * @covers WindowsAzure\Table\TableRestProxy::insertOrMergeEntity
  1213. * @covers WindowsAzure\Table\TableRestProxy::insertOrReplaceEntity
  1214. * @covers WindowsAzure\Table\TableRestProxy::mergeEntity
  1215. * @covers WindowsAzure\Table\TableRestProxy::updateEntity
  1216. */
  1217. public function testCRUDupdateEntity()
  1218. {
  1219. foreach(ConcurType::values() as $concurType) {
  1220. foreach(MutatePivot::values() as $mutatePivot) {
  1221. for ($i = 0; $i <= 1; $i++) {
  1222. foreach(TableServiceFunctionalTestData::getSimpleEntities(2) as $ent) {
  1223. $options = ($i == 0 ? null : new TableServiceOptions());
  1224. $this->crudWorker(OpType::UPDATE_ENTITY, $concurType, $mutatePivot, $ent, $options);
  1225. }
  1226. }
  1227. }
  1228. }
  1229. }
  1230. /**
  1231. * @covers WindowsAzure\Table\TableRestProxy::deleteEntity
  1232. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  1233. * @covers WindowsAzure\Table\TableRestProxy::insertOrMergeEntity
  1234. * @covers WindowsAzure\Table\TableRestProxy::insertOrReplaceEntity
  1235. * @covers WindowsAzure\Table\TableRestProxy::mergeEntity
  1236. * @covers WindowsAzure\Table\TableRestProxy::updateEntity
  1237. */
  1238. private function crudWorker($opType, $concurType, $mutatePivot, $ent, $options)
  1239. {
  1240. $exptErr = $this->expectConcurrencyFailure($opType, $concurType);
  1241. $table = $this->getCleanTable();
  1242. try {
  1243. // Upload the entity.
  1244. $initial = $this->restProxy->insertEntity($table, $ent);
  1245. $targetEnt = $this->createTargetEntity($table, $initial->getEntity(), $concurType, $mutatePivot);
  1246. $this->executeCrudMethod($table, $targetEnt, $opType, $concurType, $options);
  1247. if (!is_null($exptErr)) {
  1248. $this->fail('Expected a failure when opType=' . $opType . ' and concurType=' . $concurType . ' :' . $this->expectConcurrencyFailure($opType, $concurType));
  1249. }
  1250. $this->verifyCrudWorker($opType, $table, $ent, $targetEnt, true);
  1251. } catch (ServiceException $e) {
  1252. if (!is_null($exptErr)) {
  1253. $this->assertEquals($exptErr, $e->getCode(), 'getCode');
  1254. } else {
  1255. throw $e;
  1256. }
  1257. }
  1258. $this->clearTable($table);
  1259. }
  1260. /**
  1261. * @covers WindowsAzure\Table\TableRestProxy::batch
  1262. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  1263. */
  1264. public function testBatchPositiveFirstNoKeyMatch()
  1265. {
  1266. $this->batchPositiveOuter(ConcurType::NO_KEY_MATCH, 123);
  1267. }
  1268. /**
  1269. * @covers WindowsAzure\Table\TableRestProxy::batch
  1270. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  1271. */
  1272. public function testBatchPositiveFirstKeyMatchNoETag()
  1273. {
  1274. $this->batchPositiveOuter(ConcurType::KEY_MATCH_NO_ETAG, 234);
  1275. }
  1276. /**
  1277. * @covers WindowsAzure\Table\TableRestProxy::batch
  1278. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  1279. */
  1280. public function testBatchPositiveFirstKeyMatchETagMismatch()
  1281. {
  1282. $this->skipIfEmulated();
  1283. $this->batchPositiveOuter(ConcurType::KEY_MATCH_ETAG_MISMATCH, 345);
  1284. }
  1285. /**
  1286. * @covers WindowsAzure\Table\TableRestProxy::batch
  1287. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  1288. */
  1289. public function testBatchPositiveFirstKeyMatchETagMatch()
  1290. {
  1291. $this->batchPositiveOuter(ConcurType::KEY_MATCH_ETAG_MATCH, 456);
  1292. }
  1293. /**
  1294. * @covers WindowsAzure\Table\TableRestProxy::batch
  1295. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  1296. */
  1297. public function testBatchNegative()
  1298. {
  1299. $this->skipIfEmulated();
  1300. // The random here is not to generate random values, but to
  1301. // get a good mix of values in the table entities.
  1302. mt_srand(456);
  1303. $concurTypes = ConcurType::values();
  1304. $mutatePivots = MutatePivot::values();
  1305. $opTypes = OpType::values();
  1306. for ($j = 0; $j < 10; $j++) {
  1307. $configs = array();
  1308. foreach(TableServiceFunctionalTestData::getSimpleEntities(6) as $ent) {
  1309. $config = new BatchWorkerConfig();
  1310. $config->concurType = $concurTypes[mt_rand(0, count($concurTypes))];
  1311. $config->opType = $opTypes[mt_rand(0, count($opTypes))];
  1312. $config->mutatePivot = $mutatePivots[mt_rand(0, count($mutatePivots))];
  1313. $config->ent = $ent;
  1314. array_push($configs, $config);
  1315. }
  1316. for ($i = 0; $i <= 1; $i++) {
  1317. $options = ($i == 0 ? null : new TableServiceOptions());
  1318. $this->batchWorker($configs, $options);
  1319. }
  1320. }
  1321. }
  1322. private function verifyinsertEntityWorker($ent, $entReturned)
  1323. {
  1324. $this->verifyinsertOrMergeEntityWorker(null, $ent, $entReturned);
  1325. }
  1326. private function verifymergeEntityWorker($intitalEnt, $ent, $entReturned)
  1327. {
  1328. $this->verifyinsertOrMergeEntityWorker($intitalEnt, $ent, $entReturned);
  1329. }
  1330. private function verifyinsertOrMergeEntityWorker($initialEnt, $ent, $entReturned)
  1331. {
  1332. $expectedProps = array();
  1333. if (!is_null($initialEnt) && $initialEnt->getPartitionKey() == $ent->getPartitionKey() && $initialEnt->getRowKey() == $ent->getRowKey()) {
  1334. foreach($initialEnt->getProperties() as $pname => $actualProp) {
  1335. if (!is_null($actualProp) && !is_null($actualProp->getValue())) {
  1336. $cloneProp = null;
  1337. if (!is_null($actualProp)) {
  1338. $cloneProp = new Property();
  1339. $cloneProp->setEdmType($actualProp->getEdmType());
  1340. $cloneProp->setValue($actualProp->getValue());
  1341. }
  1342. $expectedProps[$pname] = $cloneProp;
  1343. }
  1344. }
  1345. }
  1346. foreach($ent->getProperties() as $pname => $actualProp) {
  1347. // Any properties with null values are ignored by the Merge Entity operation.
  1348. // All other properties will be updated.
  1349. if (!is_null($actualProp) && !is_null($actualProp->getValue())) {
  1350. $cloneProp = new Property();
  1351. $cloneProp->setEdmType($actualProp->getEdmType());
  1352. $cloneProp->setValue($actualProp->getValue());
  1353. $expectedProps[$pname] = $cloneProp;
  1354. }
  1355. }
  1356. $effectiveProps = array();
  1357. foreach($entReturned->getProperties() as $pname => $actualProp) {
  1358. // This is to work with Dev Storage, which returns items for all
  1359. // columns, null valued or not.
  1360. if (!is_null($actualProp) && !is_null($actualProp->getValue())) {
  1361. $cloneProp = new Property();
  1362. $cloneProp->setEdmType($actualProp->getEdmType());
  1363. $cloneProp->setValue($actualProp->getValue());
  1364. $effectiveProps[$pname] = $cloneProp;
  1365. }
  1366. }
  1367. // Compare the entities to make sure they match.
  1368. $this->assertEquals($ent->getPartitionKey(), $entReturned->getPartitionKey(), 'getPartitionKey');
  1369. $this->assertEquals($ent->getRowKey(), $entReturned->getRowKey(), 'getRowKey');
  1370. $this->assertNotNull($entReturned->getETag(), 'getETag');
  1371. if (!is_null($ent->getETag())) {
  1372. $this->assertTrue($ent->getETag() != $entReturned->getETag(), 'getETag should change after submit: initial \'' . $ent->getETag() . '\', returned \'' . $entReturned->getETag() . '\'');
  1373. }
  1374. $this->assertNotNull($entReturned->getTimestamp(), 'getTimestamp');
  1375. if (is_null($ent->getTimestamp())) {
  1376. // This property will come back, so need to account for it.
  1377. $expectedProps['Timestamp'] = null;
  1378. } else {
  1379. $this->assertEquals($ent->getTimestamp(), $entReturned->getTimestamp(), 'getTimestamp');
  1380. }
  1381. $this->assertNotNull($ent->getProperties(), 'getProperties');
  1382. // Need to skip null values from the count.
  1383. $this->assertEquals(count($expectedProps), count($effectiveProps), 'getProperties()');
  1384. foreach($expectedProps as $pname => $expectedProp) {
  1385. $actualProp = $effectiveProps;
  1386. $actualProp = $actualProp[$pname];
  1387. $this->assertNotNull($actualProp, 'getProperties()[\'' . $pname . '\')');
  1388. if (!is_null($expectedProp) ) {
  1389. $this->compareProperties($pname, $actualProp, $expectedProp);
  1390. }
  1391. $this->assertEquals($entReturned->getProperty($pname), $actualProp, 'getProperty(\'' . $pname . '\')');
  1392. $this->assertEquals($entReturned->getPropertyValue($pname), $actualProp->getValue(), 'getPropertyValue(\'' . $pname . '\')');
  1393. }
  1394. }
  1395. /**
  1396. * @covers WindowsAzure\Table\TableRestProxy::batch
  1397. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  1398. */
  1399. private function batchPositiveOuter($firstConcurType, $seed)
  1400. {
  1401. // The random here is not to generate random values, but to
  1402. // get a good mix of values in the table entities.
  1403. mt_srand($seed);
  1404. $concurTypes = ConcurType::values();
  1405. $mutatePivots = MutatePivot::values();
  1406. $opTypes = OpType::values();
  1407. // Main loop.
  1408. foreach($opTypes as $firstOpType) {
  1409. if (!is_null($this->expectConcurrencyFailure($firstOpType, $firstConcurType))) {
  1410. // Want to know there is at least one part that does not fail.
  1411. continue;
  1412. }
  1413. if ($this->isEmulated() && (
  1414. ($firstOpType == OpType::INSERT_OR_MERGE_ENTITY) ||
  1415. ($firstOpType == OpType::INSERT_OR_REPLACE_ENTITY))) {
  1416. // Emulator does not support these operations.
  1417. continue;
  1418. }
  1419. $simpleEntities = TableServiceFunctionalTestData::getSimpleEntities(6);
  1420. $configs = array();
  1421. $firstConfig = new BatchWorkerConfig();
  1422. $firstConfig->concurType = $firstConcurType;
  1423. $firstConfig->opType = $firstOpType;
  1424. $firstConfig->ent = $simpleEntities[0];
  1425. $firstConfig->mutatePivot = $mutatePivots[mt_rand(0, count($mutatePivots))];
  1426. array_push($configs, $firstConfig);
  1427. for ($i = 1; $i < count($simpleEntities); $i++) {
  1428. $config = new BatchWorkerConfig();
  1429. while (!is_null($this->expectConcurrencyFailure($config->opType, $config->concurType))) {
  1430. $config->concurType = $concurTypes[mt_rand(0, count($concurTypes))];
  1431. $config->opType = $opTypes[mt_rand(0, count($opTypes))];
  1432. if ($this->isEmulated()) {
  1433. if ($config->opType == OpType::INSERT_OR_MERGE_ENTITY) {
  1434. $config->opType = OpType::MERGE_ENTITY;
  1435. }
  1436. if ($config->opType == OpType::INSERT_OR_REPLACE_ENTITY) {
  1437. $config->opType = OpType::UPDATE_ENTITY;
  1438. }
  1439. }
  1440. }
  1441. $config->mutatePivot = $mutatePivots[mt_rand(0, count($mutatePivots) -1)];
  1442. $config->ent = $simpleEntities[$i];
  1443. array_push($configs, $config);
  1444. }
  1445. for ($i = 0; $i <= 1; $i++) {
  1446. $options = ($i == 0 ? null : new TableServiceOptions());
  1447. if ($this->isEmulated()) {
  1448. // The emulator has trouble with some batches.
  1449. for ($j = 0; $j < count($configs); $j++) {
  1450. $tmpconfigs = array();
  1451. $tmpconfigs[] = $configs[$j];
  1452. $this->batchWorker($tmpconfigs, $options);
  1453. }
  1454. } else {
  1455. $this->batchWorker($configs, $options);
  1456. }
  1457. }
  1458. }
  1459. }
  1460. /**
  1461. * @covers WindowsAzure\Table\TableRestProxy::batch
  1462. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  1463. */
  1464. private function batchWorker($configs, $options)
  1465. {
  1466. $exptErrs = array();
  1467. $expectedReturned = count($configs);
  1468. $expectedError = false;
  1469. $expectedErrorCount = 0;
  1470. for ($i = 0; $i < count($configs); $i++) {
  1471. $err = $this->expectConcurrencyFailure($configs[$i]->opType, $configs[$i]->concurType);
  1472. if (!is_null($err)) {
  1473. $expectedErrorCount++;
  1474. $expectedError = true;
  1475. }
  1476. array_push($exptErrs, $err);
  1477. }
  1478. $table = $this->getCleanTable();
  1479. try {
  1480. // Upload the initial entities and get the target entities.
  1481. $targetEnts = array();
  1482. for ($i = 0; $i < count($configs); $i++) {
  1483. $initial = $this->restProxy->insertEntity($table, $configs[$i]->ent);
  1484. array_push($targetEnts, $this->createTargetEntity($table, $initial->getEntity(),
  1485. $configs[$i]->concurType,
  1486. $configs[$i]->mutatePivot));
  1487. }
  1488. // Build up the batch.
  1489. $operations = new BatchOperations();
  1490. for ($i = 0; $i < count($configs); $i++) {
  1491. $this->buildBatchOperations($table, $operations, $targetEnts[$i],
  1492. $configs[$i]->opType,
  1493. $configs[$i]->concurType,
  1494. $configs[$i]->options);
  1495. }
  1496. // Execute the batch.
  1497. $ret = (is_null($options) ? $this->restProxy->batch($operations) : $this->restProxy->batch($operations, $options));
  1498. if (is_null($options)) {
  1499. $options = new QueryEntitiesOptions();
  1500. }
  1501. // Verify results.
  1502. if ($expectedError) {
  1503. $this->assertEquals($expectedErrorCount, count($ret->getEntries()), 'count $ret->getEntries()');
  1504. // No changes should have gone through.
  1505. for ($i = 0; $i < count($configs); $i++) {
  1506. $this->verifyCrudWorker($configs[$i]->opType, $table, $configs[$i]->ent, $configs[$i]->ent, false);
  1507. }
  1508. } else {
  1509. $this->assertEquals($expectedReturned, count($ret->getEntries()), 'count $ret->getEntries()');
  1510. for ($i = 0; $i < count($ret->getEntries()); $i++) {
  1511. $opResult = $ret->getEntries();
  1512. $opResult = $opResult[$i];
  1513. $this->verifyBatchEntryType($configs[$i]->opType, $exptErrs[$i], $opResult);
  1514. $this->verifyEntryData($table, $exptErrs[$i], $targetEnts[$i], $opResult);
  1515. // Check out the entities.
  1516. $this->verifyCrudWorker($configs[$i]->opType, $table, $configs[$i]->ent, $targetEnts[$i], true);
  1517. }
  1518. }
  1519. } catch (ServiceException $e) {
  1520. if ($expectedError) {
  1521. $this->assertEquals(TestResources::STATUS_BAD_REQUEST, $e->getCode(), 'getCode');
  1522. } else {
  1523. throw $e;
  1524. }
  1525. }
  1526. $this->clearTable($table);
  1527. }
  1528. /**
  1529. * @covers WindowsAzure\Table\TableRestProxy::getEntity
  1530. */
  1531. private function verifyEntryData($table, $exptErr, $targetEnt, $opResult)
  1532. {
  1533. if ($opResult instanceof InsertEntityResult) {
  1534. $this->verifyinsertEntityWorker($targetEnt, $opResult->getEntity());
  1535. } else if ($opResult instanceof UpdateEntityResult) {
  1536. $ger = $this->restProxy->getEntity($table, $targetEnt->getPartitionKey(), $targetEnt->getRowKey());
  1537. $this->assertEquals($opResult->getETag(), $ger->getEntity()->getETag(), 'op->getETag');
  1538. } else if (is_string($opResult)) {
  1539. // Nothing special to do.
  1540. } else if ($opResult instanceof BatchError) {
  1541. $this->assertEquals($exptErr, $opResult->getError()->getCode(), 'getError()->getCode');
  1542. } else {
  1543. $this->fail('opResult is of an unknown type');
  1544. }
  1545. }
  1546. private function verifyBatchEntryType($opType, $exptErr, $opResult)
  1547. {
  1548. if (is_null($exptErr)) {
  1549. switch ($opType) {
  1550. case OpType::INSERT_ENTITY:
  1551. $this->assertTrue($opResult instanceof InsertEntityResult,
  1552. 'When opType=' . $opType . ' expect opResult instanceof InsertEntityResult');
  1553. break;
  1554. case OpType::DELETE_ENTITY:
  1555. $this->assertTrue(
  1556. is_string($opResult),
  1557. 'When opType=' . $opType . ' expect opResult is a string');
  1558. break;
  1559. case OpType::UPDATE_ENTITY:
  1560. case OpType::INSERT_OR_REPLACE_ENTITY:
  1561. case OpType::MERGE_ENTITY:
  1562. case OpType::INSERT_OR_MERGE_ENTITY:
  1563. $this->assertTrue($opResult instanceof UpdateEntityResult,
  1564. 'When opType=' . $opType . ' expect opResult instanceof UpdateEntityResult');
  1565. break;
  1566. }
  1567. } else {
  1568. $this->assertTrue($opResult instanceof BatchError, 'When expect an error, expect opResult instanceof BatchError');
  1569. }
  1570. }
  1571. private function buildBatchOperations($table, $operations, $targetEnt, $opType, $concurType, $options)
  1572. {
  1573. switch ($opType) {
  1574. case OpType::DELETE_ENTITY:
  1575. if (is_null($options) && $concurType != ConcurType::KEY_MATCH_ETAG_MISMATCH) {
  1576. $operations->addDeleteEntity($table, $targetEnt->getPartitionKey(), $targetEnt->getRowKey(), null);
  1577. } else {
  1578. $operations->addDeleteEntity($table, $targetEnt->getPartitionKey(), $targetEnt->getRowKey(), $targetEnt->getETag());
  1579. }
  1580. break;
  1581. case OpType::INSERT_ENTITY:
  1582. $operations->addInsertEntity($table, $targetEnt);
  1583. break;
  1584. case OpType::INSERT_OR_MERGE_ENTITY:
  1585. $operations->addInsertOrMergeEntity($table, $targetEnt);
  1586. break;
  1587. case OpType::INSERT_OR_REPLACE_ENTITY:
  1588. $operations->addInsertOrReplaceEntity($table, $targetEnt);
  1589. break;
  1590. case OpType::MERGE_ENTITY:
  1591. $operations->addMergeEntity($table, $targetEnt);
  1592. break;
  1593. case OpType::UPDATE_ENTITY:
  1594. $operations->addUpdateEntity($table, $targetEnt);
  1595. break;
  1596. }
  1597. }
  1598. /**
  1599. * @covers WindowsAzure\Table\TableRestProxy::deleteEntity
  1600. * @covers WindowsAzure\Table\TableRestProxy::insertEntity
  1601. * @covers WindowsAzure\Table\TableRestProxy::insertOrMergeEntity
  1602. * @covers WindowsAzure\Table\TableRestProxy::insertOrReplaceEntity
  1603. * @covers WindowsAzure\Table\TableRestProxy::mergeEntity
  1604. * @covers WindowsAzure\Table\TableRestProxy::updateEntity
  1605. */
  1606. private function executeCrudMethod($table, $targetEnt, $opType, $concurType, $options)
  1607. {
  1608. switch ($opType) {
  1609. case OpType::DELETE_ENTITY:
  1610. if (is_null($options) && $concurType != ConcurType::KEY_MATCH_ETAG_MISMATCH) {
  1611. $this->restProxy->deleteEntity($table, $targetEnt->getPartitionKey(), $targetEnt->getRowKey());
  1612. } else {
  1613. $delOptions = new DeleteEntityOptions();
  1614. $delOptions->setETag($targetEnt->getETag());
  1615. $this->restProxy->deleteEntity($table, $targetEnt->getPartitionKey(), $targetEnt->getRowKey(), $delOptions);
  1616. }
  1617. break;
  1618. case OpType::INSERT_ENTITY:
  1619. if (is_null($options)) {
  1620. $this->restProxy->insertEntity($table, $targetEnt);
  1621. } else {
  1622. $this->restProxy->insertEntity($table, $targetEnt, $options);
  1623. }
  1624. break;
  1625. case OpType::INSERT_OR_MERGE_ENTITY:
  1626. if (is_null($options)) {
  1627. $this->restProxy->insertOrMergeEntity($table, $targetEnt);
  1628. } else {
  1629. $this->restProxy->insertOrMergeEntity($table, $targetEnt, $options);
  1630. }
  1631. break;
  1632. case OpType::INSERT_OR_REPLACE_ENTITY:
  1633. if (is_null($options)) {
  1634. $this->restProxy->insertOrReplaceEntity($table, $targetEnt);
  1635. } else {
  1636. $this->restProxy->insertOrReplaceEntity($table, $targetEnt, $options);
  1637. }
  1638. break;
  1639. case OpType::MERGE_ENTITY:
  1640. if (is_null($options)) {
  1641. $this->restProxy->mergeEntity($table, $targetEnt);
  1642. } else {
  1643. $this->restProxy->mergeEntity($table, $targetEnt, $options);
  1644. }
  1645. break;
  1646. case OpType::UPDATE_ENTITY:
  1647. if (is_null($options)) {
  1648. $this->restProxy->updateEntity($table, $targetEnt);
  1649. } else {
  1650. $this->restProxy->updateEntity($table, $targetEnt, $options);
  1651. }
  1652. break;
  1653. }
  1654. }
  1655. /**
  1656. * @covers WindowsAzure\Table\TableRestProxy::getEntity
  1657. */
  1658. private function verifyCrudWorker($opType, $table, $initialEnt, $targetEnt, $expectedSuccess)
  1659. {
  1660. $entInTable = null;
  1661. try {
  1662. $ger = $this->restProxy->getEntity($table, $targetEnt->getPartitionKey(), $targetEnt->getRowKey());
  1663. $entInTable = $ger->getEntity();
  1664. } catch (ServiceException $e) {
  1665. $this->assertTrue(($opType == OpType::DELETE_ENTITY) && (TestResources::STATUS_NOT_FOUND == $e->getCode()), '404:NotFound is expected for deletes');
  1666. }
  1667. switch ($opType) {
  1668. case OpType::DELETE_ENTITY:
  1669. // Check that the entity really is gone
  1670. if ($expectedSuccess) {
  1671. $this->assertNull($entInTable, 'Entity from table');
  1672. } else {
  1673. // Check that the message matches
  1674. $this->assertNotNull($entInTable, 'Entity from table');
  1675. $this->verifyinsertEntityWorker($targetEnt, $entInTable);
  1676. }
  1677. break;
  1678. case OpType::INSERT_ENTITY:
  1679. // Check that the message matches
  1680. $this->assertNotNull($entInTable, 'Entity from table');
  1681. $this->verifyinsertEntityWorker($targetEnt, $entInTable);
  1682. break;
  1683. case OpType::INSERT_OR_MERGE_ENTITY:
  1684. $this->assertNotNull($entInTable, 'Entity from table');
  1685. $this->verifymergeEntityWorker($initialEnt, $targetEnt, $entInTable);
  1686. break;
  1687. case OpType::INSERT_OR_REPLACE_ENTITY:
  1688. // Check that the message matches
  1689. $this->assertNotNull($entInTable, 'Entity from table');
  1690. $this->verifyinsertEntityWorker($targetEnt, $entInTable);
  1691. break;
  1692. case OpType::MERGE_ENTITY:
  1693. $this->assertNotNull($entInTable, 'Entity from table');
  1694. $this->verifymergeEntityWorker($initialEnt, $targetEnt, $entInTable);
  1695. break;
  1696. case OpType::UPDATE_ENTITY:
  1697. // Check that the message matches
  1698. $this->assertNotNull($entInTable, 'Entity from table');
  1699. $this->verifyinsertEntityWorker($targetEnt, $entInTable);
  1700. break;
  1701. }
  1702. }
  1703. /**
  1704. * @covers WindowsAzure\Table\TableRestProxy::updateEntity
  1705. */
  1706. private function createTargetEntity($table, $initialEnt, $concurType, $mutatePivot)
  1707. {
  1708. $targetEnt = TableServiceFunctionalTestUtils::cloneEntity($initialEnt);
  1709. // Update the entity/table state to get the requested concurrency type error.
  1710. switch ($concurType) {
  1711. case ConcurType::NO_KEY_MATCH:
  1712. // Mutate the keys to not match.
  1713. $targetEnt->setRowKey(TableServiceFunctionalTestData::getNewKey());
  1714. break;
  1715. case ConcurType::KEY_MATCH_NO_ETAG:
  1716. $targetEnt->setETag(null);
  1717. break;
  1718. case ConcurType::KEY_MATCH_ETAG_MISMATCH:
  1719. $newETag = $this->restProxy->updateEntity($table, $initialEnt)->getETag();
  1720. $initialEnt->setETag($newETag);
  1721. // Now the $targetEnt ETag will not match.
  1722. $this->assertTrue($targetEnt->getETag() != $initialEnt->getETag(), 'targetEnt->ETag(\'' . $targetEnt->getETag() . '\') != updated->ETag(\'' . $initialEnt->getETag() . '\')');
  1723. break;
  1724. case ConcurType::KEY_MATCH_ETAG_MATCH:
  1725. // Don't worry here.
  1726. break;
  1727. }
  1728. // Mutate the properties.
  1729. TableServiceFunctionalTestUtils::mutateEntity($targetEnt, $mutatePivot);
  1730. return $targetEnt;
  1731. }
  1732. private static function expectConcurrencyFailure($opType, $concurType)
  1733. {
  1734. if (is_null($concurType) || is_null($opType)) {
  1735. return -1;
  1736. }
  1737. switch ($concurType) {
  1738. case ConcurType::NO_KEY_MATCH:
  1739. if (($opType == OpType::DELETE_ENTITY) || ($opType == OpType::MERGE_ENTITY) || ($opType == OpType::UPDATE_ENTITY)) {
  1740. return TestResources::STATUS_NOT_FOUND;
  1741. }
  1742. break;
  1743. case ConcurType::KEY_MATCH_NO_ETAG:
  1744. if ($opType == OpType::INSERT_ENTITY) {
  1745. return TestResources::STATUS_CONFLICT;
  1746. }
  1747. break;
  1748. case ConcurType::KEY_MATCH_ETAG_MATCH:
  1749. if ($opType == OpType::INSERT_ENTITY) {
  1750. return TestResources::STATUS_CONFLICT;
  1751. }
  1752. break;
  1753. case ConcurType::KEY_MATCH_ETAG_MISMATCH:
  1754. if ($opType == OpType::INSERT_ENTITY) {
  1755. return TestResources::STATUS_CONFLICT;
  1756. } else if ($opType == OpType::INSERT_OR_REPLACE_ENTITY || $opType == OpType::INSERT_OR_MERGE_ENTITY) {
  1757. // If exists, just clobber.
  1758. return null;
  1759. }
  1760. return TestResources::STATUS_PRECONDITION_FAILED;
  1761. }
  1762. return null;
  1763. }
  1764. function compareProperties($pname, $actualProp, $expectedProp)
  1765. {
  1766. $effectiveExpectedProp = (is_null($expectedProp->getEdmType()) ? EdmType::STRING : $expectedProp->getEdmType());
  1767. $effectiveActualProp = (is_null($expectedProp->getEdmType()) ? EdmType::STRING : $expectedProp->getEdmType());
  1768. $this->assertEquals($effectiveExpectedProp, $effectiveActualProp,
  1769. 'getProperties()->get(\'' . $pname . '\')->getEdmType');
  1770. $effExp = $expectedProp->getValue();
  1771. $effAct = $actualProp->getValue();
  1772. if ($effExp instanceof \DateTime) {
  1773. $effExp = $effExp->setTimezone(new \DateTimeZone('UTC'));
  1774. }
  1775. if ($effAct instanceof \DateTime) {
  1776. $effAct = $effAct->setTimezone(new \DateTimeZone('UTC'));
  1777. }
  1778. $this->assertEquals($expectedProp->getValue(), $actualProp->getValue(), 'getProperties()->get(\'' . $pname . '\')->getValue [' . $effectiveExpectedProp . ']');
  1779. }
  1780. }