Skip to content

Commit 78bc60d

Browse files
authored
Merge pull request #88 from RonasIT/add-nova-test-trait
Add nova test trait
2 parents f8ee1f2 + 2ca150a commit 78bc60d

File tree

18 files changed

+718
-2
lines changed

18 files changed

+718
-2
lines changed

src/Tests/ModelTestState.php

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
<?php
2+
3+
namespace RonasIT\Support\Tests;
4+
5+
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
6+
use Illuminate\Database\Eloquent\Model;
7+
use Illuminate\Support\Arr;
8+
use Illuminate\Support\Collection;
9+
use Illuminate\Support\Facades\DB;
10+
use PHPUnit\Framework\Assert;
11+
use RonasIT\Support\Traits\FixturesTrait;
12+
13+
class ModelTestState extends Assert
14+
{
15+
use FixturesTrait;
16+
17+
protected Collection $state;
18+
protected Model $model;
19+
protected array $jsonFields;
20+
21+
public function __construct(string $modelClassName)
22+
{
23+
$this->model = new $modelClassName();
24+
$this->state = $this->getDataSet($this->model->getTable());
25+
$this->jsonFields = $this->getModelJSONFields();
26+
}
27+
28+
public function assertNotChanged(): void
29+
{
30+
$changes = $this->getChanges();
31+
32+
$this->assertEquals([
33+
'updated' => [],
34+
'created' => [],
35+
'deleted' => []
36+
], $changes);
37+
}
38+
39+
public function assertChangesEqualsFixture(string $fixture, bool $exportMode = false): void
40+
{
41+
$changes = $this->getChanges();
42+
43+
$this->assertEqualsFixture($fixture, $changes, $exportMode);
44+
}
45+
46+
protected function getChanges(): array
47+
{
48+
$updatedData = $this->getDataSet($this->model->getTable());
49+
50+
$updatedRecords = [];
51+
$deletedRecords = [];
52+
53+
$this->state->each(function ($originItem) use (&$updatedData, &$updatedRecords, &$deletedRecords) {
54+
$updatedItemIndex = $updatedData->search(fn ($updatedItem) => $updatedItem['id'] === $originItem['id']);
55+
56+
if ($updatedItemIndex === false) {
57+
$deletedRecords[] = $originItem;
58+
} else {
59+
$updatedItem = $updatedData->get($updatedItemIndex);
60+
$changes = array_diff_assoc($updatedItem, $originItem);
61+
62+
if (!empty($changes)) {
63+
$updatedRecords[] = array_merge(['id' => $originItem['id']], $changes);
64+
}
65+
66+
$updatedData->forget($updatedItemIndex);
67+
}
68+
});
69+
70+
return [
71+
'updated' => $this->prepareChanges($updatedRecords),
72+
'created' => $this->prepareChanges($updatedData->values()->toArray()),
73+
'deleted' => $this->prepareChanges($deletedRecords)
74+
];
75+
}
76+
77+
protected function prepareChanges(array $changes): array
78+
{
79+
$jsonFields = Arr::wrap($this->jsonFields);
80+
81+
if (empty($jsonFields)) {
82+
return $changes;
83+
}
84+
85+
return array_map(function ($item) use ($jsonFields) {
86+
foreach ($jsonFields as $jsonField) {
87+
if (Arr::has($item, $jsonField)) {
88+
$item[$jsonField] = json_decode($item[$jsonField], true);
89+
}
90+
}
91+
92+
return $item;
93+
}, $changes);
94+
}
95+
96+
protected function getModelJSONFields(): array
97+
{
98+
$casts = $this->model->getCasts();
99+
100+
$jsonCasts = array_filter($casts, fn ($cast) => $this->isJsonCast($cast));
101+
102+
return array_keys($jsonCasts);
103+
}
104+
105+
protected function isJsonCast(string $cast): bool
106+
{
107+
return ($cast === 'array') || (class_exists($cast) && is_subclass_of($cast, CastsAttributes::class));
108+
}
109+
110+
protected function getFixturePath(string $fixtureName): string
111+
{
112+
$class = get_class($this);
113+
$explodedClass = explode('\\', $class);
114+
$className = Arr::last($explodedClass);
115+
$table = $this->model->getTable();
116+
117+
return base_path("tests/fixtures/{$className}/changes/{$table}/{$fixtureName}");
118+
}
119+
120+
protected function getDataSet(string $table, string $orderField = 'id'): Collection
121+
{
122+
return DB::table($table)
123+
->orderBy($orderField)
124+
->get()
125+
->map(fn ($record) => (array) $record);
126+
}
127+
}

