This commit is contained in:
Džubara 2026-03-26 16:13:58 +01:00
parent c8408cda33
commit f491d55ca5
2 changed files with 0 additions and 592 deletions

View File

@ -1,29 +0,0 @@
import sqlite3
from database import DB_NAME
def clear_cache():
"""Vymaže všetky záznamy z databázy"""
conn = sqlite3.connect(DB_NAME)
cursor = conn.cursor()
# Vymaž všetky fact-checky
cursor.execute("DELETE FROM fact_checks")
deleted_checks = cursor.rowcount
# Vymaž všetky verified facts
cursor.execute("DELETE FROM verified_facts")
deleted_facts = cursor.rowcount
# Reset autoincrement
cursor.execute("DELETE FROM sqlite_sequence WHERE name='fact_checks'")
cursor.execute("DELETE FROM sqlite_sequence WHERE name='verified_facts'")
conn.commit()
conn.close()
print(f"✅ Cache vyčistená!")
print(f" - Vymazaných fact-checkov: {deleted_checks}")
print(f" - Vymazaných verified facts: {deleted_facts}")
if __name__ == "__main__":
clear_cache()

View File

@ -1,563 +0,0 @@
"""
Testy pre AI Fact Checker aplikáciu
Spustenie: python -m pytest test_app.py -v
Alebo: python test_app.py
"""
import pytest
import json
import os
import sys
from datetime import datetime, timedelta
# Nastavíme prostredie pre testovanie
os.environ['TESTING'] = 'True'
# Importujeme aplikáciu
from app import app, load_model, MODELS
from database import (
get_db_connection,
get_cached_result,
save_to_cache,
get_history,
get_stats,
add_verified_fact,
hash_claim,
init_db
)
# =============================================================================
# FIXTURES - Pomocné funkcie pre testy
# =============================================================================
@pytest.fixture
def client():
"""Vytvorí testovacieho klienta pre Flask aplikáciu"""
app.config['TESTING'] = True
with app.test_client() as client:
yield client
@pytest.fixture
def init_test_db():
"""Inicializuje čistú databázu pre testy"""
init_db()
yield
# Cleanup - vymažeme testovacie dáta
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute("DELETE FROM fact_checks")
cursor.execute("DELETE FROM verified_facts")
conn.commit()
conn.close()
# =============================================================================
# TESTY DATABÁZY
# =============================================================================
class TestDatabase:
"""Testy pre databázové operácie"""
def test_hash_claim_consistency(self):
"""Test: Hash toho istého výroku je vždy rovnaký"""
claim = "Bratislava je hlavné mesto Slovenska"
hash1 = hash_claim(claim)
hash2 = hash_claim(claim)
assert hash1 == hash2, "Hash by mal byť konzistentný"
def test_hash_claim_case_insensitive(self):
"""Test: Hash je case-insensitive"""
claim1 = "Bratislava je hlavné mesto Slovenska"
claim2 = "bratislava je hlavné mesto slovenska"
hash1 = hash_claim(claim1)
hash2 = hash_claim(claim2)
assert hash1 == hash2, "Hash by mal byť case-insensitive"
def test_hash_claim_different_claims(self):
"""Test: Rôzne výroky majú rôzny hash"""
claim1 = "Bratislava je hlavné mesto Slovenska"
claim2 = "Košice sú druhé najväčšie mesto"
hash1 = hash_claim(claim1)
hash2 = hash_claim(claim2)
assert hash1 != hash2, "Rôzne výroky by mali mať rôzny hash"
def test_save_and_get_cached_result(self, init_test_db):
"""Test: Uloženie a získanie cachovaného výsledku"""
claim = "Testovací výrok pre cache"
result = {
"verdict": "✅ Pravda",
"confidence": 0.85,
"nli_votes": {"entailment": 3, "contradiction": 1},
"evidence_for": ["Dôkaz 1"],
"evidence_against": [],
"sources": ["https://example.com"]
}
# Uložíme do cache
saved = save_to_cache(claim, result, model_name="roberta")
assert saved == True, "Uloženie do cache zlyhalo"
# Získame z cache
cached = get_cached_result(claim)
assert cached is not None, "Cache by nemala byť prázdna"
assert cached["verdict"] == "✅ Pravda"
assert cached["cached"] == True
assert cached["model_name"] == "roberta"
def test_cache_miss(self, init_test_db):
"""Test: Získanie neexistujúceho cachovaného výsledku"""
claim = "Tento výrok ešte nebol overený"
cached = get_cached_result(claim)
assert cached is None, "Cache by mala byť prázdna pre nový výrok"
def test_get_history(self, init_test_db):
"""Test: Získanie histórie overení"""
# Pridáme niekoľko záznamov
for i in range(5):
claim = f"Testovací výrok {i}"
result = {
"verdict": "✅ Pravda" if i % 2 == 0 else "❌ Nepravda",
"sources": [f"https://example{i}.com"]
}
save_to_cache(claim, result, model_name="roberta")
history = get_history(limit=10)
assert len(history) == 5, "História by mala obsahovať 5 záznamov"
def test_get_stats(self, init_test_db):
"""Test: Získanie štatistík"""
# Pridáme testovacie dáta
for i in range(3):
claim = f"Štatistický výrok {i}"
result = {"verdict": "✅ Pravda", "sources": []}
save_to_cache(claim, result, model_name="roberta")
# Pridáme manuálne overený fakt
add_verified_fact("Manuálne overený fakt", "TRUE", "Vysvetlenie")
stats = get_stats()
assert stats["unique_claims"] == 3
assert stats["verified_facts"] == 1
assert stats["total_checks"] >= 3
def test_add_verified_fact_duplicate(self, init_test_db):
"""Test: Pridanie duplicitného manuálne overeného faktu"""
claim = "Duplicitný fakt"
result1 = add_verified_fact(claim, "TRUE", "Prvé vysvetlenie")
result2 = add_verified_fact(claim, "FALSE", "Druhé vysvetlenie")
assert result1 == True, "Prvý fakt by sa mal pridať"
assert result2 == False, "Duplicitný fakt by sa nemal pridať"
# =============================================================================
# TESTY API ENDPOINTOV
# =============================================================================
class TestAPIEndpoints:
"""Testy pre REST API endpointy"""
def test_empty_claim_validation(self, client):
"""Test: Validácia prázdneho výroku"""
response = client.post('/api/check',
data=json.dumps({"claim": ""}),
content_type='application/json')
assert response.status_code == 400
data = json.loads(response.data)
assert "error" in data
def test_whitespace_only_claim(self, client):
"""Test: Validácia výroku s iba medzerami"""
response = client.post('/api/check',
data=json.dumps({"claim": " "}),
content_type='application/json')
assert response.status_code == 400
def test_missing_claim_field(self, client):
"""Test: Chýbajúce pole claim"""
response = client.post('/api/check',
data=json.dumps({"language": "sk"}),
content_type='application/json')
assert response.status_code == 400
def test_history_endpoint_empty(self, client, init_test_db):
"""Test: História keď je prázdna"""
response = client.get('/api/history')
assert response.status_code == 200
data = json.loads(response.data)
assert "history" in data
assert "count" in data
assert data["count"] == 0
def test_history_endpoint_with_data(self, client, init_test_db):
"""Test: História s dátami"""
# Pridáme dáta cez cache
for i in range(3):
claim = f"História test {i}"
result = {"verdict": "✅ Pravda", "sources": []}
save_to_cache(claim, result, model_name="roberta")
response = client.get('/api/history?limit=10')
assert response.status_code == 200
data = json.loads(response.data)
assert data["count"] == 3
def test_stats_endpoint(self, client):
"""Test: Štatistiky endpoint"""
response = client.get('/api/stats')
assert response.status_code == 200
data = json.loads(response.data)
assert "unique_claims" in data
assert "total_checks" in data
assert "verified_facts" in data
def test_admin_add_fact_success(self, client, init_test_db):
"""Test: Pridanie manuálne overeného faktu (admin)"""
response = client.post('/api/admin/add-fact',
data=json.dumps({
"claim": "Manuálne pridaný fakt",
"verdict": "TRUE",
"explanation": "Toto je vysvetlenie",
"source_url": "https://overenie.sk"
}),
content_type='application/json')
assert response.status_code == 200
data = json.loads(response.data)
assert "message" in data
assert data["message"] == "Overený fakt pridaný"
def test_admin_add_fact_missing_fields(self, client):
"""Test: Chýbajúce povinné polia pre admin endpoint"""
response = client.post('/api/admin/add-fact',
data=json.dumps({
"claim": "Neúplný fakt"
# Chýba verdict
}),
content_type='application/json')
assert response.status_code == 400
def test_admin_add_fact_duplicate(self, client, init_test_db):
"""Test: Pridanie duplicitného faktu"""
# Prvýkrát úspech
response1 = client.post('/api/admin/add-fact',
data=json.dumps({
"claim": "Duplicitný admin fakt",
"verdict": "TRUE"
}),
content_type='application/json')
assert response1.status_code == 200
# Druhýkrát chyba (duplicate)
response2 = client.post('/api/admin/add-fact',
data=json.dumps({
"claim": "Duplicitný admin fakt",
"verdict": "FALSE"
}),
content_type='application/json')
assert response2.status_code == 409
# =============================================================================
# TESTY VALIDÁCIE VSTUPU
# =============================================================================
class TestInputValidation:
"""Testy pre validáciu vstupov a filtre"""
def test_forbidden_word_politician(self, client):
"""Test: Zakázané slová - politici"""
response = client.post('/api/check',
data=json.dumps({
"claim": "Fico je politik"
}),
content_type='application/json')
assert response.status_code == 400
data = json.loads(response.data)
assert "forbidden_words" in data
def test_forbidden_word_vulgar(self, client):
"""Test: Zakázané slová - vulgárnosti"""
response = client.post('/api/check',
data=json.dumps({
"claim": "Toto je kokotina"
}),
content_type='application/json')
assert response.status_code == 400
def test_forbidden_word_covid(self, client):
"""Test: Zakázané slová - citlivé témy"""
response = client.post('/api/check',
data=json.dumps({
"claim": "Vakcína spôsobuje neplodnosť"
}),
content_type='application/json')
assert response.status_code == 400
def test_forbidden_word_english(self, client):
"""Test: Zakázané slová - anglické výrazy"""
response = client.post('/api/check',
data=json.dumps({
"claim": "This is bullshit"
}),
content_type='application/json')
assert response.status_code == 400
def test_clean_claim_passes(self, client):
"""Test: Čistý výrok prejde"""
# Tento test vyžaduje funkčný SerpAPI kľúč
# Ak nie je nastavený, vráti 500
if not os.getenv('SERPAPI_API_KEY'):
pytest.skip("SERPAPI_API_KEY nie je nastavený")
response = client.post('/api/check',
data=json.dumps({
"claim": "Bratislava je hlavné mesto Slovenska"
}),
content_type='application/json')
# Môže byť 200 (úspech) alebo 429 (limit API)
assert response.status_code in [200, 429]
# =============================================================================
# TESTY MODELOV
# =============================================================================
class TestModels:
"""Testy pre AI modely"""
def test_model_config_exists(self):
"""Test: Konfigurácia modelov existuje"""
assert "roberta" in MODELS
assert "mdeberta" in MODELS
def test_roberta_config(self):
"""Test: RoBERTa konfigurácia"""
config = MODELS["roberta"]
assert config["needs_translation"] == True
assert "roberta" in config["name"].lower()
def test_mdeberta_config(self):
"""Test: mDeBERTa konfigurácia"""
config = MODELS["mdeberta"]
assert config["needs_translation"] == False
assert "mdeberta" in config["name"].lower() or "DeBERTa" in config["name"]
def test_model_loading(self):
"""Test: Načítanie modelu"""
# Testujeme že funkcia load_model existuje a nespôsobí chybu
try:
load_model("roberta")
assert True
except Exception as e:
pytest.fail(f"Načítanie modelu zlyhalo: {e}")
# =============================================================================
# TESTY CACHE LOGIKY
# =============================================================================
class TestCacheLogic:
"""Testy pre cachovaciu logiku"""
def test_cache_increments_count(self, init_test_db):
"""Test: Cache inkrementuje počet overení"""
claim = "Inkrementácia test"
result = {"verdict": "✅ Pravda", "sources": []}
# Prvé uloženie
save_to_cache(claim, result, model_name="roberta")
cached1 = get_cached_result(claim)
count1 = cached1["check_count"]
# Druhé získanie (inkrementuje)
cached2 = get_cached_result(claim)
count2 = cached2["check_count"]
assert count2 == count1 + 1, "Počet overení by sa mal inkrementovať"
def test_cache_updates_timestamp(self, init_test_db):
"""Test: Cache aktualizuje časovú pečiatku"""
claim = "Timestamp test"
result = {"verdict": "✅ Pravda", "sources": []}
save_to_cache(claim, result, model_name="roberta")
cached1 = get_cached_result(claim)
timestamp1 = cached1["checked_at"]
import time
time.sleep(1) # Počkáme sekundu
cached2 = get_cached_result(claim)
timestamp2 = cached2["checked_at"]
# Timestamp by mal byť novší
assert timestamp2 >= timestamp1
def test_cache_serialization(self, init_test_db):
"""Test: Serializácia JSON polí v cache"""
claim = "JSON serializácia test"
result = {
"verdict": "✅ Pravda",
"nli_votes": {"entailment": 0.8, "contradiction": 0.2},
"evidence_for": [{"text": "Dôkaz 1", "confidence": 0.9}],
"evidence_against": [],
"sources": [{"url": "https://example.com", "label": "entailment"}]
}
save_to_cache(claim, result, model_name="mdeberta")
cached = get_cached_result(claim)
assert isinstance(cached["nli_votes"], dict)
assert isinstance(cached["evidence_for"], list)
assert isinstance(cached["evidence_against"], list)
# =============================================================================
# INTEGRÁCNE TESTY
# =============================================================================
class TestIntegration:
"""Integračné testy celého systému"""
def test_full_request_cycle(self, client, init_test_db):
"""Test: Celý cyklus požiadavky (bez SerpAPI)"""
# Testujeme validáciu a štruktúru odpovede
# Bez SerpAPI kľúča očakávame chybu 500
response = client.post('/api/check',
data=json.dumps({
"claim": "Testovací výrok",
"language": "sk",
"model": "roberta"
}),
content_type='application/json')
# Bez SerpAPI: 500, so SerpAPI: 200 alebo 429
if os.getenv('SERPAPI_API_KEY'):
assert response.status_code in [200, 429]
else:
assert response.status_code == 500
def test_model_switching(self, client):
"""Test: Prepínanie medzi modelmi"""
# Overíme že endpoint akceptuje parameter model
response = client.post('/api/check',
data=json.dumps({
"claim": "", # Prázdne pre rýchly test
"model": "mdeberta"
}),
content_type='application/json')
# Očakávame 400 (validation error) nie 500 (model error)
assert response.status_code == 400
def test_verified_fact_overrides_cache(self, client, init_test_db):
"""Test: Manuálne overený fakt má prioritu pred cache"""
claim = "Prioritný fakt"
# Najprv pridáme manuálne overený fakt
add_verified_fact(claim, "TRUE", "Oficiálne overené", "https://overenie.sk")
# Potom pridáme do cache iný výsledok
cache_result = {
"verdict": "❌ Nepravda",
"sources": ["https://ine-zdroj.sk"]
}
save_to_cache(claim, cache_result, model_name="roberta")
# Získanie by malo vrátiť manuálne overený fakt
cached = get_cached_result(claim)
assert cached is not None
assert cached["verified"] == True
assert "Overené" in cached["verdict"]
# =============================================================================
# HRANIČNÉ PRÍPADY
# =============================================================================
class TestEdgeCases:
"""Testy hraničných prípadov"""
def test_very_long_claim(self, client):
"""Test: Veľmi dlhý výrok"""
long_claim = "A" * 10000 # 10k znakov
response = client.post('/api/check',
data=json.dumps({"claim": long_claim}),
content_type='application/json')
# Aplikácia by mala zvládnuť dlhý vstup (vráti "nedostatok zdrojov" alebo spracuje)
assert response.status_code == 200 # Očakávame úspešné spracovanie
data = json.loads(response.data)
assert "verdict" in data # Mala by vrátiť verdikt
def test_unicode_claim(self, client):
"""Test: Výrok s Unicode znakmi"""
unicode_claim = "🌍 je guľatá 🌍"
response = client.post('/api/check',
data=json.dumps({"claim": unicode_claim}),
content_type='application/json')
# Aplikácia by mala správne spracovať Unicode (emoji, diakritika)
assert response.status_code == 200
data = json.loads(response.data)
assert "verdict" in data # Mala by vrátiť verdikt
def test_sql_injection_attempt(self, client):
"""Test: Pokus o SQL injection"""
malicious_claim = "'; DROP TABLE fact_checks; --"
response = client.post('/api/check',
data=json.dumps({"claim": malicious_claim}),
content_type='application/json')
# Aplikácia by mala bezpečne spracovať vstup (parametrované dotazy)
assert response.status_code == 200 # Nemalo by to zhodiť server
# Overíme že databáza stále existuje
from database import get_stats
stats = get_stats() # Ak toto prejde, SQL injection zlyhal
assert stats is not None
def test_special_characters_claim(self, client):
"""Test: Výrok so špeciálnymi znakmi"""
special_claim = "Cena je 100€ (zľava 50%!) <script>alert('x')</script>"
response = client.post('/api/check',
data=json.dumps({"claim": special_claim}),
content_type='application/json')
assert response.status_code in [400, 500, 429]
def test_empty_json_body(self, client):
"""Test: Prázdne JSON telo"""
response = client.post('/api/check',
data=json.dumps({}),
content_type='application/json')
assert response.status_code == 400
def test_invalid_json(self, client):
"""Test: Neplatné JSON"""
response = client.post('/api/check',
data="nie je json",
content_type='application/json')
assert response.status_code == 400
def test_history_limit_zero(self, client, init_test_db):
"""Test: História s limitom 0"""
# Pridáme dáta
save_to_cache("Test", {"verdict": "", "sources": []}, "roberta")
response = client.get('/api/history?limit=0')
data = json.loads(response.data)
assert data["count"] == 0
assert len(data["history"]) == 0
def test_history_large_limit(self, client, init_test_db):
"""Test: História s veľkým limitom"""
response = client.get('/api/history?limit=999999')
assert response.status_code == 200
# =============================================================================
# SPUSTENIE TESTOV
# =============================================================================
if __name__ == '__main__':
# Spustenie priamym zavolaním: python test_app.py
pytest.main([__file__, '-v', '--tb=short'])