Update framework testing infrastructure and standards

- Add phpt test runner and suite for app functions
- Introduce testing workflow to AGENT.md
- Add tests for cache, content, context, helpers, hooks, plugins,
  rendering
- Mount tests directory in dev container
This commit is contained in:
Ruben 2026-03-17 12:51:07 +01:00
parent 33943a907b
commit 449e6f8e03
22 changed files with 909 additions and 7 deletions

View file

@ -0,0 +1,45 @@
--TEST--
extractMetaDescription: returns description from metadata fields or first content paragraph
--FILE--
<?php
require '/var/www/app/constants.php';
require '/var/www/app/hooks.php';
require '/var/www/app/content.php';
require '/var/www/app/helpers.php';
$dir = sys_get_temp_dir() . '/phpt_metadesc_' . getmypid();
mkdir($dir);
// search_description takes priority
echo extractMetaDescription($dir, ['search_description' => 'SEO description', 'summary' => 'Summary']) . "\n";
// summary is fallback when no search_description
echo extractMetaDescription($dir, ['summary' => 'Page summary']) . "\n";
// null metadata, no content files
echo (extractMetaDescription($dir, null) ?? 'null') . "\n";
// First paragraph from markdown (skips headings and short lines)
file_put_contents("$dir/index.md", "# Title\n\nThis is the opening paragraph of the post.\n\nSecond paragraph.");
echo extractMetaDescription($dir, null) . "\n";
unlink("$dir/index.md");
// Heading-only markdown with no usable paragraph
file_put_contents("$dir/index.md", "# Just a Heading\n\nShort.\n");
echo (extractMetaDescription($dir, null) ?? 'null') . "\n";
unlink("$dir/index.md");
// First <p> from HTML
file_put_contents("$dir/index.html", "<h1>Title</h1><p>HTML paragraph text here.</p>");
echo extractMetaDescription($dir, null) . "\n";
unlink("$dir/index.html");
rmdir($dir);
?>
--EXPECT--
SEO description
Page summary
null
This is the opening paragraph of the post.
null
HTML paragraph text here.

View file

@ -0,0 +1,20 @@
--TEST--
extractRawDateFromFolder: extracts YYYY-MM-DD from date-prefixed folder names
--FILE--
<?php
require '/var/www/app/helpers.php';
echo extractRawDateFromFolder('2024-01-15-my-post') . "\n";
echo extractRawDateFromFolder('2024-12-31-year-end') . "\n";
echo (extractRawDateFromFolder('no-date-here') ?? 'null') . "\n";
echo (extractRawDateFromFolder('') ?? 'null') . "\n";
echo (extractRawDateFromFolder('2024-1-1-bad-format') ?? 'null') . "\n";
echo (extractRawDateFromFolder('20240115-no-separators') ?? 'null') . "\n";
?>
--EXPECT--
2024-01-15
2024-12-31
null
null
null
null

View file

@ -0,0 +1,49 @@
--TEST--
extractTitle: extracts title from # heading in .md or <h1> in .html
--FILE--
<?php
require '/var/www/app/constants.php';
require '/var/www/app/hooks.php';
require '/var/www/app/content.php';
require '/var/www/app/helpers.php';
$dir = sys_get_temp_dir() . '/phpt_title_' . getmypid();
mkdir($dir);
// No content files
echo (extractTitle($dir) ?? 'null') . "\n";
// Markdown with # heading
file_put_contents("$dir/index.md", "# My Great Post\n\nSome body text.");
echo extractTitle($dir) . "\n";
unlink("$dir/index.md");
// Markdown heading with extra whitespace
file_put_contents("$dir/index.md", "# Spaced Title \n\nBody.");
echo extractTitle($dir) . "\n";
unlink("$dir/index.md");
// HTML with <h1>
file_put_contents("$dir/index.html", "<h1>HTML Title</h1><p>Body</p>");
echo extractTitle($dir) . "\n";
unlink("$dir/index.html");
// HTML with attributes on h1
file_put_contents("$dir/index.html", '<h1 class="main">Styled Title</h1>');
echo extractTitle($dir) . "\n";
unlink("$dir/index.html");
// Markdown without heading
file_put_contents("$dir/index.md", "Just a paragraph, no heading.");
echo (extractTitle($dir) ?? 'null') . "\n";
unlink("$dir/index.md");
rmdir($dir);
?>
--EXPECT--
null
My Great Post
Spaced Title
HTML Title
Styled Title
null

View file

