Skip to content

Commit bc20d4f

Browse files
make test for HasStateMachine trait
1 parent bcb6445 commit bc20d4f

File tree

5 files changed

+253
-2
lines changed

5 files changed

+253
-2
lines changed

tests/HasStateMachineTest.php

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
<?php
2+
3+
namespace JobMetric\StateMachine\Tests;
4+
5+
use Illuminate\Support\Facades\Event;
6+
use JobMetric\StateMachine\Events\StateTransitioned;
7+
use JobMetric\StateMachine\Exceptions\InvalidStateMachineClassException;
8+
use JobMetric\StateMachine\Exceptions\ModelStateMachineInterfaceNotFoundException;
9+
use JobMetric\StateMachine\Exceptions\StateMachineNotAllowTransitionException;
10+
use JobMetric\StateMachine\Tests\Stubs\Models\Article;
11+
use JobMetric\StateMachine\Tests\Stubs\Models\Order;
12+
use Throwable;
13+
14+
class HasStateMachineTest extends TestCase
15+
{
16+
private function makeDraftArticle(): Article
17+
{
18+
return Article::create([
19+
'status' => 'draft'
20+
]);
21+
}
22+
23+
public function test_boot()
24+
{
25+
$this->expectException(ModelStateMachineInterfaceNotFoundException::class);
26+
27+
// Trying to boot a model that does not implement StateMachineContract
28+
$order = new Order;
29+
}
30+
31+
public function test_resolve_state_machine_namespace()
32+
{
33+
$article = $this->makeDraftArticle();
34+
35+
$appNamespace = trim(appNamespace(), "\\");
36+
37+
$this->assertEquals("JobMetric\\StateMachine\\Tests\\Stubs\\StateMachines", $article->resolveStateMachineNamespace());
38+
}
39+
40+
public function test_allow_transition()
41+
{
42+
$article = $this->makeDraftArticle();
43+
44+
// Allow transition from 'draft' to 'published'
45+
$article->allowTransition('status', 'draft', 'scheduled');
46+
47+
// Check if the transition is registered
48+
$this->assertTrue($article->canTransitionTo('scheduled'));
49+
$this->assertTrue($article->canTransitionTo('published'));
50+
$this->assertFalse($article->canTransitionTo('archived')); // Not allowed yet
51+
}
52+
53+
/**
54+
* @throws Throwable
55+
* @throws InvalidStateMachineClassException
56+
*/
57+
public function test_transition_to()
58+
{
59+
$article = $this->makeDraftArticle();
60+
61+
Event::fake();
62+
63+
try {
64+
// Attempting to transition to an invalid state
65+
$article->transitionTo('archived'); // This should throw an exception
66+
} catch (Throwable $exception) {
67+
$this->assertInstanceOf(StateMachineNotAllowTransitionException::class, $exception);
68+
}
69+
70+
// If the exception is not thrown, the test will fail
71+
$this->assertEquals('draft', $article->status); // Should still be in 'draft' state~
72+
73+
$article->title = '[]';
74+
$article->save();
75+
76+
// Now transition to a valid state
77+
$state = $article->transitionTo('published');
78+
79+
$this->assertTrue($state);
80+
81+
// Check if the event was dispatched
82+
Event::assertDispatched(StateTransitioned::class);
83+
84+
// Should now be in 'published' state
85+
$this->assertEquals('published', $article->status);
86+
87+
// Assuming title is a JSON string
88+
$this->assertJson($article->title);
89+
90+
// Should be able to transition to 'archived'
91+
$this->assertTrue($article->canTransitionTo('archived'));
92+
93+
// Should not be able to transition back to 'draft'
94+
$this->assertFalse($article->canTransitionTo('draft'));
95+
96+
// Assuming 'common_before' is a key in the JSON title
97+
$this->assertArrayHasKey('common_before', json_decode($article->title, true));
98+
99+
// Assuming 'common_after' is a key in the JSON title
100+
$this->assertArrayHasKey('common_after', json_decode($article->title, true));
101+
102+
// Assuming 'draft_to_publish_before' is a key in the JSON title
103+
$this->assertArrayHasKey('draft_to_publish_before', json_decode($article->title, true));
104+
105+
// Assuming 'draft_to_publish_after' is a key in the JSON title
106+
$this->assertArrayHasKey('draft_to_publish_after', json_decode($article->title, true));
107+
108+
// Check if the transition class was called
109+
$this->assertTrue(class_exists('JobMetric\StateMachine\Tests\Stubs\StateMachines\Article\ArticleStatusDraftToPublishedStateMachine'));
110+
$this->assertTrue(class_exists('JobMetric\StateMachine\Tests\Stubs\StateMachines\Article\ArticleStatusCommonStateMachine'));
111+
112+
// now transition to 'archived'
113+
$state = $article->transitionTo('archived');
114+
115+
$this->assertTrue($state);
116+
117+
// Should now be in 'archived' state
118+
$this->assertEquals('archived', $article->status);
119+
120+
// now transition to 'draft'
121+
$this->expectException(StateMachineNotAllowTransitionException::class);
122+
123+
$article->transitionTo('draft'); // This should throw an exception
124+
125+
// Should still be in 'archived' state
126+
$this->assertEquals('archived', $article->status);
127+
}
128+
129+
public function test_can_transition()
130+
{
131+
$article = $this->makeDraftArticle();
132+
133+
// valid state
134+
$this->assertTrue($article->canTransitionTo('published'));
135+
// field not in attributes
136+
$this->assertFalse($article->canTransitionTo('published', 'fake_field'));
137+
// field in attributes but not in state machine
138+
$this->assertFalse($article->canTransitionTo('published', 'title'));
139+
// invalid state
140+
$this->assertFalse($article->canTransitionTo('archived'));
141+
}
142+
143+
/**
144+
* @throws InvalidStateMachineClassException
145+
* @throws Throwable
146+
* @throws StateMachineNotAllowTransitionException
147+
*/
148+
public function test_find_transitions_to_new_state()
149+
{
150+
$article = new Article(['status' => 'draft']);
151+
$article->transitionTo('published');
152+
153+
$this->assertEquals('published', $article->status);
154+
}
155+
}

