Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;

return new class extends Migration
{
/**
* Each statement takes its own lock, as briefly as it can.
*/
public $withinTransaction = false;

/**
* Deleting a profile no longer clears the author of its commits: `DeleteUser` and `DeleteOrganization` clear it in
* code first, and a deletion that misses one now fails instead of leaving a live commit without its author. Clearing
* it in the database was only there while the code that posted commits without an author was still running.
*
* The foreign key is replaced in one statement, so commits are never left without one, and checked apart from it,
* so the scan doesn't hold up new commits. Safe to run again.
*/
public function up(): void
{
DB::statement('alter table commits drop constraint if exists commits_author_id_foreign, add constraint commits_author_id_foreign foreign key (author_id) references profiles (id) not valid');
DB::statement('alter table commits validate constraint commits_author_id_foreign');
}
};
9 changes: 9 additions & 0 deletions tests/Unit/Models/ProfileTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

declare(strict_types=1);

use App\Models\Commit;
use App\Models\Organization;
use App\Models\Profile;
use App\Models\User;
use Illuminate\Database\QueryException;
use Illuminate\Database\UniqueConstraintViolationException;

it('finds a profile by its handle whatever its case, keeping the case GitHub gives it', function (): void {
Expand All @@ -19,3 +21,10 @@

expect(fn () => Organization::factory()->create(['handle' => 'Laravel']))->toThrow(UniqueConstraintViolationException::class);
});

it('never deletes a profile its commits still point to, which the deletion clears in code first', function (): void {
$profile = Profile::factory()->create();
Commit::factory()->create(['author_id' => $profile->id]);

expect(fn () => $profile->delete())->toThrow(QueryException::class, 'commits_author_id_foreign');
});
Loading