Compare commits

..

2 commits

Author SHA1 Message Date
d17d98a5ec 152 - Freeze time in the publish job tests and cover the midnight boundary
All checks were successful
CI / ci (push) Successful in 11m24s
2026-08-15 10:12:41 +02:00
e58badf70e 152 - Give factories unique values for constrained columns 2026-08-15 10:07:40 +02:00
5 changed files with 62 additions and 5 deletions

View file

@ -17,7 +17,7 @@ public function definition(): array
{ {
return [ return [
'name' => $this->faker->words(3, true), 'name' => $this->faker->words(3, true),
'url' => $this->faker->url(), 'url' => $this->faker->unique()->url(),
'type' => $this->faker->randomElement(['website', 'rss']), 'type' => $this->faker->randomElement(['website', 'rss']),
'provider' => $this->faker->randomElement(['vrt', 'belga']), 'provider' => $this->faker->randomElement(['vrt', 'belga']),
'language_id' => null, 'language_id' => null,

View file

@ -15,7 +15,9 @@ class LanguageFactory extends Factory
public function definition(): array public function definition(): array
{ {
return [ return [
'short_code' => $this->faker->unique()->languageCode(), // Not a real language code: tests hardcode 'en', 'fr', 'nl' and
// others, and faker's pool would collide with them.
'short_code' => 'x-'.$this->faker->unique()->numerify('####'),
'name' => $this->faker->unique()->word(), 'name' => $this->faker->unique()->word(),
'native_name' => $this->faker->optional()->word(), 'native_name' => $this->faker->optional()->word(),
'is_active' => true, 'is_active' => true,

View file

@ -19,7 +19,7 @@ public function definition(): array
return [ return [
'platform' => PlatformEnum::LEMMY, 'platform' => PlatformEnum::LEMMY,
'instance_url' => 'https://lemmy.'.$this->faker->domainName(), 'instance_url' => 'https://lemmy.'.$this->faker->domainName(),
'username' => $this->faker->userName(), 'username' => $this->faker->unique()->userName(),
'password' => 'test-password', 'password' => 'test-password',
'settings' => [], 'settings' => [],
'is_active' => true, 'is_active' => true,

View file

@ -17,7 +17,7 @@ public function definition(): array
return [ return [
'platform' => 'lemmy', 'platform' => 'lemmy',
'name' => $this->faker->words(2, true), 'name' => $this->faker->words(2, true),
'url' => $this->faker->url(), 'url' => $this->faker->unique()->url(),
'is_active' => true, 'is_active' => true,
]; ];
} }
@ -34,7 +34,7 @@ public function lemmy(): static
return $this->state(fn (array $attributes) => [ return $this->state(fn (array $attributes) => [
'platform' => 'lemmy', 'platform' => 'lemmy',
'name' => 'Lemmy '.$this->faker->word(), 'name' => 'Lemmy '.$this->faker->word(),
'url' => 'https://lemmy.'.$this->faker->domainName(), 'url' => 'https://lemmy.'.$this->faker->unique()->domainName(),
]); ]);
} }
} }

View file

@ -23,6 +23,7 @@
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable; use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Carbon;
use Mockery; use Mockery;
use Tests\TestCase; use Tests\TestCase;
@ -35,6 +36,11 @@ class PublishNextArticleJobTest extends TestCase
protected function setUp(): void protected function setUp(): void
{ {
parent::setUp(); parent::setUp();
// The daily cap counts from startOfDay, so tests placing publications a
// couple of hours back land on the previous day when run after midnight.
Carbon::setTestNow('2026-07-15 12:00:00');
$this->notificationService = new NotificationService; $this->notificationService = new NotificationService;
} }
@ -319,6 +325,54 @@ public function test_daily_cap_counts_each_channel_publication_separately(): voi
$this->assertTrue(true); $this->assertTrue(true);
} }
public function test_daily_cap_counts_from_midnight_not_a_rolling_window(): void
{
Carbon::setTestNow('2026-07-15 01:00:00');
$this->createApprovedRouteArticle();
ArticlePublication::factory()->count(3)->create(['published_at' => Carbon::parse('2026-07-14 23:00:00')]);
Setting::setArticlePublishingInterval(0);
Setting::setDailyPublishCap(3);
$articleFetcherMock = Mockery::mock(FetchArticleDataAction::class);
$articleFetcherMock->shouldReceive('execute')
->once()
->andReturn(['title' => 'Test Article', 'description' => 'Test description']);
$publishingServiceMock = Mockery::mock(ArticlePublishingService::class);
$publishingServiceMock->shouldReceive('publishRouteArticle')
->once()
->andReturn(PublishOutcome::published($this->makePublication()));
$job = new PublishNextArticleJob;
$job->handle(new PublishRouteArticleAction($articleFetcherMock, $publishingServiceMock, $this->notificationService));
$this->assertTrue(true);
}
public function test_daily_cap_counts_a_publication_just_after_midnight(): void
{
Carbon::setTestNow('2026-07-15 01:00:00');
$this->createApprovedRouteArticle();
ArticlePublication::factory()->count(3)->create(['published_at' => Carbon::parse('2026-07-15 00:30:00')]);
Setting::setArticlePublishingInterval(0);
Setting::setDailyPublishCap(3);
$articleFetcherMock = Mockery::mock(FetchArticleDataAction::class);
$publishingServiceMock = Mockery::mock(ArticlePublishingService::class);
$articleFetcherMock->shouldNotReceive('execute');
$publishingServiceMock->shouldNotReceive('publishRouteArticle');
$job = new PublishNextArticleJob;
$job->handle(new PublishRouteArticleAction($articleFetcherMock, $publishingServiceMock, $this->notificationService));
$this->assertTrue(true);
}
public function test_handle_ignores_publications_from_previous_days(): void public function test_handle_ignores_publications_from_previous_days(): void
{ {
$this->createApprovedRouteArticle(); $this->createApprovedRouteArticle();
@ -572,6 +626,7 @@ public function test_job_can_be_serialized(): void
protected function tearDown(): void protected function tearDown(): void
{ {
Carbon::setTestNow();
Mockery::close(); Mockery::close();
parent::tearDown(); parent::tearDown();
} }