- Event-Medien tragen Typ (image/video/audio), Dauer und Notiz; Videos bekommen ein Poster, Audio wird per ffmpeg nach AAC (audio/mp4) gewandelt, Originale werden mit Range-Unterstützung gestreamt. - Upload nimmt eine Notiz (caption) direkt mit, PATCH ändert sie später. - Neue Event-Felder: Zeitraum (end_date), Kategorie, Personen, Meilenstein. Regeln zentral in EventFields, damit CRUD und Sync gleich prüfen. - Titel darf leer sein: ein Event entsteht mit dem ersten Eintrag. - Artisan-Befehl media:transcode-voice-notes wandelt alte WebM/Ogg-Notizen, die Safari nicht abspielen kann. - Login-Throttling, Settings-Validierung, Sync-Fehlercodes ohne Rohtext. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
127 lines
5.4 KiB
PHP
127 lines
5.4 KiB
PHP
<?php
|
||
|
||
use App\Models\Event;
|
||
use App\Models\User;
|
||
use Database\Seeders\DatabaseSeeder;
|
||
use Database\Seeders\LifelineSeeder;
|
||
use Illuminate\Support\Facades\File;
|
||
use Illuminate\Support\Facades\Hash;
|
||
|
||
function seededLifelineEventCount(): int
|
||
{
|
||
return collect(LifelineSeeder::lifelineFiles())
|
||
->sum(fn (string $path): int => count(json_decode(File::get($path), true)['events']));
|
||
}
|
||
|
||
test('lifeline seeder creates jonas and sophie with a full lifeline each', function () {
|
||
$this->seed(LifelineSeeder::class);
|
||
|
||
foreach (['jonas' => 'Jonas', 'sophie' => 'Sophie'] as $handle => $name) {
|
||
$user = User::query()->where('email', "{$handle}@thats-me.app")->first();
|
||
|
||
expect($user)->not->toBeNull()
|
||
->and($user->name)->toBe($name)
|
||
->and(Hash::check('pass', $user->password))->toBeTrue()
|
||
->and($user->email_verified_at)->not->toBeNull();
|
||
|
||
$events = Event::query()->where('user_id', $user->id)->orderBy('date')->get();
|
||
|
||
// 21 years (25–45) × 12 months, one event per month
|
||
expect($events)->toHaveCount(252)
|
||
->and($events->first()->date)->toStartWith('2005-01-')
|
||
->and($events->last()->date)->toStartWith('2025-12-')
|
||
->and($events->pluck('date')->map(fn (string $date): string => substr($date, 0, 7))->unique())->toHaveCount(252);
|
||
|
||
foreach ($events as $event) {
|
||
expect($event->title)->not->toBe('')
|
||
->and(mb_strlen($event->title))->toBeLessThanOrEqual(40)
|
||
->and($event->note)->not->toBe('')
|
||
->and((float) $event->emotion)->toBeGreaterThanOrEqual(-1)
|
||
->and((float) $event->emotion)->toBeLessThanOrEqual(1)
|
||
->and($event->image)->toBeNull();
|
||
}
|
||
}
|
||
});
|
||
|
||
test('lifeline seeder maps ratings onto the emotion bands of the event panel', function () {
|
||
$this->seed(LifelineSeeder::class);
|
||
|
||
$jonas = User::query()->where('email', 'jonas@thats-me.app')->firstOrFail();
|
||
|
||
$emotionFor = fn (int $age, int $month): float => (float) Event::query()
|
||
->where('client_id', LifelineSeeder::clientId('jonas@thats-me.app', $age, $month))
|
||
->where('user_id', $jonas->id)
|
||
->value('emotion');
|
||
|
||
// 25 / Juli: Lagerfeuer in Slowenien → Sehr positiv
|
||
expect($emotionFor(25, 7))->toBeGreaterThan(0.5);
|
||
// 25 / Januar: Erstes eigenes Projekt → Positiv
|
||
expect($emotionFor(25, 1))->toBeGreaterThan(0.15)->toBeLessThanOrEqual(0.5);
|
||
// 25 / Februar: Sonntags-Vorkochen → Neutral
|
||
expect($emotionFor(25, 2))->toBeGreaterThan(-0.15)->toBeLessThanOrEqual(0.15);
|
||
// 25 / Juni: Spätschicht im Büro → Negativ
|
||
expect($emotionFor(25, 6))->toBeGreaterThan(-0.5)->toBeLessThanOrEqual(-0.15);
|
||
// 40 / Mai: Letztes Foto von Thomas → Sehr negativ
|
||
expect($emotionFor(40, 5))->toBeLessThanOrEqual(-0.5);
|
||
});
|
||
|
||
test('lifeline seeder is idempotent and keeps manually imported key images', function () {
|
||
$this->seed(LifelineSeeder::class);
|
||
|
||
$clientId = LifelineSeeder::clientId('sophie@thats-me.app', 35, 1);
|
||
$event = Event::query()->where('client_id', $clientId)->firstOrFail();
|
||
|
||
$event->update([
|
||
'image' => 'media/sophie/clara.jpg',
|
||
'key_image_title' => 'Clara',
|
||
'title' => 'Manuell geändert',
|
||
]);
|
||
|
||
$this->seed(LifelineSeeder::class);
|
||
|
||
$event->refresh();
|
||
|
||
expect(Event::query()->count())->toBe(seededLifelineEventCount())
|
||
->and(User::query()->where('email', 'like', '%@thats-me.app')->count())->toBe(count(LifelineSeeder::lifelineFiles()))
|
||
->and($event->image)->toBe('media/sophie/clara.jpg')
|
||
->and($event->key_image_title)->toBe('Clara')
|
||
->and($event->title)->toBe('Clara ist da');
|
||
});
|
||
|
||
test('database seeder includes the lifelines', function () {
|
||
$this->seed(DatabaseSeeder::class);
|
||
|
||
foreach (['jonas', 'sophie', 'nadja', 'werner', 'bruno', 'benz', 'lukas', 'nachtschicht'] as $handle) {
|
||
expect(User::query()->where('email', "{$handle}@thats-me.app")->exists())->toBeTrue();
|
||
}
|
||
|
||
expect(Event::query()->count())->toBe(seededLifelineEventCount());
|
||
});
|
||
|
||
test('additional lifelines have gaps and end at their final year', function () {
|
||
$this->seed(LifelineSeeder::class);
|
||
|
||
$range = function (string $handle): array {
|
||
$user = User::query()->where('email', "{$handle}@thats-me.app")->firstOrFail();
|
||
$events = Event::query()->where('user_id', $user->id);
|
||
|
||
return [$events->count(), substr($events->min('date'), 0, 4), substr($events->max('date'), 0, 4)];
|
||
};
|
||
|
||
// Nadja: 1988 + 24 … 37, Werner: 1955 + 55 … 70, Bruno: 2014 + 0 … 10
|
||
expect($range('nadja'))->toBe([42, '2012', '2025'])
|
||
->and($range('werner'))->toBe([41, '2010', '2025'])
|
||
->and($range('bruno'))->toBe([35, '2014', '2024'])
|
||
->and($range('benz'))->toBe([37, '1981', '2025'])
|
||
->and($range('lukas'))->toBe([69, '2012', '2025'])
|
||
->and($range('nachtschicht'))->toBe([57, '1995', '2025']);
|
||
});
|
||
|
||
test('an explicit day separates several events in the same month', function () {
|
||
$email = 'nadja@thats-me.app';
|
||
|
||
expect(LifelineSeeder::clientId($email, 32, 5))->not->toBe(LifelineSeeder::clientId($email, 32, 5, 12))
|
||
->and(LifelineSeeder::clientId($email, 32, 5, 12))->not->toBe(LifelineSeeder::clientId($email, 32, 5, 27))
|
||
->and(LifelineSeeder::date(1988, 32, 5, $email, 12))->toBe('2020-05-12')
|
||
->and(LifelineSeeder::date(1988, 32, 5, $email))->toMatch('/^2020-05-(0[1-9]|1\d|2[0-8])$/');
|
||
});
|