#!/usr/bin/env python3 """ Tests for the /api/setup/complete route in routes/setup.py. Verifies that completing the wizard fires IDENTITY_CHANGED AND updates the primary DNS zone with the real cell name (fixing the 'mycell' placeholder written by _bootstrap_dns before the wizard runs). """ import sys import json from pathlib import Path from unittest.mock import MagicMock, patch, call import pytest sys.path.insert(0, str(Path(__file__).parent.parent / 'api')) from app import app # ── helpers ──────────────────────────────────────────────────────────────────── def _valid_payload(**overrides): base = { 'cell_name': 'mycel', 'password': 'SecurePass1!', 'domain_mode': 'lan', 'timezone': 'UTC', 'ddns_provider': 'none', } base.update(overrides) return base @pytest.fixture() def client(): app.config['TESTING'] = True app.config['SECRET_KEY'] = 'test-secret' with app.test_client() as c: yield c def _post_complete(client, payload): return client.post( '/api/setup/complete', data=json.dumps(payload), content_type='application/json', ) # ── tests ────────────────────────────────────────────────────────────────────── def test_complete_setup_calls_apply_cell_name_on_success(client, tmp_path): """After successful wizard, DNS zone is updated with the real cell name.""" mock_sm = MagicMock() mock_sm.is_setup_complete.return_value = False mock_sm.complete_setup.return_value = {'success': True, 'redirect': '/login'} mock_cm = MagicMock() mock_cm.configs = {'_identity': {'cell_name': 'mycel'}} mock_cm.get_effective_domain.return_value = 'mycel.pic.ngo' mock_nm = MagicMock() mock_sbus = MagicMock() with ( patch('app.setup_manager', mock_sm), patch('app.config_manager', mock_cm), patch('app.network_manager', mock_nm), patch('app.service_bus', mock_sbus), patch.dict('sys.modules', {'app': __import__('app')}), ): resp = _post_complete(client, _valid_payload()) assert resp.status_code == 200 mock_nm.apply_cell_name.assert_called_once_with('', 'mycel') def test_complete_setup_does_not_call_apply_cell_name_on_failure(client, tmp_path): """DNS zone is NOT touched when setup fails.""" mock_sm = MagicMock() mock_sm.is_setup_complete.return_value = False mock_sm.complete_setup.return_value = {'success': False, 'errors': ['bad']} mock_nm = MagicMock() with ( patch('app.setup_manager', mock_sm), patch('app.network_manager', mock_nm), ): resp = _post_complete(client, _valid_payload()) assert resp.status_code == 400 mock_nm.apply_cell_name.assert_not_called() def test_complete_setup_returns_410_when_already_complete(client): """Route returns 410 if setup was already done.""" mock_sm = MagicMock() mock_sm.is_setup_complete.return_value = True with patch('app.setup_manager', mock_sm): resp = _post_complete(client, _valid_payload()) assert resp.status_code == 410 def test_complete_setup_fires_identity_changed_on_success(client): """IDENTITY_CHANGED is published after successful wizard completion.""" mock_sm = MagicMock() mock_sm.is_setup_complete.return_value = False mock_sm.complete_setup.return_value = {'success': True, 'redirect': '/login'} mock_cm = MagicMock() mock_cm.configs = {'_identity': {'cell_name': 'mycel', 'domain': 'cell'}} mock_cm.get_effective_domain.return_value = 'cell' mock_sbus = MagicMock() mock_nm = MagicMock() with ( patch('app.setup_manager', mock_sm), patch('app.config_manager', mock_cm), patch('app.network_manager', mock_nm), patch('app.service_bus', mock_sbus), ): resp = _post_complete(client, _valid_payload()) assert resp.status_code == 200 mock_sbus.publish_event.assert_called_once() event_args = mock_sbus.publish_event.call_args assert event_args[0][1] == 'setup'