src/Traits/AuthTestTrait.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace RonasIT\Support\Traits;
4+
5+
use Illuminate\Auth\SessionGuard;
6+
7+
trait AuthTestTrait
8+
{
9+
public function actingViaSession(int $userId): self
10+
{
11+
$guard = 'session';
12+
$hash = sha1(SessionGuard::class);
13+
14+
$this->withSession([
15+
"login_{$guard}_{$hash}" => $userId,
16+
]);
17+
18+
return $this;
19+
}
20+
}

tests/AuthTestTraitTest.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
namespace RonasIT\Support\Tests;
4+
5+
use Illuminate\Support\Arr;
6+
use RonasIT\Support\Tests\Support\Traits\MockTrait;
7+
use RonasIT\Support\Traits\AuthTestTrait;
8+
use RonasIT\Support\Traits\FixturesTrait;
9+
10+
class AuthTestTraitTest extends HelpersTestCase
11+
{
12+
use FixturesTrait, MockTrait, AuthTestTrait;
13+
14+
public function testActingViaSession()
15+
{
16+
$userId = 1;
17+
18+
$this->actingViaSession($userId);
19+
20+
$session = $this->app['session']->getDrivers()['array'];
21+
$loginSession = $this->getLoginSession($session);
22+
23+
$this->assertNotEmpty($loginSession);
24+
$this->assertEquals('laravel_session', $session->getName());
25+
$this->assertEquals(array_values($loginSession), [$userId]);
26+
}
27+
28+
public function testActingWithEmptySession()
29+
{
30+
$session = Arr::get($this->app['session']->getDrivers(), 'array', collect());
31+
$loginSession = $this->getLoginSession($session);
32+
33+
$this->assertEmpty($loginSession);
34+
}
35+
}

tests/HelpersTestCase.php

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
namespace RonasIT\Support\Tests;
44

5-
use RonasIT\Support\Traits\FixturesTrait;
5+
use ReflectionClass;
66
use RonasIT\Support\HelpersServiceProvider;
7+
use RonasIT\Support\Traits\FixturesTrait;
78
use Orchestra\Testbench\TestCase as BaseTest;
89

910
class HelpersTestCase extends BaseTest
@@ -45,4 +46,21 @@ protected function assertSettablePropertiesReset($class): void
4546
$this->assertEquals([], $attachedRelations);
4647
$this->assertEquals([], $attachedRelationsCount);
4748
}
49+
50+
public function getLoginSession($session): array
51+
{
52+
return array_filter(
53+
$session->all(),
54+
fn ($key) => strpos($key, 'login_session_') === 0,
55+
ARRAY_FILTER_USE_KEY
56+
);
57+
}
58+
59+
protected function getProtectedProperty(ReflectionClass $reflectionClass, string $methodName, $objectInstance)
60+
{
61+
$property = $reflectionClass->getProperty($methodName);
62+
$property->setAccessible(true);
63+
64+
return $property->getValue($objectInstance);
65+
}
4866
}

