solorpower/crawler/tests/test_backward_backfill.py
haneulai a716dbef96
Some checks are pending
CI / Crawler (Python ${{ matrix.python-version }}) (3.10) (push) Waiting to run
CI / Crawler (Python ${{ matrix.python-version }}) (3.11) (push) Waiting to run
CI / API (Python 3.11) (push) Waiting to run
CI / Database migration (push) Waiting to run
CI / App web build (Node 20) (push) Waiting to run
feat: harden solar monitoring through stage 7
2026-08-07 14:07:22 +09:00

163 lines
5.3 KiB
Python

import sqlite3
import tempfile
import unittest
from contextlib import closing
from pathlib import Path
from unittest.mock import patch
import backward_backfill
def make_plant(site_id="plant-a", start_date="2026-08-01"):
return {
"id": site_id,
"name": site_id,
"type": "fake",
"start_date": start_date,
"options": {},
}
class BackwardBackfillTest(unittest.TestCase):
def setUp(self):
self.temp_dir = tempfile.TemporaryDirectory()
self.db_path = str(Path(self.temp_dir.name) / "crawler_manager.db")
self.db_path_patch = patch.object(backward_backfill, "DB_PATH", self.db_path)
self.db_path_patch.start()
def tearDown(self):
self.db_path_patch.stop()
self.temp_dir.cleanup()
def read_state(self, site_id):
with closing(sqlite3.connect(self.db_path)) as conn:
return conn.execute(
"""
SELECT last_backfilled_date, consecutive_zero_count, status
FROM backfill_state
WHERE site_id = ?
""",
(site_id,),
).fetchone()
def init_sites(self, plants, first_target="2026-08-05"):
with patch.object(backward_backfill, "get_all_plants", return_value=plants):
backward_backfill.init_backfill_states(first_target)
def run_process(self, plants, crawler, save_result=True, days=1):
with (
patch.object(backward_backfill, "get_all_plants", return_value=plants),
patch.object(backward_backfill, "get_history_crawler", return_value=crawler),
patch.object(backward_backfill, "save_history", return_value=save_result),
):
backward_backfill.process_backfill(
max_days_per_run=days,
delay_sec=0,
)
def test_first_requested_date_is_not_skipped(self):
plant = make_plant()
requested_dates = []
self.init_sites([plant])
self.assertEqual(self.read_state("plant-a")[0], "2026-08-06")
def crawler(_plant, start_date, _end_date):
requested_dates.append(start_date)
return [{
"plant_id": "plant-a",
"date": start_date,
"generation_kwh": 12.0,
}]
self.run_process([plant], crawler)
self.assertEqual(requested_dates, ["2026-08-05"])
self.assertEqual(self.read_state("plant-a"), ("2026-08-05", 0, "RUNNING"))
def test_fetch_failure_keeps_cursor_and_zero_count(self):
plant = make_plant()
self.init_sites([plant])
backward_backfill.update_backfill_state("plant-a", "2026-08-06", 7, "RUNNING")
self.run_process([plant], lambda *_args: None)
self.assertEqual(self.read_state("plant-a"), ("2026-08-06", 7, "RUNNING"))
def test_save_failure_keeps_cursor_and_zero_count(self):
plant = make_plant()
self.init_sites([plant])
def crawler(_plant, start_date, _end_date):
return [{
"plant_id": "plant-a",
"date": start_date,
"generation_kwh": 5.0,
}]
self.run_process([plant], crawler, save_result=False)
self.assertEqual(self.read_state("plant-a"), ("2026-08-06", 0, "RUNNING"))
def test_successful_no_data_advances_without_counting_zero(self):
plant = make_plant()
self.init_sites([plant])
backward_backfill.update_backfill_state("plant-a", "2026-08-06", 4, "RUNNING")
self.run_process([plant], lambda *_args: [])
self.assertEqual(self.read_state("plant-a"), ("2026-08-05", 0, "RUNNING"))
def test_thirtieth_actual_zero_completes_site(self):
plant = make_plant(start_date="2026-01-01")
self.init_sites([plant], first_target="2026-08-30")
backward_backfill.update_backfill_state("plant-a", "2026-08-31", 29, "RUNNING")
def crawler(_plant, start_date, _end_date):
return [{
"plant_id": "plant-a",
"date": start_date,
"generation_kwh": 0.0,
}]
self.run_process([plant], crawler)
self.assertEqual(self.read_state("plant-a"), ("2026-08-30", 30, "COMPLETED"))
def test_one_site_failure_does_not_block_another_site(self):
plants = [make_plant("plant-a"), make_plant("plant-b")]
self.init_sites(plants)
def crawler(plant, start_date, _end_date):
if plant["id"] == "plant-a":
return None
return [{
"plant_id": "plant-b",
"date": start_date,
"generation_kwh": 9.0,
}]
self.run_process(plants, crawler)
self.assertEqual(self.read_state("plant-a")[0], "2026-08-06")
self.assertEqual(self.read_state("plant-b")[0], "2026-08-05")
def test_start_date_is_processed_before_completion(self):
plant = make_plant(start_date="2026-08-05")
self.init_sites([plant])
def crawler(_plant, start_date, _end_date):
return [{
"plant_id": "plant-a",
"date": start_date,
"generation_kwh": 1.0,
}]
self.run_process([plant], crawler, days=2)
self.assertEqual(self.read_state("plant-a"), ("2026-08-05", 0, "COMPLETED"))
if __name__ == "__main__":
unittest.main()