Интегрирован wesp в сайт
CI / quality (push) Canceled after 0s

This commit is contained in:
влад
2026-07-17 12:57:18 +03:00
parent 5dfa06ddbe
commit 355c0ef9f1
883 changed files with 194576 additions and 177 deletions
@@ -0,0 +1,279 @@
"""Интеграционные тесты feed_quality.evaluator."""
from __future__ import annotations
import unittest
from datetime import datetime
from sqlalchemy import select
from app import create_app, db
from app.models import (
ComponentLoadingTime,
LoadingReport,
LoadingReportComponent,
Recipe,
UnloadingReport,
UnloadingReportGroup,
)
from app.models.feed_alert import FeedAlert
from app.services.feed_quality.evaluator import evaluate_loading_report, evaluate_unloading_report
from app.services.feed_quality.rules import (
EVENT_LEFT_IN_MIXER,
EVENT_LOADING_FAST,
EVENT_LOADING_TIME,
EVENT_OVERLOAD,
EVENT_UNDERLOAD,
)
from app.services.feed_quality.settings_store import save_feed_quality_settings
from app.services.setup_state import mark_setup_complete
from tests.helpers.zootech_test_helpers import ZootechTestConfig
class FeedQualityEvaluatorTests(unittest.TestCase):
def setUp(self) -> None:
self.app = create_app(ZootechTestConfig)
self.ctx = self.app.app_context()
self.ctx.push()
db.create_all()
mark_setup_complete(self.app)
db.session.add(
Recipe(
id="fq-r1",
name="Тестовый рейс",
heads_per_trip=10,
mixing_time=5,
content_hash="",
)
)
db.session.commit()
def tearDown(self) -> None:
db.session.remove()
db.drop_all()
self.ctx.pop()
def _loading_with_overload(self) -> str:
report = LoadingReport(
id="fq-lr-1",
recipe_id="fq-r1",
recipe_name="Тестовый рейс",
start_time=datetime.now(),
target_mixing_time=300,
actual_mixing_time=350,
total_weight=100.0,
created_by="system",
updated_by="system",
)
db.session.add(report)
db.session.flush()
db.session.add(
LoadingReportComponent(
report_id=report.id,
component_name="Силос",
target_weight=100.0,
actual_weight=115.0,
overload=15.0,
loading_order=1,
created_by="system",
updated_by="system",
)
)
db.session.commit()
return report.id
def test_evaluate_loading_creates_alerts(self) -> None:
report_id = self._loading_with_overload()
rows = evaluate_loading_report(report_id, send_notifications=False)
self.assertGreaterEqual(len(rows), 2)
types = {r.event_type for r in rows}
self.assertIn(EVENT_OVERLOAD, types)
def test_idempotent_re_evaluate(self) -> None:
report_id = self._loading_with_overload()
first = evaluate_loading_report(report_id, send_notifications=False)
evaluate_loading_report(report_id, send_notifications=False)
second_count = len(
db.session.execute(
select(FeedAlert).where(FeedAlert.loading_report_id == report_id)
)
.scalars()
.all()
)
self.assertEqual(second_count, len(first))
def test_fast_loading_merged_with_underload(self) -> None:
report = LoadingReport(
id="fq-lr-fast",
recipe_id="fq-r1",
recipe_name="Тестовый рейс",
start_time=datetime.now(),
target_mixing_time=60,
actual_mixing_time=60,
total_weight=62.0,
created_by="system",
updated_by="system",
)
db.session.add(report)
db.session.flush()
db.session.add(
LoadingReportComponent(
report_id=report.id,
component_name="Сено",
target_weight=62.0,
actual_weight=0.0,
overload=-62.0,
loading_order=1,
created_by="system",
updated_by="system",
)
)
now = datetime.now()
db.session.add(
ComponentLoadingTime(
report_id=report.id,
component_name="Сено",
start_time=now,
end_time=now,
loading_duration=0.9,
loading_order=1,
created_by="system",
updated_by="system",
)
)
db.session.commit()
rows = evaluate_loading_report(report.id, send_notifications=False)
component_rows = [r for r in rows if r.component_name == "Сено"]
self.assertEqual(len(component_rows), 1)
self.assertEqual(component_rows[0].event_type, EVENT_UNDERLOAD)
self.assertIn("0.9 с", component_rows[0].detail)
def test_fast_loading_separate_when_weight_ok(self) -> None:
report = LoadingReport(
id="fq-lr-fast-ok",
recipe_id="fq-r1",
recipe_name="Тестовый рейс",
start_time=datetime.now(),
target_mixing_time=60,
actual_mixing_time=60,
total_weight=62.0,
created_by="system",
updated_by="system",
)
db.session.add(report)
db.session.flush()
db.session.add(
LoadingReportComponent(
report_id=report.id,
component_name="Сено",
target_weight=62.0,
actual_weight=62.0,
overload=0.0,
loading_order=1,
created_by="system",
updated_by="system",
)
)
now = datetime.now()
db.session.add(
ComponentLoadingTime(
report_id=report.id,
component_name="Сено",
start_time=now,
end_time=now,
loading_duration=0.9,
loading_order=1,
created_by="system",
updated_by="system",
)
)
db.session.commit()
rows = evaluate_loading_report(report.id, send_notifications=False)
fast = [r for r in rows if r.component_name == "Сено"]
self.assertEqual(len(fast), 1)
self.assertEqual(fast[0].event_type, EVENT_LOADING_FAST)
def test_slow_loading_separate_when_weight_ok(self) -> None:
save_feed_quality_settings({"loading": {"warning_max_sec": 30, "critical_max_sec": 120}})
report = LoadingReport(
id="fq-lr-slow",
recipe_id="fq-r1",
recipe_name="Тестовый рейс",
start_time=datetime.now(),
target_mixing_time=60,
actual_mixing_time=60,
total_weight=100.0,
created_by="system",
updated_by="system",
)
db.session.add(report)
db.session.flush()
db.session.add(
LoadingReportComponent(
report_id=report.id,
component_name="Силос",
target_weight=100.0,
actual_weight=100.0,
overload=0.0,
loading_order=1,
created_by="system",
updated_by="system",
)
)
now = datetime.now()
db.session.add(
ComponentLoadingTime(
report_id=report.id,
component_name="Силос",
start_time=now,
end_time=now,
loading_duration=90.0,
loading_order=1,
created_by="system",
updated_by="system",
)
)
db.session.commit()
rows = evaluate_loading_report(report.id, send_notifications=False)
slow = [r for r in rows if r.component_name == "Силос"]
self.assertEqual(len(slow), 1)
self.assertEqual(slow[0].event_type, EVENT_LOADING_TIME)
def test_left_in_mixer_on_unloading(self) -> None:
report_id = self._loading_with_overload()
unloading = UnloadingReport(
id="fq-ur-1",
recipe_id="fq-r1",
recipe_name="Тестовый рейс",
loading_report_id=report_id,
start_time=datetime.now(),
total_weight=100.0,
total_unloaded_weight=90.0,
remaining_weight=10.0,
created_by="system",
updated_by="system",
)
db.session.add(unloading)
db.session.flush()
db.session.add(
UnloadingReportGroup(
report_id=unloading.id,
name="G1",
target_weight=100.0,
unloaded_weight=90.0,
remaining_weight=10.0,
distribution_type="percent",
distribution_value=100,
order=1,
created_by="system",
updated_by="system",
)
)
db.session.commit()
rows = evaluate_unloading_report(unloading.id, send_notifications=False)
types = {r.event_type for r in rows}
self.assertIn(EVENT_LEFT_IN_MIXER, types)
if __name__ == "__main__":
unittest.main()