feat(songs): track CCLI import metadata on songs table
This commit is contained in:
parent
fc2060b926
commit
73b7afcc2f
|
|
@ -16,6 +16,8 @@ class Song extends Model
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'ccli_id',
|
'ccli_id',
|
||||||
'cts_song_id',
|
'cts_song_id',
|
||||||
|
'imported_from_ccli_at',
|
||||||
|
'ccli_source_url',
|
||||||
'title',
|
'title',
|
||||||
'author',
|
'author',
|
||||||
'copyright_text',
|
'copyright_text',
|
||||||
|
|
@ -30,6 +32,7 @@ protected function casts(): array
|
||||||
return [
|
return [
|
||||||
'has_translation' => 'boolean',
|
'has_translation' => 'boolean',
|
||||||
'last_used_at' => 'datetime',
|
'last_used_at' => 'datetime',
|
||||||
|
'imported_from_ccli_at' => 'datetime',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,4 +22,12 @@ public function definition(): array
|
||||||
'last_used_at' => $this->faker->optional()->dateTimeBetween('-6 months', 'now'),
|
'last_used_at' => $this->faker->optional()->dateTimeBetween('-6 months', 'now'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function fromCcli(): self
|
||||||
|
{
|
||||||
|
return $this->state(fn (): array => [
|
||||||
|
'imported_from_ccli_at' => now(),
|
||||||
|
'ccli_source_url' => 'https://songselect.ccli.com/Songs/9999001',
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class () extends Migration {
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('songs', function (Blueprint $table): void {
|
||||||
|
$table->timestamp('imported_from_ccli_at')->nullable()->after('last_used_at');
|
||||||
|
$table->string('ccli_source_url', 500)->nullable()->after('imported_from_ccli_at');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('songs', function (Blueprint $table): void {
|
||||||
|
$table->dropColumn(['imported_from_ccli_at', 'ccli_source_url']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
54
tests/Feature/SongCcliMetadataTest.php
Normal file
54
tests/Feature/SongCcliMetadataTest.php
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\Song;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
|
use Illuminate\Support\Facades\Artisan;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
|
test('songs table has imported_from_ccli_at and ccli_source_url columns', function (): void {
|
||||||
|
expect(Schema::hasColumn('songs', 'imported_from_ccli_at'))->toBeTrue();
|
||||||
|
expect(Schema::hasColumn('songs', 'ccli_source_url'))->toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('imported_from_ccli_at defaults to null', function (): void {
|
||||||
|
$song = Song::factory()->create();
|
||||||
|
|
||||||
|
expect($song->fresh()->imported_from_ccli_at)->toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('ccli_source_url defaults to null', function (): void {
|
||||||
|
$song = Song::factory()->create();
|
||||||
|
|
||||||
|
expect($song->fresh()->ccli_source_url)->toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('imported_from_ccli_at casts to Carbon instance', function (): void {
|
||||||
|
$song = Song::factory()->create(['imported_from_ccli_at' => '2026-05-10 12:00:00']);
|
||||||
|
|
||||||
|
expect($song->fresh()->imported_from_ccli_at)->toBeInstanceOf(Carbon::class);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('fromCcli factory state populates both fields', function (): void {
|
||||||
|
$song = Song::factory()->fromCcli()->create();
|
||||||
|
$fresh = $song->fresh();
|
||||||
|
|
||||||
|
expect($fresh->imported_from_ccli_at)->not->toBeNull();
|
||||||
|
expect($fresh->imported_from_ccli_at)->toBeInstanceOf(Carbon::class);
|
||||||
|
expect($fresh->ccli_source_url)->not->toBeNull();
|
||||||
|
expect($fresh->ccli_source_url)->toContain('songselect.ccli.com');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('migration rolls back cleanly', function (): void {
|
||||||
|
Artisan::call('migrate:rollback', ['--step' => 1]);
|
||||||
|
|
||||||
|
expect(Schema::hasColumn('songs', 'imported_from_ccli_at'))->toBeFalse();
|
||||||
|
expect(Schema::hasColumn('songs', 'ccli_source_url'))->toBeFalse();
|
||||||
|
|
||||||
|
Artisan::call('migrate');
|
||||||
|
|
||||||
|
expect(Schema::hasColumn('songs', 'imported_from_ccli_at'))->toBeTrue();
|
||||||
|
expect(Schema::hasColumn('songs', 'ccli_source_url'))->toBeTrue();
|
||||||
|
});
|
||||||
Loading…
Reference in a new issue