[{'Text': 'Command "uv run pytest tests/test_gbs_tools.py tests/test_mongodb_utils.py -v 2>&1" failed with exit code 1.\n\nCommand output too long. The first 16161 bytes:\n\n```\n🪄 .zshrc loaded successfully! (modular edition)\n/Users/peterdonaghey/miniconda3/lib/python3.12/site-packages/pytest_asyncio/plugin.py:247: PytestDeprecationWarning: The configuration option "asyncio_default_fixture_loop_scope" is unset.\nThe event loop scope for asynchronous fixtures will default to the fixture caching scope. Future versions of pytest-asyncio will default the loop scope for asynchronous fixtures to function scope. Set the default fixture loop scope explicitly in order to avoid unexpected behavior in the future. Valid fixture loop scopes are: "function", "class", "module", "package", "session"\n\n  warnings.warn(PytestDeprecationWarning(_DEFAULT_FIXTURE_LOOP_SCOPE_UNSET))\n======================================= test session starts ========================================\nplatform darwin -- Python 3.12.2, pytest-8.3.3, pluggy-1.5.0 -- /Users/peterdonaghey/miniconda3/bin/python\ncachedir: .pytest_cache\nrootdir: /Users/peterdonaghey/Projects/point-topic-mcp\nconfigfile: pyproject.toml\nplugins: logfire-4.18.0, jaxtyping-0.2.31, devtools-0.12.2, mockito-0.0.4, asyncio-1.3.0, typeguard-4.4.4, anyio-4.13.0\nasyncio: mode=Mode.STRICT, debug=False, asyncio_default_fixture_loop_scope=None, asyncio_default_test_loop_scope=function\ncollected 13 items\n\ntests/test_gbs_tools.py::test_gbs_get_status_country_level FAILED                            [  7%]\ntests/test_gbs_tools.py::test_gbs_get_status_operator_not_found FAILED                       [ 15%]\ntests/test_gbs_tools.py::test_gbs_get_status_operator_level FAILED                           [ 23%]\ntests/test_gbs_tools.py::test_gbs_get_status_operator_staleness FAILED                       [ 30%]\ntests/test_gbs_tools.py::test_gbs_add_statistic_validation FAILED                            [ 38%]\ntests/test_gbs_tools.py::test_gbs_add_statistic_success FAILED                               [ 46%]\ntests/test_gbs_tools.py::test_gbs_add_statistic_duplicate_rejected FAILED                    [ 53%]\ntests/test_gbs_tools.py::test_gbs_create_source_validation FAILED                            [ 61%]\ntests/test_gbs_tools.py::test_gbs_create_source_success FAILED                               [ 69%]\ntests/test_gbs_tools.py::test_gbs_create_source_duplicate_rejected FAILED                    [ 76%]\ntests/test_gbs_tools.py::test_discovery_uses_prefix FAILED                                   [ 84%]\ntests/test_mongodb_utils.py::test_insert_one_statistic_includes_bson_dates_and_archived PASSED [ 92%]\ntests/test_mongodb_utils.py::test_insert_one_source_includes_bson_dates PASSED               [100%]\n\n============================================= FAILURES =============================================\n________________________________ test_gbs_get_status_country_level _________________________________\n\ngbs_env = <module \'point_topic_mcp.tools.gbs_tools\' from \'/Users/peterdonaghey/Projects/point-topic-mcp/src/point_topic_mcp/tools/gbs_tools.py\'>\n\n    def test_gbs_get_status_country_level(gbs_env):\n        """Country-level: returns coverage rollup with operator lists."""\n        gbs = gbs_env\n        op_id_a = ObjectId()\n        op_id_b = ObjectId()\n        mock_docs = [\n            {"_id": {"$oid": str(op_id_a)}, "name": "Alpha", "techs": ["FTTP"], "statCount": 5, "stats": [{"state": 3}] * 5},\n            {"_id": {"$oid": str(op_id_b)}, "name": "Beta", "techs": ["DSL"], "statCount": 0, "stats": []},\n        ]\n        mock_return = json.dumps({"currentPeriod": "2025Q1", "targetPeriod": "2025Q1", "docs": mock_docs})\n\n        with patch("point_topic_mcp.core.mongodb_utils._run_mongosh", return_value=mock_return):\n>           result = gbs.gbs_get_status(country="UK")\nE           AttributeError: module \'point_topic_mcp.tools.gbs_tools\' has no attribute \'gbs_get_status\'. Did you mean: \'get_status\'?\n\ntests/test_gbs_tools.py:38: AttributeError\n-------------------------------------- Captured stdout setup ---------------------------------------\n[MCP] Registering gbs_tools: env vars [\'PT_RESEARCH_DATABASE_URI\'] ✓\n[MCP] Registering gbs_tools: env vars [\'PT_RESEARCH_DATABASE_URI\'] ✓\n______________________________ test_gbs_get_status_operator_not_found ______________________________\n\ngbs_env = <module \'point_topic_mcp.tools.gbs_tools\' from \'/Users/peterdonaghey/Projects/point-topic-mcp/src/point_topic_mcp/tools/gbs_tools.py\'>\n\n    def test_gbs_get_status_operator_not_found(gbs_env):\n        """Operator-level: returns error when operator doesn\'t exist."""\n        gbs = gbs_env\n        mock_return = json.dumps({"currentPeriod": "2025Q1", "targetPeriod": "2025Q1", "docs": []})\n\n        with patch("point_topic_mcp.core.mongodb_utils._run_mongosh", return_value=mock_return):\n>           result = gbs.gbs_get_status(country="UK", operator="NoSuchOp")\nE           AttributeError: module \'point_topic_mcp.tools.gbs_tools\' has no attribute \'gbs_get_status\'. Did you mean: \'get_status\'?\n\ntests/test_gbs_tools.py:55: AttributeError\n-------------------------------------- Captured stdout setup ---------------------------------------\n[MCP] Registering gbs_tools: env vars [\'PT_RESEARCH_DATABASE_URI\'] ✓\n________________________________ test_gbs_get_status_operator_level ________________________________\n\ngbs_env = <module \'point_topic_mcp.tools.gbs_tools\' from \'/Users/peterdonaghey/Projects/point-topic-mcp/src/point_topic_mcp/tools/gbs_tools.py\'>\n\n    def test_gbs_get_status_operator_level(gbs_env):\n        """Operator-level: returns records, gaps, state breakdown, admin URL."""\n        gbs = gbs_env\n        op_id = ObjectId()\n        mock_stats = [\n            {"type": "broadband", "tech": "FTTP", "channel": "Infrastructure",\n             "domain": "Residential", "subscribers": 100000, "state": 1},\n        ]\n        mock_docs = [{\n            "_id": {"$oid": str(op_id)},\n            "name": "TestOp",\n            "techs": ["FTTP", "FTTC"],\n            "statCount": 1,\n            "stats": mock_stats,\n        }]\n        mock_return = json.dumps({"currentPeriod": "2025Q1", "targetPeriod": "2025Q1", "docs": mock_docs})\n\n        with patch("point_topic_mcp.core.mongodb_utils._run_mongosh", return_value=mock_return):\n>           result = gbs.gbs_get_status(country="UK", operator="TestOp")\nE           AttributeError: module \'point_topic_mcp.tools.gbs_tools\' has no attribute \'gbs_get_status\'. Did you mean: \'get_status\'?\n\ntests/test_gbs_tools.py:78: AttributeError\n-------------------------------------- Captured stdout setup ---------------------------------------\n[MCP] Registering gbs_tools: env vars [\'PT_RESEARCH_DATABASE_URI\'] ✓\n______________________________ test_gbs_get_status_operator_staleness ______________________________\n\ngbs_env = <module \'point_topic_mcp.tools.gbs_tools\' from \'/Users/peterdonaghey/Projects/point-topic-mcp/src/point_topic_mcp/tools/gbs_tools.py\'>\n\n    def test_gbs_get_status_operator_staleness(gbs_env):\n        """Operator-level: works with explicit period; any period is accepted."""\n        gbs = gbs_env\n        op_id = ObjectId()\n        mock_docs = [{\n            "_id": {"$oid": str(op_id)},\n            "name": "TestOp",\n            "techs": ["FTTP"],\n            "statCount": 0,\n            "stats": [],\n        }]\n        # Period specified → should be used as targetPeriod\n        mock_return = json.dumps({"currentPeriod": "2025Q3", "targetPeriod": "2025Q1", "docs": mock_docs})\n\n        with patch("point_topic_mcp.core.mongodb_utils._run_mongosh", return_value=mock_return):\n>           result = gbs.gbs_get_status(country="UK", operator="TestOp", period="2025Q1")\nE           AttributeError: module \'point_topic_mcp.tools.gbs_tools\' has no attribute \'gbs_get_status\'. Did you mean: \'get_status\'?\n\ntests/test_gbs_tools.py:110: AttributeError\n-------------------------------------- Captured stdout setup ---------------------------------------\n[MCP] Registering gbs_tools: env vars [\'PT_RESEARCH_DATABASE_URI\'] ✓\n________________________________ test_gbs_add_statistic_validation _________________________________\n\ngbs_env = <module \'point_topic_mcp.tools.gbs_tools\' from \'/Users/peterdonaghey/Projects/point-topic-mcp/src/point_topic_mcp/tools/gbs_tools.py\'>\n\n    def test_gbs_add_statistic_validation(gbs_env):\n        """gbs_add_statistic returns errors for invalid inputs."""\n        gbs = gbs_env\n\n>       result = gbs.gbs_add_statistic(\n            type="invalid",\n            operator_id="000000000000000000000001",\n            period="2025Q1",\n            tech="FTTP",\n            channel="Wrong",\n            domain="Residential",\n            subscribers=1000,\n        )\nE       AttributeError: module \'point_topic_mcp.tools.gbs_tools\' has no attribute \'gbs_add_statistic\'. Did you mean: \'add_statistic\'?\n\ntests/test_gbs_tools.py:119: AttributeError\n-------------------------------------- Captured stdout setup ---------------------------------------\n[MCP] Registering gbs_tools: env vars [\'PT_RESEARCH_DATABASE_URI\'] ✓\n__________________________________ test_gbs_add_statistic_success __________________________________\n\ngbs_env = <module \'point_topic_mcp.tools.gbs_tools\' from \'/Users/peterdonaghey/Projects/point-topic-mcp/src/point_topic_mcp/tools/gbs_tools.py\'>\n\n    def test_gbs_add_statistic_success(gbs_env):\n        """gbs_add_statistic inserts a pending record with admin URL."""\n        gbs = gbs_env\n        op_id = str(ObjectId())\n        mock_response = json.dumps({\n            "ok": True,\n            "insertedId": op_id,\n            "operator": "TestOp",\n            "country": "UK",\n            "tech": "FTTP",\n        })\n\n        with patch("point_topic_mcp.core.mongodb_utils.insert_statistic_safe", return_value=mock_response):\n>           result = gbs.gbs_add_statistic(\n                type="broadband",\n                operator_id=op_id,\n                period="2025Q3",\n                tech="FTTP",\n                channel="Infrastructure",\n                domain="Residential",\n                subscribers=50000,\n                created_by="test-agent",\n            )\nE           AttributeError: module \'point_topic_mcp.tools.gbs_tools\' has no attribute \'gbs_add_statistic\'. Did you mean: \'add_statistic\'?\n\ntests/test_gbs_tools.py:177: AttributeError\n-------------------------------------- Captured stdout setup ---------------------------------------\n[MCP] Registering gbs_tools: env vars [\'PT_RESEARCH_DATABASE_URI\'] ✓\n____________________________ test_gbs_add_statistic_duplicate_rejected _____________________________\n\ngbs_env = <module \'point_topic_mcp.tools.gbs_tools\' from \'/Users/peterdonaghey/Projects/point-topic-mcp/src/point_topic_mcp/tools/gbs_tools.py\'>\n\n    def test_gbs_add_statistic_duplicate_rejected(gbs_env):\n        """gbs_add_statistic rejects duplicate (operator, period, type, tech, channel, domain)."""\n        gbs = gbs_env\n        op_id = str(ObjectId())\n        existing_id = str(ObjectId())\n        mock_response = json.dumps({\n            "ok": False,\n            "error": f"Duplicate: statistic already exists for broadband/FTTP/Infrastructure/Residential in 2025Q3 (id={existing_id}, state=1)",\n        })\n\n        with patch("point_topic_mcp.core.mongodb_utils.insert_statistic_safe", return_value=mock_response):\n>           result = gbs.gbs_add_statistic(\n                type="broadband",\n                operator_id=op_id,\n                period="2025Q3",\n                tech="FTTP",\n                channel="Infrastructure",\n                domain="Residential",\n                subscribers=50000,\n            )\nE           AttributeError: module \'point_topic_mcp.tools.gbs_tools\' has no attribute \'gbs_add_statistic\'. Did you mean: \'add_statistic\'?\n\ntests/test_gbs_tools.py:206: AttributeError\n-------------------------------------- Captured stdout setup ---------------------------------------\n[MCP] Registering gbs_tools: env vars [\'PT_RESEARCH_DATABASE_URI\'] ✓\n________________________________ test_gbs_create_source_validation _________________________________\n\ngbs_env = <module \'point_topic_mcp.tools.gbs_tools\' from \'/Users/peterdonaghey/Projects/point-topic-mcp/src/point_topic_mcp/tools/gbs_tools.py\'>\n\n    def test_gbs_create_source_validation(gbs_env):\n        """gbs_create_source returns errors for invalid inputs."""\n        gbs = gbs_env\n\n>       result = gbs.gbs_create_source(\n            operator_id="bad",\n            year=2025,\n            quarter=1,\n            type="report",\n        )\nE       AttributeError: module \'point_topic_mcp.tools.gbs_tools\' has no attribute \'gbs_create_source\'. Did you mean: \'create_source\'?\n\ntests/test_gbs_tools.py:223: AttributeError\n-------------------------------------- Captured stdout setup ---------------------------------------\n[MCP] Registering gbs_tools: env vars [\'PT_RESEARCH_DATABASE_URI\'] ✓\n__________________________________ test_gbs_create_source_success __________________________________\n\ngbs_env = <module \'point_topic_mcp.tools.gbs_tools\' from \'/Users/peterdonaghey/Projects/point-topic-mcp/src/point_topic_mcp/tools/gbs_tools.py\'>\n\n    def test_gbs_create_source_success(gbs_env):\n        """gbs_create_source inserts a source record."""\n        gbs = gbs_env\n        op_id = str(ObjectId())\n        mock_op = {"_id": {"$oid": op_id}, "name": "TestOp"}\n        source_id = str(ObjectId())\n\n        with (\n            patch("point_topic_mcp.core.mongodb_utils.find_one_operator_by_id", return_value=mock_op),\n            patch("point_topic_mcp.core.mongodb_utils.find_sources", return_value=[]),\n            patch("point_topic_mcp.core.mongodb_utils.insert_one_source", return_value=source_id),\n        ):\n>           result = gbs.gbs_create_source(\n                operator_id=op_id,\n                year=2025,\n                quarter=3,\n                type="report",\n                url="https://example.com/report",\n            )\nE           AttributeError: module \'point_topic_mcp.tools.gbs_tools\' has no attribute \'gbs_create_source\'. Did you mean: \'create_source\'?\n\ntests/test_gbs_tools.py:269: AttributeError\n-------------------------------------- Captured stdout setup ---------------------------------------\n[MCP] Registering gbs_tools: env vars [\'PT_RESEARCH_DATABASE_URI\'] ✓\n____________________________ test_gbs_create_source_duplicate_rejected _____________________________\n\ngbs_env = <module \'point_topic_mcp.tools.gbs_tools\' from \'/Users/peterdonaghey/Projects/point-topic-mcp/src/point_topic_mcp/tools/gbs_tools.py\'>\n\n    def test_gbs_create_source_duplicate_rejected(gbs_env):\n        """gbs_create_source rejects exact duplicates."""\n        gbs = gbs_env\n        op_id = str(ObjectId())\n        mock_op = {"_id": {"$oid": op_id}, "name": "TestOp"}\n        existing_id = str(ObjectId())\n        mock_existing = [{\n            "_id": {"$oid": existing_id},\n            "type": "report",\n            "url": "https://example.com/report",\n            "fileUrl": None,\n        }]\n\n        with (\n            patch("point_topic_mcp.core.mongodb_utils.find_one_operator_by_id", return_value=mock_op),\n            patch("point_topic_mcp.core.mongodb_utils.find_sources", return_value=mock_existing),\n        ):\n>           result = gbs.gbs_create_source(\n                operator_id=op_id,\n                year=2025,\n                quarter=3,\n                type="report",\n                url="https://example.com/report",\n            )\nE           AttributeError: module \'point_topic_mcp.tools.gbs_tools\' has no attribute \'gbs_create_source\'. Did you mean: \'create_source\'?\n\ntests/test_gbs_tools.py:299: AttributeError\n-------------------------------------- Captured stdout setup ---------------------------------------\n[MCP] Registering gbs_tools: env vars [\'PT_RESEARCH_DATABASE_URI\'] ✓\n____________________________________ test_discovery_uses_prefix ____________________________________\n\ngbs_env = <module \'point_topic_mcp.tools.gbs_tools\' from \'/Users/peterdonaghey/Projects/point-topic-mcp/src/point_topic_mcp/tools/gbs_tools.py\'>\n\n    def test_discovery_uses_prefix(gbs_env):\n        """Tool auto-discovery prefixes module name."""\n        import point_topic_mcp.tools as tools\n        all_tools = tools.get_all_tools()\n        names = [n for n, _ in all_tools]\n>       assert "gbs_tools_gbs_get_status" in names\n```'}]