DueMode::Local, 'due_date' => '2026-08-14', 'due_time' => '24:00:00', 'fire_on' => '2026-08-14', 'after_due' => AfterDue::Expire, 'state' => EventState::Today, 'payload' => ['title' => 'Сегодня ДР — Вася', 'subtitle' => 'исполняется 34', 'done_label' => 'Поздравил'], ]); $event->id = 17; $event->setRelation('source', new Source(['name' => 'docs'])); return $event; } public function test_presence_is_unknown_unless_recently_seen(): void { $user = new User(['login' => 'n', 'tz' => 'UTC']); $this->assertSame(Presence::Unknown, $this->driver()->presence($user, ['chat_id' => 555])); $this->driver()->touch(555); $this->assertSame(Presence::Present, $this->driver()->presence($user, ['chat_id' => 555])); } public function test_deliver_sends_message_with_two_buttons_and_stores_message_id(): void { Http::fake(['api.telegram.org/*' => Http::response(['ok' => true, 'result' => ['message_id' => 9001]])]); $outcome = $this->driver()->deliver($this->event(), new User(['login' => 'n', 'tz' => 'UTC']), ['chat_id' => 555], new Delivery(['action_token' => 'tok'])); $this->assertTrue($outcome->ok); $this->assertSame(['message_id' => 9001], $outcome->meta); Http::assertSent(function (Request $r) { $kb = $r['reply_markup']['inline_keyboard'][0]; return str_ends_with($r->url(), '/bottest-token/sendMessage') && $r['chat_id'] === 555 && str_contains($r['text'], 'Сегодня ДР — Вася') && str_contains($r['text'], 'исполняется 34') && $kb[0] === ['text' => 'Помню', 'callback_data' => 'ack:tok'] && $kb[1] === ['text' => 'Поздравил', 'callback_data' => 'done:tok']; }); } public function test_deliver_fails_on_api_error(): void { Http::fake(['api.telegram.org/*' => Http::response(['ok' => false, 'description' => 'chat not found'], 400)]); $outcome = $this->driver()->deliver($this->event(), new User(['login' => 'n', 'tz' => 'UTC']), ['chat_id' => 555], new Delivery(['action_token' => 'tok'])); $this->assertFalse($outcome->ok); $this->assertStringContainsString('chat not found', $outcome->error); } public function test_on_done_clears_buttons_best_effort(): void { Http::fake(['api.telegram.org/*' => Http::response(['ok' => true])]); $delivery = new Delivery(['action_token' => 'tok', 'meta' => ['message_id' => 9001]]); $delivery->setRelation('channel', new \App\Models\Channel(['config' => ['chat_id' => 555]])); $this->driver()->onDone($this->event(), $delivery); Http::assertSent(fn (Request $r) => str_ends_with($r->url(), '/editMessageReplyMarkup') && $r['message_id'] === 9001 && $r['chat_id'] === 555); Http::fake(fn () => throw new \RuntimeException('down')); $this->driver()->onDone($this->event(), $delivery); // не бросает $this->addToAssertionCount(1); } }