@ -0,0 +1,33 @@
--TEST--
findCoverImage: finds cover.{ext} in directory, prefers first matching extension
--FILE--
<?php
require '/var/www/app/constants.php';
require '/var/www/app/helpers.php';
$dir = sys_get_temp_dir() . '/phpt_cover_' . getmypid();
mkdir($dir);
// No cover image
echo (findCoverImage($dir) ?? 'null') . "\n";
// cover.jpg
touch("$dir/cover.jpg");
echo findCoverImage($dir) . "\n";
// cover.webp added — jpg should still win (it's first in COVER_IMAGE_EXTENSIONS)
touch("$dir/cover.webp");
echo findCoverImage($dir) . "\n";
// Remove jpg — webp should now be found
unlink("$dir/cover.jpg");
echo findCoverImage($dir) . "\n";
array_map('unlink', glob("$dir/*"));
rmdir($dir);
?>
--EXPECT--
null
cover.jpg
cover.jpg
cover.webp

View file

@ -0,0 +1,42 @@
--TEST--
findPageCss: returns URL and hash for styles.css, null when absent
--FILE--
<?php
require '/var/www/app/helpers.php';
$contentDir = sys_get_temp_dir() . '/phpt_css_' . getmypid();
$pageDir = "$contentDir/blog/post";
mkdir($pageDir, 0777, true);
// No styles.css
echo (findPageCss($pageDir, $contentDir) ?? 'null') . "\n";
// Add styles.css
file_put_contents("$pageDir/styles.css", 'body { color: red; }');
$result = findPageCss($pageDir, $contentDir);
echo $result['url'] . "\n";
echo ($result['hash'] === md5_file("$pageDir/styles.css") ? 'hash ok' : 'hash mismatch') . "\n";
// Root-level page (no subpath)
$rootCss = "$contentDir/styles.css";
file_put_contents($rootCss, 'body {}');
$result = findPageCss($contentDir, $contentDir);
echo $result['url'] . "\n";
// Directory named styles.css is ignored
unlink("$pageDir/styles.css");
mkdir("$pageDir/styles.css");
echo (findPageCss($pageDir, $contentDir) ?? 'null') . "\n";
rmdir("$pageDir/styles.css");
unlink($rootCss);
rmdir($pageDir);
rmdir("$contentDir/blog");
rmdir($contentDir);
?>
--EXPECT--
null
/blog/post/styles.css
hash ok
/styles.css
null

View file

@ -0,0 +1,36 @@
--TEST--
findPageJs: returns URL and hash for script.js, null when absent
--FILE--
<?php
require '/var/www/app/helpers.php';
$contentDir = sys_get_temp_dir() . '/phpt_js_' . getmypid();
$pageDir = "$contentDir/section/page";
mkdir($pageDir, 0777, true);
// No script.js
echo (findPageJs($pageDir, $contentDir) ?? 'null') . "\n";
// Add script.js
file_put_contents("$pageDir/script.js", 'console.log("hi");');
$result = findPageJs($pageDir, $contentDir);
echo $result['url'] . "\n";
echo ($result['hash'] === md5_file("$pageDir/script.js") ? 'hash ok' : 'hash mismatch') . "\n";
// Root-level page (no subpath)
$rootJs = "$contentDir/script.js";
file_put_contents($rootJs, '');
$result = findPageJs($contentDir, $contentDir);
echo $result['url'] . "\n";
unlink("$pageDir/script.js");
unlink($rootJs);
rmdir($pageDir);
rmdir("$contentDir/section");
rmdir($contentDir);
?>
--EXPECT--
null
/section/page/script.js
hash ok
/script.js

View file

@ -0,0 +1,28 @@
--TEST--
findPdfFile: finds first .pdf file in a directory
--FILE--
<?php
require '/var/www/app/helpers.php';
$dir = sys_get_temp_dir() . '/phpt_pdf_' . getmypid();
mkdir($dir);
// No PDF
echo (findPdfFile($dir) ?? 'null') . "\n";
// One PDF
touch("$dir/document.pdf");
echo findPdfFile($dir) . "\n";
// Non-PDF files are ignored
touch("$dir/image.jpg");
touch("$dir/notes.txt");
echo findPdfFile($dir) . "\n";
array_map('unlink', glob("$dir/*"));
rmdir($dir);
?>
--EXPECT--
null
document.pdf
document.pdf

View file

@ -0,0 +1,39 @@
--TEST--
getSubdirectories: returns only subdirectory names, ignores files
--FILE--
<?php
require '/var/www/app/helpers.php';
$dir = sys_get_temp_dir() . '/phpt_subdirs_' . getmypid();
mkdir($dir);
// Empty directory
$result = getSubdirectories($dir);
echo (empty($result) ? 'empty' : 'not empty') . "\n";
// Non-existent directory
echo count(getSubdirectories($dir . '/nope')) . "\n";
// Add subdirectories and a file
mkdir("$dir/alpha");
mkdir("$dir/beta");
touch("$dir/file.txt");
$result = array_values(getSubdirectories($dir));
sort($result);
echo implode("\n", $result) . "\n";
// Files are not included
echo in_array('file.txt', $result) ? "file included\n" : "file excluded\n";
unlink("$dir/file.txt");
rmdir("$dir/alpha");
rmdir("$dir/beta");
rmdir($dir);
?>
--EXPECT--
empty
0
alpha
beta
file excluded