test: cover the validator, the directory and the site
This commit is contained in:
parent
003bb0b4b1
commit
1e75a69381
44 changed files with 1796 additions and 0 deletions
166
tests/test_resources.py
Normal file
166
tests/test_resources.py
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
"""The checks that read a page's stylesheet and images from a live server."""
|
||||
|
||||
from pathlib import Path
|
||||
import zlib
|
||||
|
||||
import pytest
|
||||
|
||||
from mews.fetch import Fetcher
|
||||
from mews.lint import validate_bytes
|
||||
|
||||
CANONICAL = (Path(__file__).resolve().parent.parent / "mews-0.1.css").read_bytes()
|
||||
FONT_FACE = b"""@font-face {
|
||||
font-family: "Atkinson Hyperlegible";
|
||||
src: url(/fonts/atkinson.woff2) format("woff2");
|
||||
}
|
||||
|
||||
"""
|
||||
PNG = (
|
||||
b"\x89PNG\r\n\x1a\n"
|
||||
b"\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00"
|
||||
b"\x1f\x15\xc4\x89"
|
||||
b"\x00\x00\x00\x00IEND\xaeB`\x82"
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def check(site):
|
||||
"""Validate a page served by the local server, reading its sub-resources."""
|
||||
|
||||
def run(page: bytes, path: str = "/index.html"):
|
||||
url = site.add(path, page, headers={"ETag": '"v1"'})
|
||||
with Fetcher(allow_loopback=True) as fetcher:
|
||||
return validate_bytes(page, url=url, fetcher=fetcher)
|
||||
|
||||
return run
|
||||
|
||||
|
||||
def codes(report):
|
||||
return [finding.code for finding in report.findings]
|
||||
|
||||
|
||||
def test_an_unmodified_stylesheet_copy_passes(site, check, conforming):
|
||||
site.add("/mews-0.1.css", CANONICAL)
|
||||
report = check(conforming())
|
||||
assert "stylesheet-modified" not in codes(report)
|
||||
|
||||
|
||||
def test_an_added_font_face_passes(site, check, conforming):
|
||||
site.add("/mews-0.1.css", FONT_FACE + CANONICAL)
|
||||
site.add("/fonts/atkinson.woff2", b"font")
|
||||
report = check(conforming())
|
||||
assert "stylesheet-modified" not in codes(report)
|
||||
assert "font-offsite" not in codes(report)
|
||||
|
||||
|
||||
def test_a_changed_stylesheet_fails(site, check, conforming):
|
||||
site.add("/mews-0.1.css", CANONICAL.replace(b"#faf8f3", b"#ffffff"))
|
||||
report = check(conforming())
|
||||
failure = [f for f in report.findings if f.code == "stylesheet-modified"]
|
||||
assert failure and failure[0].section == "5.2"
|
||||
assert not report.conforms
|
||||
|
||||
|
||||
def test_a_font_from_another_site_fails(site, check, conforming):
|
||||
off = FONT_FACE.replace(b"/fonts/atkinson.woff2", b"https://fonts.example.net/a")
|
||||
site.add("/mews-0.1.css", off + CANONICAL)
|
||||
report = check(conforming())
|
||||
assert "font-offsite" in codes(report)
|
||||
assert not report.conforms
|
||||
|
||||
|
||||
def test_an_import_in_the_stylesheet_fails(site, check, conforming):
|
||||
site.add("/mews-0.1.css", b'@import url("x.css");\n' + CANONICAL)
|
||||
report = check(conforming())
|
||||
assert "stylesheet-import" in codes(report)
|
||||
|
||||
|
||||
def test_a_missing_stylesheet_only_warns(site, check, conforming):
|
||||
report = check(conforming())
|
||||
warning = [f for f in report.findings if f.code == "stylesheet-unreachable"]
|
||||
assert warning and warning[0].level == "should"
|
||||
|
||||
|
||||
def test_a_stylesheet_on_another_site_fails(site, check, conforming):
|
||||
site.add("/mews-0.1.css", CANONICAL)
|
||||
report = check(conforming(stylesheet="https://cdn.example.net/mews-0.1.css"))
|
||||
assert "stylesheet-offsite" in codes(report)
|
||||
assert not report.conforms
|
||||
|
||||
|
||||
def test_the_central_stylesheet_only_warns(site, check, conforming):
|
||||
"""Linking mews.page's own copy is discouraged, not a failure."""
|
||||
report = check(conforming(stylesheet="https://mews.page/mews-0.1.css"))
|
||||
assert "stylesheet-offsite" not in codes(report)
|
||||
canonical = [f for f in report.findings if f.code == "stylesheet-canonical"]
|
||||
assert canonical and canonical[0].level == "should"
|
||||
|
||||
|
||||
def test_a_cookie_on_the_stylesheet_fails(site, check, conforming):
|
||||
site.add("/mews-0.1.css", CANONICAL, headers={"Set-Cookie": "a=1"})
|
||||
report = check(conforming())
|
||||
assert "cookie" in codes(report)
|
||||
assert not report.conforms
|
||||
|
||||
|
||||
# --- images ------------------------------------------------------------
|
||||
|
||||
|
||||
def test_a_small_png_passes(site, check, conforming):
|
||||
site.add("/mews-0.1.css", CANONICAL)
|
||||
site.add("/a.png", PNG, headers={"Content-Type": "image/png"})
|
||||
report = check(conforming(body='<p><img src="a.png" alt="A dot" /></p>'))
|
||||
assert report.conforms
|
||||
assert not report.warnings
|
||||
|
||||
|
||||
def test_an_image_that_is_not_an_image_warns(site, check, conforming):
|
||||
site.add("/mews-0.1.css", CANONICAL)
|
||||
site.add("/a.png", b"<svg/>")
|
||||
report = check(conforming(body='<p><img src="a.png" alt="x" /></p>'))
|
||||
warning = [f for f in report.findings if f.code == "image-format"]
|
||||
assert warning and warning[0].level == "should"
|
||||
assert report.conforms
|
||||
|
||||
|
||||
def test_a_large_image_warns(site, check, conforming):
|
||||
site.add("/mews-0.1.css", CANONICAL)
|
||||
site.add("/a.png", PNG + b"\x00" * 60_000)
|
||||
report = check(conforming(body='<p><img src="a.png" alt="x" /></p>'))
|
||||
assert "image-size" in codes(report)
|
||||
|
||||
|
||||
def test_camera_metadata_warns(site, check, conforming):
|
||||
site.add("/mews-0.1.css", CANONICAL)
|
||||
site.add("/a.jpg", b"\xff\xd8\xff\xe1\x00\x16Exif\x00\x00" + b"\x00" * 100)
|
||||
report = check(conforming(body='<p><img src="a.jpg" alt="x" /></p>'))
|
||||
assert "image-metadata" in codes(report)
|
||||
|
||||
|
||||
def test_a_missing_image_warns(site, check, conforming):
|
||||
site.add("/mews-0.1.css", CANONICAL)
|
||||
report = check(conforming(body='<p><img src="gone.png" alt="x" /></p>'))
|
||||
assert "image-unreachable" in codes(report)
|
||||
|
||||
|
||||
def test_only_the_first_twenty_images_are_read(site, check, conforming):
|
||||
site.add("/mews-0.1.css", CANONICAL)
|
||||
site.add("/a.png", PNG)
|
||||
body = "".join(f'<p><img src="a.png?{i}" alt="x" /></p>' for i in range(25))
|
||||
report = check(conforming(body=body))
|
||||
assert "images-not-checked" in codes(report)
|
||||
assert len([r for r in site.requests if r[0].startswith("/a.png")]) == 20
|
||||
|
||||
|
||||
def test_a_compression_bomb_is_refused(site, conforming):
|
||||
"""A short download that expands enormously is not a page."""
|
||||
body = zlib.compress(b"x" * 10_000_000)
|
||||
url = site.add(
|
||||
"/big.html",
|
||||
body,
|
||||
headers={"Content-Encoding": "deflate", "Content-Type": "text/html"},
|
||||
declare_length=False,
|
||||
)
|
||||
with Fetcher(allow_loopback=True) as fetcher:
|
||||
response = fetcher.get(url)
|
||||
assert response.truncated
|
||||
Loading…
Add table
Add a link
Reference in a new issue