test: ActivityLogIndexesTest regression guard for D-06
PR-2 verified that Spatie's activitylog default migration creates the
composite indexes RFC-WS-7 §3.14 / addendum D-06 require — via
nullableMorphs('subject') and nullableMorphs('causer'), which emit
indexes named `subject` on (subject_type, subject_id) and `causer` on
(causer_type, causer_id).
This test queries information_schema.STATISTICS and fails if either
composite is missing, regardless of the index name. It guards against
silent regression when:
- A future Spatie major release changes nullableMorphs semantics.
- A developer rewrites the activity_log migration without preserving
the morph indexes.
- A schema-dump regeneration drops them.
Test count 1539 to 1541. Larastan clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
92
api/tests/Feature/Database/ActivityLogIndexesTest.php
Normal file
92
api/tests/Feature/Database/ActivityLogIndexesTest.php
Normal file
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature\Database;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Regression guard for RFC-WS-7 §3.14 / addendum D-06 composite indexes
|
||||
* on the activity_log table.
|
||||
*
|
||||
* Spatie's activitylog default migration calls nullableMorphs('subject')
|
||||
* and nullableMorphs('causer'), which create composite indexes named
|
||||
* `subject` on (subject_type, subject_id) and `causer` on
|
||||
* (causer_type, causer_id). RFC-WS-7 §3.14 / addendum D-06 require
|
||||
* exactly these indexes for query planner support on activity_log
|
||||
* lookups by morph subject/causer; PR-2 verified they already exist
|
||||
* via information_schema.
|
||||
*
|
||||
* This test fails when:
|
||||
* - A future Spatie major release changes nullableMorphs() semantics.
|
||||
* - A developer rewrites the activity_log migration without keeping
|
||||
* the morph indexes.
|
||||
* - A new schema-dump regeneration silently drops them.
|
||||
*/
|
||||
final class ActivityLogIndexesTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_subject_composite_index_exists(): void
|
||||
{
|
||||
$this->assertCompositeIndexExists(
|
||||
table: 'activity_log',
|
||||
columns: ['subject_type', 'subject_id'],
|
||||
description: 'subject composite index (RFC-WS-7 §3.14 / D-06)',
|
||||
);
|
||||
}
|
||||
|
||||
public function test_causer_composite_index_exists(): void
|
||||
{
|
||||
$this->assertCompositeIndexExists(
|
||||
table: 'activity_log',
|
||||
columns: ['causer_type', 'causer_id'],
|
||||
description: 'causer composite index (RFC-WS-7 §3.14 / D-06)',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<string> $columns
|
||||
*/
|
||||
private function assertCompositeIndexExists(string $table, array $columns, string $description): void
|
||||
{
|
||||
$database = config('database.connections.mysql.database');
|
||||
|
||||
$rows = DB::select(
|
||||
'SELECT INDEX_NAME, COLUMN_NAME, SEQ_IN_INDEX
|
||||
FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = ?
|
||||
AND TABLE_NAME = ?
|
||||
ORDER BY INDEX_NAME, SEQ_IN_INDEX',
|
||||
[$database, $table],
|
||||
);
|
||||
|
||||
$indexColumns = [];
|
||||
foreach ($rows as $row) {
|
||||
$indexColumns[$row->INDEX_NAME][(int) $row->SEQ_IN_INDEX] = $row->COLUMN_NAME;
|
||||
}
|
||||
|
||||
$found = false;
|
||||
foreach ($indexColumns as $sequence) {
|
||||
ksort($sequence);
|
||||
if (array_values($sequence) === $columns) {
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$this->assertTrue(
|
||||
$found,
|
||||
sprintf(
|
||||
'Expected composite index on %s(%s) — %s. Found indexes: %s',
|
||||
$table,
|
||||
implode(', ', $columns),
|
||||
$description,
|
||||
json_encode($indexColumns, JSON_PRETTY_PRINT),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user