mirror of
https://ghproxy.net/https://github.com/fairpm/fair-plugin.git
synced 2026-08-10 13:50:27 +08:00
Six items from the testing plan, now done: 1. install-activate-update.spec.ts (@slow) — full install/activate/update flow via Direct Install tab. Mock server extended with hello-dolly DID fixtures (did-doc + metadata-doc) and zip artifact serving route at /artifacts/*.zip. 2. avatar-upload.spec.ts — profile page rendering, avatar section presence, display name accessibility. Avatar image verification handles Gravatar rate-limiting (at least one loaded = pass). 3. update-error-row.spec.ts — plugins page structure, FAIR error row detection, no JS errors on plugins page. 4. SignatureVerificationIntegrationTest.php — 5 tests running inside Docker WP container. Covers verify_file_signature() (WP 5.2+) with live sodium Ed25519 keys, tampered file rejection, missing-key rejection, wrong-signature rejection, key encoding round-trip. NB: WP 6.4 uses SHA-384 (not SHA-512) for file hashing. 5. Coverage reporting in CI — added dedicated 'coverage' job to phpunit-tests.yml. Runs on push to main/development/release only, uses PHP 8.4 + XDebug, uploads HTML coverage artifact. 6. minMsi threshold — set minMsi=9 (current baseline), minCoveredMsi=90. Updated mock server: - /artifacts/<file.zip> route serves zips from tests/fixtures/zips/ - hello-dolly DID/meta fixtures for browser install flow Updated integration bootstrap: - require wp-admin/includes/file.php for verify_file_signature() Totals: 262 unit (489 assertions), 17 integration (48), 12 HTTP (38), 23 browser (non-slow), 3 browser (@slow). Exit code 0. Signed-off-by: Chuck Adams <chaz@chaz.works>
189 lines
6.1 KiB
PHP
189 lines
6.1 KiB
PHP
<?php
|
|
/**
|
|
* Mock DID / FAIR Repository Server for Integration Tests.
|
|
*
|
|
* Emulates PLC Directory and FAIR Repository APIs using fixture JSON.
|
|
* Routes by path only (Host header ignored). Logs all requests to
|
|
* a file so integration tests can assert on HTTP interactions.
|
|
*
|
|
* GET /did:plc:{identifier} → DID document
|
|
* GET /metadata/{did} → Metadata document
|
|
* GET /{did}/metadata → Metadata document (alt format)
|
|
* GET /health → 200 OK + fixture count
|
|
* GET /log → Request log (file-based, persistent)
|
|
* POST /log/reset → Clear request log
|
|
*
|
|
* Usage: php -S 0.0.0.0:8080 index.php
|
|
*
|
|
* @package FAIR
|
|
*/
|
|
|
|
declare( strict_types = 1 );
|
|
|
|
// ── Configuration ──────────────────────────────────────────────────
|
|
$FIXTURES_DIR = __DIR__ . '/fixtures';
|
|
$LOG_FILE = sys_get_temp_dir() . '/fair-mock-server-log.jsonl';
|
|
|
|
// ── Fixtures ───────────────────────────────────────────────────────
|
|
function load_fixtures( string $dir ): array {
|
|
$fixtures = [];
|
|
$files = glob( "{$dir}/*.json" );
|
|
if ( $files === false ) {
|
|
return $fixtures;
|
|
}
|
|
foreach ( $files as $file ) {
|
|
$fixtures[ basename( $file, '.json' ) ] = file_get_contents( $file );
|
|
}
|
|
return $fixtures;
|
|
}
|
|
|
|
$FIXTURES = load_fixtures( $FIXTURES_DIR );
|
|
|
|
$DID_MAP = [
|
|
'did:plc:z72i7hdynmk6r22z27h6tvur' => 'did-doc-integration',
|
|
'did:plc:no-services' => 'did-doc-no-services-integration',
|
|
'did:plc:ppicmk23c5pimdivve34bcp2' => 'did-doc-valid',
|
|
'did:plc:alias-valid' => 'did-doc-alias-valid',
|
|
'did:plc:alias-invalid-domain' => 'did-doc-alias-invalid-domain',
|
|
'did:plc:no-keys' => 'did-doc-no-keys',
|
|
'did:plc:hellodolly000000000000001' => 'did-doc-hello-dolly',
|
|
];
|
|
|
|
$META_MAP = [
|
|
'did:plc:z72i7hdynmk6r22z27h6tvur' => 'metadata-doc-full',
|
|
'did:plc:minimal12345678901234567890' => 'metadata-doc-minimal',
|
|
'did:plc:hellodolly000000000000001' => 'metadata-doc-hello-dolly',
|
|
];
|
|
|
|
// ── File-based logging ─────────────────────────────────────────────
|
|
|
|
function log_entry( string $method, string $uri, int $code ): void {
|
|
$entry = json_encode( [
|
|
'time' => gmdate( 'c' ),
|
|
'method' => $method,
|
|
'uri' => $uri,
|
|
'code' => $code,
|
|
] );
|
|
@file_put_contents( $GLOBALS['LOG_FILE'], $entry . "\n", FILE_APPEND | LOCK_EX );
|
|
}
|
|
|
|
function read_log(): array {
|
|
if ( ! file_exists( $GLOBALS['LOG_FILE'] ) ) {
|
|
return [];
|
|
}
|
|
$lines = file( $GLOBALS['LOG_FILE'], FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES );
|
|
if ( $lines === false ) {
|
|
return [];
|
|
}
|
|
return array_map( 'json_decode', $lines );
|
|
}
|
|
|
|
function reset_log(): void {
|
|
@unlink( $GLOBALS['LOG_FILE'] );
|
|
}
|
|
|
|
// ── Routing ────────────────────────────────────────────────────────
|
|
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
|
|
$uri = rtrim( parse_url( $_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH ), '/' );
|
|
|
|
// Health.
|
|
if ( $uri === '/health' ) {
|
|
http_response_code( 200 );
|
|
header( 'Content-Type: application/json' );
|
|
echo json_encode( [ 'status' => 'ok', 'fixtures' => count( $FIXTURES ) ] );
|
|
log_entry( $method, $uri, 200 );
|
|
exit;
|
|
}
|
|
|
|
// Log read.
|
|
if ( $uri === '/log' && $method === 'GET' ) {
|
|
http_response_code( 200 );
|
|
header( 'Content-Type: application/json' );
|
|
echo json_encode( read_log(), JSON_PRETTY_PRINT );
|
|
exit;
|
|
}
|
|
|
|
// Log reset.
|
|
if ( $uri === '/log/reset' && $method === 'POST' ) {
|
|
reset_log();
|
|
http_response_code( 200 );
|
|
header( 'Content-Type: application/json' );
|
|
echo json_encode( [ 'status' => 'reset' ] );
|
|
exit;
|
|
}
|
|
|
|
// DID document (PLC Directory).
|
|
if ( preg_match( '#^/(did:plc:[a-z0-9]+)(/.*)?$#', $uri, $m ) ) {
|
|
$did = $m[1];
|
|
$sub = $m[2] ?? '';
|
|
|
|
if ( $sub === '/log/audit' || $sub === '/log/last' || $sub === '/log' ) {
|
|
http_response_code( 200 );
|
|
header( 'Content-Type: application/json' );
|
|
echo '[]';
|
|
log_entry( $method, $uri, 200 );
|
|
exit;
|
|
}
|
|
|
|
if ( isset( $DID_MAP[ $did ] ) ) {
|
|
http_response_code( 200 );
|
|
header( 'Content-Type: application/json' );
|
|
echo $FIXTURES[ $DID_MAP[ $did ] ] ?? '{}';
|
|
log_entry( $method, $uri, 200 );
|
|
exit;
|
|
}
|
|
|
|
http_response_code( 404 );
|
|
header( 'Content-Type: application/json' );
|
|
echo json_encode( [ 'error' => 'DID not found' ] );
|
|
log_entry( $method, $uri, 404 );
|
|
exit;
|
|
}
|
|
|
|
// Metadata document (FAIR Repository).
|
|
$did = null;
|
|
if ( preg_match( '#^/metadata/(did:plc:[a-z0-9]+)$#', $uri, $m ) ) {
|
|
$did = $m[1];
|
|
} elseif ( preg_match( '#^/(did:plc:[a-z0-9]+)/metadata$#', $uri, $m ) ) {
|
|
$did = $m[1];
|
|
}
|
|
|
|
if ( $did !== null ) {
|
|
if ( isset( $META_MAP[ $did ] ) ) {
|
|
http_response_code( 200 );
|
|
header( 'Content-Type: application/json+fair' );
|
|
echo $FIXTURES[ $META_MAP[ $did ] ] ?? '{}';
|
|
log_entry( $method, $uri, 200 );
|
|
exit;
|
|
}
|
|
|
|
http_response_code( 404 );
|
|
header( 'Content-Type: application/json' );
|
|
echo json_encode( [ 'error' => 'Metadata not found' ] );
|
|
log_entry( $method, $uri, 404 );
|
|
exit;
|
|
}
|
|
|
|
// Zip artifact serving (so browser tests can install packages).
|
|
if ( preg_match( '#^/artifacts/(.+\.zip)$#', $uri, $m ) ) {
|
|
$zip_file = __DIR__ . '/fixtures/zips/' . basename( $m[1] );
|
|
if ( file_exists( $zip_file ) ) {
|
|
http_response_code( 200 );
|
|
header( 'Content-Type: application/zip' );
|
|
header( 'Content-Length: ' . filesize( $zip_file ) );
|
|
readfile( $zip_file );
|
|
log_entry( $method, $uri, 200 );
|
|
exit;
|
|
}
|
|
http_response_code( 404 );
|
|
header( 'Content-Type: application/json' );
|
|
echo json_encode( [ 'error' => 'Artifact not found' ] );
|
|
log_entry( $method, $uri, 404 );
|
|
exit;
|
|
}
|
|
|
|
// Fallback.
|
|
http_response_code( 404 );
|
|
header( 'Content-Type: application/json' );
|
|
echo json_encode( [ 'error' => 'Not found', 'uri' => $uri ] );
|
|
log_entry( $method, $uri, 404 );
|