= 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 — valid active link\n"; $link = $model->create($adminId, null, null); $createdIds[] = $link['id']; $result = $model->validateLink($link['slug']); slAssertEq(true, $result['valid'], 'valid=true'); 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 // ========================================================================= echo "Test 7: validateLink — expired link\n"; $pastDate = date('Y-m-d H:i:s', strtotime('-1 day')); $expiredLink = $model->create($adminId, null, $pastDate); $createdIds[] = $expiredLink['id']; $result = $model->validateLink($expiredLink['slug']); slAssertEq(false, $result['valid'], 'valid=false for expired'); slAssertEq('expired', $result['reason'], 'reason=expired'); echo "\n"; // ========================================================================= // TEST 8: validateLink — not expired (future date) // ========================================================================= echo "Test 8: validateLink — future expiry is still valid\n"; $futureDate = date('Y-m-d H:i:s', strtotime('+30 days')); $futureLink = $model->create($adminId, null, $futureDate); $createdIds[] = $futureLink['id']; $result = $model->validateLink($futureLink['slug']); slAssertEq(true, $result['valid'], 'valid=true for future expiry'); echo "\n"; // ========================================================================= // TEST 9: validateLink — needs_password when password is set // ========================================================================= echo "Test 9: validateLink — needs_password\n"; $pwLink = $model->create($adminId, 'secret123', 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 10: verifyPassword — correct password // ========================================================================= echo "Test 10: verifyPassword — correct password\n"; $pwLinkRow = $model->findBySlug($pwLink['slug']); slAssertEq(true, $model->verifyPassword($pwLinkRow, 'secret123'), 'correct password accepted'); slAssertEq(false, $model->verifyPassword($pwLinkRow, 'wrongpass'), 'wrong password rejected'); slAssertEq(false, $model->verifyPassword($pwLinkRow, ''), 'empty password rejected'); echo "\n"; // ========================================================================= // TEST 11: verifyPassword — link with no password always passes // ========================================================================= echo "Test 11: verifyPassword — no password set always returns true\n"; $noPwRow = $model->findBySlug($link['slug']); slAssertEq(true, $model->verifyPassword($noPwRow, ''), 'no-pw link: empty string passes'); slAssertEq(true, $model->verifyPassword($noPwRow, 'anything'), 'no-pw link: any string passes'); 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;