Add CNAME create update and delete tools
This commit is contained in:
@@ -5,6 +5,7 @@ import importlib.util
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
import urllib.parse
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
@@ -39,6 +40,19 @@ class FakeOpener:
|
||||
|
||||
|
||||
class StratoParserTests(unittest.TestCase):
|
||||
@staticmethod
|
||||
def dns_form(*records: tuple[str, str, str]) -> str:
|
||||
fields = []
|
||||
for record_type, prefix, value in records:
|
||||
fields.append(
|
||||
f'<select name="type"><option value="{record_type}" selected>'
|
||||
f'{record_type}</option></select>'
|
||||
f'<input name="prefix" value="{prefix}">'
|
||||
f'<textarea name="value">{value}</textarea>'
|
||||
)
|
||||
fields.append('<input name="action_change_txt_records" value="Speichern">')
|
||||
return "".join(fields)
|
||||
|
||||
def test_dns_form_is_parsed_without_script_or_markup(self) -> None:
|
||||
html = """
|
||||
<select name="type"><option value="TXT">TXT</option><option value="CNAME" selected>CNAME</option></select>
|
||||
@@ -47,6 +61,7 @@ class StratoParserTests(unittest.TestCase):
|
||||
<select name="type"><option value="TXT" selected>TXT</option></select>
|
||||
<input name="prefix" value="_acme-challenge">
|
||||
<textarea name="value">public-validation-value</textarea>
|
||||
<input name="action_change_txt_records" value="Speichern">
|
||||
"""
|
||||
records = module.parse_records(html)
|
||||
self.assertEqual(records[0].as_dict(), {
|
||||
@@ -91,11 +106,7 @@ class StratoParserTests(unittest.TestCase):
|
||||
<table id="package_list"><tr><td>example.de Hosting</td>
|
||||
<td><a href="?node=x&cID=42">open</a></td></tr></table>
|
||||
"""
|
||||
records_html = """
|
||||
<select name="type"><option value="CNAME" selected>CNAME</option></select>
|
||||
<input name="prefix" value="media">
|
||||
<textarea name="value">proxy.example.net.</textarea>
|
||||
"""
|
||||
records_html = self.dns_form(("CNAME", "media", "proxy.example.net."))
|
||||
opener = FakeOpener([
|
||||
FakeResponse(module.STRATO_URL, "login"),
|
||||
FakeResponse(module.STRATO_URL + "?sessionID=test-session", "welcome"),
|
||||
@@ -111,6 +122,133 @@ class StratoParserTests(unittest.TestCase):
|
||||
self.assertEqual(methods, ["GET", "POST", "GET", "GET"])
|
||||
self.assertNotIn("secret-value", opener.requests[1][0].full_url)
|
||||
|
||||
def client_for_write(self, responses: list[FakeResponse]) -> tuple[object, FakeOpener]:
|
||||
opener = FakeOpener(responses)
|
||||
config = module.StratoConfig(
|
||||
"customer", "secret-value", "example.de",
|
||||
package_id="42", write_enabled=True,
|
||||
)
|
||||
client = module.StratoClient(config, opener=opener, sleep=lambda _seconds: None)
|
||||
client.session_id = "test-session"
|
||||
return client, opener
|
||||
|
||||
@staticmethod
|
||||
def posted_form(opener: FakeOpener) -> dict[str, list[str]]:
|
||||
posts = [request for request, _timeout in opener.requests if request.method == "POST"]
|
||||
assert len(posts) == 1
|
||||
return urllib.parse.parse_qs(posts[0].data.decode(), keep_blank_values=True)
|
||||
|
||||
def test_create_cname_preserves_all_existing_records_and_verifies(self) -> None:
|
||||
before = self.dns_form(
|
||||
("TXT", "_acme-challenge", "validation-value"),
|
||||
("CNAME", "media", "proxy.example.net."),
|
||||
)
|
||||
after = self.dns_form(
|
||||
("TXT", "_acme-challenge", "validation-value"),
|
||||
("CNAME", "media", "proxy.example.net."),
|
||||
("CNAME", "chat", "gateway.example.net"),
|
||||
)
|
||||
client, opener = self.client_for_write([
|
||||
FakeResponse(module.STRATO_URL, before),
|
||||
FakeResponse(module.STRATO_URL, "saved"),
|
||||
FakeResponse(module.STRATO_URL, after),
|
||||
])
|
||||
|
||||
created = client.create_cname("chat.example.de", "gateway.example.net.")
|
||||
|
||||
self.assertEqual(created.as_dict(), {
|
||||
"type": "CNAME", "prefix": "chat", "value": "gateway.example.net"
|
||||
})
|
||||
posted = self.posted_form(opener)
|
||||
self.assertEqual(posted["type"], ["TXT", "CNAME", "CNAME"])
|
||||
self.assertEqual(posted["prefix"], ["_acme-challenge", "media", "chat"])
|
||||
self.assertEqual(
|
||||
posted["value"],
|
||||
["validation-value", "proxy.example.net.", "gateway.example.net"],
|
||||
)
|
||||
self.assertEqual(posted["action_change_txt_records"], ["Speichern"])
|
||||
|
||||
def test_update_cname_can_change_target_and_prefix(self) -> None:
|
||||
before = self.dns_form(
|
||||
("TXT", "verification", "keep-me"),
|
||||
("CNAME", "old", "old-target.example.net"),
|
||||
)
|
||||
after = self.dns_form(
|
||||
("TXT", "verification", "keep-me"),
|
||||
("CNAME", "new", "new-target.example.net"),
|
||||
)
|
||||
client, opener = self.client_for_write([
|
||||
FakeResponse(module.STRATO_URL, before),
|
||||
FakeResponse(module.STRATO_URL, "saved"),
|
||||
FakeResponse(module.STRATO_URL, after),
|
||||
])
|
||||
|
||||
old, new = client.update_cname("old", "new-target.example.net", new_prefix="new")
|
||||
|
||||
self.assertEqual(old.prefix, "old")
|
||||
self.assertEqual(new.prefix, "new")
|
||||
posted = self.posted_form(opener)
|
||||
self.assertEqual(posted["prefix"], ["verification", "new"])
|
||||
self.assertEqual(posted["value"], ["keep-me", "new-target.example.net"])
|
||||
|
||||
def test_delete_cname_preserves_unrelated_records_and_verifies(self) -> None:
|
||||
before = self.dns_form(
|
||||
("TXT", "verification", "keep-me"),
|
||||
("CNAME", "remove", "old.example.net"),
|
||||
("CNAME", "keep", "keep.example.net"),
|
||||
)
|
||||
after = self.dns_form(
|
||||
("TXT", "verification", "keep-me"),
|
||||
("CNAME", "keep", "keep.example.net"),
|
||||
)
|
||||
client, opener = self.client_for_write([
|
||||
FakeResponse(module.STRATO_URL, before),
|
||||
FakeResponse(module.STRATO_URL, "saved"),
|
||||
FakeResponse(module.STRATO_URL, after),
|
||||
])
|
||||
|
||||
deleted = client.delete_cname("remove")
|
||||
|
||||
self.assertEqual(deleted.prefix, "remove")
|
||||
posted = self.posted_form(opener)
|
||||
self.assertEqual(posted["prefix"], ["verification", "keep"])
|
||||
self.assertEqual(posted["value"], ["keep-me", "keep.example.net"])
|
||||
|
||||
def test_write_is_blocked_without_explicit_enable_flag(self) -> None:
|
||||
opener = FakeOpener([
|
||||
FakeResponse(module.STRATO_URL, self.dns_form(
|
||||
("CNAME", "media", "proxy.example.net")
|
||||
)),
|
||||
])
|
||||
config = module.StratoConfig(
|
||||
"customer", "secret-value", "example.de", package_id="42"
|
||||
)
|
||||
client = module.StratoClient(config, opener=opener)
|
||||
client.session_id = "test-session"
|
||||
|
||||
with self.assertRaises(module.StratoWriteDisabledError):
|
||||
client.delete_cname("media")
|
||||
self.assertEqual([request.method for request, _ in opener.requests], ["GET"])
|
||||
|
||||
def test_invalid_names_and_collisions_fail_before_post(self) -> None:
|
||||
with self.assertRaises(module.StratoError):
|
||||
module.normalize_cname_prefix("@", "example.de")
|
||||
with self.assertRaises(module.StratoError):
|
||||
module.normalize_cname_prefix("foo.*", "example.de")
|
||||
with self.assertRaises(module.StratoError):
|
||||
module.normalize_cname_target("https://target.example.net/path")
|
||||
with self.assertRaises(module.StratoError):
|
||||
module.normalize_cname_target("target..example.net")
|
||||
|
||||
client, opener = self.client_for_write([
|
||||
FakeResponse(module.STRATO_URL, self.dns_form(
|
||||
("TXT", "chat", "already-used")
|
||||
)),
|
||||
])
|
||||
with self.assertRaisesRegex(module.StratoError, "already exists"):
|
||||
client.create_cname("chat", "target.example.net")
|
||||
self.assertEqual([request.method for request, _ in opener.requests], ["GET"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user