Pages /legal et /confidentialite réécrites d'après le système réel, purge des classes archivées depuis 90 jours (prouvée par Pest), archivage côté prof. Polices, xterm.js et CodeMirror ne sont plus chargés depuis Google ni jsDelivr. Pages d'aperçu signalées, sans vrais établissements ni faux chiffres. Journaux Laravel gardés 14 jours. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
99 lines
4.1 KiB
PHP
99 lines
4.1 KiB
PHP
<?php
|
||
|
||
use App\Models\Classe;
|
||
use App\Models\Correction;
|
||
use App\Models\Eleve;
|
||
use App\Models\Lab;
|
||
use App\Models\LabSession;
|
||
use App\Models\LabSessionMachine;
|
||
use App\Models\Seance;
|
||
use App\Models\User;
|
||
|
||
/**
|
||
* Une classe complète : 2 élèves, une séance, une session de lab terminée
|
||
* avec une machine et une correction.
|
||
*/
|
||
function classeComplete(?int $archiveeIlYaJours): Classe
|
||
{
|
||
$classe = Classe::factory()->create();
|
||
if ($archiveeIlYaJours !== null) {
|
||
$classe->forceFill(['archivee_a' => now()->subDays($archiveeIlYaJours)])->save();
|
||
}
|
||
$eleves = Eleve::factory()->count(2)->create(['classe_id' => $classe->id]);
|
||
$lab = Lab::factory()->create();
|
||
$seance = Seance::create(['classe_id' => $classe->id, 'lab_id' => $lab->id, 'statut' => Seance::TERMINEE]);
|
||
$session = LabSession::create(['seance_id' => $seance->id, 'eleve_id' => $eleves[0]->id, 'lab_id' => $lab->id, 'etat' => LabSession::EXPIRE]);
|
||
$session->machines()->create(['nom' => 'pc-a', 'instance_id' => 'uuid-a']);
|
||
$session->corrections()->create(['reussi' => true, 'note' => 10, 'note_max' => 10, 'details' => []]);
|
||
|
||
return $classe;
|
||
}
|
||
|
||
test('la purge supprime une classe archivée depuis plus de 90 jours et toutes les données de ses élèves', function () {
|
||
$classe = classeComplete(91);
|
||
$prof = $classe->prof;
|
||
|
||
$this->artisan('classes:purger-archivees')->expectsOutput('1 classe(s) purgée(s).')->assertSuccessful();
|
||
|
||
expect(Classe::find($classe->id))->toBeNull()
|
||
->and(Eleve::where('classe_id', $classe->id)->count())->toBe(0)
|
||
->and(Seance::where('classe_id', $classe->id)->count())->toBe(0)
|
||
->and(LabSession::count())->toBe(0)
|
||
->and(LabSessionMachine::count())->toBe(0)
|
||
->and(Correction::count())->toBe(0)
|
||
// Le compte du prof n'est pas une donnée d'élève : il reste.
|
||
->and(User::find($prof->id))->not->toBeNull();
|
||
});
|
||
|
||
test('la purge épargne les classes archivées depuis moins de 90 jours et les classes en cours', function () {
|
||
$recente = classeComplete(89);
|
||
$enCours = classeComplete(null);
|
||
|
||
$this->artisan('classes:purger-archivees')->expectsOutput('0 classe(s) purgée(s).');
|
||
|
||
expect(Classe::find($recente->id))->not->toBeNull()
|
||
->and(Classe::find($enCours->id))->not->toBeNull()
|
||
->and(Eleve::count())->toBe(4)
|
||
->and(Correction::count())->toBe(2);
|
||
});
|
||
|
||
test('la purge ne touche qu’aux classes concernées', function () {
|
||
$aPurger = classeComplete(120);
|
||
$aGarder = classeComplete(null);
|
||
|
||
$this->artisan('classes:purger-archivees');
|
||
|
||
expect(Classe::pluck('id')->all())->toBe([$aGarder->id])
|
||
->and(Eleve::pluck('classe_id')->unique()->all())->toBe([$aGarder->id]);
|
||
});
|
||
|
||
test('un prof archive sa classe : plus de séance possible, date de suppression annoncée', function () {
|
||
$classe = classeComplete(null);
|
||
$prof = $classe->prof;
|
||
$jeton = $prof->demarreNouvelleSession();
|
||
$connecte = fn () => $this->withSession(['jeton_session_web' => $jeton])->actingAs($prof, 'web');
|
||
|
||
$connecte()->post(route('mes-classes.archiver', $classe))->assertRedirect(route('mes-classes.show', $classe));
|
||
|
||
$classe->refresh();
|
||
expect($classe->estArchivee())->toBeTrue()
|
||
->and($classe->supprimeeLe()->toDateString())->toBe(now()->addDays(90)->toDateString());
|
||
|
||
$connecte()->get(route('mes-classes.show', $classe))
|
||
->assertSee('Classe archivée')
|
||
->assertSee($classe->supprimeeLe()->timezone(config('app.fuseau_utilisateurs'))->format('d/m/Y'));
|
||
|
||
$connecte()->post(route('mes-classes.seances.store', $classe), ['lab_id' => Lab::first()->id])->assertSessionHasErrors('prevue_a');
|
||
expect(Seance::where('classe_id', $classe->id)->where('statut', '!=', Seance::TERMINEE)->count())->toBe(0);
|
||
});
|
||
|
||
test('un autre prof ne peut pas archiver la classe', function () {
|
||
$classe = classeComplete(null);
|
||
$intrus = User::factory()->create();
|
||
$jeton = $intrus->demarreNouvelleSession();
|
||
|
||
$this->withSession(['jeton_session_web' => $jeton])->actingAs($intrus, 'web')
|
||
->post(route('mes-classes.archiver', $classe))->assertForbidden();
|
||
|
||
expect($classe->refresh()->estArchivee())->toBeFalse();
|
||
});
|