mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-08-10 07:11:18 +02:00
Two critical fixes: 1. Relink flow no longer destroys/recreates FilePond instances: The relink (XamxamRelinkFile) and PeerTube relink (XamxamRelinkPeerTube) previously refreshed the entire fichiers fragment via HTMX after pond.addFile(). This triggered destroyFilePondsIn on ALL pools, which could fire server.remove callbacks and move existing files to corbeille. Now just closes the modal — the file is already added to the pool in-place, and syncOrderInput creates the hidden form input. 2. Cleanup page « Corbeille (restaurable) » now actually shows files: _cleanup-stats-data.php previously classified trash files by checking if the thesis_files DB row still existed. But both deleteThesisFileToTrash and FilepondHandler::handleRemove DELETE the DB row. So ALL trash files appeared as `stale` (not restorable). Now uses the JSON sidecar file presence as the classification criterion — if the sidecar exists and is recent, the file is restorable regardless of DB row state. Also removed unused DB query from _cleanup-stats-data.php.
243 lines
10 KiB
PHP
243 lines
10 KiB
PHP
<?php
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
/**
|
|
* PureLogicTest — PHPUnit version covering pure logic that isn't in other test classes.
|
|
*
|
|
* Includes: splitJuryByRole, collectCaptionPaths, detectFileType,
|
|
* generateAuthorSlug, ExportController CSV_HEADERS consistency.
|
|
*/
|
|
class PureLogicTest extends TestCase
|
|
{
|
|
private function getTfeController(): TfeController
|
|
{
|
|
// We need a TfeController instance to test protected methods.
|
|
// Use the anonymous subclass pattern.
|
|
$db = TestDatabase::getInstance();
|
|
return new class ($db) extends TfeController {
|
|
public function exposedSplitJuryByRole(array $jury): array
|
|
{
|
|
return $this->splitJuryByRole($jury);
|
|
}
|
|
public function exposedCollectCaptionPaths(array $files): array
|
|
{
|
|
return $this->collectCaptionPaths($files);
|
|
}
|
|
};
|
|
}
|
|
|
|
private function getThesisCreateController(): ThesisCreateController
|
|
{
|
|
$db = TestDatabase::getInstance();
|
|
return new class ($db) extends ThesisCreateController {
|
|
public function exposedDetectFileType(string $mimeType, string $ext): string
|
|
{
|
|
return $this->detectFileType($mimeType, $ext);
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Invoke the private hasFilePondQueueData() method via reflection.
|
|
* The method lives in the ThesisFileHandler trait, used by ThesisCreateController.
|
|
*/
|
|
private function invokeHasFilePondQueueData(array $post): bool
|
|
{
|
|
$ctrl = $this->getThesisCreateController();
|
|
$ref = new ReflectionMethod(ThesisCreateController::class, 'hasFilePondQueueData');
|
|
return $ref->invoke($ctrl, $post);
|
|
}
|
|
|
|
// ── splitJuryByRole ──────────────────────────────────────────────────────
|
|
|
|
public function testSplitJuryByRoleAllRoles(): void
|
|
{
|
|
$ctrl = $this->getTfeController();
|
|
$jury = [
|
|
['name' => 'Alice', 'role' => 'president', 'is_external' => 0, 'is_ulb' => 0],
|
|
['name' => 'Bob', 'role' => 'promoteur', 'is_external' => 0, 'is_ulb' => 0],
|
|
['name' => 'Carol', 'role' => 'promoteur', 'is_external' => 1, 'is_ulb' => 1],
|
|
['name' => 'Dave', 'role' => 'promoteur', 'is_external' => 1, 'is_ulb' => 0],
|
|
['name' => 'Eve', 'role' => 'lecteur', 'is_external' => 0, 'is_ulb' => 0],
|
|
['name' => 'Frank', 'role' => 'lecteur', 'is_external' => 1, 'is_ulb' => 0],
|
|
];
|
|
$split = $ctrl->exposedSplitJuryByRole($jury);
|
|
|
|
$this->assertSame(['Alice'], $split['presidents']);
|
|
$this->assertSame(['Bob'], $split['internes']);
|
|
$this->assertSame(['Carol'], $split['ulb']);
|
|
$this->assertSame(['Dave'], $split['externes']);
|
|
$this->assertSame(['Eve'], $split['lecteurs_internes']);
|
|
$this->assertSame(['Frank'], $split['lecteurs_externes']);
|
|
}
|
|
|
|
public function testSplitJuryByRoleEmptyNameSkipped(): void
|
|
{
|
|
$ctrl = $this->getTfeController();
|
|
$jury = [['name' => '', 'role' => 'president', 'is_external' => 0, 'is_ulb' => 0]];
|
|
$split = $ctrl->exposedSplitJuryByRole($jury);
|
|
|
|
$this->assertEmpty($split['presidents']);
|
|
}
|
|
|
|
public function testSplitJuryByRoleEmptyJury(): void
|
|
{
|
|
$ctrl = $this->getTfeController();
|
|
$split = $ctrl->exposedSplitJuryByRole([]);
|
|
|
|
$this->assertEmpty($split['presidents']);
|
|
$this->assertEmpty($split['internes']);
|
|
$this->assertEmpty($split['ulb']);
|
|
$this->assertEmpty($split['externes']);
|
|
$this->assertEmpty($split['lecteurs_internes']);
|
|
$this->assertEmpty($split['lecteurs_externes']);
|
|
}
|
|
|
|
// ── collectCaptionPaths ──────────────────────────────────────────────────
|
|
|
|
public function testCollectCaptionPathsVttByMime(): void
|
|
{
|
|
$ctrl = $this->getTfeController();
|
|
$files = [
|
|
['mime_type' => 'application/pdf', 'file_path' => 'main.pdf'],
|
|
['mime_type' => 'text/vtt', 'file_path' => 'captions1.vtt'],
|
|
['mime_type' => 'video/mp4', 'file_path' => 'video.mp4'],
|
|
['mime_type' => 'text/plain', 'file_path' => 'captions2.vtt'],
|
|
];
|
|
$captions = $ctrl->exposedCollectCaptionPaths($files);
|
|
|
|
// Only the VTT files, in order
|
|
$this->assertCount(2, $captions);
|
|
$this->assertSame('captions1.vtt', $captions[0]);
|
|
$this->assertSame('captions2.vtt', $captions[1]);
|
|
}
|
|
|
|
public function testCollectCaptionPathsVttByExtension(): void
|
|
{
|
|
$ctrl = $this->getTfeController();
|
|
$files = [
|
|
['mime_type' => 'application/octet-stream', 'file_path' => 'sub.vtt'],
|
|
['mime_type' => 'video/mp4', 'file_path' => 'video.mp4'],
|
|
];
|
|
$captions = $ctrl->exposedCollectCaptionPaths($files);
|
|
|
|
$this->assertCount(1, $captions);
|
|
$this->assertSame('sub.vtt', $captions[0]);
|
|
}
|
|
|
|
public function testCollectCaptionPathsNoVttReturnsEmpty(): void
|
|
{
|
|
$ctrl = $this->getTfeController();
|
|
$files = [['mime_type' => 'video/mp4', 'file_path' => 'video.mp4']];
|
|
|
|
$this->assertEmpty($ctrl->exposedCollectCaptionPaths($files));
|
|
}
|
|
|
|
// ── detectFileType ───────────────────────────────────────────────────────
|
|
|
|
public function testDetectFileTypeByMime(): void
|
|
{
|
|
$ctrl = $this->getThesisCreateController();
|
|
|
|
$this->assertSame('caption', $ctrl->exposedDetectFileType('text/vtt', 'vtt'));
|
|
$this->assertSame('audio', $ctrl->exposedDetectFileType('audio/mpeg', 'mp3'));
|
|
$this->assertSame('audio', $ctrl->exposedDetectFileType('audio/ogg', 'ogg'));
|
|
$this->assertSame('video', $ctrl->exposedDetectFileType('video/mp4', 'mp4'));
|
|
$this->assertSame('video', $ctrl->exposedDetectFileType('video/webm', 'webm'));
|
|
$this->assertSame('main', $ctrl->exposedDetectFileType('application/pdf', 'pdf'));
|
|
$this->assertSame('image', $ctrl->exposedDetectFileType('image/jpeg', 'jpg'));
|
|
$this->assertSame('image', $ctrl->exposedDetectFileType('image/png', 'png'));
|
|
$this->assertSame('other', $ctrl->exposedDetectFileType('application/zip', 'zip'));
|
|
}
|
|
|
|
public function testDetectFileTypeByExtensionFallback(): void
|
|
{
|
|
$ctrl = $this->getThesisCreateController();
|
|
|
|
$this->assertSame('audio', $ctrl->exposedDetectFileType('application/octet-stream', 'mp3'));
|
|
$this->assertSame('video', $ctrl->exposedDetectFileType('application/octet-stream', 'mp4'));
|
|
$this->assertSame('main', $ctrl->exposedDetectFileType('application/octet-stream', 'pdf'));
|
|
$this->assertSame('image', $ctrl->exposedDetectFileType('application/octet-stream', 'webp'));
|
|
$this->assertSame('caption', $ctrl->exposedDetectFileType('application/octet-stream', 'vtt'));
|
|
}
|
|
|
|
// ── hasFilePondQueueData ────────────────────────────────────────────────
|
|
|
|
public function testHasFilePondQueueDataReturnsTrueForHexId(): void
|
|
{
|
|
$post = ['queue_file' => ['tfe' => ['abc123def456abc123def456abc123de']]];
|
|
$this->assertTrue($this->invokeHasFilePondQueueData($post));
|
|
}
|
|
|
|
public function testHasFilePondQueueDataReturnsTrueForPeertubeVideo(): void
|
|
{
|
|
$post = ['queue_file' => ['tfe' => ['peertube:video:bmpQZTUPv4ou8ufiwajV63']]];
|
|
$this->assertTrue(
|
|
$this->invokeHasFilePondQueueData($post),
|
|
'peertube:video:UUID should be detected as FilePond data'
|
|
);
|
|
}
|
|
|
|
public function testHasFilePondQueueDataReturnsTrueForPeertubeAudio(): void
|
|
{
|
|
$post = ['queue_file' => ['tfe' => ['peertube:audio:xyz123']]];
|
|
$this->assertTrue(
|
|
$this->invokeHasFilePondQueueData($post),
|
|
'peertube:audio:UUID should be detected as FilePond data'
|
|
);
|
|
}
|
|
|
|
public function testHasFilePondQueueDataReturnsTrueForMixedIds(): void
|
|
{
|
|
$post = ['queue_file' => ['tfe' => ['123', 'peertube:video:abc123', '456']]];
|
|
$this->assertTrue(
|
|
$this->invokeHasFilePondQueueData($post),
|
|
'Mixed array containing a peertube: ID should be detected'
|
|
);
|
|
}
|
|
|
|
public function testHasFilePondQueueDataReturnsTrueForHexInCoverQueue(): void
|
|
{
|
|
$post = ['queue_file' => ['cover' => ['abcdef1234567890abcdef1234567890']]];
|
|
$this->assertTrue($this->invokeHasFilePondQueueData($post));
|
|
}
|
|
|
|
public function testHasFilePondQueueDataReturnsTrueForPeertubeInCoverQueue(): void
|
|
{
|
|
$post = ['queue_file' => ['cover' => ['peertube:video:uuid1']]];
|
|
$this->assertTrue($this->invokeHasFilePondQueueData($post));
|
|
}
|
|
|
|
public function testHasFilePondQueueDataReturnsFalseForNumericIdsOnly(): void
|
|
{
|
|
$post = ['queue_file' => ['tfe' => ['123', '456']]];
|
|
$this->assertFalse($this->invokeHasFilePondQueueData($post));
|
|
}
|
|
|
|
public function testHasFilePondQueueDataReturnsFalseForEmptyInput(): void
|
|
{
|
|
$this->assertFalse($this->invokeHasFilePondQueueData([]));
|
|
$this->assertFalse($this->invokeHasFilePondQueueData(['queue_file' => []]));
|
|
$this->assertFalse($this->invokeHasFilePondQueueData(['queue_file' => ['tfe' => []]]));
|
|
}
|
|
|
|
public function testHasFilePondQueueDataReturnsFalseForEmptyStrings(): void
|
|
{
|
|
$post = ['queue_file' => ['tfe' => ['', ' ']]];
|
|
$this->assertFalse($this->invokeHasFilePondQueueData($post));
|
|
}
|
|
|
|
public function testHasFilePondQueueDataHandlesScalarNotArray(): void
|
|
{
|
|
$post = ['queue_file' => ['tfe' => 'peertube:video:singleUuid']];
|
|
$this->assertTrue($this->invokeHasFilePondQueueData($post));
|
|
|
|
$post2 = ['queue_file' => ['tfe' => 'abc123def456abc123def456abc123de']];
|
|
$this->assertTrue($this->invokeHasFilePondQueueData($post2));
|
|
|
|
$post3 = ['queue_file' => ['tfe' => '123']];
|
|
$this->assertFalse($this->invokeHasFilePondQueueData($post3));
|
|
}
|
|
}
|