Files
ripe-cidr-collector/tests/test_db.py
T
ayurishchevandClaude Sonnet 5 bcf8156085 Initial commit: RIPE CIDR/FQDN collector
Collector daemon, FastAPI server (addresses, diff, collect, sources),
SQLite storage with change journal, Docker Compose deployment,
tests, documentation and project rules.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 07:29:38 +03:00

79 lines
3.9 KiB
Python

import datetime
import json
import os
import sys
import pytest
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import cidr_collector as cc
import db
@pytest.fixture
def files(tmp_path, monkeypatch):
for attr, name in (("DB_FILE", "ripe.db"), ("DATA_FILE", "data.json"), ("FQDN_DATA_FILE", "fqdn_data.json")):
monkeypatch.setattr(cc, attr, str(tmp_path / name))
return tmp_path
def test_legacy_json_import(files):
before = datetime.datetime.now().isoformat(timespec="seconds")
(files / "data.json").write_text(json.dumps({
# Новый формат: first_seen/last_seen сохраняются
"1": {"last_updated": "2026-02-01T00:00:00", "prefixes": ["1.0.0.0/24"],
"seen": {"1.0.0.0/24": {"first_seen": "2026-01-01T00:00:00", "last_seen": "2026-02-01T00:00:00"}}},
# Старый формат без seen: first_seen = last_updated, last_seen = момент миграции
"2": {"last_updated": "2025-12-01T00:00:00", "prefixes": ["2.0.0.0/24"]}}))
(files / "fqdn_data.json").write_text(json.dumps(
{"example.com": {"last_updated": "2025-12-01T00:00:00", "ips": ["9.9.9.9"]}}))
with db.session() as conn:
rows = {(k, s, v): (f, l) for k, s, v, f, l in conn.execute("SELECT * FROM addresses")}
assert rows[("asn", "1", "1.0.0.0/24")] == ("2026-01-01T00:00:00", "2026-02-01T00:00:00")
first, last = rows[("asn", "2", "2.0.0.0/24")]
assert first == "2025-12-01T00:00:00" and last >= before
assert ("fqdn", "example.com", "9.9.9.9") in rows
# Оригиналы переименованы (резервная копия), повторное открытие ничего не импортирует
assert not (files / "data.json").exists() and len(list(files.glob("data.json.migrated-*"))) == 1
(files / "data.json").write_text(json.dumps({"3": {"prefixes": ["3.0.0.0/24"]}}))
with db.session() as conn:
assert db.count_values(conn) == 3
def test_change_journal(files):
now = datetime.datetime.now()
(files / "data.json").write_text(json.dumps({"9": {"prefixes": ["9.0.0.0/24"]}}))
with db.session() as conn:
# Импорт из JSON в журнал не попадает
assert db.get_changes(conn, {"asn"}, cursor=0) == (set(), set(), 0)
with db.transaction(conn):
db.merge_source(conn, "asn", "1", {"a", "b"}, now, 90)
db.merge_source(conn, "asn", "2", {"b"}, now, 90)
added, removed, c1 = db.get_changes(conn, {"asn"}, cursor=0)
assert (added, removed) == ({"a", "b"}, set())
# Значение b держит второй источник: исчезает только a
with db.transaction(conn):
db.purge_source(conn, "asn", "1")
assert db.get_changes(conn, {"asn"}, cursor=c1)[:2] == (set(), {"a"})
# Удалено и возвращено в одном интервале - изменений нет; повтор с новым курсором пуст
c2 = db.get_changes(conn, {"asn"}, cursor=c1)[2]
with db.transaction(conn):
db.purge_source(conn, "asn", "2")
db.merge_source(conn, "asn", "2", {"b"}, now, 90)
added, removed, c3 = db.get_changes(conn, {"asn"}, cursor=c2)
assert (added, removed) == (set(), set()) and c3 > c2
assert db.get_changes(conn, {"fqdn"}, cursor=0)[:2] == (set(), set()) # фильтр по типу
# Очистка сдвигает горизонт: старый курсор недействителен, текущий - нет; курсор «из будущего» тоже
with db.transaction(conn):
assert db.prune_changes(conn, now + datetime.timedelta(days=2), 1) > 0
assert db.get_changes(conn, {"asn"}, cursor=0) is None
assert db.get_changes(conn, {"asn"}, cursor=c3 + 1) is None
assert db.get_changes(conn, {"asn"}, cursor=c3) == (set(), set(), c3)