Files
pic/tests/test_identity_validation.py
roof 4215e03ac6 fix: autosave, cell name overflow, length validation, apply-and-verify tests
Autosave on Apply (was broken):
- App.jsx called useDraftConfig() in the same component that rendered
  DraftConfigProvider — a component cannot consume context it provides.
  Fixed by splitting into AppCore (consumes context, all logic) and App
  (thin shell that wraps AppCore in DraftConfigProvider).  The hook now
  runs inside the provider and hasDirty()/flushAll() work correctly.

Cell name / domain length validation (255-char DNS standard):
- api/app.py: reject cell_name or domain > 255 chars or empty with 400
- api/app.py: reject ip_range without CIDR prefix (bare IPs shift all VIPs)
- webui/src/pages/Settings.jsx: cellNameError + domainError computed values
  block saveIdentity and show inline error; maxLength={255} on inputs
- tests/test_identity_validation.py: 8 unit tests for the new validation

Cell name overflow on all pages:
- Dashboard.jsx: add min-w-0 to flex child div + truncate + title on cell_name
- CellNetwork.jsx: min-w-0 + truncate + title on cell_name, domain, endpoint,
  vpn_subnet in invite cards and connected-cells list

Apply-and-verify integration tests:
- tests/integration/test_apply_propagation.py: TestPendingState (no restarts)
  and TestApplyAndVerify (triggers real container restart + health poll)
  covering the full save → apply → wait → verify propagation lifecycle

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 05:29:09 -04:00

95 lines
2.7 KiB
Python

"""
Unit tests for cell_name and domain length validation in update_config().
"""
import sys
import os
import json
# Ensure api/ is on the path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'api'))
import pytest
from app import app
@pytest.fixture
def client():
app.config['TESTING'] = True
with app.test_client() as c:
yield c
def put_config(client, payload):
return client.put(
'/api/config',
data=json.dumps(payload),
content_type='application/json',
)
# ---------------------------------------------------------------------------
# cell_name validation
# ---------------------------------------------------------------------------
def test_cell_name_too_long_returns_400(client):
"""cell_name > 255 characters must be rejected with 400."""
resp = put_config(client, {'cell_name': 'a' * 256})
assert resp.status_code == 400
body = json.loads(resp.data)
assert 'cell_name' in body['error']
assert '255' in body['error']
def test_cell_name_exactly_255_returns_200(client):
"""cell_name of exactly 255 characters must be accepted."""
resp = put_config(client, {'cell_name': 'a' * 255})
assert resp.status_code == 200
def test_cell_name_empty_string_returns_400(client):
"""Empty cell_name must be rejected with 400."""
resp = put_config(client, {'cell_name': ''})
assert resp.status_code == 400
body = json.loads(resp.data)
assert 'cell_name' in body['error']
def test_cell_name_valid_returns_200(client):
"""A short, valid cell_name must be accepted."""
resp = put_config(client, {'cell_name': 'mycell'})
assert resp.status_code == 200
# ---------------------------------------------------------------------------
# domain validation
# ---------------------------------------------------------------------------
def test_domain_too_long_returns_400(client):
"""domain > 255 characters must be rejected with 400."""
resp = put_config(client, {'domain': 'b' * 256})
assert resp.status_code == 400
body = json.loads(resp.data)
assert 'domain' in body['error']
assert '255' in body['error']
def test_domain_exactly_255_returns_200(client):
"""domain of exactly 255 characters must be accepted."""
resp = put_config(client, {'domain': 'b' * 255})
assert resp.status_code == 200
def test_domain_empty_string_returns_400(client):
"""Empty domain must be rejected with 400."""
resp = put_config(client, {'domain': ''})
assert resp.status_code == 400
body = json.loads(resp.data)
assert 'domain' in body['error']
def test_domain_valid_returns_200(client):
"""A short, valid domain must be accepted."""
resp = put_config(client, {'domain': 'cell.local'})
assert resp.status_code == 200