2014-09-26 20:41:28 +02:00
|
|
|
<?php
|
|
|
|
namespace Szurubooru\Tests\Services;
|
2014-11-18 21:22:46 +01:00
|
|
|
use Szurubooru\Entities\Snapshot;
|
2014-10-08 14:47:47 +02:00
|
|
|
use Szurubooru\Dao\SnapshotDao;
|
|
|
|
use Szurubooru\Dao\TransactionManager;
|
|
|
|
use Szurubooru\Services\AuthService;
|
|
|
|
use Szurubooru\Services\HistoryService;
|
|
|
|
use Szurubooru\Services\TimeService;
|
|
|
|
use Szurubooru\Tests\AbstractTestCase;
|
|
|
|
|
|
|
|
final class HistoryServiceTest extends AbstractTestCase
|
2014-09-26 20:41:28 +02:00
|
|
|
{
|
|
|
|
private $snapshotDaoMock;
|
|
|
|
private $timeServiceMock;
|
|
|
|
private $authServiceMock;
|
|
|
|
private $transactionManagerMock;
|
|
|
|
|
2014-11-18 21:22:46 +01:00
|
|
|
public static function dataDifferenceProvider()
|
2014-09-26 20:41:28 +02:00
|
|
|
{
|
|
|
|
yield
|
|
|
|
[
|
2014-11-18 21:22:46 +01:00
|
|
|
[],
|
|
|
|
[],
|
|
|
|
['+' => [], '-' => []]
|
2014-09-26 20:41:28 +02:00
|
|
|
];
|
|
|
|
|
|
|
|
yield
|
|
|
|
[
|
2014-11-18 21:22:46 +01:00
|
|
|
['key' => 'unchangedValue'],
|
|
|
|
['key' => 'unchangedValue'],
|
|
|
|
['+' => [], '-' => []]
|
2014-09-26 20:41:28 +02:00
|
|
|
];
|
|
|
|
|
|
|
|
yield
|
|
|
|
[
|
|
|
|
['key' => 'newValue'],
|
|
|
|
[],
|
|
|
|
[
|
2014-11-19 10:22:59 +01:00
|
|
|
'+' => ['key' => 'newValue'],
|
2014-09-26 20:41:28 +02:00
|
|
|
'-' => []
|
|
|
|
]
|
|
|
|
];
|
|
|
|
|
|
|
|
yield
|
|
|
|
[
|
|
|
|
[],
|
|
|
|
['key' => 'deletedValue'],
|
|
|
|
[
|
|
|
|
'+' => [],
|
2014-11-19 10:22:59 +01:00
|
|
|
'-' => ['key' => 'deletedValue']
|
2014-09-26 20:41:28 +02:00
|
|
|
]
|
|
|
|
];
|
|
|
|
|
|
|
|
yield
|
|
|
|
[
|
|
|
|
['key' => 'changedValue'],
|
|
|
|
['key' => 'oldValue'],
|
|
|
|
[
|
2014-11-19 10:22:59 +01:00
|
|
|
'+' => ['key' => 'changedValue'],
|
|
|
|
'-' => ['key' => 'oldValue']
|
2014-09-26 20:41:28 +02:00
|
|
|
]
|
|
|
|
];
|
|
|
|
|
|
|
|
yield
|
|
|
|
[
|
|
|
|
['key' => []],
|
|
|
|
['key' => []],
|
|
|
|
[
|
|
|
|
'+' => [],
|
|
|
|
'-' => []
|
|
|
|
]
|
|
|
|
];
|
|
|
|
|
|
|
|
yield
|
|
|
|
[
|
|
|
|
['key' => ['newArrayElement']],
|
|
|
|
['key' => []],
|
|
|
|
[
|
2014-11-19 10:22:59 +01:00
|
|
|
'+' => ['key' => ['newArrayElement']],
|
2014-09-26 20:41:28 +02:00
|
|
|
'-' => []
|
|
|
|
]
|
|
|
|
];
|
|
|
|
|
|
|
|
yield
|
|
|
|
[
|
|
|
|
['key' => []],
|
|
|
|
['key' => ['removedArrayElement']],
|
|
|
|
[
|
|
|
|
'+' => [],
|
2014-11-19 10:22:59 +01:00
|
|
|
'-' => ['key' => ['removedArrayElement']]
|
2014-09-26 20:41:28 +02:00
|
|
|
]
|
|
|
|
];
|
|
|
|
|
|
|
|
yield
|
|
|
|
[
|
2014-11-19 10:22:59 +01:00
|
|
|
['key' => ['unchangedArrayElement', 'newArrayElement']],
|
|
|
|
['key' => ['unchangedArrayElement', 'oldArrayElement']],
|
2014-09-26 20:41:28 +02:00
|
|
|
[
|
2014-11-19 10:22:59 +01:00
|
|
|
'+' => ['key' => ['newArrayElement']],
|
|
|
|
'-' => ['key' => ['oldArrayElement']]
|
2014-09-26 20:41:28 +02:00
|
|
|
]
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setUp()
|
|
|
|
{
|
|
|
|
parent::setUp();
|
2014-10-08 14:47:47 +02:00
|
|
|
$this->snapshotDaoMock = $this->mock(SnapshotDao::class);
|
2014-11-18 21:22:46 +01:00
|
|
|
$this->transactionManagerMock = $this->mockTransactionManager();
|
2014-10-08 14:47:47 +02:00
|
|
|
$this->timeServiceMock = $this->mock(TimeService::class);
|
|
|
|
$this->authServiceMock = $this->mock(AuthService::class);
|
2014-09-26 20:41:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-11-18 21:22:46 +01:00
|
|
|
* @dataProvider dataDifferenceProvider
|
2014-09-26 20:41:28 +02:00
|
|
|
*/
|
2014-11-18 21:22:46 +01:00
|
|
|
public function testDataDifference($newData, $oldData, $expectedResult)
|
|
|
|
{
|
|
|
|
$historyService = $this->getHistoryService();
|
|
|
|
$this->assertEquals($expectedResult, $historyService->getDataDifference($newData, $oldData));
|
|
|
|
}
|
|
|
|
|
|
|
|
public static function mergingProvider()
|
2014-09-26 20:41:28 +02:00
|
|
|
{
|
2014-11-18 21:22:46 +01:00
|
|
|
{
|
2014-11-19 19:07:29 +01:00
|
|
|
//basic merging
|
2014-11-18 21:22:46 +01:00
|
|
|
$oldSnapshot = new Snapshot(1);
|
|
|
|
$oldSnapshot->setTime(date('c', 1));
|
|
|
|
$oldSnapshot->setOperation(Snapshot::OPERATION_CREATION);
|
|
|
|
$oldSnapshot->setData(['old' => '1']);
|
|
|
|
|
|
|
|
$newSnapshot = new Snapshot(2);
|
|
|
|
$newSnapshot->setTime(date('c', 2));
|
|
|
|
$newSnapshot->setOperation(Snapshot::OPERATION_CHANGE);
|
|
|
|
$newSnapshot->setData(['new' => '2']);
|
|
|
|
|
|
|
|
$expectedSnapshot = new Snapshot(1);
|
|
|
|
$expectedSnapshot->setTime(date('c', 3));
|
|
|
|
$expectedSnapshot->setOperation(Snapshot::OPERATION_CREATION);
|
|
|
|
$expectedSnapshot->setData(['new' => '2']);
|
2014-11-19 10:22:59 +01:00
|
|
|
$expectedSnapshot->setDataDifference(['+' => ['new' => '2'], '-' => []]);
|
2014-11-18 21:22:46 +01:00
|
|
|
|
2014-11-19 19:07:29 +01:00
|
|
|
yield [[$oldSnapshot], $newSnapshot, $expectedSnapshot, date('c', 3), [2]];
|
2014-11-18 21:22:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2014-11-19 19:07:29 +01:00
|
|
|
//too big time gap for merge
|
2014-11-18 21:22:46 +01:00
|
|
|
$oldSnapshot = new Snapshot(1);
|
|
|
|
$oldSnapshot->setOperation(Snapshot::OPERATION_CREATION);
|
|
|
|
$oldSnapshot->setData(['old' => '1']);
|
|
|
|
|
|
|
|
$newSnapshot = new Snapshot(2);
|
|
|
|
$newSnapshot->setOperation(Snapshot::OPERATION_CHANGE);
|
|
|
|
$newSnapshot->setData(['new' => '2']);
|
|
|
|
|
|
|
|
$expectedSnapshot = new Snapshot(2);
|
|
|
|
$expectedSnapshot->setTime(date('c', 3000));
|
|
|
|
$expectedSnapshot->setOperation(Snapshot::OPERATION_CHANGE);
|
|
|
|
$expectedSnapshot->setData(['new' => '2']);
|
2014-11-19 10:22:59 +01:00
|
|
|
$expectedSnapshot->setDataDifference(['+' => ['new' => '2'], '-' => ['old' => '1']]);
|
2014-11-18 21:22:46 +01:00
|
|
|
|
2014-11-19 19:07:29 +01:00
|
|
|
yield [[$oldSnapshot], $newSnapshot, $expectedSnapshot, date('c', 3000), []];
|
2014-11-18 21:22:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2014-11-19 19:07:29 +01:00
|
|
|
//operations done by different user shouldn't be merged
|
2014-11-18 21:22:46 +01:00
|
|
|
$oldSnapshot = new Snapshot(1);
|
|
|
|
$oldSnapshot->setOperation(Snapshot::OPERATION_CREATION);
|
|
|
|
$oldSnapshot->setData(['old' => '1']);
|
|
|
|
$oldSnapshot->setUserId(1);
|
|
|
|
|
|
|
|
$newSnapshot = new Snapshot(2);
|
|
|
|
$newSnapshot->setOperation(Snapshot::OPERATION_CHANGE);
|
|
|
|
$newSnapshot->setData(['new' => '2']);
|
|
|
|
$newSnapshot->setUserId(2);
|
|
|
|
|
|
|
|
$expectedSnapshot = new Snapshot(2);
|
|
|
|
$expectedSnapshot->setOperation(Snapshot::OPERATION_CHANGE);
|
|
|
|
$expectedSnapshot->setData(['new' => '2']);
|
2014-11-19 10:22:59 +01:00
|
|
|
$expectedSnapshot->setDataDifference(['+' => ['new' => '2'], '-' => ['old' => '1']]);
|
2014-11-18 21:22:46 +01:00
|
|
|
$expectedSnapshot->setUserId(null);
|
|
|
|
|
2014-11-19 19:07:29 +01:00
|
|
|
yield [[$oldSnapshot], $newSnapshot, $expectedSnapshot, null, []];
|
2014-11-18 21:22:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2014-11-19 19:07:29 +01:00
|
|
|
//merge that leaves only delete snapshot should be removed altogether
|
2014-11-18 21:22:46 +01:00
|
|
|
$oldSnapshot = new Snapshot(1);
|
|
|
|
$oldSnapshot->setOperation(Snapshot::OPERATION_CREATION);
|
|
|
|
$oldSnapshot->setData(['old' => '1']);
|
|
|
|
|
|
|
|
$newSnapshot = new Snapshot(2);
|
|
|
|
$newSnapshot->setOperation(Snapshot::OPERATION_DELETE);
|
|
|
|
$newSnapshot->setData(['new' => '2']);
|
|
|
|
|
2014-11-19 19:07:29 +01:00
|
|
|
yield [[$oldSnapshot], $newSnapshot, null, null, [2, 1]];
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
//chaining to creation snapshot should preserve operation type
|
|
|
|
$oldestSnapshot = new Snapshot(1);
|
|
|
|
$oldestSnapshot->setOperation(Snapshot::OPERATION_CREATION);
|
|
|
|
$oldestSnapshot->setData(['oldest' => '0']);
|
|
|
|
|
|
|
|
$oldSnapshot = new Snapshot(2);
|
|
|
|
$oldSnapshot->setOperation(Snapshot::OPERATION_CHANGE);
|
|
|
|
$oldSnapshot->setData(['old' => '1']);
|
|
|
|
|
|
|
|
$newSnapshot = new Snapshot(3);
|
|
|
|
$newSnapshot->setOperation(Snapshot::OPERATION_CHANGE);
|
|
|
|
$newSnapshot->setData(['oldest' => '0', 'new' => '2']);
|
|
|
|
|
|
|
|
$expectedSnapshot = new Snapshot(1);
|
|
|
|
$expectedSnapshot->setOperation(Snapshot::OPERATION_CREATION);
|
|
|
|
$expectedSnapshot->setData(['oldest' => '0', 'new' => '2']);
|
|
|
|
$expectedSnapshot->setDataDifference(['+' => ['oldest' => '0', 'new' => '2'], '-' => []]);
|
|
|
|
|
|
|
|
yield [[$oldSnapshot, $oldestSnapshot], $newSnapshot, $expectedSnapshot, null, [3, 2]];
|
|
|
|
|
|
|
|
$newSnapshot = clone($newSnapshot);
|
|
|
|
$newSnapshot->setId(null);
|
|
|
|
yield [[$oldSnapshot, $oldestSnapshot], $newSnapshot, $expectedSnapshot, null, [2]];
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
//chaining to edit snapshot should update operation type
|
|
|
|
$oldestSnapshot = new Snapshot(1);
|
|
|
|
$oldestSnapshot->setOperation(Snapshot::OPERATION_CREATION);
|
|
|
|
$oldestSnapshot->setData(['oldest' => '0']);
|
|
|
|
$oldestSnapshot->setTime(date('c', 1));
|
|
|
|
|
|
|
|
$oldSnapshot = new Snapshot(2);
|
|
|
|
$oldSnapshot->setOperation(Snapshot::OPERATION_CHANGE);
|
|
|
|
$oldSnapshot->setData(['old' => '1']);
|
|
|
|
$oldSnapshot->setTime(date('c', 400));
|
|
|
|
|
|
|
|
$newSnapshot = new Snapshot(3);
|
|
|
|
$newSnapshot->setOperation(Snapshot::OPERATION_DELETE);
|
|
|
|
$newSnapshot->setData(['new' => '2']);
|
|
|
|
$newSnapshot->setTime(date('c', 401));
|
|
|
|
|
2014-11-18 21:22:46 +01:00
|
|
|
$expectedSnapshot = new Snapshot(2);
|
|
|
|
$expectedSnapshot->setOperation(Snapshot::OPERATION_DELETE);
|
|
|
|
$expectedSnapshot->setData(['new' => '2']);
|
2014-11-19 19:07:29 +01:00
|
|
|
$expectedSnapshot->setDataDifference(['+' => ['new' => '2'], '-' => ['oldest' => '0']]);
|
|
|
|
$expectedSnapshot->setTime(date('c', 402));
|
|
|
|
|
|
|
|
yield [[$oldSnapshot, $oldestSnapshot], $newSnapshot, $expectedSnapshot, date('c', 402), [3]];
|
2014-11-18 21:22:46 +01:00
|
|
|
|
2014-11-19 19:07:29 +01:00
|
|
|
$newSnapshot = clone($newSnapshot);
|
|
|
|
$newSnapshot->setId(null);
|
|
|
|
yield [[$oldSnapshot, $oldestSnapshot], $newSnapshot, $expectedSnapshot, date('c', 402), []];
|
2014-11-18 21:22:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dataProvider mergingProvider
|
|
|
|
*/
|
2014-11-19 19:07:29 +01:00
|
|
|
public function testMerging($earlierSnapshots, $newSnapshot, $expectedSnapshot, $currentTime, $expectedDeletions = [])
|
2014-11-18 21:22:46 +01:00
|
|
|
{
|
|
|
|
$this->timeServiceMock->method('getCurrentTime')->willReturn($currentTime);
|
|
|
|
|
|
|
|
$this->snapshotDaoMock
|
2014-11-19 19:07:29 +01:00
|
|
|
->expects($this->once())
|
2014-11-18 21:22:46 +01:00
|
|
|
->method('findEarlierSnapshots')
|
2014-11-19 19:07:29 +01:00
|
|
|
->willReturn($earlierSnapshots);
|
2014-11-18 21:22:46 +01:00
|
|
|
|
|
|
|
$this->snapshotDaoMock
|
2014-11-19 19:07:29 +01:00
|
|
|
->expects($this->exactly($expectedSnapshot === null ? 0 : 1))
|
2014-11-18 21:22:46 +01:00
|
|
|
->method('save')
|
|
|
|
->will($this->returnCallback(function($param) use (&$actualSnapshot)
|
|
|
|
{
|
|
|
|
$actualSnapshot = $param;
|
|
|
|
}));
|
|
|
|
|
2014-11-19 19:07:29 +01:00
|
|
|
$this->snapshotDaoMock
|
|
|
|
->expects($this->exactly(count($expectedDeletions)))
|
|
|
|
->method('deleteById')
|
|
|
|
->withConsecutive(...array_map(function($del) { return [$del]; }, $expectedDeletions));
|
|
|
|
|
2014-09-26 20:41:28 +02:00
|
|
|
$historyService = $this->getHistoryService();
|
2014-11-18 21:22:46 +01:00
|
|
|
$historyService->saveSnapshot($newSnapshot);
|
|
|
|
$this->assertEntitiesEqual($expectedSnapshot, $actualSnapshot);
|
2014-09-26 20:41:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private function getHistoryService()
|
|
|
|
{
|
2014-10-08 14:47:47 +02:00
|
|
|
return new HistoryService(
|
2014-09-26 20:41:28 +02:00
|
|
|
$this->snapshotDaoMock,
|
|
|
|
$this->transactionManagerMock,
|
|
|
|
$this->timeServiceMock,
|
|
|
|
$this->authServiceMock);
|
|
|
|
}
|
|
|
|
}
|