"""Tests for app/views/wizard.py module.""" from unittest.mock import patch import pytest @pytest.mark.integration class TestWizardViews: """Tests for wizard view routes.""" def test_setup_wizard_step_1(self, client): """Test setup wizard first step.""" response = client.get("/setup?step=1") assert response.status_code == 200 def test_setup_wizard_step_2(self, client): """Test setup wizard second step.""" response = client.get("/setup?step=2") assert response.status_code == 200 def test_setup_wizard_step_3(self, client): """Test setup wizard third step.""" response = client.get("/setup?step=3") assert response.status_code == 200 def test_setup_wizard_invalid_step(self, client): """Test setup wizard with invalid step number.""" response = client.get("/setup?step=0") assert response.status_code == 200 def test_setup_wizard_high_step(self, client): """Test setup wizard with step higher than max.""" response = client.get("/setup?step=999") assert response.status_code == 200 def test_setup_wizard_skip(self, client): """Test skipping the setup wizard.""" response = client.get("/setup/skip", follow_redirects=False) assert response.status_code in (200, 303) @patch("app.utils.settings_service.get_setting_from_db") def test_setup_wizard_step_with_db_value(self, mock_get_setting, client): """Test GET /setup when settings have a stored DB value (covers lines 56-57).""" mock_get_setting.return_value = "stored_db_value" response = client.get("/setup?step=1") assert response.status_code == 200 @pytest.mark.integration class TestWizardViewsPost: """Tests for wizard view POST routes.""" @patch("app.views.wizard.save_setting_to_db") def test_setup_wizard_save_valid_data(self, mock_save, client): """Test saving valid wizard settings.""" mock_save.return_value = True response = client.post( "/setup", data={ "step": "1", "database_url": "sqlite:///test.db", "redis_url": "redis://localhost:6379/0", }, follow_redirects=False, ) assert response.status_code == 303 assert "/setup?step=2" in response.headers["location"] # At least one save should have been called assert mock_save.call_count >= 1 @patch("app.views.wizard.save_setting_to_db") def test_setup_wizard_save_empty_values_skipped(self, mock_save, client): """Test that empty values are skipped during save.""" mock_save.return_value = True response = client.post( "/setup", data={ "step": "1", "openai_api_key": "", # Empty value should be skipped "azure_endpoint": " ", # Whitespace only should be skipped }, follow_redirects=False, ) assert response.status_code == 303 # Should not have called save for empty values assert mock_save.call_count == 0 @patch("app.views.wizard.save_setting_to_db") @patch("app.views.wizard.token_hex") def test_setup_wizard_auto_generate_session_secret(self, mock_token, mock_save, client): """Test auto-generation of session secret.""" mock_token.return_value = "auto_generated_secret_token_12345678" mock_save.return_value = True response = client.post( "/setup", data={ "step": "2", # session_secret is in step 2 "session_secret": "auto-generate", }, follow_redirects=False, ) assert response.status_code == 303 mock_token.assert_called_once_with(32) # Verify that the auto-generated token was saved mock_save.assert_called_once() call_args = mock_save.call_args[0] assert call_args[1] == "session_secret" assert call_args[2] == "auto_generated_secret_token_12345678" @patch("app.views.wizard.save_setting_to_db") def test_setup_wizard_save_last_step_redirects_home(self, mock_save, client): """Test that last step redirects to home.""" mock_save.return_value = True # Step 3 is typically the last step response = client.post( "/setup", data={ "step": "3", "some_setting": "value", }, follow_redirects=False, ) assert response.status_code == 303 assert "/?setup=complete" in response.headers["location"] @patch("app.views.wizard.save_setting_to_db") def test_setup_wizard_save_failed_setting(self, mock_save, client): """Test handling when save_setting_to_db returns False.""" mock_save.return_value = False response = client.post( "/setup", data={ "step": "1", "some_key": "some_value", }, follow_redirects=False, ) # Should still continue even if save fails assert response.status_code == 303 @patch("app.views.wizard.save_setting_to_db") def test_setup_wizard_save_returns_false_for_valid_key(self, mock_save, client): """Test branch when save_setting_to_db returns False for a real wizard key (covers branch 111->99).""" mock_save.return_value = False response = client.post( "/setup", data={ "step": "1", "database_url": "sqlite:///test.db", }, follow_redirects=False, ) # Should proceed to next step even when save returns False assert response.status_code == 303 assert "/setup?step=2" in response.headers["location"] mock_save.assert_called() @patch("app.views.wizard.save_setting_to_db") def test_setup_wizard_save_exception_handling(self, mock_save, client): """Test exception handling in setup_wizard_save.""" mock_save.side_effect = Exception("Database error") response = client.post( "/setup", data={ "step": "1", "database_url": "sqlite:///test.db", }, follow_redirects=False, ) assert response.status_code == 303 assert "error=save_failed" in response.headers["location"] assert "step=1" in response.headers["location"] @pytest.mark.integration class TestWizardSkip: """Tests for wizard skip functionality.""" @patch("app.views.wizard.save_setting_to_db") def test_setup_wizard_skip_success(self, mock_save, client): """Test successful skipping of setup wizard.""" mock_save.return_value = True response = client.get("/setup/skip", follow_redirects=False) assert response.status_code == 303 assert response.headers["location"] == "/" mock_save.assert_called_once() call_args = mock_save.call_args[0] assert call_args[1] == "_setup_wizard_skipped" assert call_args[2] == "true" @patch("app.views.wizard.save_setting_to_db") def test_setup_wizard_skip_exception_handling(self, mock_save, client): """Test exception handling when skipping wizard.""" mock_save.side_effect = Exception("Database error") response = client.get("/setup/skip", follow_redirects=False) # Should still redirect to home even on error assert response.status_code == 303 assert response.headers["location"] == "/" @pytest.mark.integration class TestWizardUndoSkip: """Tests for wizard undo-skip functionality.""" @patch("app.utils.settings_service.delete_setting_from_db") def test_setup_wizard_undo_skip_success(self, mock_delete, client): """Test successful undo of wizard skip (covers lines 166-171).""" mock_delete.return_value = True response = client.get("/setup/undo-skip", follow_redirects=False) assert response.status_code == 303 assert "/setup?step=1" in response.headers["location"] mock_delete.assert_called_once() call_args = mock_delete.call_args[0] assert call_args[1] == "_setup_wizard_skipped" @patch("app.utils.settings_service.delete_setting_from_db") def test_setup_wizard_undo_skip_exception_handling(self, mock_delete, client): """Test exception handling when undoing wizard skip (covers lines 172-174).""" mock_delete.side_effect = Exception("Database error") response = client.get("/setup/undo-skip", follow_redirects=False) assert response.status_code == 303 assert response.headers["location"] == "/settings"