public inbox for tools@linux.kernel.org
 help / color / mirror / Atom feed
From: Tamir Duberstein <tamird@gmail.com>
To: "Kernel.org Tools" <tools@kernel.org>
Cc: Konstantin Ryabitsev <konstantin@linuxfoundation.org>,
	 Tamir Duberstein <tamird@gmail.com>
Subject: [PATCH 02/14] Type make_msg and drop test suppressions
Date: Fri, 10 Apr 2026 18:37:53 -0400	[thread overview]
Message-ID: <20260410-harden-type-checking-v1-2-fcf314d9d748@gmail.com> (raw)
In-Reply-To: <20260410-harden-type-checking-v1-0-fcf314d9d748@gmail.com>

Replace the make_msg class helper with a typed callable fixture and
update tests to call it directly. This keeps argument checking in the
tests while simplifying the fixture implementation.

Remove the temporary mypy suppressions now that the underlying typing
issues are fixed in the test code.

Signed-off-by: Tamir Duberstein <tamird@gmail.com>
---
 pyproject.toml             | 13 --------
 tests/conftest.py          | 79 ++++++++++++++++++++++++++--------------------
 tests/test_auth_headers.py |  4 +--
 tests/test_email_utils.py  | 60 ++++++++++++++++++-----------------
 tests/test_formatting.py   | 74 ++++++++++++++++++++++---------------------
 tests/test_message.py      | 10 +++---
 tests/test_thread.py       | 34 ++++++++++----------
 7 files changed, 140 insertions(+), 134 deletions(-)

diff --git a/pyproject.toml b/pyproject.toml
index e31a05c..7c9e1da 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -57,18 +57,5 @@ strict = true
 module = "authheaders"
 ignore_missing_imports = true
 
-[[tool.mypy.overrides]]
-module = [
-    "test_email_utils",
-    "test_formatting",
-    "test_message",
-    "test_thread",
-]
-disable_error_code = ["attr-defined"]
-
-[[tool.mypy.overrides]]
-module = "test_auth_headers"
-disable_error_code = ["unused-ignore"]
-
 [tool.ruff]
 target-version = "py39"
diff --git a/tests/conftest.py b/tests/conftest.py
index cf1a9c7..b84fc35 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -4,6 +4,7 @@ from __future__ import annotations
 
 import email.utils
 import textwrap
+from typing import Protocol
 
 import pytest
 
@@ -12,41 +13,51 @@ from email.message import EmailMessage
 from liblore import emlpolicy
 
 
+class MsgFactory(Protocol):
+    def __call__(
+        self,
+        subject: str = ...,
+        from_addr: tuple[str, str] = ...,
+        msgid: str | None = ...,
+        in_reply_to: str | None = ...,
+        references: list[str] | None = ...,
+        body: str = ...,
+        date: str | None = ...,
+    ) -> EmailMessage: ...
+
+
 @pytest.fixture()
-def make_msg() -> type:
-    """Factory fixture that returns a helper class for building test messages."""
-
-    class MsgFactory:
-        _counter = 0
-
-        @classmethod
-        def create(
-            cls,
-            subject: str = 'Test message',
-            from_addr: tuple[str, str] = ('Test User', 'test@example.com'),
-            msgid: str | None = None,
-            in_reply_to: str | None = None,
-            references: list[str] | None = None,
-            body: str = 'Hello, world!\n',
-            date: str | None = None,
-        ) -> EmailMessage:
-            cls._counter += 1
-            if msgid is None:
-                msgid = f'msg{cls._counter}@example.com'
-            msg = EmailMessage(policy=emlpolicy)
-            msg['Subject'] = subject
-            msg['From'] = email.utils.formataddr(from_addr)
-            msg['Message-Id'] = f'<{msgid}>'
-            if in_reply_to:
-                msg['In-Reply-To'] = f'<{in_reply_to}>'
-            if references:
-                msg['References'] = ' '.join(f'<{r}>' for r in references)
-            if date:
-                msg['Date'] = date
-            msg.set_content(body)
-            return msg
-
-    return MsgFactory
+def make_msg() -> MsgFactory:
+    """Factory fixture that returns a helper callable for test messages."""
+    counter = 0
+
+    def create(
+        subject: str = 'Test message',
+        from_addr: tuple[str, str] = ('Test User', 'test@example.com'),
+        msgid: str | None = None,
+        in_reply_to: str | None = None,
+        references: list[str] | None = None,
+        body: str = 'Hello, world!\n',
+        date: str | None = None,
+    ) -> EmailMessage:
+        nonlocal counter
+        counter += 1
+        if msgid is None:
+            msgid = f'msg{counter}@example.com'
+        msg = EmailMessage(policy=emlpolicy)
+        msg['Subject'] = subject
+        msg['From'] = email.utils.formataddr(from_addr)
+        msg['Message-Id'] = f'<{msgid}>'
+        if in_reply_to:
+            msg['In-Reply-To'] = f'<{in_reply_to}>'
+        if references:
+            msg['References'] = ' '.join(f'<{r}>' for r in references)
+        if date:
+            msg['Date'] = date
+        msg.set_content(body)
+        return msg
+
+    return create
 
 
 @pytest.fixture()
