Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/github_agent_bridge/reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ def __init__(self, config: ImapConfig, queue: JobQueue, policy: Policy, mark_see
self.mark_seen = mark_seen

def fetch_once(self) -> int:
for attempt in range(2):
try:
return self._fetch_once()
except imaplib.IMAP4.abort:
if attempt:
raise
return 0

def _fetch_once(self) -> int:
last_uid = int(self.queue.get_state("last_uid", "0") or 0)
count = 0
imap = imaplib.IMAP4_SSL(self.config.host, self.config.port)
Expand Down
67 changes: 67 additions & 0 deletions tests/test_reader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import imaplib

import pytest

from github_agent_bridge.reader import ImapConfig, ImapReader


class QueueStub:
def get_state(self, key, default=None):
return default


class AbortOnSelect:
def __init__(self, *args):
self.logged_out = False

def login(self, username, password):
return "OK", []

def select(self, mailbox):
raise imaplib.IMAP4.abort("command: SELECT => socket error: EOF")

def logout(self):
self.logged_out = True


class EmptyMailbox:
def __init__(self, *args):
self.logged_out = False

def login(self, username, password):
return "OK", []

def select(self, mailbox):
return "OK", []

def uid(self, command, *args):
assert command == "search"
return "OK", [b""]

def logout(self):
self.logged_out = True


def make_reader():
config = ImapConfig("imap.example.com", 993, "bot@example.com", "secret")
return ImapReader(config, QueueStub(), object())


def test_fetch_once_reconnects_after_imap_abort(monkeypatch):
connections = [AbortOnSelect(), EmptyMailbox()]

monkeypatch.setattr(imaplib, "IMAP4_SSL", lambda *args: connections.pop(0))

assert make_reader().fetch_once() == 0
assert connections == []


def test_fetch_once_raises_after_second_imap_abort(monkeypatch):
connections = [AbortOnSelect(), AbortOnSelect()]

monkeypatch.setattr(imaplib, "IMAP4_SSL", lambda *args: connections.pop(0))

with pytest.raises(imaplib.IMAP4.abort, match="socket error: EOF"):
make_reader().fetch_once()

assert connections == []