test_settings_config.py 5.03 KB
"""U-030 tests: c_concurrency_config_update / c_log_retention_config_update.
Real sqlite, no mocking needed.

Run: python3 -m unittest src.test_settings_config -v
"""

import unittest

from src import schema, settings_config


class SettingsConfigTestBase(unittest.TestCase):
    def setUp(self):
        self.conn = schema.connect(":memory:")
        schema.create_schema(self.conn)
        self.conn.commit()

    def tearDown(self):
        self.conn.close()


class ConcurrencyConfigTest(SettingsConfigTestBase):
    def test_get_returns_none_when_unconfigured(self):
        self.assertIsNone(settings_config.get_concurrency_config(self.conn))

    def test_creates_config_on_first_use(self):
        result = settings_config.upsert_concurrency_config(self.conn, 5, "admin")
        self.assertEqual(result["max_concurrent_goals"], 5)
        self.assertEqual(settings_config.get_concurrency_config(self.conn)["max_concurrent_goals"], 5)

    def test_updates_existing_config(self):
        settings_config.upsert_concurrency_config(self.conn, 5, "admin")
        result = settings_config.upsert_concurrency_config(self.conn, 10, "admin2")
        self.assertEqual(result["max_concurrent_goals"], 10)
        self.assertEqual(
            self.conn.execute("SELECT COUNT(*) FROM org_concurrency_config").fetchone()[0], 1
        )

    def test_below_range_rejected(self):
        with self.assertRaises(settings_config.InvalidParamError):
            settings_config.upsert_concurrency_config(self.conn, 0, "admin")

    def test_above_range_rejected(self):
        with self.assertRaises(settings_config.InvalidParamError):
            settings_config.upsert_concurrency_config(self.conn, 51, "admin")

    def test_range_boundaries_accepted(self):
        settings_config.upsert_concurrency_config(self.conn, 1, "admin")
        settings_config.upsert_concurrency_config(self.conn, 50, "admin")


class LoopLimitConfigTest(SettingsConfigTestBase):
    """2026-08-08: Work Graph self-planning + loop-back (docs/todo/
    work-graph-self-planning-and-loop-back-design.md §6). No implicit
    'default' config_id -- callers always pass one explicitly (they look up
    "{org_node}:{node_type}", "{org_node}:*", "default" in turn)."""

    def test_get_returns_none_when_unconfigured(self):
        self.assertIsNone(settings_config.get_loop_limit_config(self.conn, "planner:benchmark"))

    def test_creates_config_on_first_use(self):
        result = settings_config.upsert_loop_limit_config(self.conn, "planner:benchmark", 5, "admin")
        self.assertEqual(result["max_loop_attempts"], 5)
        self.assertEqual(
            settings_config.get_loop_limit_config(self.conn, "planner:benchmark")["max_loop_attempts"], 5
        )

    def test_updates_existing_config(self):
        settings_config.upsert_loop_limit_config(self.conn, "planner:benchmark", 5, "admin")
        result = settings_config.upsert_loop_limit_config(self.conn, "planner:benchmark", 2, "admin2")
        self.assertEqual(result["max_loop_attempts"], 2)
        self.assertEqual(
            self.conn.execute("SELECT COUNT(*) FROM work_graph_loop_limit_config").fetchone()[0], 1
        )

    def test_different_config_ids_are_independent(self):
        settings_config.upsert_loop_limit_config(self.conn, "planner:benchmark", 5, "admin")
        settings_config.upsert_loop_limit_config(self.conn, "planner:*", 2, "admin")
        self.assertEqual(
            settings_config.get_loop_limit_config(self.conn, "planner:benchmark")["max_loop_attempts"], 5
        )
        self.assertEqual(settings_config.get_loop_limit_config(self.conn, "planner:*")["max_loop_attempts"], 2)

    def test_below_one_rejected(self):
        with self.assertRaises(settings_config.InvalidParamError):
            settings_config.upsert_loop_limit_config(self.conn, "default", 0, "admin")


class LogRetentionConfigTest(SettingsConfigTestBase):
    def test_get_returns_none_when_unconfigured(self):
        self.assertIsNone(settings_config.get_log_retention_config(self.conn))

    def test_creates_config_on_first_use(self):
        result = settings_config.upsert_log_retention_config(self.conn, 14, "admin")
        self.assertEqual(result["retention_days"], 14)
        self.assertIsNone(result["max_total_size_mb"])

    def test_updates_existing_config(self):
        settings_config.upsert_log_retention_config(self.conn, 14, "admin")
        result = settings_config.upsert_log_retention_config(self.conn, 30, "admin", max_total_size_mb=500)
        self.assertEqual(result["retention_days"], 30)
        self.assertEqual(result["max_total_size_mb"], 500)
        self.assertEqual(self.conn.execute("SELECT COUNT(*) FROM log_retention_config").fetchone()[0], 1)

    def test_zero_or_negative_retention_days_rejected(self):
        with self.assertRaises(settings_config.InvalidParamError):
            settings_config.upsert_log_retention_config(self.conn, 0, "admin")
        with self.assertRaises(settings_config.InvalidParamError):
            settings_config.upsert_log_retention_config(self.conn, -5, "admin")


if __name__ == "__main__":
    unittest.main()