-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_examples.py
More file actions
103 lines (71 loc) · 3 KB
/
Copy pathtest_examples.py
File metadata and controls
103 lines (71 loc) · 3 KB
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
"""Living-documentation property tests that *use* the recipes on real code.
Each test states a property a real system should have and checks it with a
recipe strategy. Run them with::
pytest examples/test_examples.py
They intentionally read like the tests you would write in your own project.
"""
from __future__ import annotations
import ipaddress
import json
from datetime import datetime, timezone
from decimal import Decimal
from typing import Any
from urllib.parse import urlsplit
from hypothesis import given
from hypothesis import strategies as st
from hypothesis_recipes import (
bounding_boxes,
dst_safe_datetimes,
emails,
ip_addresses,
json_values,
money_tuples,
urls,
)
from hypothesis_recipes.money import minor_units
# --- money: converting to integer minor units and back is lossless ----------
def to_minor_units(amount: Decimal, currency: str) -> int:
"""Represent a money amount as an integer number of minor units."""
return int((amount * (Decimal(10) ** minor_units(currency))).to_integral_value())
def from_minor_units(units: int, currency: str) -> Decimal:
places = minor_units(currency)
return (Decimal(units) * Decimal(1).scaleb(-places)).quantize(
Decimal(1).scaleb(-places)
)
@given(money_tuples())
def test_money_round_trips_through_cents(pair: "tuple[Decimal, str]") -> None:
amount, currency = pair
units = to_minor_units(amount, currency)
assert from_minor_units(units, currency) == amount
# --- geo: a bounding box always contains its own corners --------------------
@given(bounding_boxes())
def test_bbox_contains_corners(bbox: "tuple[float, float, float, float]") -> None:
west, south, east, north = bbox
corners = [(west, south), (west, north), (east, south), (east, north)]
for lon, lat in corners:
assert west <= lon <= east
assert south <= lat <= north
# --- json: any generated value round-trips through json.dumps/loads ---------
@given(json_values())
def test_json_round_trips(value: Any) -> None:
assert json.loads(json.dumps(value, allow_nan=False)) == value
# --- net: a generated URL parses cleanly with urllib.parse ------------------
@given(urls())
def test_url_parses(url: str) -> None:
parts = urlsplit(url)
assert parts.scheme and parts.hostname
assert parts.geturl() == url
# --- net: a generated IP is always valid per the stdlib ---------------------
@given(ip_addresses())
def test_ip_is_valid(addr: str) -> None:
assert ipaddress.ip_address(addr).version in (4, 6)
# --- text: emails survive a naive "local@domain" split ----------------------
@given(emails())
def test_email_splits_cleanly(email: str) -> None:
local, sep, domain = email.partition("@")
assert sep == "@"
assert local and domain and "@" not in domain
# --- dates: a DST-safe datetime is stable under a UTC round-trip ------------
@given(dst_safe_datetimes())
def test_dst_safe_datetime_stable(dt: datetime) -> None:
assert dt == dt.astimezone(timezone.utc).astimezone(dt.tzinfo)