telegram: a 200 that is not the bot API envelope is not a send (V-615)
The sink parsed the response, and when the body did not unmarshal it fell through to the status check and returned nil on any 2xx. This box reaches telegram through a relay, and a relay that is up but cannot reach api.telegram.org answers 200 with a page of its own. That read as delivered: the dispatcher wrote a 'sent' outbox row and MarkSent restarted the repeat clock, so a sev4 alarm nobody received went quiet for a full interval. Only ok=true is a send now. The response cap moves from 4096 to 64KiB, because a truncated body no longer parses and would read as a failure, and error lines carry a 200-byte snippet instead of the relay's whole page. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -291,6 +291,66 @@ func TestSendReturnsErrorOnTelegramError(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// A relay that is up but cannot reach api.telegram.org answers 200 with a page
|
||||
// of its own. That is not a delivered message, and calling it one silences a
|
||||
// sev4 alarm for a full repeat interval.
|
||||
func TestSendRefusesA200ThatIsNotTheBotAPIEnvelope(t *testing.T) {
|
||||
rs := newRecordingServer(t, http.StatusOK, `<html><body>proxy: upstream unreachable</body></html>`)
|
||||
srv := httptest.NewServer(rs.handler())
|
||||
defer srv.Close()
|
||||
|
||||
sink, _ := New(sinkCfg(srv.URL))
|
||||
err := sink.Send(context.Background(), nudgeSendable(loop.Sev4, "down"))
|
||||
if err == nil {
|
||||
t.Fatal("want error on a 200 that is not the bot API envelope")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "not a confirmed send") {
|
||||
t.Fatalf("error should say the send is unconfirmed, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// The success path must stay a success: ok=true on 200 is a send.
|
||||
func TestSendAcceptsOkTrue(t *testing.T) {
|
||||
rs := newRecordingServer(t, http.StatusOK, `{"ok":true,"result":{"message_id":7}}`)
|
||||
srv := httptest.NewServer(rs.handler())
|
||||
defer srv.Close()
|
||||
|
||||
sink, _ := New(sinkCfg(srv.URL))
|
||||
if err := sink.Send(context.Background(), nudgeSendable(loop.Sev4, "down")); err != nil {
|
||||
t.Fatalf("want success on ok=true, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// A body long enough to have been truncated by the old 4096-byte cap still
|
||||
// parses, so a real send is not reported as a failure.
|
||||
func TestSendAcceptsAnOversizedButValidEnvelope(t *testing.T) {
|
||||
rs := newRecordingServer(t, http.StatusOK,
|
||||
`{"ok":true,"result":{"message_id":7,"text":"`+strings.Repeat("x", 8000)+`"}}`)
|
||||
srv := httptest.NewServer(rs.handler())
|
||||
defer srv.Close()
|
||||
|
||||
sink, _ := New(sinkCfg(srv.URL))
|
||||
if err := sink.Send(context.Background(), nudgeSendable(loop.Sev4, "down")); err != nil {
|
||||
t.Fatalf("want success on a large ok=true envelope, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// The error line carries a snippet, not the relay's whole page.
|
||||
func TestSendErrorDoesNotCarryTheWholeBody(t *testing.T) {
|
||||
rs := newRecordingServer(t, http.StatusBadGateway, strings.Repeat("z", 5000))
|
||||
srv := httptest.NewServer(rs.handler())
|
||||
defer srv.Close()
|
||||
|
||||
sink, _ := New(sinkCfg(srv.URL))
|
||||
err := sink.Send(context.Background(), nudgeSendable(loop.Sev4, "down"))
|
||||
if err == nil {
|
||||
t.Fatal("want error on 502")
|
||||
}
|
||||
if len(err.Error()) > 500 {
|
||||
t.Fatalf("error line should be trimmed, got %d bytes", len(err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendReturnsErrorOnNon2xx(t *testing.T) {
|
||||
rs := newRecordingServer(t, http.StatusBadGateway, "bad gateway")
|
||||
srv := httptest.NewServer(rs.handler())
|
||||
|
||||
Reference in New Issue
Block a user