File size: 4,645 Bytes
5796881
 
 
 
 
4e043b3
5796881
 
 
 
 
4e043b3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5796881
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e44fdef
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import httpx
import pytest
import respx

from helpers import load_fixture
from scrap import parse_patent_html, scrap_patent_async, scrap_patent_bulk_async

FULL_PATENT_HTML = load_fixture("patents", "full_patent.html")
MISSING_TITLE_HTML = load_fixture("patents", "missing_title.html")


def test_parse_patent_html_extracts_all_fields_with_no_network_involved():
    """`scrap_patent_async` is now a thin fetch that delegates to this - a
    plain, synchronous function of the HTML string, so the parsing logic
    (including the regex-based section splitting, which is the part most
    likely to break when Google Patents changes its markup) can be tested
    directly against a saved page with no client/respx/event loop needed.
    """
    result = parse_patent_html(FULL_PATENT_HTML, "https://patents.google.com/patent/US11930446B2/en")

    assert result.title == "Widget with improved gadget mechanism"
    codes = {c.code: c.description for c in result.classifications}
    assert codes == {
        "G06F17/30": "Database structures therefor",
        "G06F17/50": "Other database related",
        "H04L9/00": "Cryptographic mechanisms",
    }


def test_parse_patent_html_raises_when_page_has_no_title():
    with pytest.raises(ValueError):
        parse_patent_html(MISSING_TITLE_HTML, "https://patents.google.com/patent/BOGUS/en")


async def test_scrap_patent_async_extracts_all_fields():
    url = "https://patents.google.com/patent/US11930446B2/en"
    with respx.mock:
        respx.get(url).mock(return_value=httpx.Response(200, text=FULL_PATENT_HTML))
        async with httpx.AsyncClient() as client:
            result = await scrap_patent_async(client, url)

    assert result.title == "Widget with improved gadget mechanism"
    assert result.abstract == "A widget comprising a gadget and a mechanism for improving widget performance."
    assert result.field_of_invention == "This invention relates to widgets and gadgets for testing purposes."
    assert result.background == "Prior art widgets suffered from several problems described herein."
    assert "1. A widget comprising a gadget." in result.claims
    assert "2. The widget of claim 1, further comprising a mechanism." in result.claims

    codes = {c.code: c.description for c in result.classifications}
    assert codes == {
        "G06F17/30": "Database structures therefor",
        "G06F17/50": "Other database related",
        "H04L9/00": "Cryptographic mechanisms",
    }


async def test_scrap_patent_async_raises_when_page_has_no_title():
    """Pins the documented behavior at scrap.py: an unexpected page layout
    (interstitial, non-patent page, markup change) raises ValueError rather
    than crashing on `None.get(...)`, so callers can treat it as a failed
    scrape.
    """
    url = "https://patents.google.com/patent/BOGUS/en"
    with respx.mock:
        respx.get(url).mock(return_value=httpx.Response(200, text=MISSING_TITLE_HTML))
        async with httpx.AsyncClient() as client:
            with pytest.raises(ValueError):
                await scrap_patent_async(client, url)


async def test_scrap_patent_async_raises_on_http_error():
    url = "https://patents.google.com/patent/US00000000/en"
    with respx.mock:
        respx.get(url).mock(return_value=httpx.Response(404))
        async with httpx.AsyncClient() as client:
            with pytest.raises(httpx.HTTPStatusError):
                await scrap_patent_async(client, url)


async def test_scrap_patent_bulk_async_separates_successes_from_failures():
    ok_url = "https://patents.google.com/patent/US_OK/en"
    fail_url = "https://patents.google.com/patent/US_FAIL/en"
    with respx.mock:
        respx.get(ok_url).mock(return_value=httpx.Response(200, text=FULL_PATENT_HTML))
        respx.get(fail_url).mock(return_value=httpx.Response(404))
        async with httpx.AsyncClient() as client:
            result = await scrap_patent_bulk_async(client, ["US_OK", "US_FAIL"])

    assert len(result.patents) == 1
    assert result.patents[0].title == "Widget with improved gadget mechanism"
    assert result.failed_ids == ["US_FAIL"]


def test_parse_patent_html_strips_whitespace_around_the_title():
    """Google Patents' DC.title meta content is frequently padded with
    newlines and indentation from the surrounding markup, which would
    otherwise end up in the API response and in every downstream citation.
    """
    html = '<html><head><meta name="DC.title" content="  Widget apparatus\n  "></head><body></body></html>'

    result = parse_patent_html(html, "https://patents.google.com/patent/US1/en")

    assert result.title == "Widget apparatus"