mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-06-25 16:19:19 +02:00
229 lines
10 KiB
PHP
229 lines
10 KiB
PHP
<?php
|
|
|
|
/**
|
|
* ShareLink Unit Test
|
|
*
|
|
* Tests pure-logic methods that require no HTTP context:
|
|
* - generateSlug() — format, uniqueness, entropy
|
|
* - validateLink() — all branches: not_found, archived, disabled, expired, needs_password, valid
|
|
* - verifyPassword() — correct / wrong / no-password links
|
|
*/
|
|
|
|
putenv('DB_ENV=test');
|
|
|
|
if (!defined('APP_ROOT')) {
|
|
define('APP_ROOT', dirname(__DIR__, 2));
|
|
}
|
|
if (!defined('STORAGE_ROOT')) {
|
|
define('STORAGE_ROOT', APP_ROOT . '/storage');
|
|
}
|
|
|
|
require_once APP_ROOT . '/src/Database.php';
|
|
require_once APP_ROOT . '/src/ShareLink.php';
|
|
|
|
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
|
|
function slAssert(bool $cond, string $label): void
|
|
{
|
|
if ($cond) {
|
|
echo " ✓ $label\n";
|
|
} else {
|
|
throw new RuntimeException("FAIL: $label");
|
|
}
|
|
}
|
|
|
|
function slAssertEq(mixed $expected, mixed $actual, string $label): void
|
|
{
|
|
if ($expected === $actual) {
|
|
echo " ✓ $label\n";
|
|
} else {
|
|
$e = var_export($expected, true);
|
|
$a = var_export($actual, true);
|
|
throw new RuntimeException("FAIL $label\n expected: $e\n actual: $a");
|
|
}
|
|
}
|
|
|
|
// ── Setup ─────────────────────────────────────────────────────────────────────
|
|
|
|
echo "ShareLink Unit Test\n";
|
|
echo "===================\n\n";
|
|
|
|
$db = Database::getInstance();
|
|
$model = new ShareLink($db);
|
|
|
|
// We need a dummy admin user id — just use 1 (or any int; share_links.created_by is not FK-checked)
|
|
$adminId = 1;
|
|
$createdIds = [];
|
|
|
|
try {
|
|
|
|
// =========================================================================
|
|
// TEST 1: generateSlug — format YYYYMMDD-XXXXXXXX
|
|
// =========================================================================
|
|
echo "Test 1: generateSlug — format\n";
|
|
$slug = ShareLink::generateSlug();
|
|
slAssert(
|
|
(bool) preg_match('/^\d{8}-[A-Z2-7]{8}$/', $slug),
|
|
"slug matches YYYYMMDD-[BASE32]{8}: $slug"
|
|
);
|
|
$year = (int) substr($slug, 0, 4);
|
|
$month = (int) substr($slug, 4, 2);
|
|
$day = (int) substr($slug, 6, 2);
|
|
slAssert($year >= 2020 && $year <= 2100, 'year in plausible range');
|
|
slAssert($month >= 1 && $month <= 12, 'month in range');
|
|
slAssert($day >= 1 && $day <= 31, 'day in range');
|
|
echo "\n";
|
|
|
|
// =========================================================================
|
|
// TEST 2: generateSlug — two calls produce different slugs
|
|
// =========================================================================
|
|
echo "Test 2: generateSlug — uniqueness\n";
|
|
$slugs = [];
|
|
for ($i = 0; $i < 20; $i++) {
|
|
$slugs[] = ShareLink::generateSlug();
|
|
}
|
|
slAssertEq(count($slugs), count(array_unique($slugs)), '20 consecutive slugs are all unique');
|
|
echo "\n";
|
|
|
|
// =========================================================================
|
|
// TEST 3: validateLink — not_found on missing slug
|
|
// =========================================================================
|
|
echo "Test 3: validateLink — not_found on missing slug\n";
|
|
$result = $model->validateLink('NONEXISTENT-SLUG');
|
|
slAssertEq(false, $result['valid'], 'valid=false');
|
|
slAssertEq('not_found', $result['reason'], 'reason=not_found');
|
|
|
|
$result = $model->validateLink(null);
|
|
slAssertEq(false, $result['valid'], 'null slug: valid=false');
|
|
slAssertEq('not_found', $result['reason'], 'null slug: reason=not_found');
|
|
|
|
$result = $model->validateLink('');
|
|
slAssertEq(false, $result['valid'], 'empty slug: valid=false');
|
|
slAssertEq('not_found', $result['reason'], 'empty slug: reason=not_found');
|
|
echo "\n";
|
|
|
|
// =========================================================================
|
|
// TEST 4: validateLink — valid active link with no password
|
|
// =========================================================================
|
|
echo "Test 4: validateLink — link with auto-generated password needs password\n";
|
|
$link = $model->create($adminId, null, null);
|
|
$createdIds[] = $link['id'];
|
|
|
|
$result = $model->validateLink($link['slug']);
|
|
slAssertEq(false, $result['valid'], 'valid=false (has auto-generated password)');
|
|
slAssertEq('needs_password', $result['reason'], 'reason=needs_password');
|
|
slAssert(isset($result['link']), 'link row returned');
|
|
echo "\n";
|
|
|
|
// =========================================================================
|
|
// TEST 5: validateLink — disabled link
|
|
// =========================================================================
|
|
echo "Test 5: validateLink — disabled link\n";
|
|
$model->toggleActive($link['id']); // deactivate
|
|
$result = $model->validateLink($link['slug']);
|
|
slAssertEq(false, $result['valid'], 'valid=false after disable');
|
|
slAssertEq('disabled', $result['reason'], 'reason=disabled');
|
|
$model->toggleActive($link['id']); // restore
|
|
echo "\n";
|
|
|
|
// =========================================================================
|
|
// TEST 6: validateLink — archived link
|
|
// =========================================================================
|
|
echo "Test 6: validateLink — archived link\n";
|
|
$archivedLink = $model->create($adminId, null, null);
|
|
$createdIds[] = $archivedLink['id'];
|
|
$model->archive($archivedLink['id']);
|
|
$result = $model->validateLink($archivedLink['slug']);
|
|
slAssertEq(false, $result['valid'], 'valid=false for archived');
|
|
slAssertEq('archived', $result['reason'], 'reason=archived');
|
|
echo "\n";
|
|
|
|
// =========================================================================
|
|
// TEST 7: validateLink — expired link (needs_password takes priority)
|
|
// =========================================================================
|
|
echo "Test 7: validateLink — expired link with password\n";
|
|
$pastDate = date('Y-m-d H:i:s', strtotime('-1 day'));
|
|
$expiredLink = $model->create($adminId, $pastDate);
|
|
$createdIds[] = $expiredLink['id'];
|
|
$result = $model->validateLink($expiredLink['slug']);
|
|
slAssertEq(false, $result['valid'], 'valid=false');
|
|
slAssertEq('expired', $result['reason'], 'reason=expired');
|
|
echo "\n";
|
|
|
|
// =========================================================================
|
|
// TEST 8: validateLink — needs_password (all links have passwords now)
|
|
// =========================================================================
|
|
echo "Test 8: validateLink — needs_password\n";
|
|
$pwLink = $model->create($adminId, null);
|
|
$createdIds[] = $pwLink['id'];
|
|
$result = $model->validateLink($pwLink['slug']);
|
|
slAssertEq(false, $result['valid'], 'valid=false (needs password)');
|
|
slAssertEq('needs_password', $result['reason'], 'reason=needs_password');
|
|
slAssert(isset($result['link']), 'link row returned even when password needed');
|
|
echo "\n";
|
|
|
|
// =========================================================================
|
|
// TEST 9: verifyPassword — correct auto-generated password
|
|
// =========================================================================
|
|
echo "Test 9: verifyPassword — correct auto-generated password\n";
|
|
$pwLinkRow = $model->findBySlug($pwLink['slug']);
|
|
$plainPassword = $pwLink['_plain_password'] ?? '';
|
|
slAssert($plainPassword !== '', 'auto-generated password is non-empty');
|
|
slAssertEq(true, $model->verifyPassword($pwLinkRow, $plainPassword), 'correct password accepted');
|
|
slAssertEq(false, $model->verifyPassword($pwLinkRow, 'wrongpass'), 'wrong password rejected');
|
|
slAssertEq(false, $model->verifyPassword($pwLinkRow, ''), 'empty password rejected');
|
|
echo "\n";
|
|
|
|
// =========================================================================
|
|
// TEST 10: verifyPassword — any link requires correct password
|
|
// =========================================================================
|
|
echo "Test 10: verifyPassword — wrong password rejected\n";
|
|
$anyLinkRow = $model->findBySlug($link['slug']);
|
|
slAssertEq(false, $model->verifyPassword($anyLinkRow, ''), 'empty string rejected');
|
|
slAssertEq(false, $model->verifyPassword($anyLinkRow, 'anything'), 'random string rejected');
|
|
slAssertEq(true, $model->verifyPassword($anyLinkRow, $link['_plain_password'] ?? ''), 'correct password accepted');
|
|
echo "\n";
|
|
|
|
// =========================================================================
|
|
// TEST 12: incrementUsage — counter goes up
|
|
// =========================================================================
|
|
echo "Test 12: incrementUsage — counter increments\n";
|
|
$fresh = $model->findById($link['id']);
|
|
$before = (int)$fresh['usage_count'];
|
|
$model->incrementUsage($link['id']);
|
|
$model->incrementUsage($link['id']);
|
|
$after = (int)($model->findById($link['id'])['usage_count'] ?? 0);
|
|
slAssertEq($before + 2, $after, 'usage_count incremented by 2');
|
|
echo "\n";
|
|
|
|
// =========================================================================
|
|
// TEST 13: objet_restriction is stored and returned
|
|
// =========================================================================
|
|
echo "Test 13: objet_restriction stored correctly\n";
|
|
$restrictedLink = $model->create($adminId, null, null, 'tfe');
|
|
$createdIds[] = $restrictedLink['id'];
|
|
slAssertEq('tfe', $restrictedLink['objet_restriction'], 'objet_restriction=tfe stored');
|
|
|
|
$anyLink = $model->create($adminId, null, null, 'invalid_value');
|
|
$createdIds[] = $anyLink['id'];
|
|
slAssertEq('tfe', $anyLink['objet_restriction'], 'invalid objet_restriction defaults to tfe');
|
|
echo "\n";
|
|
|
|
echo "✅ All ShareLink tests passed!\n";
|
|
$result = true;
|
|
|
|
} catch (Exception $e) {
|
|
echo '❌ FAIL: ' . $e->getMessage() . "\n";
|
|
$result = false;
|
|
} finally {
|
|
$pdo = $db->getConnection();
|
|
foreach ($createdIds as $id) {
|
|
try {
|
|
$pdo->prepare('DELETE FROM share_links WHERE id = ?')->execute([$id]);
|
|
} catch (Exception $e) { /* ignore */
|
|
}
|
|
}
|
|
}
|
|
|
|
return $result ?? false;
|