[{'Text': '"""Tests for read_gbs tool.\n\nMocks mongodb_utils to test validation, EJSON parsing, and error paths\nwithout requiring a real MongoDB connection.\n"""\n\nimport json\nfrom unittest.mock import patch\n\nimport pytest\n\n\n@pytest.fixture\ndef gbs_env():\n    """Ensure PT_RESEARCH_DATABASE_URI looks set so tools register."""\n    with patch.dict("os.environ", {"PT_RESEARCH_DATABASE_URI": "mongodb://localhost:27017/t"}, clear=False):\n        import importlib\n        import point_topic_mcp.tools.gbs_query_tools as gbs\n        importlib.reload(gbs)\n        yield gbs\n\n\ndef test_invalid_json(gbs_env):\n    """Returns error for unparseable JSON."""\n    gbs = gbs_env\n    result = gbs.read_gbs(pipeline_json="not json")\n    assert "Error" in result\n    assert "Invalid JSON" in result\n\n\ndef test_no_pipeline(gbs_env):\n    """Returns error when no pipeline provided."""\n    gbs = gbs_env\n    result = gbs.read_gbs()\n    assert "Error" in result\n    assert "Provide" in result\n\n\ndef test_write_stage_rejected(gbs_env):\n    """$out and $merge are rejected as write stages."""\n    gbs = gbs_env\n    result = gbs.read_gbs(pipeline_json=\'[{"$match": {}}, {"$out": "test"}]\')\n    assert "Error" in result\n    assert "write-only" in result\n\n    result = gbs.read_gbs(pipeline_json=\'[{"$match": {}}, {"$merge": {}}]\')\n    assert "Error" in result\n    assert "write-only" in result\n\n\ndef test_not_a_list(gbs_env):\n    """Pipeline must be a list."""\n    gbs = gbs_env\n    result = gbs.read_gbs(pipeline_json=\'"not a list"\')\n    assert "Error" in result\n    assert "must be a JSON array" in result\n\n    result = gbs.read_gbs(pipeline_json=\'{}\')\n    assert "Error" in result\n    assert "must be a JSON array" in result\n\n\ndef test_empty_pipeline_stage(gbs_env):\n    """Empty stage objects are rejected."""\n    gbs = gbs_env\n    result = gbs.read_gbs(pipeline_json=\'[{}]\')\n    assert "Error" in result\n    assert "empty" in result\n\n\ndef test_successful_query(gbs_env):\n    """Valid pipeline runs and returns formatted JSON."""\n    gbs = gbs_env\n    pipeline = \'{"$match": {"country": "United Kingdom"}}, {"$limit": 1}\'\n    mock_result = [{"_id": {"$oid": "abc123"}, "name": "TestOp", "country": "United Kingdom"}]\n\n    with patch("point_topic_mcp.core.mongodb_utils.run_aggregation", return_value=mock_result):\n        result = gbs.read_gbs(pipeline_json=f\'[{pipeline}]\')\n        parsed = json.loads(result)\n        assert len(parsed) == 1\n        assert parsed[0]["name"] == "TestOp"\n        assert parsed[0]["country"] == "United Kingdom"\n\n\ndef test_empty_result(gbs_env):\n    """Empty result set returns helpful message, not error."""\n    gbs = gbs_env\n\n    with patch("point_topic_mcp.core.mongodb_utils.run_aggregation", return_value=[]):\n        result = gbs.read_gbs(pipeline_json=\'[{"$match": {"name": "NoSuchOp"}}]\')\n        assert "no documents matched" in result\n\n\ndef test_collection_detection(gbs_env):\n    """Auto-detects the correct collection from $match keys."""\n    gbs = gbs_env\n\n    # GlobalVariables collection detected\n    with patch("point_topic_mcp.core.mongodb_utils.run_aggregation", return_value=[{"currentStatsPeriod": "2026Q1"}]) as mock:\n        result = gbs.read_gbs(pipeline_json=\'[{"$match": {"isCurrent": true}}]\')\n        call_args = mock.call_args\n        assert call_args is not None\n        _, kwargs = call_args\n        assert kwargs.get("collection") == "GlobalVariables"\n\n    # Statistics collection detected\n    with patch("point_topic_mcp.core.mongodb_utils.run_aggregation", return_value=[]) as mock:\n        result = gbs.read_gbs(pipeline_json=\'[{"$match": {"period": "2026Q1", "type": "broadband"}}]\')\n        call_args = mock.call_args\n        _, kwargs = call_args\n        assert kwargs.get("collection") == "Statistics"\n\n    # Operator collection detected\n    with patch("point_topic_mcp.core.mongodb_utils.run_aggregation", return_value=[]) as mock:\n        result = gbs.read_gbs(pipeline_json=\'[{"$match": {"name": "BT Group", "country": "United Kingdom"}}]\')\n        call_args = mock.call_args\n        _, kwargs = call_args\n        assert kwargs.get("collection") == "Operator"\n\n    # Sources collection detected\n    with patch("point_topic_mcp.core.mongodb_utils.run_aggregation", return_value=[]) as mock:\n        result = gbs.read_gbs(pipeline_json=\'[{"$match": {"year": 2026, "quarter": 1}}]\')\n        call_args = mock.call_args\n        _, kwargs = call_args\n        assert kwargs.get("collection") == "Sources"\n\n\ndef test_ejson_in_pipeline(gbs_env):\n    """EJSON syntax ($oid, $date) is accepted and passed through."""\n    gbs = gbs_env\n    pipeline = \'[{"$match": {"operatorId": {"$oid": "662ff7a3f9d5751ffbf300f6"}, "period": "2026Q1"}}]\'\n    mock_result = [{"tech": "FTTP", "subscribers": 316000}]\n\n    with patch("point_topic_mcp.core.mongodb_utils.run_aggregation", return_value=mock_result) as mock:\n        result = gbs.read_gbs(pipeline_json=pipeline)\n        parsed = json.loads(result)\n        assert parsed[0]["subscribers"] == 316000\n\n        # Verify EJSON $oid was passed through in the pipeline\n        call_args = mock.call_args\n        assert call_args is not None\n        pipe_arg = call_args[0][0]\n        # The pipeline should contain $oid\n        assert any("$oid" in json.dumps(s) for s in pipe_arg)\n\n\ndef test_double_underscore_field(gbs_env):\n    """Fields like __PERIOD__ are just passed through as-is."""\n    gbs = gbs_env\n    pipeline = \'[{"$match": {"period": "__TEST__"}}]\'\n\n    with patch("point_topic_mcp.core.mongodb_utils.run_aggregation", return_value=[{"period": "__TEST__"}]) as mock:\n        result = gbs.read_gbs(pipeline_json=pipeline)\n        parsed = json.loads(result)\n        assert parsed[0]["period"] == "__TEST__"\n\n\ndef test_discovery_uses_prefix(gbs_env):\n    """Tool auto-discovery prefixes module name as gbs_query_tools_read_gbs."""\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_query_tools_read_gbs" in names\n'}]