mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2026-02-13 15:48:52 -06:00
Implements persistent flag tracking to ensure default client and project are only created on fresh installations and never recreated after user deletion during updates or restarts. - Added initial_data_seeded flag to InstallationConfig - Updated all 3 database initialization scripts to check flag - Added 3 unit tests (all passing) - Created comprehensive documentation Fixes issue where defaults were recreated after deletion during updates.
210 lines
8.0 KiB
Python
210 lines
8.0 KiB
Python
"""
|
|
Tests for installation configuration and setup
|
|
"""
|
|
|
|
import os
|
|
import json
|
|
import pytest
|
|
from unittest.mock import patch
|
|
from app.utils.installation import InstallationConfig, get_installation_config
|
|
|
|
|
|
@pytest.fixture
|
|
def temp_config_dir(tmp_path):
|
|
"""Create a temporary config directory"""
|
|
config_dir = tmp_path / "data"
|
|
config_dir.mkdir()
|
|
return str(config_dir)
|
|
|
|
|
|
@pytest.fixture
|
|
def installation_config(temp_config_dir, monkeypatch):
|
|
"""Create an InstallationConfig instance with temporary directory"""
|
|
monkeypatch.setattr('app.utils.installation.InstallationConfig.CONFIG_DIR', temp_config_dir)
|
|
config = InstallationConfig()
|
|
return config
|
|
|
|
|
|
class TestInstallationConfig:
|
|
"""Test installation configuration management"""
|
|
|
|
def test_installation_salt_generation(self, installation_config):
|
|
"""Test that installation salt is generated and persisted"""
|
|
# First call should generate salt
|
|
salt1 = installation_config.get_installation_salt()
|
|
assert salt1 is not None
|
|
assert len(salt1) == 64 # 32 bytes = 64 hex chars
|
|
|
|
# Second call should return same salt
|
|
salt2 = installation_config.get_installation_salt()
|
|
assert salt1 == salt2
|
|
|
|
def test_installation_id_generation(self, installation_config):
|
|
"""Test that installation ID is generated and persisted"""
|
|
# First call should generate ID
|
|
id1 = installation_config.get_installation_id()
|
|
assert id1 is not None
|
|
assert len(id1) == 16
|
|
|
|
# Second call should return same ID
|
|
id2 = installation_config.get_installation_id()
|
|
assert id1 == id2
|
|
|
|
def test_installation_id_uniqueness(self, temp_config_dir, monkeypatch):
|
|
"""Test that each installation gets a unique ID"""
|
|
monkeypatch.setattr('app.utils.installation.InstallationConfig.CONFIG_DIR', temp_config_dir)
|
|
|
|
config1 = InstallationConfig()
|
|
id1 = config1.get_installation_id()
|
|
|
|
# Create a new instance (simulating restart)
|
|
config2 = InstallationConfig()
|
|
id2 = config2.get_installation_id()
|
|
|
|
# Should be the same ID (persisted)
|
|
assert id1 == id2
|
|
|
|
def test_setup_completion(self, installation_config):
|
|
"""Test setup completion tracking"""
|
|
# Initially not complete
|
|
assert not installation_config.is_setup_complete()
|
|
|
|
# Mark as complete
|
|
installation_config.mark_setup_complete(telemetry_enabled=True)
|
|
assert installation_config.is_setup_complete()
|
|
assert installation_config.get_telemetry_preference() is True
|
|
|
|
# Verify persistence
|
|
config2 = InstallationConfig()
|
|
assert config2.is_setup_complete()
|
|
assert config2.get_telemetry_preference() is True
|
|
|
|
def test_telemetry_preference(self, installation_config):
|
|
"""Test telemetry preference management"""
|
|
# Default is False
|
|
assert installation_config.get_telemetry_preference() is False
|
|
|
|
# Enable telemetry
|
|
installation_config.set_telemetry_preference(True)
|
|
assert installation_config.get_telemetry_preference() is True
|
|
|
|
# Disable telemetry
|
|
installation_config.set_telemetry_preference(False)
|
|
assert installation_config.get_telemetry_preference() is False
|
|
|
|
def test_config_persistence(self, installation_config, temp_config_dir):
|
|
"""Test that configuration is persisted to disk"""
|
|
# Set some values
|
|
salt = installation_config.get_installation_salt()
|
|
installation_id = installation_config.get_installation_id()
|
|
installation_config.mark_setup_complete(telemetry_enabled=True)
|
|
|
|
# Read the file directly
|
|
config_path = os.path.join(temp_config_dir, 'installation.json')
|
|
assert os.path.exists(config_path)
|
|
|
|
with open(config_path, 'r') as f:
|
|
data = json.load(f)
|
|
|
|
assert data['telemetry_salt'] == salt
|
|
assert data['installation_id'] == installation_id
|
|
assert data['setup_complete'] is True
|
|
assert data['telemetry_enabled'] is True
|
|
|
|
def test_get_all_config(self, installation_config):
|
|
"""Test retrieving all configuration"""
|
|
# Generate salt and ID first (lazy initialization)
|
|
salt = installation_config.get_installation_salt()
|
|
installation_id = installation_config.get_installation_id()
|
|
|
|
installation_config.mark_setup_complete(telemetry_enabled=True)
|
|
|
|
config = installation_config.get_all_config()
|
|
assert 'telemetry_salt' in config
|
|
assert 'installation_id' in config
|
|
assert 'setup_complete' in config
|
|
assert config['setup_complete'] is True
|
|
|
|
def test_initial_data_seeding_tracking(self, installation_config):
|
|
"""Test that initial data seeding is tracked"""
|
|
# Initially not seeded
|
|
assert not installation_config.is_initial_data_seeded()
|
|
|
|
# Mark as seeded
|
|
installation_config.mark_initial_data_seeded()
|
|
assert installation_config.is_initial_data_seeded()
|
|
|
|
# Verify persistence
|
|
config2 = InstallationConfig()
|
|
assert config2.is_initial_data_seeded()
|
|
|
|
def test_initial_data_seeding_persistence(self, installation_config, temp_config_dir):
|
|
"""Test that initial data seeding flag is persisted to disk"""
|
|
# Mark as seeded
|
|
installation_config.mark_initial_data_seeded()
|
|
|
|
# Read the file directly
|
|
config_path = os.path.join(temp_config_dir, 'installation.json')
|
|
assert os.path.exists(config_path)
|
|
|
|
with open(config_path, 'r') as f:
|
|
data = json.load(f)
|
|
|
|
assert data['initial_data_seeded'] is True
|
|
assert 'initial_data_seeded_at' in data
|
|
|
|
def test_initial_data_seeding_default_value(self, installation_config):
|
|
"""Test that initial data seeding defaults to False"""
|
|
# Should default to False for new installations
|
|
assert installation_config.is_initial_data_seeded() is False
|
|
|
|
|
|
class TestSetupRoutes:
|
|
"""Test setup routes"""
|
|
|
|
def test_setup_page_redirects_if_complete(self, client, installation_config):
|
|
"""Test that setup page redirects if setup is already complete"""
|
|
# Mark setup as complete
|
|
installation_config.mark_setup_complete(telemetry_enabled=False)
|
|
|
|
# Try to access setup page
|
|
response = client.get('/setup')
|
|
assert response.status_code in [302, 200] # May redirect or show page
|
|
|
|
def test_setup_completion_flow(self, client, installation_config):
|
|
"""Test completing the setup"""
|
|
# Patch the global get_installation_config to return our fixture
|
|
with patch('app.routes.setup.get_installation_config') as mock_get_config:
|
|
mock_get_config.return_value = installation_config
|
|
|
|
# Ensure setup is not complete
|
|
assert not installation_config.is_setup_complete()
|
|
|
|
# Access setup page
|
|
response = client.get('/setup')
|
|
assert response.status_code == 200
|
|
|
|
# Complete setup with telemetry enabled
|
|
response = client.post('/setup', data={
|
|
'telemetry_enabled': 'on'
|
|
}, follow_redirects=False)
|
|
|
|
# Should redirect after completion
|
|
assert response.status_code == 302
|
|
|
|
# Verify setup is complete
|
|
assert installation_config.is_setup_complete()
|
|
assert installation_config.get_telemetry_preference() is True
|
|
|
|
def test_setup_without_telemetry(self, client, installation_config):
|
|
"""Test completing setup with telemetry disabled"""
|
|
# Complete setup without telemetry
|
|
response = client.post('/setup', data={}, follow_redirects=False)
|
|
|
|
# Should redirect after completion
|
|
assert response.status_code == 302
|
|
|
|
# Verify telemetry is disabled
|
|
assert installation_config.get_telemetry_preference() is False
|
|
|