Files
sillyhome-next/tests/ml/test_predictor.py
Otto df2ddacfbf
Some checks failed
quality / test (3.11) (push) Has been cancelled
quality / test (3.13) (push) Has been cancelled
quality / test (3.11) (pull_request) Has been cancelled
quality / test (3.13) (pull_request) Has been cancelled
ML-008: add statistical baseline model
Closes #19
2026-06-13 20:13:06 +02:00

59 lines
1.9 KiB
Python

from __future__ import annotations
import pytest
from app.ml.feature_store import FeatureStore, FeatureVector
from app.ml.predictor import Predictor
from app.ml.training import TrainingPipeline
def _vector(sensor_id: str, temperature: float, label: str | None = None) -> FeatureVector:
return FeatureVector(sensor_id=sensor_id, values={"temperature": temperature}, label=label)
def predictor() -> Predictor:
store = FeatureStore()
store.add_batch(
[
_vector("sensor.kitchen", 19.0),
_vector("sensor.kitchen", 20.0),
_vector("sensor.bedroom", 18.5),
]
)
pipeline = TrainingPipeline(store)
pipeline.run("artifact_v1")
return Predictor(pipeline)
def test_predict_returns_statistical_forecast() -> None:
p = predictor()
result = p.predict("artifact_v1", _vector("sensor.kitchen", 21.0))
assert result.artifact_id == "artifact_v1"
assert result.sensor_id == "sensor.kitchen"
assert result.predictions == {"temperature": 22.0}
assert 0.0 < result.confidence <= 1.0
assert result.model_type == "statistical_baseline"
def test_predict_rejects_unknown_sensor() -> None:
p = predictor()
with pytest.raises(ValueError):
p.predict("artifact_v1", _vector("sensor.unknown", 10.0))
def test_predict_batch_matches_single_calls() -> None:
p = predictor()
entities = [_vector("sensor.kitchen", 21.0), _vector("sensor.bedroom", 19.0)]
assert p.predict_batch("artifact_v1", entities) == [
p.predict("artifact_v1", item) for item in entities
]
def test_default_artifact_returns_last_registered() -> None:
store = FeatureStore()
store.add_batch([_vector("sensor.kitchen", 19.0), _vector("sensor.bedroom", 18.5)])
pipeline = TrainingPipeline(store)
pipeline.run("first")
pipeline.run("second")
assert Predictor.default_artifact(pipeline).artifact_id == "second"