Files
peptide-news-plugin/tests/unit/FetcherTest.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

82 lines
2.6 KiB
PHP
Executable File

<?php
/**
* Unit tests for Peptide_News_Fetcher.
*
* @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-fetcher.php';
class FetcherTest extends TestCase {
/**
* @var \Peptide_News_Fetcher
*/
private $fetcher;
protected function setUp(): void {
$this->fetcher = new \Peptide_News_Fetcher();
}
/**
* add_custom_cron_schedules() should add the expected intervals.
*/
public function test_add_custom_cron_schedules_adds_intervals(): void {
$existing = array(
'hourly' => array(
'interval' => 3600,
'display' => 'Once Hourly',
),
);
$result = $this->fetcher->add_custom_cron_schedules( $existing );
// Original schedule must be preserved.
$this->assertArrayHasKey( 'hourly', $result );
// Custom schedules must be added.
$this->assertArrayHasKey( 'every_fifteen_minutes', $result );
$this->assertArrayHasKey( 'every_thirty_minutes', $result );
$this->assertArrayHasKey( 'every_four_hours', $result );
$this->assertArrayHasKey( 'every_six_hours', $result );
}
/**
* Custom intervals should have correct durations.
*/
public function test_cron_schedule_intervals_are_correct(): void {
$result = $this->fetcher->add_custom_cron_schedules( array() );
$this->assertEquals( 900, $result['every_fifteen_minutes']['interval'] );
$this->assertEquals( 1800, $result['every_thirty_minutes']['interval'] );
$this->assertEquals( 14400, $result['every_four_hours']['interval'] );
$this->assertEquals( 21600, $result['every_six_hours']['interval'] );
}
/**
* Custom schedules should have display names.
*/
public function test_cron_schedules_have_display_names(): void {
$result = $this->fetcher->add_custom_cron_schedules( array() );
foreach ( $result as $key => $schedule ) {
$this->assertArrayHasKey( 'display', $schedule, "Schedule '{$key}' missing 'display' key." );
$this->assertNotEmpty( $schedule['display'], "Schedule '{$key}' has empty display name." );
}
}
/**
* Passing an empty array should still return all custom schedules.
*/
public function test_cron_schedules_work_with_empty_input(): void {
$result = $this->fetcher->add_custom_cron_schedules( array() );
$this->assertCount( 4, $result );
}
}