forked from OurHike/OurHike
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_lib_commons.py
More file actions
281 lines (209 loc) · 10.7 KB
/
Copy pathtest_lib_commons.py
File metadata and controls
281 lines (209 loc) · 10.7 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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
"""Tests for lib/commons.py - the pure eligibility/selection rules deciding
which Wikimedia Commons files may ship on a waypoint card. All fixtures are
tiny synthetic imageinfo dicts built in test code (see TESTING.md); the API
shapes they imitate were checked against real Commons API responses while
building fetch_poi_images.py (2026-08-07).
"""
from datetime import date
import pytest
from lib.commons import (
eligible_photo,
license_allows_reuse,
may_be_a_jpeg,
meta_value,
parse_date_taken,
pick_photo,
strip_html,
)
CUTOFF = date(2024, 8, 7)
def _meta(value):
return {"value": value, "source": "commons-desc-page"}
def _imageinfo(
taken="2025-06-18 14:22:31",
license_id="cc-by-sa-4.0",
license_short="CC BY-SA 4.0",
artist='<a href="//commons.wikimedia.org/wiki/User:JD">Jane Doe</a>',
attribution=None,
mime="image/jpeg",
thumburl="https://upload.wikimedia.org/thumb/shelter-640.jpg",
url="https://upload.wikimedia.org/shelter.jpg",
):
"""One imageinfo entry the way the API really shapes it - extmetadata
values wrapped in {"value": ...} dicts, keys absent when unknown."""
extmetadata = {}
if taken is not None:
extmetadata["DateTimeOriginal"] = _meta(taken)
if license_id is not None:
extmetadata["License"] = _meta(license_id)
if license_short is not None:
extmetadata["LicenseShortName"] = _meta(license_short)
if artist is not None:
extmetadata["Artist"] = _meta(artist)
if attribution is not None:
extmetadata["Attribution"] = _meta(attribution)
info = {"mime": mime, "url": url, "descriptionurl": "https://commons.wikimedia.org/wiki/File:Shelter.jpg"}
if thumburl is not None:
info["thumburl"] = thumburl
return {**info, "extmetadata": extmetadata}
# --- license_allows_reuse ---
@pytest.mark.parametrize(
("license_id", "allowed"),
[
("cc-by-sa-4.0", True),
("cc-by-4.0", True),
("CC-BY-SA-4.0", True), # case must not matter
("cc0", True),
("pd", True),
("pd-us", True),
("cc-by-nc-4.0", False),
("cc-by-nc-sa-2.0", False),
("cc-by-nd-4.0", False),
("gfdl", False),
("copyrighted-free-use-maybe", False), # unknown ids are rejected, not shipped-and-hoped
("", False),
],
)
def test_license_allows_reuse(license_id, allowed):
assert license_allows_reuse(license_id) is allowed
def test_nc_is_rejected_even_though_the_id_starts_with_cc_by():
"""The trap a naive prefix allowlist would fall into: "cc-by-nc-4.0"
starts with "cc-by", so prefix matching would ship NonCommercial photos -
putting any future paid tier (features/PRICING_MODEL.md territory) in
licence breach the day it launches."""
assert license_allows_reuse("cc-by-nc-4.0") is False
assert license_allows_reuse("cc-by-4.0") is True
@pytest.mark.parametrize("license_id", ["cc-by-2.0", "cc-by-2.5", "cc-by-3.0", "cc-by-sa-2.0", "cc-by-sa-3.0"])
def test_pre_4_0_cc_licences_are_rejected_because_one_link_cannot_meet_them(license_id):
"""CC 4.0's s3(a)(2) lets a single link to the Commons file page satisfy
attribution; the 2.0/2.5/3.0 licences instead require the licence URI
itself to ship with every copy, which the card's one-link credit line
does not do. Realistic input, not a corner: Flickr-to-Commons imports
arrive as cc-by-2.0 with current EXIF dates, so without this gate a
fresh, ineligible photo ships in breach."""
assert license_allows_reuse(license_id) is False
# --- parse_date_taken ---
@pytest.mark.parametrize(
("raw", "expected"),
[
("2025-06-18 14:22:31", date(2025, 6, 18)), # the common Commons shape
("2025:06:18 14:22:31", date(2025, 6, 18)), # raw EXIF colons
('<time class="dtstart" datetime="2013-07-29">29 July 2013</time>', date(2013, 7, 29)),
("May 2016", None), # a prose age cannot honestly pass a freshness bar
("circa 1998", None),
("", None),
("2025-99-99", None), # digits in the right shape but not a date
],
)
def test_parse_date_taken(raw, expected):
assert parse_date_taken(raw) == expected
# --- strip_html / meta_value ---
def test_strip_html_flattens_commons_artist_markup_to_a_creditable_name():
assert strip_html('<a href="//commons.wikimedia.org/wiki/User:JD">Jane Doe</a>') == "Jane Doe"
assert strip_html(" plain name ") == "plain name"
def test_meta_value_reads_the_api_wrapper_and_tolerates_absent_keys():
extmetadata = {"License": _meta("cc0"), "Artist": {"value": None}}
assert meta_value(extmetadata, "License") == "cc0"
assert meta_value(extmetadata, "Artist") == ""
assert meta_value(extmetadata, "DateTimeOriginal") == ""
# --- eligible_photo ---
def test_eligible_photo_builds_the_full_record_for_a_shippable_file():
record = eligible_photo("File:Shelter.jpg", 42.5, _imageinfo(), cutoff=CUTOFF)
assert record == {
"title": "File:Shelter.jpg",
"distance_m": 42.5,
"url": "https://upload.wikimedia.org/thumb/shelter-640.jpg", # the sized thumb, not the original
"page_url": "https://commons.wikimedia.org/wiki/File:Shelter.jpg",
"author": "Jane Doe",
"license": "CC BY-SA 4.0",
"taken": "2025-06-18",
}
def test_eligible_photo_rejects_non_jpeg_files():
"""The mime gate is the maps-and-diagrams filter: Commons geosearch
happily returns SVG trail maps and PNG elevation charts sitting on the
same coordinates, and none of those are "a picture of the place"."""
assert eligible_photo("File:Map.svg", 5.0, _imageinfo(mime="image/svg+xml"), cutoff=CUTOFF) is None
assert eligible_photo("File:Chart.png", 5.0, _imageinfo(mime="image/png"), cutoff=CUTOFF) is None
def test_eligible_photo_rejects_a_photo_older_than_the_cutoff():
assert eligible_photo("File:Old.jpg", 5.0, _imageinfo(taken="2019-05-01 09:00:00"), cutoff=CUTOFF) is None
def test_eligible_photo_rejects_a_file_with_no_parseable_capture_date():
"""No EXIF date, no deal - both because an unknown age cannot pass an
age bar, and because scans/diagrams/renders are exactly the files that
lack one."""
assert eligible_photo("File:Scan.jpg", 5.0, _imageinfo(taken=None), cutoff=CUTOFF) is None
assert eligible_photo("File:Scan.jpg", 5.0, _imageinfo(taken="around 2003"), cutoff=CUTOFF) is None
def test_eligible_photo_rejects_licences_ourhike_cannot_meet():
assert eligible_photo("File:NC.jpg", 5.0, _imageinfo(license_id="cc-by-nc-4.0"), cutoff=CUTOFF) is None
assert eligible_photo("File:NoLicence.jpg", 5.0, _imageinfo(license_id=None), cutoff=CUTOFF) is None
def test_eligible_photo_requires_an_author_for_attribution_licences():
"""CC BY's whole deal is the credit. A CC BY photo with nobody to name
is unusable, not "usable, uncredited"."""
assert eligible_photo("File:Anon.jpg", 5.0, _imageinfo(artist=None), cutoff=CUTOFF) is None
def test_eligible_photo_ships_public_domain_files_with_no_author_to_credit():
record = eligible_photo(
"File:PD.jpg", 5.0, _imageinfo(license_id="pd", license_short="Public domain", artist=None), cutoff=CUTOFF
)
assert record is not None
assert record["author"] is None
assert record["license"] == "Public domain"
def test_eligible_photo_honors_an_explicit_attribution_field_over_artist():
"""A file's Attribution field is the uploader saying "credit me exactly
like this" - the licence makes honoring it the condition of use."""
info = _imageinfo(attribution="Photo courtesy of the Green Mountain Club")
record = eligible_photo("File:GMC.jpg", 5.0, info, cutoff=CUTOFF)
assert record is not None
assert record["author"] == "Photo courtesy of the Green Mountain Club"
def test_eligible_photo_rejects_a_file_with_no_sized_thumbnail_rather_than_shipping_the_original():
"""Commons originals are full-resolution camera files - a shelter photo
is routinely 3-15 MB - and the card's slot is 264 CSS pixels wide.
Falling back to the original would put a multi-megabyte download on a
hiker's data plan to fill a thumbnail-sized box. No sized rendering
means no photo; the placeholder is the honest fallback."""
info = _imageinfo(thumburl=None)
assert info["url"] == "https://upload.wikimedia.org/shelter.jpg" # the original IS available
assert eligible_photo("File:Shelter.jpg", 5.0, info, cutoff=CUTOFF) is None
def test_eligible_photo_ships_the_sized_thumbnail_not_the_original():
record = eligible_photo("File:Shelter.jpg", 5.0, _imageinfo(), cutoff=CUTOFF)
assert record is not None
assert record["url"] == "https://upload.wikimedia.org/thumb/shelter-640.jpg"
assert record["url"] != _imageinfo()["url"]
def test_eligible_photo_labels_the_licence_from_its_id_when_no_short_name_exists():
record = eligible_photo("File:Shelter.jpg", 5.0, _imageinfo(license_short=None), cutoff=CUTOFF)
assert record is not None
assert record["license"] == "CC-BY-SA-4.0"
# --- pick_photo ---
def _candidate(distance_m, taken):
return {"title": f"File:{distance_m}-{taken}.jpg", "distance_m": distance_m, "taken": taken}
def test_pick_photo_prefers_the_nearest_file_over_a_newer_farther_one():
"""The closest file is the likeliest to depict the point rather than the
view from it - a month of recency does not outweigh 200 metres of
being a photo of something else."""
near_older = _candidate(12.0, "2024-10-01")
far_newer = _candidate(210.0, "2026-01-01")
assert pick_photo([far_newer, near_older]) is near_older
def test_pick_photo_breaks_a_distance_tie_toward_the_newest_capture_date():
older = _candidate(30.0, "2024-09-15")
newer = _candidate(30.0, "2026-02-20")
assert pick_photo([older, newer]) is newer
def test_pick_photo_returns_none_for_no_candidates():
assert pick_photo([]) is None
@pytest.mark.parametrize("title", ["File:Blood Mountain Shelter.jpg", "File:Spring.JPG", "File:View.jpeg"])
def test_may_be_a_jpeg_accepts_the_extensions_a_camera_photo_arrives_under(title):
assert may_be_a_jpeg(title) is True
@pytest.mark.parametrize(
"title",
[
# The one that matters: a geotagged PDF makes the batched imageinfo
# call unformable, and the API fails the whole batch rather than the
# one file (see _JPEG_TITLE_RE).
"File:Survey of the Presidency of Bengal (IA b33090993).pdf",
"File:Old trail map.djvu",
"File:Shelter walkthrough.webm",
"File:Trail map.svg",
"File:Screenshot.png",
# A file merely *mentioning* jpg is not one - the extension is the end
# of the title or it is nothing.
"File:How to convert jpg to png.pdf",
],
)
def test_may_be_a_jpeg_rejects_what_cannot_be_a_camera_photo(title):
assert may_be_a_jpeg(title) is False