from __future__ import annotations import importlib.util import unittest import urllib.error from unittest import mock from . import PROJECT_ROOT _SOURCE = PROJECT_ROOT / "roles" / "web-app-docs" / "files" / "test" / "extract.py" _spec = importlib.util.spec_from_file_location("docs_test_extract", _SOURCE) extract = importlib.util.module_from_spec(_spec) _spec.loader.exec_module(extract) PAGE = """\

Quick Setup

Development

\
make onboard

Production

\
APP=web-app-x
echo "a & b"
""" WITHOUT = '

Overview

No setup here.

' class TestBlock(unittest.TestCase): def _parsed(self, page: str) -> str: parser = extract.Block() parser.feed(page) return parser.text def test_it_takes_the_block_below_the_production_heading(self) -> None: text = self._parsed(PAGE) self.assertIn("APP=web-app-x", text) self.assertNotIn("make onboard", text) def test_entities_come_back_as_characters(self) -> None: self.assertIn('"a & b"', self._parsed(PAGE)) def test_highlight_markup_does_not_reach_the_commands(self) -> None: self.assertNotIn(" None: self.assertEqual(self._parsed(WITHOUT).strip(), "") class TestPageUrl(unittest.TestCase): def test_it_addresses_the_deployed_working_tree(self) -> None: """`latest` is the newest commit, which need not carry this role. A role renamed or added since that commit has no page there, and the test then reports a missing production block for a page the version never had. The deployed working tree is the one the run just built. """ self.assertEqual( extract.page_url("http://host:8036/", "web-app-x"), "http://host:8036/deployed/roles/web-app-x/README.html", ) class TestFetch(unittest.TestCase): def _response(self, status: int, body: bytes = b""): response = mock.MagicMock() response.status = status response.read.return_value = body response.__enter__.return_value = response return response def test_a_ready_page_is_returned_at_once(self) -> None: with mock.patch.object(extract.urllib.request, "urlopen") as opener: opener.return_value = self._response(200, b"

ok

") self.assertEqual(extract.fetch("http://x/", timeout=1), "

ok

") def test_it_waits_while_the_version_is_building(self) -> None: answers = [self._response(202), self._response(200, b"

ok

")] with ( mock.patch.object(extract.urllib.request, "urlopen", side_effect=answers), mock.patch.object(extract.time, "sleep"), ): self.assertEqual( extract.fetch("http://x/", timeout=60, poll=0), "

ok

" ) def test_a_build_that_never_finishes_times_out(self) -> None: with ( mock.patch.object(extract.urllib.request, "urlopen") as opener, mock.patch.object(extract.time, "sleep"), ): opener.return_value = self._response(202) with self.assertRaises(TimeoutError): extract.fetch("http://x/", timeout=0, poll=0) def test_an_error_other_than_building_is_raised(self) -> None: failure = urllib.error.HTTPError("http://x/", 404, "gone", {}, None) with ( mock.patch.object(extract.urllib.request, "urlopen", side_effect=failure), self.assertRaises(urllib.error.HTTPError), ): extract.fetch("http://x/", timeout=1) class TestBlockEntry(unittest.TestCase): def test_a_page_without_instructions_is_an_error(self) -> None: with ( mock.patch.object(extract, "fetch", return_value=WITHOUT), self.assertRaises(ValueError), ): extract.block("http://x/", "web-app-x", timeout=1) if __name__ == "__main__": unittest.main()