Paillasse/portail/tests/Feature/StockageDevoirsTest.php

148 lines
7.3 KiB
PHP
Raw Normal View History

<?php
use App\Models\Classe;
use App\Models\Devoir;
use App\Models\Eleve;
use App\Models\RenduDevoir;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
/** Active le disque s3 factice et le fait passer pour configuré. */
function avecDisqueS3Factice(): void
{
config(['filesystems.disks.s3.bucket' => 'bucket-de-test']);
Storage::fake('s3');
}
test('tant que le bucket s3 n’est pas configuré, le dépôt de fichier est masqué et refusé', function () {
$classe = Classe::factory()->create();
$eleve = Eleve::factory()->create(['classe_id' => $classe->id]);
$devoir = Devoir::create(['classe_id' => $classe->id, 'titre' => 'Test', 'consigne' => 'Test', 'echeance' => now()->addDay()]);
eleve($this, $eleve)->get(route('devoirs.eleve.show', $devoir))->assertOk()->assertDontSee('Joindre un fichier');
eleve($this, $eleve)->post(route('devoirs.eleve.rendre', $devoir), [
'fichier' => UploadedFile::fake()->create('devoir.pdf', 100, 'application/pdf'),
])->assertStatus(503);
});
test('un élève peut rendre un devoir avec un fichier seul, sans texte', function () {
avecDisqueS3Factice();
$classe = Classe::factory()->create();
$eleve = Eleve::factory()->create(['classe_id' => $classe->id]);
$devoir = Devoir::create(['classe_id' => $classe->id, 'titre' => 'Test', 'consigne' => 'Test', 'echeance' => now()->addDay()]);
eleve($this, $eleve)->get(route('devoirs.eleve.show', $devoir))->assertOk()->assertSee('Joindre un fichier');
eleve($this, $eleve)->post(route('devoirs.eleve.rendre', $devoir), [
'fichier' => UploadedFile::fake()->create('devoir.pdf', 100, 'application/pdf'),
])->assertRedirect();
$rendu = RenduDevoir::where('devoir_id', $devoir->id)->where('eleve_id', $eleve->id)->first();
expect($rendu)->not->toBeNull();
expect($rendu->reponse)->toBeNull();
expect($rendu->aUnFichier())->toBeTrue();
expect($rendu->fichier_nom_original)->toBe('devoir.pdf');
Storage::disk('s3')->assertExists($rendu->fichier_chemin);
});
test('un rendu sans réponse ni fichier est refusé', function () {
avecDisqueS3Factice();
$classe = Classe::factory()->create();
$eleve = Eleve::factory()->create(['classe_id' => $classe->id]);
$devoir = Devoir::create(['classe_id' => $classe->id, 'titre' => 'Test', 'consigne' => 'Test', 'echeance' => now()->addDay()]);
eleve($this, $eleve)->post(route('devoirs.eleve.rendre', $devoir), [])->assertSessionHasErrors(['reponse', 'fichier']);
expect(RenduDevoir::count())->toBe(0);
});
test('un fichier trop lourd ou d’une extension refusée est rejeté', function () {
avecDisqueS3Factice();
$classe = Classe::factory()->create();
$eleve = Eleve::factory()->create(['classe_id' => $classe->id]);
$devoir = Devoir::create(['classe_id' => $classe->id, 'titre' => 'Test', 'consigne' => 'Test', 'echeance' => now()->addDay()]);
eleve($this, $eleve)->post(route('devoirs.eleve.rendre', $devoir), [
'fichier' => UploadedFile::fake()->create('trop-gros.pdf', 11 * 1024, 'application/pdf'),
])->assertSessionHasErrors('fichier');
eleve($this, $eleve)->post(route('devoirs.eleve.rendre', $devoir), [
'fichier' => UploadedFile::fake()->create('script.exe', 10, 'application/x-msdownload'),
])->assertSessionHasErrors('fichier');
expect(RenduDevoir::count())->toBe(0);
});
test('remplacer son fichier supprime l’ancien de l’object storage, un nouvel envoi en texte seul le conserve', function () {
avecDisqueS3Factice();
$classe = Classe::factory()->create();
$eleve = Eleve::factory()->create(['classe_id' => $classe->id]);
$devoir = Devoir::create(['classe_id' => $classe->id, 'titre' => 'Test', 'consigne' => 'Test', 'echeance' => now()->addDay()]);
eleve($this, $eleve)->post(route('devoirs.eleve.rendre', $devoir), [
'fichier' => UploadedFile::fake()->create('premier.pdf', 100, 'application/pdf'),
])->assertRedirect();
$cheminPremier = RenduDevoir::first()->fichier_chemin;
Storage::disk('s3')->assertExists($cheminPremier);
// Un nouvel envoi en texte seul (pas de nouveau fichier) garde l'ancien fichier.
eleve($this, $eleve)->post(route('devoirs.eleve.rendre', $devoir), ['reponse' => 'Je précise ma réponse'])->assertRedirect();
$rendu = RenduDevoir::first();
expect($rendu->fichier_chemin)->toBe($cheminPremier)->and($rendu->reponse)->toBe('Je précise ma réponse');
Storage::disk('s3')->assertExists($cheminPremier);
// Un nouveau fichier remplace l'ancien, qui est supprimé de l'object storage.
eleve($this, $eleve)->post(route('devoirs.eleve.rendre', $devoir), [
'fichier' => UploadedFile::fake()->create('second.pdf', 100, 'application/pdf'),
])->assertRedirect();
Storage::disk('s3')->assertMissing($cheminPremier);
expect(RenduDevoir::count())->toBe(1);
});
test('seul le prof propriétaire peut télécharger le fichier rendu par un élève', function () {
avecDisqueS3Factice();
$classe = Classe::factory()->create();
$eleve = Eleve::factory()->create(['classe_id' => $classe->id]);
$devoir = Devoir::create(['classe_id' => $classe->id, 'titre' => 'Test', 'consigne' => 'Test', 'echeance' => now()->addDay()]);
eleve($this, $eleve)->post(route('devoirs.eleve.rendre', $devoir), [
'fichier' => UploadedFile::fake()->create('devoir.pdf', 100, 'application/pdf'),
])->assertRedirect();
$rendu = RenduDevoir::first();
commeProf($this, $classe->prof)->get(route('mes-classes.devoirs.telecharger', [$classe, $devoir, $rendu]))->assertRedirect();
$autreClasse = Classe::factory()->create();
commeProf($this, $autreClasse->prof)->get(route('mes-classes.devoirs.telecharger', [$classe, $devoir, $rendu]))->assertForbidden();
});
test('le prof voit le fichier déposé par un élève dans la page du devoir', function () {
avecDisqueS3Factice();
$classe = Classe::factory()->create();
$eleve = Eleve::factory()->create(['classe_id' => $classe->id]);
$devoir = Devoir::create(['classe_id' => $classe->id, 'titre' => 'Test', 'consigne' => 'Test', 'echeance' => now()->addDay()]);
eleve($this, $eleve)->post(route('devoirs.eleve.rendre', $devoir), [
'fichier' => UploadedFile::fake()->create('rapport-reseau.pdf', 100, 'application/pdf'),
])->assertRedirect();
commeProf($this, $classe->prof)->get(route('mes-classes.devoirs.show', [$classe, $devoir]))
->assertSee('rapport-reseau.pdf');
});
test('purger une classe archivée supprime aussi les fichiers de ses rendus sur l’object storage', function () {
avecDisqueS3Factice();
$classe = Classe::factory()->create();
$eleve = Eleve::factory()->create(['classe_id' => $classe->id]);
$devoir = Devoir::create(['classe_id' => $classe->id, 'titre' => 'Test', 'consigne' => 'Test', 'echeance' => now()->addDay()]);
eleve($this, $eleve)->post(route('devoirs.eleve.rendre', $devoir), [
'fichier' => UploadedFile::fake()->create('devoir.pdf', 100, 'application/pdf'),
])->assertRedirect();
$chemin = RenduDevoir::first()->fichier_chemin;
Storage::disk('s3')->assertExists($chemin);
$classe->forceFill(['archivee_a' => now()->subDays(91)])->save();
$this->artisan('classes:purger-archivees');
Storage::disk('s3')->assertMissing($chemin);
expect(RenduDevoir::count())->toBe(0);
});