fix: DDNS update token in body, webdav gating, regression tests
Unit Tests / test (push) Successful in 7m25s
Unit Tests / test (push) Successful in 7m25s
- PicNgoDDNS.update(): send token in request body instead of Authorization header; DDNS server validates it from body (was returning HTTP 422 on every heartbeat, leaving IP record stale after fresh install) - peers.py / Peers.jsx: webdav service_access only valid when 'files' store service is installed; was always shown even with no services, confusing users into thinking WebDAV was pre-installed - 10 new regression tests: DDNS update body contract, Caddy always regenerates on startup with no services, peer role allowed on /api/services/active, webdav gating by installed services Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,7 @@ from ddns_manager import (
|
||||
DDNSManager,
|
||||
DDNSProvider,
|
||||
DDNSError,
|
||||
DDNSTokenExpired,
|
||||
PicNgoDDNS,
|
||||
CloudflareDDNS,
|
||||
DuckDNSDDNS,
|
||||
@@ -103,9 +104,9 @@ class TestPicNgoDDNSRegister(unittest.TestCase):
|
||||
|
||||
|
||||
class TestPicNgoDDNSUpdate(unittest.TestCase):
|
||||
"""PicNgoDDNS.update() calls the correct URL with Authorization header."""
|
||||
"""PicNgoDDNS.update() sends token in the request body (DDNS server validates it there)."""
|
||||
|
||||
def test_update_uses_bearer_token(self):
|
||||
def test_update_sends_token_in_body(self):
|
||||
provider = PicNgoDDNS(api_base_url='https://ddns.example.com')
|
||||
mock_resp = _make_response(200)
|
||||
with patch('requests.put', return_value=mock_resp) as mock_put:
|
||||
@@ -113,11 +114,19 @@ class TestPicNgoDDNSUpdate(unittest.TestCase):
|
||||
mock_put.assert_called_once()
|
||||
args, kwargs = mock_put.call_args
|
||||
self.assertEqual(args[0], 'https://ddns.example.com/api/v1/update')
|
||||
self.assertIn('Authorization', kwargs['headers'])
|
||||
self.assertEqual(kwargs['headers']['Authorization'], 'Bearer mytoken')
|
||||
self.assertEqual(kwargs['json'], {'ip': '5.6.7.8'})
|
||||
# Token must be in the JSON body — server validates it there, not in Authorization
|
||||
self.assertEqual(kwargs['json'], {'ip': '5.6.7.8', 'token': 'mytoken'})
|
||||
self.assertTrue(result)
|
||||
|
||||
def test_update_does_not_use_bearer_header(self):
|
||||
"""Token must NOT be sent as Authorization: Bearer — server ignores it and returns 422."""
|
||||
provider = PicNgoDDNS(api_base_url='https://ddns.example.com')
|
||||
mock_resp = _make_response(200)
|
||||
with patch('requests.put', return_value=mock_resp) as mock_put:
|
||||
provider.update('mytoken', '1.2.3.4')
|
||||
_, kwargs = mock_put.call_args
|
||||
self.assertNotIn('Authorization', kwargs.get('headers', {}))
|
||||
|
||||
def test_update_raises_ddns_error_on_failure(self):
|
||||
provider = PicNgoDDNS()
|
||||
mock_resp = _make_response(403, text='Forbidden')
|
||||
@@ -125,6 +134,13 @@ class TestPicNgoDDNSUpdate(unittest.TestCase):
|
||||
with self.assertRaises(DDNSError):
|
||||
provider.update('badtoken', '1.2.3.4')
|
||||
|
||||
def test_update_raises_ddns_token_expired_on_401(self):
|
||||
provider = PicNgoDDNS()
|
||||
mock_resp = _make_response(401, text='Unauthorized')
|
||||
with patch('requests.put', return_value=mock_resp):
|
||||
with self.assertRaises(DDNSTokenExpired):
|
||||
provider.update('expiredtoken', '1.2.3.4')
|
||||
|
||||
|
||||
class TestPicNgoDDNSChallenges(unittest.TestCase):
|
||||
"""PicNgoDDNS.dns_challenge_create/delete call correct endpoints."""
|
||||
|
||||
Reference in New Issue
Block a user