mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-08 19:56:53 +00:00
Merge branch 'feature/updater' into feature/store-i18n
16 conflicts, resolved per hunk: - sys-stk-front-proxy: tasks/main.yml keeps the network-sibling loop; the single stack_host_contents write of http.conf plus vhost and the front_proxy_vhost_changed gate move into tasks/vhost.yml, with the http.conf decided per sibling domain so an onion vhost gets no HTTPS redirect. - sys-front-tls: 01_http.yml is gone as the updater intends; the cert-renew include in main.yml gates on the vhost domain's TLS. - sys-svc-compose: the updater's resolve_host_cid without the remote echo and the prepared pending flag in up.yml, written with IS_SWARM_MODE and the repeated-when block grouping. - web-app-nextcloud: the updater's post-upgrade gate and pending-only plugin configuration, which already carry the pinned-plugin handling. - web-app-xwiki: one XWIKI_CORE_VERSION for the readiness probe and style.css.j2; OIDC/LDAP follow xwiki_extensions_ready at bootstrap, the S3 blob store stays off there as TestBlobstoreRenderOrder demands. - roles_with_service: onion swap and dashboard card key combined, the canonical domain is the canonical URL's host in both branches. - sys-ctl-hlth-csp: --clearnet-timeout and --concurrency together; run_checker forwards its options to build_docker_cmd, keeping the script under the 500-line limit. - web-app-matrix: Element branding and web_client_location side by side. - web-app-stalwart/README.md: the generated Cosmos section is dropped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
99123762df
72 files changed
+2844
-345
No files matched your search
@@ -0,0 +1,46 @@
|
||||
"""Unit tests for ``utils/roles/applications/dashboard.py``."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
|
||||
from ansible.errors import AnsibleError
|
||||
|
||||
from utils.roles.applications.dashboard import card_target_key
|
||||
|
||||
|
||||
class TestCardTargetKey(unittest.TestCase):
|
||||
def test_a_binding_without_the_key_targets_the_primary_domain(self):
|
||||
self.assertEqual(
|
||||
"", card_target_key("web-app-foo", {"enabled": True, "shared": True})
|
||||
)
|
||||
|
||||
def test_a_missing_binding_targets_the_primary_domain(self):
|
||||
self.assertEqual("", card_target_key("web-app-foo", None))
|
||||
|
||||
def test_the_named_key_is_returned(self):
|
||||
self.assertEqual(
|
||||
"element",
|
||||
card_target_key("web-app-matrix", {"card": "element"}),
|
||||
)
|
||||
|
||||
def test_surrounding_whitespace_is_dropped(self):
|
||||
self.assertEqual(
|
||||
"webmail",
|
||||
card_target_key("web-app-stalwart", {"card": " webmail "}),
|
||||
)
|
||||
|
||||
def test_an_alias_canonical_key_is_not_a_card_target(self):
|
||||
self.assertEqual("", card_target_key("web-svc-cdn", {"canonical": "cdn"}))
|
||||
|
||||
def test_an_empty_key_fails_loudly(self):
|
||||
with self.assertRaisesRegex(AnsibleError, "web-app-foo"):
|
||||
card_target_key("web-app-foo", {"card": " "})
|
||||
|
||||
def test_a_non_string_key_fails_loudly(self):
|
||||
with self.assertRaises(AnsibleError):
|
||||
card_target_key("web-app-foo", {"card": ["element"]})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,73 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
import io
|
||||
import json
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.testing import patch_module_args
|
||||
|
||||
from utils.directory_batch import ensure_all
|
||||
|
||||
|
||||
def _entry(path, mode="0755"):
|
||||
return {"path": str(path), "mode": mode, "owner": None, "group": None}
|
||||
|
||||
|
||||
class EnsureAllTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self._tmp = tempfile.TemporaryDirectory()
|
||||
self.root = Path(self._tmp.name)
|
||||
|
||||
def tearDown(self):
|
||||
self._tmp.cleanup()
|
||||
|
||||
def _ensure(self, entries, *, check_mode=False):
|
||||
with patch_module_args({"_ansible_check_mode": check_mode}):
|
||||
module = AnsibleModule(argument_spec={}, supports_check_mode=True)
|
||||
return ensure_all(module, entries)
|
||||
|
||||
def test_returns_the_changed_paths_in_order_with_one_diff_each(self):
|
||||
first = self.root / "a"
|
||||
second = self.root / "b"
|
||||
changed_paths, diffs = self._ensure([_entry(first), _entry(second)])
|
||||
self.assertEqual(changed_paths, [str(first), str(second)])
|
||||
self.assertEqual(
|
||||
[diff["after"]["path"] for diff in diffs], [str(first), str(second)]
|
||||
)
|
||||
|
||||
def test_unchanged_paths_are_left_out(self):
|
||||
existing = self.root / "kept"
|
||||
existing.mkdir()
|
||||
existing.chmod(0o755)
|
||||
self.assertEqual(self._ensure([_entry(existing)]), ([], []))
|
||||
|
||||
def test_empty_entries_change_nothing(self):
|
||||
self.assertEqual(self._ensure([]), ([], []))
|
||||
|
||||
def test_check_mode_reports_without_creating(self):
|
||||
target = self.root / "new"
|
||||
changed_paths, _ = self._ensure([_entry(target)], check_mode=True)
|
||||
self.assertEqual(changed_paths, [str(target)])
|
||||
self.assertFalse(target.exists())
|
||||
|
||||
def test_first_error_fails_with_the_paths_changed_before_it(self):
|
||||
created = self.root / "created"
|
||||
blocker = self.root / "blocker"
|
||||
never = self.root / "never"
|
||||
blocker.write_text("", encoding="utf-8")
|
||||
buffer = io.StringIO()
|
||||
with contextlib.redirect_stdout(buffer), self.assertRaises(SystemExit):
|
||||
self._ensure([_entry(created), _entry(blocker), _entry(never)])
|
||||
result = json.loads(buffer.getvalue())
|
||||
self.assertTrue(result["failed"])
|
||||
self.assertEqual(result["path"], str(blocker))
|
||||
self.assertEqual(result["changed_paths"], [str(created)])
|
||||
self.assertFalse(never.exists())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user