This commit is contained in:
1 parent
646ba3be81
commit
aae5b73581
14 files changed
+883
-142
No files matched your search
+68
-2
@@ -10,7 +10,7 @@ import threading
|
||||
import unittest
|
||||
import subprocess
|
||||
import sys
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import Mock, patch
|
||||
from urllib.parse import urlsplit
|
||||
|
||||
from solar_relay.config import InverterConfig, UploadConfig, date_bound, load_config
|
||||
@@ -244,7 +244,7 @@ class DeliveryTests(unittest.TestCase):
|
||||
reading = client.collect("test")
|
||||
self.assertEqual(reading["errors"], {})
|
||||
self.store.add(reading)
|
||||
uploader = Uploader(UploadConfig(url=base + "/ingest"), self.store, token="test-token")
|
||||
uploader = Uploader(UploadConfig(url=base + "/ingest", token="test-token"), self.store)
|
||||
httpd.fail_once = True
|
||||
with self.assertRaises(UploadError):
|
||||
uploader.upload_once()
|
||||
@@ -323,6 +323,27 @@ class DeliveryTests(unittest.TestCase):
|
||||
with self.assertRaisesRegex(ValueError, "SOLAR_RELAY_TOKEN"):
|
||||
Uploader(UploadConfig(url="https://example.test/ingest"), self.store)
|
||||
|
||||
def test_config_upload_token_takes_precedence_over_environment(self):
|
||||
reading = sample()
|
||||
self.store.add(reading)
|
||||
http = Mock()
|
||||
http.request.return_value = {"accepted_ids": [reading["id"]]}
|
||||
with patch("solar_relay.upload.os.environ", {"SOLAR_RELAY_TOKEN": "env-token"}):
|
||||
uploader = Uploader(UploadConfig(url="https://example.test/ingest", token="config-token"), self.store, http=http)
|
||||
self.assertEqual(uploader.upload_once(), 1)
|
||||
self.assertEqual(http.request.call_args.kwargs["token"], "config-token")
|
||||
|
||||
def test_empty_config_token_uses_environment_fallback(self):
|
||||
with patch("solar_relay.upload.os.environ", {"BACKUP_TOKEN": "env-token"}):
|
||||
uploader = Uploader(UploadConfig(url="https://example.test/ingest", token_env="BACKUP_TOKEN"), self.store)
|
||||
self.assertEqual(uploader.token, "env-token")
|
||||
|
||||
def test_invalid_config_token_does_not_fall_back_or_leak_its_value(self):
|
||||
with patch("solar_relay.upload.os.environ", {"SOLAR_RELAY_TOKEN": "env-token"}):
|
||||
with self.assertRaises(ValueError) as raised:
|
||||
Uploader(UploadConfig(url="https://example.test/ingest", token="invalid secret token"), self.store)
|
||||
self.assertNotIn("invalid secret token", str(raised.exception))
|
||||
|
||||
def test_cli_sample_and_export_use_the_persistent_archive(self):
|
||||
with server() as (_, base):
|
||||
config = Path(self.directory.name) / "config.toml"
|
||||
@@ -381,6 +402,51 @@ class ConfigTests(unittest.TestCase):
|
||||
with self.assertRaises(ValueError):
|
||||
self.load('\n[http]\nbase_path="/../config"\n')
|
||||
|
||||
def test_history_account_settings_and_migration_from_query_api_keys(self):
|
||||
config, _ = self.load('\n[history]\nusername_env="BACKUP_USER"\npassword_env="BACKUP_PASSWORD"\n')
|
||||
self.assertEqual(config.history.username_env, "BACKUP_USER")
|
||||
self.assertEqual(config.history.password_env, "BACKUP_PASSWORD")
|
||||
self.assertFalse(config.history.enabled)
|
||||
config, _ = self.load('\n[history]\naccess_key_id_env="OLD_ID"\naccess_key_value_env="OLD_KEY"\n')
|
||||
self.assertEqual(config.history.username_env, "SOLARWEB_USERNAME")
|
||||
self.assertEqual(config.history.password_env, "SOLARWEB_PASSWORD")
|
||||
for name in ("username_env", "password_env"):
|
||||
with self.subTest(name=name), self.assertRaisesRegex(ValueError, "environment variable"):
|
||||
self.load(f'\n[history]\n{name}="invalid name"\n')
|
||||
|
||||
def test_history_uses_retention_instead_of_legacy_lookback_setting(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
path = Path(directory) / "synthetic-test.toml"
|
||||
path.write_text('[collector]\nretention_days=2\n[history]\nenabled=true\n'
|
||||
'pv_system_id="73733052-6f10-47a5-9746-7f7e76ebcb8c"\nlookback_days=7\n')
|
||||
config = load_config(path)
|
||||
self.assertEqual(config.collector.retention_days, 2)
|
||||
self.assertTrue(config.history.enabled)
|
||||
self.assertFalse(hasattr(config.history, "lookback_days"))
|
||||
|
||||
def test_private_config_accepts_credentials_and_redacts_them_from_repr(self):
|
||||
config, _ = self.load('\n[upload]\ntoken="synthetic-shared-token"\n'
|
||||
'\n[history]\nusername="synthetic@example.test"\npassword="synthetic password é &"\n')
|
||||
self.assertEqual(config.upload.token, "synthetic-shared-token")
|
||||
self.assertEqual(config.history.username, "synthetic@example.test")
|
||||
self.assertEqual(config.history.password, "synthetic password é &")
|
||||
for secret in (config.upload.token, config.history.username, config.history.password):
|
||||
self.assertNotIn(secret, repr(config))
|
||||
|
||||
def test_partial_or_mistyped_private_credentials_are_rejected_without_values(self):
|
||||
for fields in ['username="synthetic@example.test"', 'password="synthetic-secret"',
|
||||
'username=" "\npassword="synthetic-secret"', 'username=42', 'password=42']:
|
||||
with self.subTest(fields=fields):
|
||||
with self.assertRaises(ValueError) as raised:
|
||||
self.load(f'\n[history]\n{fields}\n')
|
||||
self.assertNotIn("synthetic-secret", str(raised.exception))
|
||||
self.assertNotIn("synthetic@example.test", str(raised.exception))
|
||||
for value in ['42', '"invalid secret token"']:
|
||||
with self.subTest(value=value):
|
||||
with self.assertRaisesRegex(ValueError, "upload.token") as raised:
|
||||
self.load(f'\n[upload]\ntoken={value}\n')
|
||||
self.assertNotIn("invalid secret token", str(raised.exception))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user