Release v1.4.0 #146

Merged
myrmidex merged 71 commits from release/v1.4.0 into main 2026-08-15 00:36:54 +02:00
2 changed files with 157 additions and 0 deletions
Showing only changes of commit 6270c97c29 - Show all commits

View file

@ -0,0 +1,37 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
// The column never had a constraint, so pre-existing rows may point at deleted channels.
$orphans = DB::table('article_publications')
->whereNotIn('platform_channel_id', DB::table('platform_channels')->select('id'));
if (($count = $orphans->count()) > 0) {
Log::warning("Deleting {$count} orphaned article_publications rows before adding the channel foreign key.");
$orphans->delete();
}
Schema::table('article_publications', function (Blueprint $table) {
$table->foreign('platform_channel_id')
->references('id')
->on('platform_channels')
->onDelete('cascade');
});
}
public function down(): void
{
// Orphan rows deleted in up() are not restored.
Schema::table('article_publications', function (Blueprint $table) {
$table->dropForeign(['platform_channel_id']);
});
}
};

View file

@ -0,0 +1,120 @@
<?php
namespace Tests\Feature;
use App\Models\Article;
use App\Models\ArticlePublication;
use App\Models\Feed;
use App\Models\Keyword;
use App\Models\PlatformAccount;
use App\Models\PlatformChannel;
use App\Models\Route;
use App\Models\RouteArticle;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class ChannelDeletionCascadeTest extends TestCase
{
use RefreshDatabase;
/**
* @return array{PlatformChannel, Feed}
*/
private function channelWithEverything(): array
{
$feed = Feed::factory()->create();
/** @var PlatformChannel $channel */
$channel = PlatformChannel::factory()->create();
Route::create([
'feed_id' => $feed->id,
'platform_channel_id' => $channel->id,
'priority' => 50,
'is_active' => true,
]);
Keyword::create([
'feed_id' => $feed->id,
'platform_channel_id' => $channel->id,
'keyword' => 'brussels',
'is_active' => true,
]);
$article = Article::factory()->create(['feed_id' => $feed->id]);
RouteArticle::factory()->create([
'feed_id' => $feed->id,
'platform_channel_id' => $channel->id,
'article_id' => $article->id,
]);
ArticlePublication::factory()->create([
'article_id' => $article->id,
'platform_channel_id' => $channel->id,
]);
$account = PlatformAccount::factory()->create();
$channel->platformAccounts()->attach($account->id, [
'is_active' => true,
'priority' => 1,
'created_at' => now(),
'updated_at' => now(),
]);
return [$channel, $feed];
}
public function test_deleting_a_channel_cascades_to_every_dependent_table(): void
{
[$channel] = $this->channelWithEverything();
$this->assertSame(1, Route::count());
$this->assertSame(1, Keyword::count());
$this->assertSame(1, RouteArticle::count());
$this->assertSame(1, ArticlePublication::count());
$channel->delete();
$this->assertSame(0, Route::count());
$this->assertSame(0, Keyword::count());
$this->assertSame(0, RouteArticle::count());
$this->assertSame(0, ArticlePublication::count());
$this->assertDatabaseCount('platform_account_channels', 0);
}
public function test_deleting_a_channel_leaves_the_feed_and_article_intact(): void
{
[$channel, $feed] = $this->channelWithEverything();
$channel->delete();
$this->assertDatabaseHas('feeds', ['id' => $feed->id]);
$this->assertSame(1, Article::count());
}
public function test_deleting_a_channel_leaves_other_channels_untouched(): void
{
[$channel] = $this->channelWithEverything();
$other = PlatformChannel::factory()->create();
$channel->delete();
$this->assertDatabaseHas('platform_channels', ['id' => $other->id]);
}
public function test_publications_for_another_channel_survive(): void
{
[$channel] = $this->channelWithEverything();
$other = PlatformChannel::factory()->create();
$article = Article::factory()->create();
ArticlePublication::factory()->create([
'article_id' => $article->id,
'platform_channel_id' => $other->id,
]);
$channel->delete();
$this->assertSame(1, ArticlePublication::count());
}
}