-
-
Notifications
You must be signed in to change notification settings - Fork 51
Expand file tree
/
Copy pathRetryMechanismTest.php
More file actions
153 lines (122 loc) · 5.44 KB
/
RetryMechanismTest.php
File metadata and controls
153 lines (122 loc) · 5.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
<?php
declare(strict_types=1);
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\QueryException;
use Relaticle\Flowforge\Board;
use Relaticle\Flowforge\Concerns\InteractsWithBoard;
use Relaticle\Flowforge\Tests\Fixtures\Task;
// Create a testable class that exposes protected methods
class RetryMechanismTestHelper
{
use InteractsWithBoard {
isDuplicatePositionError as public;
}
public function getBoard(): Board
{
throw new RuntimeException('Not implemented for testing');
}
public function getBoardQuery(): ?Builder
{
return null;
}
}
describe('isDuplicatePositionError detection', function () {
test('detects SQLite UNIQUE constraint failure', function () {
$helper = new RetryMechanismTestHelper;
// SQLite constraint violation
$exception = new QueryException(
'sqlite',
'INSERT INTO tasks ...',
[],
new PDOException('SQLSTATE[23000]: Integrity constraint violation: 19 UNIQUE constraint failed: tasks.status, tasks.order_position')
);
// Set errorInfo with SQLite error code
$reflection = new ReflectionProperty($exception, 'errorInfo');
$reflection->setAccessible(true);
$reflection->setValue($exception, ['23000', 19, 'UNIQUE constraint failed']);
expect($helper->isDuplicatePositionError($exception))->toBeTrue();
});
test('detects MySQL ER_DUP_ENTRY error', function () {
$helper = new RetryMechanismTestHelper;
// MySQL duplicate entry error
$exception = new QueryException(
'mysql',
'INSERT INTO tasks ...',
[],
new PDOException("SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '100.5-todo' for key 'unique_position_per_column'")
);
$reflection = new ReflectionProperty($exception, 'errorInfo');
$reflection->setAccessible(true);
$reflection->setValue($exception, ['23000', 1062, 'Duplicate entry']);
expect($helper->isDuplicatePositionError($exception))->toBeTrue();
});
test('detects PostgreSQL unique_violation error', function () {
$helper = new RetryMechanismTestHelper;
// PostgreSQL unique violation error
$exception = new QueryException(
'pgsql',
'INSERT INTO tasks ...',
[],
new PDOException('SQLSTATE[23505]: Unique violation: duplicate key value violates unique constraint "unique_position_per_column"')
);
$reflection = new ReflectionProperty($exception, 'errorInfo');
$reflection->setAccessible(true);
$reflection->setValue($exception, ['23505', 23505, 'duplicate key value']);
expect($helper->isDuplicatePositionError($exception))->toBeTrue();
});
test('detects error by message containing unique_position_per_column', function () {
$helper = new RetryMechanismTestHelper;
// Generic error with constraint name in message
$exception = new QueryException(
'sqlite',
'INSERT INTO tasks ...',
[],
new PDOException('UNIQUE constraint failed: unique_position_per_column')
);
$reflection = new ReflectionProperty($exception, 'errorInfo');
$reflection->setAccessible(true);
$reflection->setValue($exception, ['23000', 999, 'unknown error']); // Unknown error code
expect($helper->isDuplicatePositionError($exception))->toBeTrue();
});
test('returns false for non-duplicate errors', function () {
$helper = new RetryMechanismTestHelper;
// Foreign key constraint error (not duplicate)
$exception = new QueryException(
'mysql',
'INSERT INTO tasks ...',
[],
new PDOException('SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails')
);
$reflection = new ReflectionProperty($exception, 'errorInfo');
$reflection->setAccessible(true);
$reflection->setValue($exception, ['23000', 1452, 'foreign key constraint fails']);
expect($helper->isDuplicatePositionError($exception))->toBeFalse();
});
});
describe('database unique constraint behavior', function () {
beforeEach(function () {
Task::create(['title' => 'Existing', 'status' => 'todo', 'order_position' => '1500.0000000000']);
});
test('unique constraint throws QueryException on duplicate', function () {
expect(fn () => Task::create([
'title' => 'Duplicate',
'status' => 'todo',
'order_position' => '1500.0000000000',
]))->toThrow(QueryException::class);
});
test('same position in different columns is allowed', function () {
// Same position, different status (column)
$task = Task::create([
'title' => 'Different Column',
'status' => 'in_progress', // Different column
'order_position' => '1500.0000000000', // Same position
]);
expect($task->exists)->toBeTrue();
});
test('null positions are allowed', function () {
// NULL is special in unique constraints - multiple NULLs are allowed
Task::create(['title' => 'Null 1', 'status' => 'todo', 'order_position' => null]);
$task2 = Task::create(['title' => 'Null 2', 'status' => 'todo', 'order_position' => null]);
expect($task2->exists)->toBeTrue();
});
});