Files
peptide-news-plugin/tests/unit/RestApiTest.php
Terence b7da4f42b9 feat: add LLM cost tracking with budget enforcement and admin dashboard
- New Cost_Tracker class: logs every API call (model, tokens, cost), enforces
  monthly budgets with hard-stop mode, provides aggregated reporting
- New llm_costs database table with indexes on created_at, model, operation
- Modified LLM class: call_openrouter_with_usage() extracts token usage from
  OpenRouter responses; budget gate checks before each API call
- Admin settings: monthly budget (USD), enforcement mode (disabled/warn/hard-stop),
  cost log retention period
- Admin cost dashboard: budget progress bar, daily cost chart (Chart.js),
  per-model and per-operation breakdowns, recent API call log
- PHPUnit tests: 16 tests covering cost calculation, pricing lookups,
  budget enforcement logic, and model validation
- Updated ARCHITECTURE.md with new table schema, data flow, and key decisions
- Updated CONVENTIONS.md with "how to add cost tracking for a new operation"
- Version bump to 2.4.0

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 04:18:30 +07:00

103 lines
3.0 KiB
PHP
Executable File

<?php
/**
* Unit tests for Peptide_News_Rest_API.
*
* @package PeptideNews\Tests
*/
namespace PeptideNews\Tests\Unit;
use PHPUnit\Framework\TestCase;
// Load the class under test.
require_once PEPTIDE_NEWS_PLUGIN_DIR . 'includes/class-peptide-news-rest-api.php';
class RestApiTest extends TestCase {
/**
* @var \Peptide_News_Rest_API
*/
private $api;
protected function setUp(): void {
$this->api = new \Peptide_News_Rest_API();
}
/**
* validate_date_format() should accept valid Y-m-d dates.
*/
public function test_validate_date_accepts_valid_format(): void {
$request = new \WP_REST_Request();
$result = $this->api->validate_date_format( '2026-01-15', $request, 'start_date' );
$this->assertTrue( $result );
}
/**
* validate_date_format() should accept today's date.
*/
public function test_validate_date_accepts_today(): void {
$request = new \WP_REST_Request();
$result = $this->api->validate_date_format( date( 'Y-m-d' ), $request, 'end_date' );
$this->assertTrue( $result );
}
/**
* validate_date_format() should reject invalid formats.
*/
public function test_validate_date_rejects_wrong_format(): void {
$request = new \WP_REST_Request();
$result = $this->api->validate_date_format( '01-15-2026', $request, 'start_date' );
$this->assertInstanceOf( \WP_Error::class, $result );
}
/**
* validate_date_format() should reject empty strings.
*/
public function test_validate_date_rejects_empty(): void {
$request = new \WP_REST_Request();
$result = $this->api->validate_date_format( '', $request, 'start_date' );
$this->assertInstanceOf( \WP_Error::class, $result );
}
/**
* validate_date_format() should reject non-date strings.
*/
public function test_validate_date_rejects_garbage(): void {
$request = new \WP_REST_Request();
$result = $this->api->validate_date_format( 'not-a-date', $request, 'start_date' );
$this->assertInstanceOf( \WP_Error::class, $result );
}
/**
* validate_date_format() should reject dates with slashes.
*/
public function test_validate_date_rejects_slashes(): void {
$request = new \WP_REST_Request();
$result = $this->api->validate_date_format( '2026/01/15', $request, 'start_date' );
$this->assertInstanceOf( \WP_Error::class, $result );
}
/**
* check_admin_permissions() should return WP_Error when user lacks capability.
*/
public function test_check_admin_permissions_denies_non_admin(): void {
// Stub current_user_can to return false.
if ( ! function_exists( 'current_user_can' ) ) {
function current_user_can() {
return false;
}
}
$request = new \WP_REST_Request();
$result = $this->api->check_admin_permissions( $request );
$this->assertInstanceOf( \WP_Error::class, $result );
}
}