Skip to content

Commit f0ef307

Browse files
author
mvelasqu
committed
test: update tests coverage for new cases
1 parent 0e1573b commit f0ef307

4 files changed

Lines changed: 234 additions & 4 deletions

File tree

docs/source/conf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,13 @@
3434
html_title = "r2x-sienna"
3535
html_theme_options = {
3636
"navigation_with_keys": True,
37-
"source_repository": "https://github.nrel.gov/PCM/r2x-sienna/",
37+
"source_repository": "https://github.com/NatLabRockies/r2x-sienna/",
3838
"source_branch": "main",
3939
"source_directory": "docs/",
4040
"footer_icons": [
4141
{
4242
"name": "GitHub",
43-
"url": "https://github.nrel.gov/PCM/r2x-sienna",
43+
"url": "https://github.com/NatLabRockies/r2x-sienna",
4444
"html": """
4545
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 16 16">
4646
<path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"></path>

src/r2x_sienna/models/costs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ class ThermalGenerationCost(OperationalCost):
8888
References
8989
----------
9090
.. [1] National Renewable Energy Laboratory. "Thermal Generation Cost Model Library."
91-
Available: https://nrel-sienna.github.io/PowerSystems.jl/stable/model_library/thermal_generation_cost/
91+
Available: https://github.com/Sienna-Platform/PowerSystems.jl/blob/main/docs/src/model_library/thermal_generation_cost.md
9292
"""
9393

9494
fixed: Annotated[NonNegativeFloat, Field(description="Cost of using fuel in $ or $/hr.")] = 0.0

tests/test_data_upgrader.py

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
from types import SimpleNamespace
1414

15-
from rust_ok import Ok
15+
from rust_ok import Err, Ok
1616

1717
import r2x_sienna.plugins as plugins
1818
import r2x_sienna.upgrader.data_upgrader as data_upgrader
@@ -383,3 +383,107 @@ def test_version_detector_and_run_sienna_upgrades_paths(tmp_path: Path, monkeypa
383383
monkeypatch.setattr(data_upgrader, "_resolve_json_path", lambda path: None)
384384
result_ok = run_sienna_upgrades(json_path=tmp_path, ctx=SimpleNamespace())
385385
assert result_ok.is_ok()
386+
387+
388+
def test_upgrader_upgrade_error_paths(tmp_path: Path, monkeypatch) -> None:
389+
upgrader = SiennaUpgrader(tmp_path)
390+
391+
# current_version autodetection fails
392+
monkeypatch.setattr(upgrader.version_reader, "read_version", lambda _path: None)
393+
result = upgrader.upgrade(current_version=None)
394+
assert result.is_err()
395+
assert "Could not determine Sienna version" in str(result.err())
396+
397+
# SYSTEM upgrade with no JSON files in directory
398+
empty_dir = tmp_path / "empty_system_dir"
399+
empty_dir.mkdir()
400+
empty_upgrader = SiennaUpgrader(empty_dir)
401+
result = empty_upgrader.upgrade(current_version="1.0.0", upgrade_type=UpgradeType.SYSTEM)
402+
assert result.is_err()
403+
assert "No JSON file found" in str(result.err())
404+
405+
# SYSTEM upgrade with invalid JSON file
406+
bad_json = tmp_path / "bad_system.json"
407+
bad_json.write_text("{invalid", encoding="utf-8")
408+
bad_upgrader = SiennaUpgrader(bad_json)
409+
result = bad_upgrader.upgrade(current_version="1.0.0", upgrade_type=UpgradeType.SYSTEM)
410+
assert result.is_err()
411+
assert "Failed to load JSON" in str(result.err())
412+
413+
414+
def test_upgrader_upgrade_step_failure_and_file_mode_error(tmp_path: Path, monkeypatch) -> None:
415+
system_json = tmp_path / "system.json"
416+
system_json.write_text(json.dumps({"data": {"components": []}}), encoding="utf-8")
417+
418+
def broken_upgrade(_data: dict[str, Any]) -> dict[str, Any]:
419+
raise RuntimeError("step failed")
420+
421+
failing_system_step = UpgradeStep(
422+
name="broken_upgrade",
423+
func=broken_upgrade,
424+
target_version="2.0.0",
425+
upgrade_type=UpgradeType.SYSTEM,
426+
)
427+
upgrader = SiennaUpgrader(system_json, steps=[failing_system_step])
428+
result = upgrader.upgrade(current_version="1.0.0", upgrade_type=UpgradeType.SYSTEM)
429+
assert result.is_err()
430+
assert "Failed broken_upgrade" in str(result.err())
431+
432+
file_step = UpgradeStep(
433+
name="file_step",
434+
func=lambda path: path,
435+
target_version="2.0.0",
436+
upgrade_type=UpgradeType.FILE,
437+
)
438+
file_upgrader = SiennaUpgrader(system_json, steps=[file_step])
439+
monkeypatch.setattr(data_upgrader, "run_upgrade_step", lambda path, step: Err("file step failed"))
440+
result = file_upgrader.upgrade(current_version="1.0.0", upgrade_type=UpgradeType.FILE)
441+
assert result.is_err()
442+
assert "file step failed" in str(result.err())
443+
444+
445+
def test_run_sienna_upgrades_store_and_h5_error_paths(tmp_path: Path, monkeypatch) -> None:
446+
system_json = tmp_path / "sys.json"
447+
system_json.write_text(
448+
json.dumps({"data_format_version": "5.0.0", "data": {"time_series_storage_file": "ts.h5"}}),
449+
encoding="utf-8",
450+
)
451+
h5_path = tmp_path / "ts.h5"
452+
h5_path.write_bytes(b"dummy")
453+
454+
class Store:
455+
folder = tmp_path
456+
457+
# Cover store.folder branch and "could not detect version" skip.
458+
monkeypatch.setattr(data_upgrader.SiennaUpgrader.version_reader, "read_version", lambda path: None)
459+
monkeypatch.setattr(data_upgrader, "_resolve_json_path", lambda path: None)
460+
result_skip = run_sienna_upgrades(store=Store(), ctx=SimpleNamespace())
461+
assert result_skip.is_ok()
462+
463+
# Cover upgrade result error branch.
464+
monkeypatch.setattr(data_upgrader.SiennaUpgrader.version_reader, "read_version", lambda path: "5.0.0")
465+
monkeypatch.setattr(data_upgrader.SiennaUpgrader, "upgrade", lambda self, **kwargs: Err("upgrade failed"))
466+
result_upgrade_err = run_sienna_upgrades(store=Store(), ctx=SimpleNamespace())
467+
assert result_upgrade_err.is_err()
468+
assert "upgrade failed" in str(result_upgrade_err.err())
469+
470+
# Cover JSON/H5 inspection error branch.
471+
monkeypatch.setattr(data_upgrader.SiennaUpgrader, "upgrade", lambda self, **kwargs: Ok(self.path))
472+
monkeypatch.setattr(data_upgrader, "_resolve_json_path", lambda path: system_json)
473+
monkeypatch.setattr(
474+
data_upgrader,
475+
"_resolve_time_series_h5_path",
476+
lambda path: (_ for _ in ()).throw(RuntimeError("inspect failed")),
477+
)
478+
result_inspect_err = run_sienna_upgrades(store=Store(), ctx=SimpleNamespace())
479+
assert result_inspect_err.is_err()
480+
assert "Failed to inspect JSON" in str(result_inspect_err.err())
481+
482+
# Cover H5 upgrade error branch.
483+
monkeypatch.setattr(data_upgrader, "_resolve_time_series_h5_path", lambda path: h5_path)
484+
monkeypatch.setattr(
485+
data_upgrader, "_upgrade_h5_time_series_metadata", lambda path: Err("h5 upgrade failed")
486+
)
487+
result_h5_err = run_sienna_upgrades(store=Store(), ctx=SimpleNamespace())
488+
assert result_h5_err.is_err()
489+
assert "h5 upgrade failed" in str(result_h5_err.err())

tests/test_exporter.py

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
import json
2+
from pathlib import Path
3+
from types import SimpleNamespace
4+
from uuid import uuid4
25

36
import pytest
47
from r2x_core import DataStore, PluginContext
8+
from rust_ok import Err, Ok
59

610
from r2x_sienna import (
711
SiennaConfig,
812
SiennaExporter,
913
SiennaParser,
1014
)
15+
import r2x_sienna.exporter as exporter_mod
1116
from r2x_sienna.models import (
1217
ACBus,
1318
MinMax,
@@ -582,3 +587,124 @@ def test_pjm_time_series_files_exist(data_folder):
582587

583588
assert json_file.exists(), f"PJM JSON file not found: {json_file}"
584589
assert h5_file.exists(), f"PJM H5 file not found: {h5_file}"
590+
591+
592+
def test_iter_supplemental_attributes_fallback_and_empty():
593+
class WithMethod:
594+
def iter_supplemental_attributes(self):
595+
return [1, 2]
596+
597+
class Empty:
598+
pass
599+
600+
assert list(exporter_mod.iter_supplemental_attributes(WithMethod())) == [1, 2]
601+
assert tuple(exporter_mod.iter_supplemental_attributes(Empty())) == ()
602+
603+
604+
def test_serialize_supplemental_attributes_skips_unsupported_type():
605+
class UnsupportedAttr:
606+
def __init__(self):
607+
self.uuid = uuid4()
608+
609+
class Component:
610+
def __init__(self):
611+
self.uuid = uuid4()
612+
613+
attr = UnsupportedAttr()
614+
component = Component()
615+
616+
class ComponentMgr:
617+
def iter_all(self):
618+
return [component]
619+
620+
class FakeSystem:
621+
_component_mgr = ComponentMgr()
622+
623+
@staticmethod
624+
def iter_supplemental_attributes():
625+
return [attr]
626+
627+
@staticmethod
628+
def has_supplemental_attribute(_component):
629+
return True
630+
631+
@staticmethod
632+
def get_supplemental_attributes_with_component(_component):
633+
return [attr]
634+
635+
assert exporter_mod.serialize_single_supplemental_attribute(attr) is None
636+
result = exporter_mod.serialize_supplemental_attributes(FakeSystem())
637+
assert result == {"attributes": [], "associations": []}
638+
639+
640+
def test_exporter_on_validate_returns_err_when_mkdir_fails(simple_test_system, tmp_path, monkeypatch):
641+
cfg = SiennaConfig(output_path=str(tmp_path / "nested" / "out.json"), model_year=2010)
642+
ctx = PluginContext(config=cfg, system=simple_test_system, store=DataStore(path=tmp_path))
643+
exporter = SiennaExporter.from_context(ctx)
644+
645+
def _raise_mkdir(self, parents=False, exist_ok=False):
646+
raise OSError("mkdir failed")
647+
648+
monkeypatch.setattr(Path, "mkdir", _raise_mkdir)
649+
result = exporter.on_validate()
650+
assert result.is_err()
651+
assert "Failed to create output directory" in str(result.err())
652+
653+
654+
def test_exporter_on_export_returns_err_when_component_serialization_raises(
655+
simple_test_system, tmp_path, monkeypatch
656+
):
657+
cfg = SiennaConfig(output_path=str(tmp_path / "out.json"), model_year=2010)
658+
ctx = PluginContext(config=cfg, system=simple_test_system, store=DataStore(path=tmp_path))
659+
exporter = SiennaExporter.from_context(ctx)
660+
exporter.should_export_time_series = False
661+
exporter.system_data = {"data_information": {}, "system_information": {}}
662+
663+
def _boom(_component):
664+
raise RuntimeError("serialize boom")
665+
666+
monkeypatch.setattr(exporter_mod, "serialize_component_to_psy", _boom)
667+
result = exporter.on_export()
668+
assert result.is_err()
669+
assert "Export failed" in str(result.err())
670+
671+
672+
def test_normalize_time_series_metadata_raises_on_upgrade_error(simple_test_system, tmp_path, monkeypatch):
673+
cfg = SiennaConfig(output_path=str(tmp_path / "out.json"), model_year=2010)
674+
ctx = PluginContext(config=cfg, system=simple_test_system, store=DataStore(path=tmp_path))
675+
exporter = SiennaExporter.from_context(ctx)
676+
677+
import r2x_sienna.upgrader.data_upgrader as du
678+
679+
monkeypatch.setattr(du, "_upgrade_h5_time_series_metadata", lambda _path: Err("bad metadata"))
680+
with pytest.raises(RuntimeError, match="bad metadata"):
681+
exporter._normalize_time_series_metadata(tmp_path / "dummy.h5")
682+
683+
684+
def test_patch_time_series_owner_types_no_metadata_dataset(simple_test_system, tmp_path):
685+
import h5py
686+
687+
cfg = SiennaConfig(output_path=str(tmp_path / "out.json"), model_year=2010)
688+
ctx = PluginContext(config=cfg, system=simple_test_system, store=DataStore(path=tmp_path))
689+
exporter = SiennaExporter.from_context(ctx)
690+
691+
h5_path = tmp_path / "no_meta.h5"
692+
with h5py.File(h5_path, "w") as f:
693+
f.create_dataset("not_time_series_metadata", data=[1, 2, 3])
694+
695+
exporter._patch_time_series_owner_types(h5_path)
696+
697+
698+
def test_to_psy_raises_on_validate_and_export_errors(simple_test_system, tmp_path, monkeypatch):
699+
output_file = tmp_path / "legacy.json"
700+
config = SimpleNamespace(system_base_power=100.0, scenario="base")
701+
system_data = {"data_information": {}, "system_information": {}}
702+
703+
monkeypatch.setattr(SiennaExporter, "on_validate", lambda self: Err("validation failed"))
704+
with pytest.raises(Exception, match="Validation failed"):
705+
exporter_mod.to_psy(config, simple_test_system, system_data, str(output_file))
706+
707+
monkeypatch.setattr(SiennaExporter, "on_validate", lambda self: Ok(None))
708+
monkeypatch.setattr(SiennaExporter, "on_export", lambda self: Err("export failed"))
709+
with pytest.raises(Exception, match="Export failed"):
710+
exporter_mod.to_psy(config, simple_test_system, system_data, str(output_file))

0 commit comments

Comments
 (0)