Files
ksef_app/tests/test_admin_system.py
Mateusz Gruszczyński 35571df778 push
2026-03-13 11:03:13 +01:00

188 lines
10 KiB
Python

from datetime import datetime, timedelta
from pathlib import Path
from requests import exceptions as requests_exceptions
from app.extensions import db
from app.models.audit_log import AuditLog
from app.models.company import Company
from app.models.invoice import MailDelivery, NotificationLog, SyncEvent
from app.models.setting import AppSetting
from app.models.sync_log import SyncLog
from app.services.ksef_service import KSeFService, RequestsKSeFAdapter
class DummyResponse:
def __init__(self, payload, status_code=200):
self._payload = payload
self.status_code = status_code
self.content = b'{}'
self.text = '{}'
def json(self):
return self._payload
def test_admin_system_data_page(auth_client, monkeypatch):
from app.services.ceidg_service import CeidgService
monkeypatch.setattr(CeidgService, 'diagnostics', lambda self: {
'status': 'ok',
'message': 'HTTP 200',
'environment': 'test',
'url': 'https://test.example/ceidg',
'sample': {'ok': True},
'technical_details': None,
})
response = auth_client.get('/admin/system-data')
body = response.get_data(as_text=True)
assert response.status_code == 200
assert 'Dane systemowe' in body
assert 'Połączenie KSeF' in body
assert 'Połączenie CEIDG' in body
assert 'Proces i health systemu' in body
assert 'Użytkownicy' in body
assert 'Diagnoza' not in body
assert 'Co sprawdzić' not in body
def test_admin_health_redirects_to_system_data(auth_client):
response = auth_client.get('/admin/health', follow_redirects=False)
assert response.status_code == 302
assert '/admin/system-data' in response.headers['Location']
def test_ksef_diagnostics_mock(app):
with app.app_context():
company = Company.query.first()
data = KSeFService(company_id=company.id).diagnostics()
assert data['status'] == 'mock'
assert data['base_url'] == 'mock://ksef'
assert 'documentExample' in data['sample']
def test_ksef_diagnostics_requests_adapter(app, monkeypatch):
with app.app_context():
company = Company.query.first()
AppSetting.set(f'company.{company.id}.ksef.mock_mode', 'false')
db.session.commit()
monkeypatch.setattr(RequestsKSeFAdapter, '_request', lambda self, method, path, params=None, json=None: {'status': 'healthy', 'version': 'demo'})
data = KSeFService(company_id=company.id).diagnostics()
assert data['status'] == 'ok'
assert data['sample']['status'] == 'healthy'
assert data['base_url'].startswith('https://')
def test_ceidg_diagnostics_timeout(app, monkeypatch):
from app.services.ceidg_service import CeidgService
def raise_timeout(*args, **kwargs):
raise requests_exceptions.ConnectTimeout('connect timeout')
monkeypatch.setattr('app.services.ceidg_service.requests.get', raise_timeout)
with app.app_context():
AppSetting.set('ceidg.environment', 'test')
AppSetting.set('ceidg.api_key', 'diagnostic-key', encrypt=True)
db.session.commit()
data = CeidgService().diagnostics()
assert data['status'] == 'error'
assert 'Timeout' in data['message']
assert data['environment'] == 'test'
def test_cleanup_logs_removes_old_records(app, auth_client):
with app.app_context():
old_dt = datetime.utcnow() - timedelta(days=120)
company = Company.query.first()
invoice = company.invoices.first()
if invoice is None:
from app.models.invoice import Invoice, InvoiceType, InvoiceStatus
invoice = Invoice(company_id=company.id, ksef_number='X1', invoice_number='FV/1', contractor_name='Test', issue_date=old_dt.date(), net_amount=1, vat_amount=0.23, gross_amount=1.23, invoice_type=InvoiceType.SALE, status=InvoiceStatus.NEW)
db.session.add(invoice)
db.session.flush()
db.session.add(AuditLog(action='old', target_type='system', created_at=old_dt, updated_at=old_dt))
db.session.add(SyncLog(sync_type='manual', status='done', started_at=old_dt, company_id=company.id, created_at=old_dt, updated_at=old_dt))
db.session.add(NotificationLog(invoice_id=invoice.id, channel='mail', status='done', created_at=old_dt, updated_at=old_dt))
db.session.add(MailDelivery(invoice_id=invoice.id, recipient='a@example.com', status='sent', created_at=old_dt, updated_at=old_dt))
db.session.add(SyncEvent(invoice_id=invoice.id, status='ok', created_at=old_dt, updated_at=old_dt))
db.session.commit()
response = auth_client.post('/admin/logs/cleanup', data={'days': 90}, follow_redirects=True)
assert response.status_code == 200
body = response.get_data(as_text=True)
assert 'Usunięto stare logi starsze niż 90 dni' in body
with app.app_context():
assert AuditLog.query.filter_by(action='old').count() == 0
assert SyncLog.query.filter_by(sync_type='manual').count() == 0
assert NotificationLog.query.count() == 0
assert MailDelivery.query.count() == 0
def test_database_backup_download(auth_client, app):
response = auth_client.post('/admin/database/backup')
assert response.status_code == 200
assert 'attachment;' in response.headers.get('Content-Disposition', '')
assert 'db_backup_' in response.headers.get('Content-Disposition', '')
with app.app_context():
backup_dir = Path(app.config['BACKUP_PATH'])
assert any(path.name.startswith('db_backup_') for path in backup_dir.iterdir())
def test_clear_mock_data_removes_legacy_mock_invoices(app, auth_client):
from app.models.invoice import Invoice, InvoiceStatus, InvoiceType
with app.app_context():
company = Company.query.first()
company_id = company.id
rows = [
Invoice(company_id=company_id, ksef_number='KSEF/MOCK/C1/2026/10001', invoice_number='FV/1/001/2026', contractor_name='Firma 1-1', issue_date=datetime.utcnow().date(), net_amount=1, vat_amount=0.23, gross_amount=1.23, invoice_type=InvoiceType.SALE, status=InvoiceStatus.NEW, source='ksef', issued_status='received', external_metadata={'source': 'mock', 'sequence': 1, 'company_id': company_id}),
Invoice(company_id=company_id, ksef_number='KSEF/MOCK/C1/2026/10002', invoice_number='FV/1/002/2026', contractor_name='Firma 1-2', issue_date=datetime.utcnow().date(), net_amount=1, vat_amount=0.23, gross_amount=1.23, invoice_type=InvoiceType.SALE, status=InvoiceStatus.NEW, source='ksef', issued_status='received', external_metadata={'source': 'mock', 'sequence': 2, 'company_id': company_id}),
Invoice(company_id=company_id, ksef_number='KSEF/MOCK/C1/2026/10003', invoice_number='FV/1/003/2026', contractor_name='Firma 1-3', issue_date=datetime.utcnow().date(), net_amount=1, vat_amount=0.23, gross_amount=1.23, invoice_type=InvoiceType.SALE, status=InvoiceStatus.NEW, source='ksef', issued_status='received', external_metadata={'source': 'mock', 'sequence': 3, 'company_id': company_id}),
Invoice(company_id=company_id, ksef_number='KSEF/MOCK/C1/2026/10004', invoice_number='FV/1/004/2026', contractor_name='Firma 1-4', issue_date=datetime.utcnow().date(), net_amount=1, vat_amount=0.23, gross_amount=1.23, invoice_type=InvoiceType.SALE, status=InvoiceStatus.NEW, source='ksef', issued_status='received', external_metadata={'source': 'mock', 'sequence': 4, 'company_id': company_id}),
Invoice(company_id=company_id, ksef_number='KSEF/MOCK/C1/2026/10005', invoice_number='FV/1/005/2026', contractor_name='Firma 1-5', issue_date=datetime.utcnow().date(), net_amount=1, vat_amount=0.23, gross_amount=1.23, invoice_type=InvoiceType.SALE, status=InvoiceStatus.NEW, source='ksef', issued_status='received', external_metadata={'source': 'mock', 'sequence': 5, 'company_id': company_id}),
Invoice(company_id=company_id, ksef_number='PENDING/FV/2026/03/0002', invoice_number='FV/2026/03/0002', contractor_name='aaaa', issue_date=datetime.utcnow().date(), net_amount=1, vat_amount=0.23, gross_amount=1.23, invoice_type=InvoiceType.SALE, status=InvoiceStatus.NEW, source='issued', issued_status='draft', external_metadata={}),
Invoice(company_id=company_id, ksef_number='NFZ-PENDING/FV/2026/03/0003', invoice_number='FV/2026/03/0003', contractor_name='NFZ', issue_date=datetime.utcnow().date(), net_amount=1, vat_amount=0.23, gross_amount=1.23, invoice_type=InvoiceType.SALE, status=InvoiceStatus.NEW, source='nfz', issued_status='draft', external_metadata={'nfz': {'x': 1}}),
Invoice(company_id=company_id, ksef_number='KSEF/MOCK/ISSUED/C1/2026/1773052148', invoice_number='FV/2026/03/0004', contractor_name='aaaa', issue_date=datetime.utcnow().date(), net_amount=1, vat_amount=0.23, gross_amount=1.23, invoice_type=InvoiceType.SALE, status=InvoiceStatus.NEW, source='issued', issued_status='issued_mock', external_metadata={}),
]
db.session.add_all(rows)
AppSetting.set(f'company.{company_id}.ksef.mock_mode', 'true')
db.session.commit()
assert Invoice.query.count() == 8
assert Invoice.query.filter(Invoice.ksef_number.like('%MOCK%')).count() == 6
response = auth_client.post('/admin/mock-data/clear', follow_redirects=True)
assert response.status_code == 200
body = response.get_data(as_text=True)
assert 'Usunięto dane mock: faktury 6' in body
with app.app_context():
remaining_numbers = [row[0] for row in db.session.query(Invoice.invoice_number).order_by(Invoice.id).all()]
assert remaining_numbers == ['FV/2026/03/0002', 'FV/2026/03/0003']
assert AppSetting.get(f'company.{company_id}.ksef.mock_mode') == 'false'
def test_save_ceidg_settings_persists_api_key(app, auth_client):
long_api_key = 'A1B2C3D4' * 32
response = auth_client.post(
'/admin/ceidg/save',
data={'environment': 'test', 'api_key': long_api_key},
follow_redirects=True,
)
assert response.status_code == 200
with app.app_context():
assert AppSetting.get('ceidg.environment') == 'test'
assert AppSetting.get('ceidg.api_key', decrypt=True) == long_api_key
stored = AppSetting.query.filter_by(key='ceidg.api_key').first()
assert stored is not None
assert stored.is_encrypted is True
assert stored.value != long_api_key