from __future__ import annotations import time import pytest from publicpaste.cli import main, read_input from publicpaste.core import PasteError, paste_text from publicpaste.providers import ( DpasteProvider, PasteCentosProvider, PasteProvider, PasteRsProvider, ProviderError, ProviderResult, SnippetHostProvider, TermbinProvider, ZeroXZeroProvider, ) @pytest.mark.parametrize( ("parser", "body", "headers", "expected"), [ (PasteRsProvider.parse_response, "https://paste.rs/abc\n", {}, "https://paste.rs/abc"), (DpasteProvider.parse_response, b"https://dpaste.org/ABC123\n", {}, "https://dpaste.org/ABC123"), (ZeroXZeroProvider.parse_response, "https://0x0.st/abcd.txt\n", {}, "https://0x0.st/abcd.txt"), (SnippetHostProvider.parse_response, "", {"Location": "/abc123"}, "https://snippet.host/abc123"), ( SnippetHostProvider.parse_response, 'snippet', {}, "https://snippet.host/xyz", ), ( PasteCentosProvider.parse_response, '{"url":"https://paste.centos.org/view/42"}', {}, "https://paste.centos.org/view/42", ), (PasteCentosProvider.parse_response, '{"id": 42}', {}, "https://paste.centos.org/view/42"), (TermbinProvider.parse_response, "https://termbin.com/abcd\n", {}, "https://termbin.com/abcd"), ], ) def test_provider_parse_response(parser, body: bytes | str, headers: dict[str, str], expected: str) -> None: assert parser(body, headers) == expected def test_provider_parse_response_rejects_missing_url() -> None: with pytest.raises(ProviderError): PasteRsProvider.parse_response("not a url") class FakeProvider(PasteProvider): name = "fake" def __init__(self, url: str | None = None, delay: float = 0.0, error: str | None = None) -> None: self.url = url self.delay = delay self.error = error def paste(self, text: str, *, timeout: float) -> ProviderResult: if self.delay: time.sleep(self.delay) if self.error: raise ProviderError(self.error) assert self.url is not None return ProviderResult(provider=self.name, url=self.url) def test_paste_text_returns_first_success() -> None: slow = FakeProvider("https://paste.rs/slow", delay=0.2) fast = FakeProvider("https://paste.rs/fast", delay=0.01) result = paste_text("hello", providers=[slow, fast], overall_timeout=1.0) assert result.url == "https://paste.rs/fast" assert result.provider == "fake" def test_paste_text_aggregates_all_failures() -> None: with pytest.raises(PasteError) as exc_info: paste_text( "hello", providers=[FakeProvider(error="boom"), FakeProvider(error="nope")], overall_timeout=1.0, ) assert "all providers failed" in str(exc_info.value) assert exc_info.value.errors == {"fake": "nope"} or exc_info.value.errors == {"fake": "boom"} def test_cli_reads_positional_text(monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]) -> None: def fake_paste_text(text: str, **kwargs): assert text == "hello" return type("Result", (), {"url": "https://paste.rs/abc", "provider": "paste.rs"})() monkeypatch.setattr("publicpaste.cli.paste_text", fake_paste_text) assert main(["hello"]) == 0 assert capsys.readouterr().out == "https://paste.rs/abc\n" def test_cli_json_from_stdin(monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]) -> None: def fake_paste_text(text: str, **kwargs): assert text == "from stdin" return type("Result", (), {"url": "https://paste.rs/abc", "provider": "paste.rs"})() monkeypatch.setattr("publicpaste.cli.paste_text", fake_paste_text) monkeypatch.setattr("sys.stdin", type("Stdin", (), {"read": lambda self: "from stdin"})()) assert main(["--json"]) == 0 assert '"provider": "paste.rs"' in capsys.readouterr().out def test_read_input_rejects_text_and_file(tmp_path) -> None: path = tmp_path / "a.txt" path.write_text("file", encoding="utf-8") args = type("Args", (), {"text": "hello", "file": path})() with pytest.raises(ValueError): read_input(args, stdin=None) # type: ignore[arg-type]