Files
moko-platform/tests/Unit/CliFrameworkTest.php
T
Jonathan Miller ae2860c3b5
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: moko-platform CI / CI Summary (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: moko-platform CI / CI Summary (push) Blocked by required conditions
Generic: Repo Health / Release configuration (push) Blocked by required conditions
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Successful in 6s
Generic: Repo Health / Access control (push) Successful in 9s
Universal: PR Check / Validate PR (pull_request) Failing after 10s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 22s
Universal: Auto Version Bump / Version Bump (push) Failing after 23s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 1m13s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 1m17s
chore(release): bump to 09.22.00 — CliFramework migration
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-31 12:14:34 -05:00

168 lines
5.8 KiB
PHP

<?php
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
declare(strict_types=1);
namespace MokoStandards\Tests\Unit;
use MokoEnterprise\CliFramework;
use PHPUnit\Framework\TestCase;
/**
* Unit tests for CliFramework base class.
*
* @covers \MokoEnterprise\CliFramework
*/
class CliFrameworkTest extends TestCase
{
// ── Exit code constants ──────────────────────────────────────────────
public function testExitCodeConstants(): void
{
$this->assertSame(0, CliFramework::EXIT_SUCCESS);
$this->assertSame(1, CliFramework::EXIT_FAILURE);
$this->assertSame(2, CliFramework::EXIT_USAGE);
$this->assertSame(3, CliFramework::EXIT_NOT_FOUND);
$this->assertSame(4, CliFramework::EXIT_PERMISSION);
}
// ── JSON output ─────────────────────────────────────────────────────
public function testJsonOutputProducesValidJson(): void
{
$tool = $this->createTool(function ($self) {
return $self->jsonOutput('pass', ['key' => 'value'], [], ['soft warning']);
});
$output = $this->captureOutput($tool);
$decoded = json_decode($output, true);
$this->assertNotNull($decoded, 'jsonOutput must produce valid JSON');
$this->assertSame('pass', $decoded['status']);
$this->assertSame(0, $decoded['exit_code']);
$this->assertSame(['key' => 'value'], $decoded['data']);
$this->assertSame([], $decoded['errors']);
$this->assertSame(['soft warning'], $decoded['warnings']);
$this->assertArrayHasKey('duration_ms', $decoded['metadata']);
$this->assertArrayHasKey('timestamp', $decoded['metadata']);
}
public function testJsonOutputFailStatus(): void
{
$tool = $this->createTool(function ($self) {
return $self->jsonOutput('fail', null, ['something broke']);
});
$output = $this->captureOutput($tool);
$decoded = json_decode($output, true);
$this->assertSame('fail', $decoded['status']);
$this->assertSame(1, $decoded['exit_code']);
$this->assertSame(['something broke'], $decoded['errors']);
}
public function testJsonOutputCustomExitCode(): void
{
$tool = $this->createTool(function ($self) {
return $self->jsonOutput('error', null, ['not found'], [], 3);
});
$output = $this->captureOutput($tool);
$decoded = json_decode($output, true);
$this->assertSame(3, $decoded['exit_code']);
}
// ── Table rendering ─────────────────────────────────────────────────
public function testTableRendersHeadersAndRows(): void
{
$tool = $this->createTool(function ($self) {
$self->table(['Name', 'Status'], [
['foo', 'ok'],
['bar', 'fail'],
]);
return 0;
});
// Table output is suppressed by --quiet, so run without it.
$_SERVER['argv'] = ['test', '--no-color'];
ob_start();
$tool->execute();
$output = ob_get_clean() ?: '';
$this->assertStringContainsString('Name', $output);
$this->assertStringContainsString('Status', $output);
$this->assertStringContainsString('foo', $output);
$this->assertStringContainsString('bar', $output);
$this->assertStringContainsString('+', $output); // separator
}
// ── Helpers ──────────────────────────────────────────────────────────
/**
* Create a testable CliFramework subclass with a custom run() callback.
*/
private function createTool(callable $runCallback): CliFramework
{
return new class ($runCallback) extends CliFramework {
private $callback;
public function __construct(callable $callback)
{
$this->callback = $callback;
parent::__construct('test-tool', '1.0.0');
}
protected function configure(): void
{
$this->setDescription('Test tool');
}
protected function run(): int
{
return ($this->callback)($this);
}
// Expose protected methods for testing.
public function jsonOutput(
string $status,
mixed $data = null,
array $errors = [],
array $warnings = [],
int $exitCode = -1
): int {
return parent::jsonOutput($status, $data, $errors, $warnings, $exitCode);
}
public function table(array $headers, array $rows): void
{
parent::table($headers, $rows);
}
};
}
/**
* Capture stdout from a tool's run method via execute().
*
* The banner and other output is included, so JSON tests should
* extract the JSON portion (last complete JSON object) from the output.
*/
private function captureOutput(CliFramework $tool): string
{
// Suppress banner by simulating --quiet, but jsonOutput still writes.
$_SERVER['argv'] = ['test', '--quiet'];
ob_start();
$tool->execute();
$output = ob_get_clean() ?: '';
// Extract JSON object from output (may have log lines before it).
if (preg_match('/\{[\s\S]*\}\s*$/m', $output, $m)) {
return $m[0];
}
return $output;
}
}