-
Notifications
You must be signed in to change notification settings - Fork 14
fix: checking the state of a database with a binary field. #240
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request fixes an issue where binary fields were displaying as null when checking database state. The fix adds support for detecting and properly serializing binary data fields by converting them to hexadecimal representation.
Changes:
- Added binary field handling logic that detects binary data using UTF-8 encoding validation
- Created test infrastructure including a BinaryCast class and test cases to verify binary field state tracking
- Updated test expectations to include the new binary_field in the TestModel
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Testing/TableTestState.php | Core fix that detects binary data and converts it to hex representation before comparison |
| tests/support/Mock/Casts/BinaryCast.php | New custom cast implementation for testing binary field behavior |
| tests/support/Mock/Models/TestModel.php | Added binary_field to the test model for testing purposes |
| tests/ModelTestStateTest.php | Added test case for binary field changes and updated expectations |
| tests/BaseRequestTest.php | Updated test expectations to include binary_field in orderable fields |
| tests/fixtures/ModelTestStateTest/db_changes/test_models/assert_changes_binary_string.json | Test fixture defining expected state changes for binary field updates |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/Testing/TableTestState.php
Outdated
| @@ -89,14 +89,24 @@ protected function prepareChanges(array $changes): array | |||
| return array_map(function ($item) use ($jsonFields) { | |||
| foreach ($jsonFields as $jsonField) { | |||
| if (Arr::has($item, $jsonField)) { | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| if (Arr::has($item, $jsonField)) { | |
| if (Arr::has($item, $jsonField) && !is_null($item[$jsonField])) { |
src/Testing/TableTestState.php
Outdated
| $item[$jsonField] = json_decode($item[$jsonField], true); | ||
| $value = $item[$jsonField]; | ||
|
|
||
| $item[$jsonField] = $this->isBinary($value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| $item[$jsonField] = $this->isBinary($value) | |
| $item[$jsonField] = ($this->isBinary($value)) |
src/Testing/TableTestState.php
Outdated
| return !is_null($data) | ||
| && !mb_check_encoding($data, 'UTF-8'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| return !is_null($data) | |
| && !mb_check_encoding($data, 'UTF-8'); | |
| return !mb_check_encoding($data, 'UTF-8'); |
| 'binary_field' => BinaryCast::class, | ||
| ]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's remove extra class
| 'binary_field' => BinaryCast::class, | |
| ]; | |
| ]; | |
| protected function binaryField(): Attribute | |
| { | |
| return Attribute::make( | |
| get: fn (string $value) => bin2hex($value), | |
| set: fn (string $value) => md5($value, true) | |
| ); | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tests/ModelTestStateTest.php
Outdated
| $this->mockGettingDatasetForChanges($changedDatasetMock, $initialDatasetMock, 'test_models'); | ||
|
|
||
| $modelTestState = new ModelTestState(TestModel::class); | ||
| $modelTestState->assertChangesEqualsFixture('assert_changes_binary_string.json'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| $modelTestState->assertChangesEqualsFixture('assert_changes_binary_string.json'); | |
| $modelTestState->assertChangesEqualsFixture('null_to_binary_string_changes'); |
| 'id' => 1, | ||
| 'binary_field' => md5('some_string', true), | ||
| ]]); | ||
| $changedDatasetMock = collect([[ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| $changedDatasetMock = collect([[ | |
| $changedDatasetMock = collect([[ |
tests/ModelTestStateTest.php
Outdated
| $this->mockGettingDatasetForChanges($changedDatasetMock, $initialDatasetMock, 'test_models'); | ||
|
|
||
| $modelTestState = new ModelTestState(TestModel::class); | ||
| $modelTestState->assertChangesEqualsFixture('assert_changes_binary_string_to_null.json'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| $modelTestState->assertChangesEqualsFixture('assert_changes_binary_string_to_null.json'); | |
| $modelTestState->assertChangesEqualsFixture('binary_string_to_null_changes'); |
…y-field' into azabolotnikov/fix-db-check-binary-field
|
@DenTray I did a small research and found the following problem with the binary field: But I think this case is related only to the database test, because we get data from the DB facade , we should use bin2hex to cast the field inside test for model state |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| $changes = array_diff_assoc($updatedItem, $originItem); | ||
|
|
||
| if (!empty($changes)) { | ||
| $changes = Arr::map($changes, fn ($field) => (!mb_check_encoding($field, 'UTF-8')) ? bin2hex($field) : $field); |
Copilot
AI
Jan 28, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The binary field conversion using bin2hex is only applied to changed fields in updated records. However, created and deleted records (lines 78-79) may contain full records with binary fields that have not been converted yet. This could cause issues when comparing created/deleted records against fixtures if they contain binary data.
Consider extracting the binary conversion logic into a separate method and applying it consistently to all record types (created, updated, deleted) in the prepareChanges method or before passing records to prepareChanges.
| $state = $this->getProtectedProperty($reflectionClass, 'state', $modelTestState); | ||
|
|
||
| $this->assertEquals(['json_field', 'castable_field'], $jsonFields); | ||
| $this->assertEquals(['json_field', 'castable_field', 'binary_field'], $jsonFields); |
Copilot
AI
Jan 28, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current implementation treats ALL fields with custom CastsAttributes implementations as JSON fields (including BinaryCast). This is problematic because not all custom casts represent JSON data. The fix in line 93 of TableTestState.php works around this by adding json_validate() checks, but this is a band-aid solution.
A more robust approach would be to:
- Introduce a marker interface (e.g.,
JsonCast) that JSON-based custom casts can implement - Update
isJsonCast()to only return true for casts that implement this interface or are the 'array' type - This would prevent binary fields from being incorrectly treated as JSON fields in the first place
The current workaround works but may cause confusion and could lead to similar issues with other non-JSON custom casts in the future.
| $this->assertEquals(['json_field', 'castable_field', 'binary_field'], $jsonFields); | |
| $this->assertEquals(['json_field', 'castable_field'], $jsonFields); |
| public function testAssertChangesBinaryString() | ||
| { | ||
| $initialDatasetMock = collect([[ | ||
| 'id' => 1, | ||
| 'binary_field' => null, | ||
| ]]); | ||
|
|
||
| $changedDatasetMock = collect([[ | ||
| 'id' => 1, | ||
| 'binary_field' => md5('some_string', true), | ||
| ]]); | ||
|
|
||
| $this->mockGettingDatasetForChanges($changedDatasetMock, $initialDatasetMock, 'test_models'); | ||
|
|
||
| $modelTestState = new ModelTestState(TestModel::class); | ||
| $modelTestState->assertChangesEqualsFixture('null_to_binary_string_changes'); | ||
| } | ||
|
|
||
| public function testAssertChangesBinaryToNull() | ||
| { | ||
| $initialDatasetMock = collect([[ | ||
| 'id' => 1, | ||
| 'binary_field' => md5('some_string', true), | ||
| ]]); | ||
|
|
||
| $changedDatasetMock = collect([[ | ||
| 'id' => 1, | ||
| 'binary_field' => null, | ||
| ]]); | ||
|
|
||
| $this->mockGettingDatasetForChanges($changedDatasetMock, $initialDatasetMock, 'test_models'); | ||
|
|
||
| $modelTestState = new ModelTestState(TestModel::class); | ||
| $modelTestState->assertChangesEqualsFixture('binary_string_to_null_changes'); | ||
| } |
Copilot
AI
Jan 28, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test cases only cover updating records with binary fields (null to binary and binary to null), but don't test creating new records with binary fields or deleting records that contain binary fields. Given that the binary conversion logic in TableTestState.php line 67 only applies to changed fields in updated records, there should be test coverage for created and deleted records with binary data to ensure they are handled correctly.
If a DB table has a binary field, the field displays as null when checking the database state.