"""Integration tests for McpBlockDevProvider.""" import pytest from fastmcp import Client, FastMCP from gnuradio_mcp.providers.mcp_block_dev import McpBlockDevProvider @pytest.fixture def mcp_app(): """Create a FastMCP app with block dev provider.""" app = FastMCP("Block Dev Test") McpBlockDevProvider.create(app) return app class TestDynamicBlockDevMode: """Tests for dynamic block development mode registration.""" @pytest.mark.asyncio async def test_block_dev_mode_starts_disabled(self, mcp_app): """Block dev mode should be disabled by default.""" async with Client(mcp_app) as client: result = await client.call_tool(name="get_block_dev_mode") assert result.data.enabled is False assert result.data.tools_registered == [] @pytest.mark.asyncio async def test_enable_block_dev_mode_registers_tools(self, mcp_app): """Enabling block dev mode should register generation tools.""" async with Client(mcp_app) as client: result = await client.call_tool(name="enable_block_dev_mode") assert result.data.enabled is True assert "generate_sync_block" in result.data.tools_registered assert "validate_block_code" in result.data.tools_registered assert "parse_block_prompt" in result.data.tools_registered @pytest.mark.asyncio async def test_disable_block_dev_mode_removes_tools(self, mcp_app): """Disabling block dev mode should remove generation tools.""" async with Client(mcp_app) as client: # Enable first await client.call_tool(name="enable_block_dev_mode") # Then disable result = await client.call_tool(name="disable_block_dev_mode") assert result.data.enabled is False assert result.data.tools_registered == [] @pytest.mark.asyncio async def test_enable_block_dev_mode_idempotent(self, mcp_app): """Enabling block dev mode multiple times should be idempotent.""" async with Client(mcp_app) as client: result1 = await client.call_tool(name="enable_block_dev_mode") result2 = await client.call_tool(name="enable_block_dev_mode") assert result1.data.enabled == result2.data.enabled assert set(result1.data.tools_registered) == set(result2.data.tools_registered) @pytest.mark.asyncio async def test_disable_block_dev_mode_idempotent(self, mcp_app): """Disabling block dev mode multiple times should be idempotent.""" async with Client(mcp_app) as client: result1 = await client.call_tool(name="disable_block_dev_mode") result2 = await client.call_tool(name="disable_block_dev_mode") assert result1.data.enabled is False assert result2.data.enabled is False class TestBlockDevTools: """Tests for block development tools when enabled.""" @pytest.mark.asyncio async def test_generate_sync_block_creates_valid_code(self, mcp_app): """Generate a sync block and verify it validates.""" async with Client(mcp_app) as client: # Enable block dev mode await client.call_tool(name="enable_block_dev_mode") # Generate a block result = await client.call_tool( name="generate_sync_block", arguments={ "name": "test_gain", "description": "Multiply by gain factor", "inputs": [{"dtype": "float", "vlen": 1}], "outputs": [{"dtype": "float", "vlen": 1}], "parameters": [{"name": "gain", "dtype": "float", "default": 1.0}], "work_template": "gain", }, ) assert result.data.is_valid is True assert "gr.sync_block" in result.data.source_code assert "self.gain" in result.data.source_code @pytest.mark.asyncio async def test_generate_basic_block_creates_valid_code(self, mcp_app): """Generate a basic block and verify it validates.""" async with Client(mcp_app) as client: await client.call_tool(name="enable_block_dev_mode") result = await client.call_tool( name="generate_basic_block", arguments={ "name": "packet_extract", "description": "Extract packets", "inputs": [{"dtype": "byte", "vlen": 1}], "outputs": [{"dtype": "byte", "vlen": 1}], "parameters": [], "work_logic": "self.consume_each(1); return 1", }, ) assert result.data.is_valid is True assert "gr.basic_block" in result.data.source_code assert "general_work" in result.data.source_code @pytest.mark.asyncio async def test_validate_block_code_success(self, mcp_app): """Validate syntactically correct code.""" async with Client(mcp_app) as client: await client.call_tool(name="enable_block_dev_mode") valid_code = ''' import numpy from gnuradio import gr class blk(gr.sync_block): def __init__(self): gr.sync_block.__init__( self, name="test", in_sig=[numpy.float32], out_sig=[numpy.float32] ) def work(self, input_items, output_items): output_items[0][:] = input_items[0] return len(output_items[0]) ''' result = await client.call_tool( name="validate_block_code", arguments={"source_code": valid_code}, ) assert result.data.is_valid is True @pytest.mark.asyncio async def test_validate_block_code_syntax_error(self, mcp_app): """Validate code with syntax errors.""" async with Client(mcp_app) as client: await client.call_tool(name="enable_block_dev_mode") invalid_code = """ def broken(: pass """ result = await client.call_tool( name="validate_block_code", arguments={"source_code": invalid_code}, ) assert result.data.is_valid is False @pytest.mark.asyncio async def test_generate_interp_block(self, mcp_app): """Generate an interpolating block.""" async with Client(mcp_app) as client: await client.call_tool(name="enable_block_dev_mode") result = await client.call_tool( name="generate_interp_block", arguments={ "name": "upsample_2x", "description": "Upsample by 2", "inputs": [{"dtype": "float", "vlen": 1}], "outputs": [{"dtype": "float", "vlen": 1}], "interpolation": 2, "parameters": [], }, ) assert result.data.is_valid is True assert "gr.interp_block" in result.data.source_code @pytest.mark.asyncio async def test_generate_decim_block(self, mcp_app): """Generate a decimating block.""" async with Client(mcp_app) as client: await client.call_tool(name="enable_block_dev_mode") result = await client.call_tool( name="generate_decim_block", arguments={ "name": "downsample_4x", "description": "Downsample by 4", "inputs": [{"dtype": "float", "vlen": 1}], "outputs": [{"dtype": "float", "vlen": 1}], "decimation": 4, "parameters": [], }, ) assert result.data.is_valid is True assert "gr.decim_block" in result.data.source_code class TestBlockDevResources: """Tests for block dev prompt template resources.""" @pytest.mark.asyncio async def test_sync_block_prompt_resource(self, mcp_app): """Verify sync block prompt resource is available.""" async with Client(mcp_app) as client: resources = await client.list_resources() resource_uris = [r.uri for r in resources] # Check that our prompt resources are registered assert any("sync-block" in str(uri) for uri in resource_uris) @pytest.mark.asyncio async def test_basic_block_prompt_resource(self, mcp_app): """Verify basic block prompt resource is available.""" async with Client(mcp_app) as client: resources = await client.list_resources() resource_uris = [r.uri for r in resources] assert any("basic-block" in str(uri) for uri in resource_uris) @pytest.mark.asyncio async def test_decoder_chain_prompt_resource(self, mcp_app): """Verify decoder chain prompt resource is available.""" async with Client(mcp_app) as client: resources = await client.list_resources() resource_uris = [r.uri for r in resources] assert any("decoder-chain" in str(uri) for uri in resource_uris) @pytest.mark.asyncio async def test_common_patterns_prompt_resource(self, mcp_app): """Verify common patterns prompt resource is available.""" async with Client(mcp_app) as client: resources = await client.list_resources() resource_uris = [r.uri for r in resources] assert any("common-patterns" in str(uri) for uri in resource_uris) class TestToolNotAvailableWhenDisabled: """Tests that tools are not available when block dev mode is disabled.""" @pytest.mark.asyncio async def test_generate_sync_block_not_available_when_disabled(self, mcp_app): """generate_sync_block should not be callable when mode is disabled.""" async with Client(mcp_app) as client: # Ensure disabled await client.call_tool(name="disable_block_dev_mode") # List available tools tools = await client.list_tools() tool_names = [t.name for t in tools] # Generation tools should not be in the list assert "generate_sync_block" not in tool_names assert "validate_block_code" not in tool_names assert "generate_basic_block" not in tool_names # But mode control tools should be assert "get_block_dev_mode" in tool_names assert "enable_block_dev_mode" in tool_names assert "disable_block_dev_mode" in tool_names