Initial commit: AutoBlogger v0.1.0

WordPress plugin that auto-researches topics from Reddit, generates
SEO-optimized articles via OpenRouter LLMs, runs them through a chief
editor agent, and publishes on a daily schedule.

Includes:
- Full generation pipeline (collect → analyze → score → generate → review → publish)
- Cost tracking and budget enforcement
- Admin UI: settings, review queue, metrics, activity log
- CI/CD pipeline (GitHub Actions → WordPress REST deploy)
- Unit test suite (PHPUnit + Brain Monkey)
- Structured logging with configurable verbosity

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Terence Ong
2026-04-10 16:10:27 +07:00
commit 68c6f67bcf
65 changed files with 8833 additions and 0 deletions

View File

@@ -0,0 +1,86 @@
<?php
declare(strict_types=1);
/**
* Unit tests for Autoblogger_Editorial_Review value object.
*
* The editorial review verdict drives the publish/draft/reject decision.
* A null revised_content on a "revised" verdict caused a bug (now fixed) —
* these tests ensure the value object correctly handles edge cases.
*/
class EditorialReviewTest extends \PHPUnit\Framework\TestCase {
public function test_approved_review(): void {
$review = new Autoblogger_Editorial_Review( [
'verdict' => 'approved',
'notes' => 'Good article, ready to publish.',
'quality_score' => 0.85,
'seo_score' => 0.78,
'issues' => [],
] );
$this->assertSame( 'approved', $review->get_verdict() );
$this->assertSame( 'Good article, ready to publish.', $review->get_notes() );
$this->assertNull( $review->get_revised_content() );
$this->assertEqualsWithDelta( 0.85, $review->get_quality_score(), 0.01 );
$this->assertEqualsWithDelta( 0.78, $review->get_seo_score(), 0.01 );
$this->assertEmpty( $review->get_issues() );
}
public function test_revised_review_with_content(): void {
$review = new Autoblogger_Editorial_Review( [
'verdict' => 'revised',
'notes' => 'Fixed heading structure.',
'revised_content' => '<h1>Improved Article</h1><p>Better content.</p>',
'quality_score' => 0.80,
'seo_score' => 0.90,
'issues' => [ 'H2 tags were missing' ],
] );
$this->assertSame( 'revised', $review->get_verdict() );
$this->assertNotNull( $review->get_revised_content() );
$this->assertStringContainsString( 'Improved Article', $review->get_revised_content() );
$this->assertCount( 1, $review->get_issues() );
}
/**
* Edge case: "revised" verdict but null revised_content.
* This happened in production when the LLM returned a malformed response.
* The pipeline handles this with a null-coalesce fallback.
*/
public function test_revised_verdict_with_null_content(): void {
$review = new Autoblogger_Editorial_Review( [
'verdict' => 'revised',
'notes' => 'Supposedly revised but no content provided.',
'revised_content' => null,
] );
$this->assertSame( 'revised', $review->get_verdict() );
$this->assertNull( $review->get_revised_content() );
}
public function test_rejected_review(): void {
$review = new Autoblogger_Editorial_Review( [
'verdict' => 'rejected',
'notes' => 'Off-topic and factually inaccurate.',
'quality_score' => 0.2,
'seo_score' => 0.1,
'issues' => [ 'Off-topic', 'Factual errors', 'Poor structure' ],
] );
$this->assertSame( 'rejected', $review->get_verdict() );
$this->assertCount( 3, $review->get_issues() );
$this->assertLessThan( 0.5, $review->get_quality_score() );
}
public function test_default_scores_are_zero(): void {
$review = new Autoblogger_Editorial_Review( [
'verdict' => 'rejected',
'notes' => 'Minimal data.',
] );
$this->assertEqualsWithDelta( 0.0, $review->get_quality_score(), 0.01 );
$this->assertEqualsWithDelta( 0.0, $review->get_seo_score(), 0.01 );
$this->assertEmpty( $review->get_issues() );
}
}