tests/ModelTestStateTest.php

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
namespace RonasIT\Support\Tests;
4+
5+
use ReflectionClass;
6+
use RonasIT\Support\Tests\Support\Mock\TestModel;
7+
use RonasIT\Support\Tests\Support\Traits\ModelTestStateMockTrait;
8+
use RonasIT\Support\Tests\Support\Traits\MockTrait;
9+
10+
class ModelTestStateTest extends HelpersTestCase
11+
{
12+
use ModelTestStateMockTrait;
13+
use MockTrait;
14+
15+
public function setUp(): void
16+
{
17+
parent::setUp();
18+
19+
self::$tables = null;
20+
21+
putenv('FAIL_EXPORT_JSON=false');
22+
}
23+
24+
public function testInitialization()
25+
{
26+
$datasetMock = collect($this->getJsonFixture('initialization/dataset.json'));
27+
$originRecords = collect($this->getJsonFixture('initialization/origin_records.json'));
28+
29+
$this->mockGettingDataset($datasetMock);
30+
31+
$modelTestState = new ModelTestState(TestModel::class);
32+
$reflectionClass = new ReflectionClass($modelTestState);
33+
34+
$jsonFields = $this->getProtectedProperty($reflectionClass, 'jsonFields', $modelTestState);
35+
$state = $this->getProtectedProperty($reflectionClass, 'state', $modelTestState);
36+
37+
$this->assertNotEmpty($jsonFields);
38+
$this->assertEquals(['json_field', 'castable_field'], $jsonFields);
39+
$this->assertEquals($originRecords, $state);
40+
}
41+
42+
public function testAssertChangesEqualsFixture()
43+
{
44+
$initialDatasetMock = collect($this->getJsonFixture('changes_equals_fixture/initial_dataset.json'));
45+
$changedDatasetMock = collect($this->getJsonFixture('changes_equals_fixture/changed_dataset.json'));
46+
$this->mockGettingDatasetForChanges($changedDatasetMock, $initialDatasetMock);
47+
48+
$modelTestState = new ModelTestState(TestModel::class);
49+
$modelTestState->assertChangesEqualsFixture('assertion_fixture.json');
50+
}
51+
52+
public function testAssertNoChanges()
53+
{
54+
$datasetMock = collect($this->getJsonFixture('get_without_changes/dataset.json'));
55+
$this->mockGettingDataset($datasetMock);
56+
57+
$modelTestState = new ModelTestState(TestModel::class);
58+
$modelTestState->assertNotChanged();
59+
}
60+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
[
2+
{
3+
"id": 1,
4+
"user_id": 1,
5+
"title": "Title1",
6+
"text": "Text1",
7+
"is_public": true,
8+
"description": null,
9+
"json_column": {
10+
"field1": "value",
11+
"field2": [
12+
2,
13+
3
14+
],
15+
"field3": {
16+
"one": 1,
17+
"two": 2
18+
}
19+
},
20+
"created_at": "2018-10-10 10:10:10",
21+
"updated_at": "2018-10-10 10:10:10"
22+
},
23+
{
24+
"id": 2,
25+
"user_id": 2,
26+
"title": "Title2",
27+
"text": "Text2",
28+
"is_public": true,
29+
"description": null,
30+
"json_column": {
31+
"one": "first value",
32+
"two": "second value"
33+
},
34+
"created_at": "2018-10-10 10:10:10",
35+
"updated_at": "2018-10-10 10:10:10"
36+
},
37+
{
38+
"id": 3,
39+
"user_id": 3,
40+
"title": "Title3",
41+
"text": "Text3",
42+
"is_public": true,
43+
"description": null,
44+
"json_column": [
45+
2,
46+
"two"
47+
],
48+
"created_at": "2018-10-10 10:10:10",
49+
"updated_at": "2018-10-10 10:10:10"
50+
}
51+
]
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
[
2+
{
3+
"id": 1,
4+
"user_id": 1,
5+
"title": "Title1",
6+
"text": "Text1",
7+
"is_public": true,
8+
"description": null,
9+
"json_column": "{\"field1\": \"value\", \"field2\": [2, 3], \"field3\": {\"one\": 1, \"two\": 2}}",
10+
"created_at": "2018-10-10 10:10:10",
11+
"updated_at": "2018-10-10 10:10:10"
12+
},
13+
{
14+
"id": 2,
15+
"user_id": 2,
16+
"title": "Title2",
17+
"text": "Text2",
18+
"is_public": true,
19+
"description": null,
20+
"json_column": "{\"one\": \"first value\", \"two\": \"second value\"}",
21+
"created_at": "2018-10-10 10:10:10",
22+
"updated_at": "2018-10-10 10:10:10"
23+
},
24+
{
25+
"id": 3,
26+
"user_id": 3,
27+
"title": "Title3",
28+
"text": "Text3",
29+
"is_public": true,
30+
"description": null,
31+
"json_column": "[2, \"two\"]",
32+
"created_at": "2018-10-10 10:10:10",
33+
"updated_at": "2018-10-10 10:10:10"
34+
}
35+
]

0 commit comments

Comments
 (0)