mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-06-25 16:19:19 +02:00
- New fragment endpoint POST/GET /partage/fragments/draft.php: saves all form fields to PHP session, excludes file/csrf/slug fields GET returns JSON for JS hydration on page load rotates both global CSRF and share CSRF tokens in sync - form.php accepts optional $formExtraAttrs and $showAutosaveStatus: allows injecting HTMX attributes and 'Brouillon enregistré' indicator - renderShareLinkForm adds hx-post with change/input debounce trigger, loads autosave-handler.js, hydrate fields from draft on page load - Draft cleared on successful form submission in handleShareLinkSubmission - autosave-handler.js now also updates share_link_token hidden input when rotating CSRF token (partage form uses both csrf_token and share_link_token) - Added .autosave-status CSS to form.css (was admin.css-only) - Updated fragment routing to accept GET requests (needed for draft hydration)
141 lines
4.4 KiB
PHP
141 lines
4.4 KiB
PHP
<?php
|
|
|
|
/**
|
|
* TestDatabase — helper for integration tests that need a real SQLite database.
|
|
*
|
|
* Creates an in-memory SQLite database, loads the full schema + seed data,
|
|
* and provides a Database instance connected to it. Teardown discards the DB.
|
|
*/
|
|
|
|
/**
|
|
* Thin Database subclass that accepts a pre-built PDO connection.
|
|
* Bypasses the normal path-based constructor.
|
|
*/
|
|
class TestDatabaseInstance extends Database
|
|
{
|
|
public function __construct(PDO $pdo)
|
|
{
|
|
// Inject PDO directly via reflection, then flag as ready
|
|
$ref = new ReflectionProperty(Database::class, 'pdo');
|
|
$ref->setValue($this, $pdo);
|
|
|
|
$pathRef = new ReflectionProperty(Database::class, 'dbPath');
|
|
$pathRef->setValue($this, ':memory:');
|
|
}
|
|
}
|
|
|
|
class TestDatabase
|
|
{
|
|
private static ?PDO $pdo = null;
|
|
private static ?Database $db = null;
|
|
|
|
/**
|
|
* Get or create the shared test Database instance.
|
|
* Uses an in-memory SQLite DB so tests are fast and isolated.
|
|
*/
|
|
public static function getInstance(): Database
|
|
{
|
|
if (self::$db === null) {
|
|
self::$pdo = new PDO('sqlite::memory:');
|
|
self::$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
self::$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
|
|
self::$pdo->exec('PRAGMA foreign_keys = ON');
|
|
self::$pdo->exec('PRAGMA journal_mode = MEMORY');
|
|
|
|
// Load schema
|
|
$schema = file_get_contents(APP_ROOT . '/storage/schema.sql');
|
|
if ($schema === false) {
|
|
throw new RuntimeException('Failed to read schema.sql');
|
|
}
|
|
self::$pdo->exec($schema);
|
|
|
|
// Create a Database wrapper injecting our PDO
|
|
self::$db = new TestDatabaseInstance(self::$pdo);
|
|
}
|
|
|
|
return self::$db;
|
|
}
|
|
|
|
/**
|
|
* Get the raw PDO connection (for queries that bypass the Database class).
|
|
*/
|
|
public static function getPDO(): PDO
|
|
{
|
|
// Ensure the DB is booted
|
|
self::getInstance();
|
|
return self::$pdo;
|
|
}
|
|
|
|
/**
|
|
* Reset all test data between tests.
|
|
* Preserves seed data (orientations, access types, etc.) but removes
|
|
* any theses, authors, tags, share links etc. created during a test.
|
|
*/
|
|
public static function resetData(): void
|
|
{
|
|
$pdo = self::getPDO();
|
|
// Order matters due to FK constraints
|
|
$tables = [
|
|
'file_access_audit',
|
|
'file_access_sessions',
|
|
'file_access_tokens',
|
|
'file_access_requests',
|
|
'thesis_files',
|
|
'thesis_tags',
|
|
'thesis_formats',
|
|
'thesis_languages',
|
|
'thesis_supervisors',
|
|
'thesis_authors',
|
|
'theses',
|
|
'share_links',
|
|
'tags',
|
|
'authors',
|
|
'supervisors',
|
|
'admin_audit_log',
|
|
'audit_log',
|
|
];
|
|
foreach ($tables as $table) {
|
|
$pdo->exec("DELETE FROM $table");
|
|
}
|
|
// Re-seed tags (some tests rely on tags existing)
|
|
try {
|
|
$pdo->exec('DELETE FROM tags WHERE deleted_at IS NOT NULL');
|
|
} catch (Exception $e) {
|
|
// tags table already empty
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Seed some basic test data: an author, a thesis.
|
|
* Returns [authorId, thesisId].
|
|
*
|
|
* @return array{0: int, 1: int}
|
|
*/
|
|
public static function seedBasicThesis(string $title = 'Test Thesis', string $authorName = 'Test Author', int $year = 2024): array
|
|
{
|
|
$pdo = self::getPDO();
|
|
|
|
// Insert author
|
|
$pdo->prepare('INSERT INTO authors (name) VALUES (?)')->execute([$authorName]);
|
|
$authorId = (int)$pdo->lastInsertId();
|
|
|
|
// Insert thesis
|
|
$pdo->prepare(
|
|
"INSERT INTO theses (title, year, identifier, is_published, objet) VALUES (?, ?, ?, 1, 'tfe')"
|
|
)->execute([$title, $year, "$year-001"]);
|
|
|
|
$thesisId = (int)$pdo->lastInsertId();
|
|
|
|
// Link author
|
|
$pdo->prepare('INSERT INTO thesis_authors (thesis_id, author_id) VALUES (?, ?)')
|
|
->execute([$thesisId, $authorId]);
|
|
|
|
// Insert a cover file
|
|
$pdo->prepare(
|
|
"INSERT INTO thesis_files (thesis_id, file_type, file_path, file_name, file_size, mime_type) VALUES (?, 'cover', ?, ?, 0, 'image/jpeg')"
|
|
)->execute([$thesisId, "documents/$year-001/cover.jpg", 'cover.jpg']);
|
|
|
|
return [$authorId, $thesisId];
|
|
}
|
|
}
|