type(InstanceType::Mastodon) ->enabled() ->create(['consecutive_poll_failures' => 0]); $service = new PollAlertService; $service->recordFailure($instance, 'test'); $this->assertDatabaseHas('fedi_discover_instances', [ 'id' => $instance->id, 'consecutive_poll_failures' => 1, ]); } public function test_no_alert_sent_below_threshold(): void { Http::fake(); config([ 'services.ntfy.url' => 'https://ntfy.example.com', 'services.ntfy.topic' => 'trove-alerts', 'services.ntfy.threshold' => 3, ]); $instance = Instance::factory() ->type(InstanceType::Mastodon) ->enabled() ->create(['consecutive_poll_failures' => 1]); // will become 2 after recordFailure $service = new PollAlertService; $service->recordFailure($instance, 'test'); Http::assertNothingSent(); } public function test_alert_sent_when_threshold_is_reached(): void { Http::fake(); config([ 'services.ntfy.url' => 'https://ntfy.example.com', 'services.ntfy.topic' => 'trove-alerts', 'services.ntfy.threshold' => 3, ]); $instance = Instance::factory() ->type(InstanceType::Mastodon) ->enabled() ->create(['consecutive_poll_failures' => 2]); // will become 3 after recordFailure = exactly at threshold $service = new PollAlertService; $service->recordFailure($instance, 'test'); Http::assertSent(function ($request) { return $request->url() === 'https://ntfy.example.com/trove-alerts' && $request->method() === 'POST'; }); } public function test_alert_sent_when_count_exceeds_threshold(): void { Http::fake(); config([ 'services.ntfy.url' => 'https://ntfy.example.com', 'services.ntfy.topic' => 'trove-alerts', 'services.ntfy.threshold' => 3, ]); $instance = Instance::factory() ->type(InstanceType::Mastodon) ->enabled() ->create(['consecutive_poll_failures' => 3]); // will become 4 after recordFailure = above threshold $service = new PollAlertService; $service->recordFailure($instance, 'test'); Http::assertSent(function ($request) { return $request->url() === 'https://ntfy.example.com/trove-alerts' && $request->method() === 'POST'; }); } public function test_no_alert_sent_when_threshold_is_zero(): void { Http::fake(); config([ 'services.ntfy.url' => 'https://ntfy.example.com', 'services.ntfy.topic' => 'trove-alerts', 'services.ntfy.threshold' => 0, ]); $instance = Instance::factory() ->type(InstanceType::Mastodon) ->enabled() ->create(['consecutive_poll_failures' => 5]); $service = new PollAlertService; $service->recordFailure($instance, 'test'); Http::assertNothingSent(); } public function test_no_alert_sent_when_topic_is_null(): void { Http::fake(); config([ 'services.ntfy.url' => 'https://ntfy.example.com', 'services.ntfy.topic' => null, 'services.ntfy.threshold' => 3, ]); $instance = Instance::factory() ->type(InstanceType::Mastodon) ->enabled() ->create(['consecutive_poll_failures' => 2]); // will become 3 after recordFailure = at threshold $service = new PollAlertService; $service->recordFailure($instance, 'test'); Http::assertNothingSent(); } public function test_alert_body_contains_instance_url_and_message(): void { Http::fake(); config([ 'services.ntfy.url' => 'https://ntfy.example.com', 'services.ntfy.topic' => 'trove-alerts', 'services.ntfy.threshold' => 3, ]); $instance = Instance::factory() ->type(InstanceType::Mastodon) ->enabled() ->create([ 'url' => 'https://mastodon.social', 'consecutive_poll_failures' => 2, // will become 3 = at threshold ]); $service = new PollAlertService; $service->recordFailure($instance, 'connection refused after 3 retries'); Http::assertSent(function ($request) { return str_contains($request->body(), 'https://mastodon.social') && str_contains($request->body(), 'connection refused after 3 retries'); }); } }