tests/Stubs/Models/Article.php

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,45 @@
66
use JobMetric\StateMachine\Contracts\StateMachineContract;
77
use JobMetric\StateMachine\HasStateMachine;
88

9+
/**
10+
* @property string $title
11+
* @property string $status
12+
*
13+
* @method static create(string[] $array)
14+
*/
915
class Article extends Model implements StateMachineContract
1016
{
1117
use HasStateMachine;
1218

1319
public $timestamps = false;
1420
protected $fillable = [
21+
'title',
1522
'status'
1623
];
1724
protected $casts = [
25+
'title' => 'string',
1826
'status' => 'string',
1927
];
2028

2129
public function stateMachineAllowTransition(): void
2230
{
2331
$this->allowTransition('status', 'draft', 'published');
24-
$this->allowTransition('status', 'published', 'archived', fn ($model) => $model->isAllowedToArchive());
32+
$this->allowTransition('status', 'published', 'archived', fn ($model) => $model->isPublishedToArchive());
33+
$this->allowTransition('status', 'archived', 'draft', fn ($model) => $model->isArchiveToDraft());
2534
}
2635

27-
public function isAllowedToArchive(): bool
36+
public function resolveStateMachineNamespace(): string
37+
{
38+
return "JobMetric\\StateMachine\\Tests\\Stubs\\StateMachines";
39+
}
40+
41+
public function isPublishedToArchive(): bool
2842
{
2943
return true;
3044
}
45+
46+
public function isArchiveToDraft(): bool
47+
{
48+
return false;
49+
}
3150
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
namespace JobMetric\StateMachine\Tests\Stubs\StateMachines\Article;
4+
5+
use Illuminate\Database\Eloquent\Model;
6+
use JobMetric\StateMachine\Contracts\StateMachine;
7+
8+
class ArticleStatusCommonStateMachine extends StateMachine
9+
{
10+
11+
public function before(Model $model, mixed $from, mixed $to): void
12+
{
13+
$array_title = json_decode($model->title, true);
14+
15+
if (is_array($array_title)) {
16+
$array_title = array_merge($array_title, [
17+
'common_before' => true
18+
]);
19+
20+
$model->title = json_encode($array_title);
21+
$model->save();
22+
}
23+
}
24+
25+
public function after(Model $model, mixed $from, mixed $to): void
26+
{
27+
$array_title = json_decode($model->title, true);
28+
29+
if (is_array($array_title)) {
30+
$array_title = array_merge($array_title, [
31+
'common_after' => true
32+
]);
33+
34+
$model->title = json_encode($array_title);
35+
$model->save();
36+
}
37+
}
38+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
namespace JobMetric\StateMachine\Tests\Stubs\StateMachines\Article;
4+
5+
use Illuminate\Database\Eloquent\Model;
6+
use JobMetric\StateMachine\Contracts\StateMachine;
7+
8+
class ArticleStatusDraftToPublishedStateMachine extends StateMachine
9+
{
10+
11+
public function before(Model $model, mixed $from, mixed $to): void
12+
{
13+
$array_title = json_decode($model->title, true);
14+
15+
if (is_array($array_title)) {
16+
$array_title = array_merge($array_title, [
17+
'draft_to_publish_before' => true
18+
]);
19+
20+
$model->title = json_encode($array_title);
21+
$model->save();
22+
}
23+
}
24+
25+
public function after(Model $model, mixed $from, mixed $to): void
26+
{
27+
$array_title = json_decode($model->title, true);
28+
29+
if (is_array($array_title)) {
30+
$array_title = array_merge($array_title, [
31+
'draft_to_publish_after' => true
32+
]);
33+
34+
$model->title = json_encode($array_title);
35+
$model->save();
36+
}
37+
}
38+
}

tests/database/migrations/2023_01_01_000000_create_articles_table.php renamed to tests/database/migrations/create_articles_table.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public function up(): void
1515
Schema::create('articles', function (Blueprint $table) {
1616
$table->id();
1717

18+
$table->string('title')->nullable();
1819
$table->string('status');
1920
});
2021
}

0 commit comments

Comments
 (0)