diff --git a/tests/test_auth_headers.py b/tests/test_auth_headers.py
index 6c524b5..9edf4cd 100644
--- a/tests/test_auth_headers.py
+++ b/tests/test_auth_headers.py
@@ -67,8 +67,8 @@ class TestAuthenticateMsgs:
             assert msg['Authentication-Results'] == (
                 'liblore; dkim=pass header.d=example.com'
             )
-            fake.authenticate_message.assert_called_once()  # type: ignore[attr-defined]
-            call_kwargs = fake.authenticate_message.call_args  # type: ignore[attr-defined]
+            fake.authenticate_message.assert_called_once()
+            call_kwargs = fake.authenticate_message.call_args
             assert call_kwargs[0][1] == 'liblore'
             assert call_kwargs[1]['dkim'] is True
             assert call_kwargs[1]['dmarc'] is True
diff --git a/tests/test_email_utils.py b/tests/test_email_utils.py
index 97c4ff3..ad8775f 100644
--- a/tests/test_email_utils.py
+++ b/tests/test_email_utils.py
@@ -4,6 +4,8 @@ from __future__ import annotations
 
 from email.message import EmailMessage
 
+from conftest import MsgFactory
+
 from liblore import emlpolicy
 from liblore.utils import (
     msg_get_author,
@@ -16,43 +18,43 @@ from liblore.utils import (
 
 
 class TestMsgGetSubject:
-    def test_plain_subject(self, make_msg: type) -> None:
-        msg = make_msg.create(subject='Just a plain subject')
+    def test_plain_subject(self, make_msg: MsgFactory) -> None:
+        msg = make_msg(subject='Just a plain subject')
         assert msg_get_subject(msg) == 'Just a plain subject'
 
-    def test_no_strip(self, make_msg: type) -> None:
-        msg = make_msg.create(subject='[PATCH v3 2/5] subsys: fix thing')
+    def test_no_strip(self, make_msg: MsgFactory) -> None:
+        msg = make_msg(subject='[PATCH v3 2/5] subsys: fix thing')
         assert msg_get_subject(msg) == '[PATCH v3 2/5] subsys: fix thing'
 
-    def test_strip_patch_prefix(self, make_msg: type) -> None:
-        msg = make_msg.create(subject='[PATCH v3 2/5] subsys: fix thing')
+    def test_strip_patch_prefix(self, make_msg: MsgFactory) -> None:
+        msg = make_msg(subject='[PATCH v3 2/5] subsys: fix thing')
         assert msg_get_subject(msg, strip_prefixes=True) == 'subsys: fix thing'
 
-    def test_strip_re_and_prefix(self, make_msg: type) -> None:
-        msg = make_msg.create(subject='Re: [PATCH] Something cool')
+    def test_strip_re_and_prefix(self, make_msg: MsgFactory) -> None:
+        msg = make_msg(subject='Re: [PATCH] Something cool')
         assert msg_get_subject(msg, strip_prefixes=True) == 'Something cool'
 
-    def test_strip_multiple_prefixes(self, make_msg: type) -> None:
-        msg = make_msg.create(subject='[RFC PATCH v2 0/3] [net-next] New feature')
+    def test_strip_multiple_prefixes(self, make_msg: MsgFactory) -> None:
+        msg = make_msg(subject='[RFC PATCH v2 0/3] [net-next] New feature')
         result = msg_get_subject(msg, strip_prefixes=True)
         assert result == 'New feature'
 
-    def test_strip_aw_prefix(self, make_msg: type) -> None:
-        msg = make_msg.create(subject='Aw: [PATCH] German reply')
+    def test_strip_aw_prefix(self, make_msg: MsgFactory) -> None:
+        msg = make_msg(subject='Aw: [PATCH] German reply')
         assert msg_get_subject(msg, strip_prefixes=True) == 'German reply'
 
     def test_no_subject(self) -> None:
         msg = EmailMessage(policy=emlpolicy)
         assert msg_get_subject(msg) == ''
 
-    def test_strip_no_brackets(self, make_msg: type) -> None:
-        msg = make_msg.create(subject='No brackets here')
+    def test_strip_no_brackets(self, make_msg: MsgFactory) -> None:
+        msg = make_msg(subject='No brackets here')
         assert msg_get_subject(msg, strip_prefixes=True) == 'No brackets here'
 
 
 class TestMsgGetAuthor:
-    def test_normal_from(self, make_msg: type) -> None:
-        msg = make_msg.create(from_addr=('Jane Doe', 'jane@example.com'))
+    def test_normal_from(self, make_msg: MsgFactory) -> None:
+        msg = make_msg(from_addr=('Jane Doe', 'jane@example.com'))
         name, addr = msg_get_author(msg)
         assert name == 'Jane Doe'
         assert addr == 'jane@example.com'
@@ -72,23 +74,23 @@ class TestMsgGetAuthor:
 
 
 class TestMsgGetPayload:
-    def test_plain_body(self, make_msg: type) -> None:
-        msg = make_msg.create(body='This is the body.\n')
+    def test_plain_body(self, make_msg: MsgFactory) -> None:
+        msg = make_msg(body='This is the body.\n')
         assert 'This is the body.' in msg_get_payload(msg)
 
-    def test_strip_signature(self, make_msg: type) -> None:
-        msg = make_msg.create(body='Body text.\n-- \nMy Sig\n')
+    def test_strip_signature(self, make_msg: MsgFactory) -> None:
+        msg = make_msg(body='Body text.\n-- \nMy Sig\n')
         result = msg_get_payload(msg, strip_signature=True)
         assert 'Body text.' in result
         assert 'My Sig' not in result
 
-    def test_keep_signature(self, make_msg: type) -> None:
-        msg = make_msg.create(body='Body text.\n-- \nMy Sig\n')
+    def test_keep_signature(self, make_msg: MsgFactory) -> None:
+        msg = make_msg(body='Body text.\n-- \nMy Sig\n')
         result = msg_get_payload(msg, strip_signature=False)
         assert 'My Sig' in result
 
-    def test_strip_quoted(self, make_msg: type) -> None:
-        msg = make_msg.create(body='My reply.\n> Quoted line.\nMore text.\n')
+    def test_strip_quoted(self, make_msg: MsgFactory) -> None:
+        msg = make_msg(body='My reply.\n> Quoted line.\nMore text.\n')
         result = msg_get_payload(msg, strip_quoted=True)
         assert 'My reply.' in result
         assert 'Quoted line.' not in result
@@ -118,12 +120,12 @@ class TestMsgGetRecipients:
 
 
 class TestSortMsgsByReceived:
-    def test_sorts_by_date(self, make_msg: type) -> None:
-        msg1 = make_msg.create(
+    def test_sorts_by_date(self, make_msg: MsgFactory) -> None:
+        msg1 = make_msg(
             msgid='older@x.com',
             date='Mon, 01 Jan 2024 00:00:00 +0000',
         )
-        msg2 = make_msg.create(
+        msg2 = make_msg(
             msgid='newer@x.com',
             date='Tue, 02 Jan 2024 00:00:00 +0000',
         )
@@ -132,8 +134,8 @@ class TestSortMsgsByReceived:
         assert result[0]['Message-Id'] == '<older@x.com>'
         assert result[1]['Message-Id'] == '<newer@x.com>'
 
-    def test_skips_dateless(self, make_msg: type) -> None:
-        msg = make_msg.create()
+    def test_skips_dateless(self, make_msg: MsgFactory) -> None:
+        msg = make_msg()
         # No Date header set at all — del it if make_msg added one
         if 'Date' in msg:
             del msg['Date']
diff --git a/tests/test_formatting.py b/tests/test_formatting.py
index 45afa64..a13593e 100644
--- a/tests/test_formatting.py
+++ b/tests/test_formatting.py
@@ -6,6 +6,8 @@ import pytest
 
 from email.message import EmailMessage
 
+from conftest import MsgFactory
+
 from liblore.utils import (
     clean_header,
     format_addrs,
@@ -165,9 +167,9 @@ class TestGetMsgAsBytes:
 
 
 class TestMinimizeThread:
-    def test_headers_filtered(self, make_msg: type) -> None:
+    def test_headers_filtered(self, make_msg: MsgFactory) -> None:
         """Only headers in MINIMIZE_KEEP_HEADERS are kept."""
-        msg = make_msg.create(subject='Test', body='Hello.\n')
+        msg = make_msg(subject='Test', body='Hello.\n')
         msg['X-Custom'] = 'should be dropped'
         msg['List-Id'] = '<test.example.com>'
         result = minimize_thread([msg])
@@ -176,9 +178,9 @@ class TestMinimizeThread:
         assert result[0]['X-Custom'] is None
         assert result[0]['List-Id'] is None
 
-    def test_keep_headers_default(self, make_msg: type) -> None:
+    def test_keep_headers_default(self, make_msg: MsgFactory) -> None:
         """All default MINIMIZE_KEEP_HEADERS are preserved when present."""
-        msg = make_msg.create(
+        msg = make_msg(
             subject='Test',
             from_addr=('Alice', 'alice@example.com'),
             body='Hello.\n',
@@ -200,9 +202,9 @@ class TestMinimizeThread:
         assert mmsg['Reply-To'] is not None
         assert mmsg['In-Reply-To'] is not None
 
-    def test_custom_keep_headers(self, make_msg: type) -> None:
+    def test_custom_keep_headers(self, make_msg: MsgFactory) -> None:
         """Callers can override which headers to keep."""
-        msg = make_msg.create(subject='Test', body='Hello.\n',
+        msg = make_msg(subject='Test', body='Hello.\n',
                               date='Mon, 01 Jan 2024 00:00:00 +0000')
         result = minimize_thread([msg], keep_headers=('Subject',))
         assert len(result) == 1
@@ -210,10 +212,10 @@ class TestMinimizeThread:
         assert result[0]['From'] is None
         assert result[0]['Date'] is None
 
-    def test_multi_level_quotes_stripped(self, make_msg: type) -> None:
+    def test_multi_level_quotes_stripped(self, make_msg: MsgFactory) -> None:
         """Lines with >> (multi-level quoting) are removed."""
         body = 'My reply.\n> Single quote.\n>> Double quote.\nMore text.\n'
-        msg = make_msg.create(body=body)
+        msg = make_msg(body=body)
         result = minimize_thread([msg])
         assert len(result) == 1
         text = result[0].get_payload()
@@ -222,26 +224,26 @@ class TestMinimizeThread:
         assert 'Double quote.' not in text
         assert 'More text.' in text
 
-    def test_empty_quote_lines_stripped(self, make_msg: type) -> None:
+    def test_empty_quote_lines_stripped(self, make_msg: MsgFactory) -> None:
         """Bare '>' lines are cleaned up."""
         body = 'Reply.\n>\n> Real quote.\nMore text.\n'
-        msg = make_msg.create(body=body)
+        msg = make_msg(body=body)
         result = minimize_thread([msg])
         text = result[0].get_payload()
         assert 'Reply.' in text
         assert 'Real quote.' in text
         assert 'More text.' in text
 
-    def test_bottom_quotes_dropped(self, make_msg: type) -> None:
+    def test_bottom_quotes_dropped(self, make_msg: MsgFactory) -> None:
         """Trailing quoted blocks at the end of a message are dropped."""
         body = 'My reply.\n> Original message.\n'
-        msg = make_msg.create(body=body)
+        msg = make_msg(body=body)
         result = minimize_thread([msg])
         text = result[0].get_payload()
         assert 'My reply.' in text
         assert 'Original message.' not in text
 
-    def test_diff_preserved(self, make_msg: type) -> None:
+    def test_diff_preserved(self, make_msg: MsgFactory) -> None:
         """Messages with diff content are not minimized."""
         body = (
             '> Quoted context.\n'
@@ -252,42 +254,42 @@ class TestMinimizeThread:
             '-old\n'
             '+new\n'
         )
-        msg = make_msg.create(body=body)
+        msg = make_msg(body=body)
         result = minimize_thread([msg])
         text = result[0].get_payload()
         assert 'diff --git' in text
         assert 'Quoted context.' in text
 
-    def test_diffstat_preserved(self, make_msg: type) -> None:
+    def test_diffstat_preserved(self, make_msg: MsgFactory) -> None:
         """Messages with diffstat content are not minimized."""
         body = '> Quoted.\n 3 files changed, 10 insertions(+), 5 deletions(-)\n'
-        msg = make_msg.create(body=body)
+        msg = make_msg(body=body)
         result = minimize_thread([msg])
         text = result[0].get_payload()
         assert 'Quoted.' in text
         assert '3 files changed' in text
 
-    def test_empty_after_minimize_dropped(self, make_msg: type) -> None:
+    def test_empty_after_minimize_dropped(self, make_msg: MsgFactory) -> None:
         """Messages that become empty after minimization are dropped."""
         body = '> Only quoted text.\n>> And deeper quotes.\n'
-        msg = make_msg.create(body=body)
+        msg = make_msg(body=body)
         result = minimize_thread([msg])
         assert len(result) == 0
 
-    def test_signature_preserved(self, make_msg: type) -> None:
+    def test_signature_preserved(self, make_msg: MsgFactory) -> None:
         """Compliant signatures are kept after quote processing."""
         body = 'My reply.\n-- \nJane Doe\n'
-        msg = make_msg.create(body=body)
+        msg = make_msg(body=body)
         result = minimize_thread([msg])
         text = result[0].get_payload()
         assert 'My reply.' in text
         assert '-- \n' in text
         assert 'Jane Doe' in text
 
-    def test_trailing_quote_before_sig_dropped(self, make_msg: type) -> None:
+    def test_trailing_quote_before_sig_dropped(self, make_msg: MsgFactory) -> None:
         """A trailing quoted block before a signature is dropped."""
         body = 'My reply.\n> Huge quoted original.\n> More quoting.\n-- \nJane Doe\n'
-        msg = make_msg.create(body=body)
+        msg = make_msg(body=body)
         result = minimize_thread([msg])
         text = result[0].get_payload()
         assert 'My reply.' in text
@@ -295,14 +297,14 @@ class TestMinimizeThread:
         assert 'More quoting.' not in text
         assert 'Jane Doe' in text
 
-    def test_only_quotes_before_sig_dropped(self, make_msg: type) -> None:
+    def test_only_quotes_before_sig_dropped(self, make_msg: MsgFactory) -> None:
         """Message with only quotes before a signature is dropped entirely."""
         body = '> Only quoted text.\n-- \nJane Doe\n'
-        msg = make_msg.create(body=body)
+        msg = make_msg(body=body)
         result = minimize_thread([msg])
         assert len(result) == 0
 
-    def test_reduce_quote_context(self, make_msg: type) -> None:
+    def test_reduce_quote_context(self, make_msg: MsgFactory) -> None:
         """Long quoted blocks are reduced to the last paragraph."""
         body = (
             'On Monday, Julius Caesar wrote:\n'
@@ -318,7 +320,7 @@ class TestMinimizeThread:
             '> Third paragraph line two.\n'
             'My reply here.\n'
         )
-        msg = make_msg.create(body=body)
+        msg = make_msg(body=body)
         result = minimize_thread([msg], reduce_quote_context=True)
         text = result[0].get_payload()
         assert '... skip 7 lines ...' in text
@@ -328,7 +330,7 @@ class TestMinimizeThread:
         assert 'Second paragraph' not in text
         assert 'My reply here.' in text
 
-    def test_reduce_quote_context_short_quote_untouched(self, make_msg: type) -> None:
+    def test_reduce_quote_context_short_quote_untouched(self, make_msg: MsgFactory) -> None:
         """Quotes with 5 or fewer skippable lines are left alone."""
         body = (
             '> Line one.\n'
@@ -337,29 +339,29 @@ class TestMinimizeThread:
             '> Last para.\n'
             'Reply.\n'
         )
-        msg = make_msg.create(body=body)
+        msg = make_msg(body=body)
         result = minimize_thread([msg], reduce_quote_context=True)
         text = result[0].get_payload()
         assert 'skip' not in text
         assert 'Line one.' in text
         assert 'Last para.' in text
 
-    def test_reduce_quote_context_off_by_default(self, make_msg: type) -> None:
+    def test_reduce_quote_context_off_by_default(self, make_msg: MsgFactory) -> None:
         """Long quotes are untouched when reduce_quote_context is False."""
         lines = ''.join(f'> Line {i}.\n' for i in range(20))
         body = f'{lines}Reply.\n'
-        msg = make_msg.create(body=body)
+        msg = make_msg(body=body)
         result = minimize_thread([msg])
         text = result[0].get_payload()
         assert 'skip' not in text
         assert 'Line 0.' in text
         assert 'Line 19.' in text
 
-    def test_reduce_quote_context_preserves_sig(self, make_msg: type) -> None:
+    def test_reduce_quote_context_preserves_sig(self, make_msg: MsgFactory) -> None:
         """Signature is preserved when reducing quote context."""
         lines = ''.join(f'> Line {i}.\n' for i in range(10))
         body = f'On Monday, someone wrote:\n{lines}>\n> Last para.\nReply.\n-- \nKR\n'
-        msg = make_msg.create(body=body)
+        msg = make_msg(body=body)
         result = minimize_thread([msg], reduce_quote_context=True)
         text = result[0].get_payload()
         assert 'skip' in text
@@ -368,11 +370,11 @@ class TestMinimizeThread:
         assert '-- \n' in text
         assert 'KR' in text
 
-    def test_multiple_messages(self, make_msg: type) -> None:
+    def test_multiple_messages(self, make_msg: MsgFactory) -> None:
         """Multiple messages in a thread are all processed."""
-        msg1 = make_msg.create(body='First message.\n')
-        msg2 = make_msg.create(body='Reply.\n> First message.\n')
-        msg3 = make_msg.create(body='> Only quotes.\n')
+        msg1 = make_msg(body='First message.\n')
+        msg2 = make_msg(body='Reply.\n> First message.\n')
+        msg3 = make_msg(body='> Only quotes.\n')
         result = minimize_thread([msg1, msg2, msg3])
         # msg3 should be dropped (all quotes)
         assert len(result) == 2
diff --git a/tests/test_message.py b/tests/test_message.py
index 1a20afb..947e112 100644
--- a/tests/test_message.py
+++ b/tests/test_message.py
@@ -4,6 +4,8 @@ from __future__ import annotations
 
 from email.message import EmailMessage
 
+from conftest import MsgFactory
+
 from liblore.utils import clean_header, get_clean_msgid, parse_message
 
 
@@ -35,16 +37,16 @@ class TestCleanHeader:
 
 
 class TestGetCleanMsgid:
-    def test_extracts_msgid(self, make_msg: type) -> None:
-        msg = make_msg.create(msgid='test123@example.com')
+    def test_extracts_msgid(self, make_msg: MsgFactory) -> None:
+        msg = make_msg(msgid='test123@example.com')
         assert get_clean_msgid(msg) == 'test123@example.com'
 
     def test_missing_header(self) -> None:
         msg = EmailMessage()
         assert get_clean_msgid(msg) is None
 
-    def test_custom_header(self, make_msg: type) -> None:
-        msg = make_msg.create(in_reply_to='parent@example.com')
+    def test_custom_header(self, make_msg: MsgFactory) -> None:
+        msg = make_msg(in_reply_to='parent@example.com')
         assert get_clean_msgid(msg, 'In-Reply-To') == 'parent@example.com'
 
 
diff --git a/tests/test_thread.py b/tests/test_thread.py
index b404a2a..62a9a1e 100644
--- a/tests/test_thread.py
+++ b/tests/test_thread.py
@@ -2,35 +2,37 @@
 """Tests for liblore.thread."""
 from __future__ import annotations
 
+from conftest import MsgFactory
+
 from liblore.utils import get_clean_msgid, get_strict_thread
 
 
 class TestGetStrictThread:
-    def test_simple_thread(self, make_msg: type) -> None:
-        root = make_msg.create(msgid='root@x.com', subject='Root')
-        reply = make_msg.create(
+    def test_simple_thread(self, make_msg: MsgFactory) -> None:
+        root = make_msg(msgid='root@x.com', subject='Root')
+        reply = make_msg(
             msgid='reply@x.com',
             subject='Re: Root',
             in_reply_to='root@x.com',
         )
-        unrelated = make_msg.create(msgid='other@x.com', subject='Other')
+        unrelated = make_msg(msgid='other@x.com', subject='Other')
         result = get_strict_thread([root, reply, unrelated], 'root@x.com')
         assert result is not None
         ids = {get_clean_msgid(m) for m in result}
         assert ids == {'root@x.com', 'reply@x.com'}
 
-    def test_returns_none_for_missing_msgid(self, make_msg: type) -> None:
-        msg = make_msg.create(msgid='exists@x.com')
+    def test_returns_none_for_missing_msgid(self, make_msg: MsgFactory) -> None:
+        msg = make_msg(msgid='exists@x.com')
         result = get_strict_thread([msg], 'nonexistent@x.com')
         assert result is None
 
-    def test_noparent(self, make_msg: type) -> None:
-        parent = make_msg.create(msgid='parent@x.com')
-        child = make_msg.create(
+    def test_noparent(self, make_msg: MsgFactory) -> None:
+        parent = make_msg(msgid='parent@x.com')
+        child = make_msg(
             msgid='child@x.com',
             in_reply_to='parent@x.com',
         )
-        grandchild = make_msg.create(
+        grandchild = make_msg(
             msgid='grandchild@x.com',
             in_reply_to='child@x.com',
         )
@@ -44,10 +46,10 @@ class TestGetStrictThread:
         assert 'child@x.com' in ids
         assert 'grandchild@x.com' in ids
 
-    def test_references_chain(self, make_msg: type) -> None:
-        msg1 = make_msg.create(msgid='a@x.com')
-        msg2 = make_msg.create(msgid='b@x.com', references=['a@x.com'])
-        msg3 = make_msg.create(
+    def test_references_chain(self, make_msg: MsgFactory) -> None:
+        msg1 = make_msg(msgid='a@x.com')
+        msg2 = make_msg(msgid='b@x.com', references=['a@x.com'])
+        msg3 = make_msg(
             msgid='c@x.com',
             references=['a@x.com', 'b@x.com'],
         )
@@ -55,8 +57,8 @@ class TestGetStrictThread:
         assert result is not None
         assert len(result) == 3
 
-    def test_single_message(self, make_msg: type) -> None:
-        msg = make_msg.create(msgid='solo@x.com')
+    def test_single_message(self, make_msg: MsgFactory) -> None:
+        msg = make_msg(msgid='solo@x.com')
         result = get_strict_thread([msg], 'solo@x.com')
         assert result is not None
         assert len(result) == 1

-- 
2.53.0


  parent reply	other threads:[~2026-04-10 22:38 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-10 22:37 [PATCH 00/14] Harden local type checking and test mocking Tamir Duberstein
2026-04-10 22:37 ` [PATCH 01/14] Add b4 CI checks and mypy suppressions Tamir Duberstein
2026-04-10 22:37 ` Tamir Duberstein [this message]
2026-04-10 22:37 ` [PATCH 03/14] Add ruff import checks to b4 CI Tamir Duberstein
2026-04-10 22:37 ` [PATCH 04/14] Add ruff format check to CI Tamir Duberstein
2026-04-10 22:37 ` [PATCH 05/14] Add pyright strict checks " Tamir Duberstein
2026-04-10 22:37 ` [PATCH 06/14] Replace HTTP session mocks with responses Tamir Duberstein
2026-04-10 22:37 ` [PATCH 07/14] Add ty checks to CI Tamir Duberstein
2026-04-10 22:37 ` [PATCH 08/14] Drop redundant read-only property test Tamir Duberstein
2026-04-10 22:38 ` [PATCH 09/14] Type from_git_config keyword arguments Tamir Duberstein
2026-04-10 22:38 ` [PATCH 10/14] Add authheaders stub and typed callable Tamir Duberstein
2026-04-10 22:38 ` [PATCH 11/14] Replace batch mocks with subclasses Tamir Duberstein
2026-04-10 22:38 ` [PATCH 12/14] Use CompletedProcess in git config tests Tamir Duberstein
2026-04-10 22:38 ` [PATCH 13/14] Update README for uv-based dev checks Tamir Duberstein
2026-04-10 22:38 ` [PATCH 14/14] Add b4 send configuration Tamir Duberstein

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260410-harden-type-checking-v1-2-fcf314d9d748@gmail.com \
    --to=tamird@gmail.com \
    --cc=konstantin@linuxfoundation.org \
    --cc=tools@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox