M8-T03: adopt DSMR history into meter sources
This commit is contained in:
+29
-15
@@ -21,7 +21,7 @@ import pytest
|
||||
import sqlalchemy.exc
|
||||
from alembic import command
|
||||
from alembic.config import Config
|
||||
from sqlalchemy import create_engine, event as sa_event, inspect, text
|
||||
from sqlalchemy import UniqueConstraint, create_engine, event as sa_event, inspect, text
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.db import Base
|
||||
@@ -114,31 +114,42 @@ def test_energy_tables_exist_after_upgrade(energy_db):
|
||||
|
||||
|
||||
def test_dsmr_reading_columns(energy_db):
|
||||
"""dsmr_reading must have id, recorded_at (NOT NULL), source_id (nullable), payload (NOT NULL)."""
|
||||
"""dsmr_reading stores its telegram id separately from its source identity."""
|
||||
inspector = inspect(energy_db)
|
||||
columns = {col["name"]: col for col in inspector.get_columns("dsmr_reading")}
|
||||
|
||||
assert "id" in columns and not columns["id"]["nullable"]
|
||||
assert "recorded_at" in columns and not columns["recorded_at"]["nullable"]
|
||||
assert "source_id" in columns and columns["source_id"]["nullable"]
|
||||
assert "telegram_id" in columns and columns["telegram_id"]["nullable"]
|
||||
assert "meter_source_id" in columns and not columns["meter_source_id"]["nullable"]
|
||||
assert "payload" in columns and not columns["payload"]["nullable"]
|
||||
|
||||
|
||||
def test_dsmr_reading_recorded_at_unique(energy_db):
|
||||
"""dsmr_reading.recorded_at is the UNIQUE de-dup key (telegram-id-independent)."""
|
||||
def test_dsmr_reading_source_timestamp_unique(energy_db):
|
||||
"""DSMR de-duplication is unique per configured source and timestamp."""
|
||||
inspector = inspect(energy_db)
|
||||
unique_constraints = inspector.get_unique_constraints("dsmr_reading")
|
||||
unique_cols = [col for uc in unique_constraints for col in uc["column_names"]]
|
||||
assert "recorded_at" in unique_cols, "recorded_at must have a unique constraint"
|
||||
assert ("meter_source_id", "recorded_at") in {
|
||||
tuple(uc["column_names"]) for uc in unique_constraints
|
||||
}
|
||||
foreign_keys = {
|
||||
tuple(foreign_key["constrained_columns"]): foreign_key
|
||||
for foreign_key in inspector.get_foreign_keys("dsmr_reading")
|
||||
}
|
||||
assert foreign_keys[("meter_source_id",)]["referred_table"] == "meter_source"
|
||||
assert foreign_keys[("meter_source_id",)]["options"]["ondelete"] == "RESTRICT"
|
||||
assert "ix_dsmr_reading_meter_source_id" in {
|
||||
index["name"] for index in inspector.get_indexes("dsmr_reading")
|
||||
}
|
||||
|
||||
|
||||
def test_dsmr_reading_source_id_not_unique(energy_db):
|
||||
"""dsmr_reading.source_id (telegram id) must NOT be unique — it overflows/resets,
|
||||
def test_dsmr_reading_telegram_id_not_unique(energy_db):
|
||||
"""dsmr_reading.telegram_id must NOT be unique — it overflows/resets,
|
||||
so it is kept only as a reference value and never relied on for dedup."""
|
||||
inspector = inspect(energy_db)
|
||||
unique_constraints = inspector.get_unique_constraints("dsmr_reading")
|
||||
unique_cols = [col for uc in unique_constraints for col in uc["column_names"]]
|
||||
assert "source_id" not in unique_cols, "source_id must NOT have a unique constraint"
|
||||
assert "telegram_id" not in unique_cols, "telegram_id must NOT have a unique constraint"
|
||||
|
||||
|
||||
def test_energy_contract_columns(energy_db):
|
||||
@@ -410,12 +421,15 @@ def test_energy_cost_period_meter_id_fk_ondelete_restrict():
|
||||
)
|
||||
|
||||
|
||||
def test_dsmr_reading_recorded_at_unique_in_metadata():
|
||||
"""DsmrReading.recorded_at must be the unique de-dup key in ORM metadata,
|
||||
and source_id must NOT be unique (decoupled from the telegram id)."""
|
||||
def test_dsmr_reading_source_timestamp_unique_in_metadata():
|
||||
"""DsmrReading de-duplicates by source/timestamp, never telegram id."""
|
||||
table = Base.metadata.tables["dsmr_reading"]
|
||||
assert table.columns["recorded_at"].unique, "recorded_at must be declared unique"
|
||||
assert not table.columns["source_id"].unique, "source_id must NOT be unique"
|
||||
assert not table.columns["telegram_id"].unique, "telegram_id must NOT be unique"
|
||||
assert any(
|
||||
tuple(constraint.columns.keys()) == ("meter_source_id", "recorded_at")
|
||||
for constraint in table.constraints
|
||||
if isinstance(constraint, UniqueConstraint)
|
||||
)
|
||||
|
||||
|
||||
def test_tibber_price_starts_at_unique_in_metadata():
|
||||
|
||||
Reference in New Issue
Block a user