Unit Test Report

Generated
2026-02-08 08:10:19

Pass Rate

74%

Results Distribution

46
Total
34
Passed
12
Failed

Code Coverage

73.3%
Lines Covered

Detailed Results

Total Duration: 113.71s
Test Case Outcome Duration
test_get_connection_error
tests/test_chia_client.py::TestChiaRpcClient::test_get_connection_error
PASSED
0.0024s
Test handling of ConnectionError.
Execution Timeline Total: 0.0024s
Setup
Call
Teardown
    def test_get_connection_error(self, mock_post):
        """Test handling of ConnectionError."""
        mock_post.side_effect = requests.exceptions.ConnectionError("Connection refused")
        
        res = self.client.get("test_endpoint")
        self.assertFalse(res["success"])
        self.assertIn("Connection refused", res["error"])
test_get_generic_exception
tests/test_chia_client.py::TestChiaRpcClient::test_get_generic_exception
PASSED
0.0006s
Test handling of generic exceptions.
Execution Timeline Total: 0.0006s
Setup
Call
Teardown
    def test_get_generic_exception(self, mock_post):
        """Test handling of generic exceptions."""
        mock_post.side_effect = Exception("Boom")
        
        res = self.client.get("test_endpoint")
        self.assertFalse(res["success"])
        self.assertEqual(res["error"], "Boom")
test_get_success
tests/test_chia_client.py::TestChiaRpcClient::test_get_success
PASSED
0.0007s
Test successful RPC call.
Execution Timeline Total: 0.0007s
Setup
Call
Teardown
    def test_get_success(self, mock_post):
        """Test successful RPC call."""
        mock_response = MagicMock()
        mock_response.json.return_value = {"success": True, "foo": "bar"}
        mock_post.return_value = mock_response

        # We need to re-init client locally to mock session properly or just patch the existing one
        # Here we rely on self.client created in setUp, which already has a session.
        # But requests.Session() is instantiated in __init__.
        # So we patch the method on the class or instance.
        
        res = self.client.get("test_endpoint", {"data": 1})
        self.assertEqual(res["success"], True)
        self.assertEqual(res["foo"], "bar")
        mock_post.assert_called_once()
        args, kwargs = mock_post.call_args
        self.assertTrue("https://localhost:8555/test_endpoint" in args[0])
        self.assertEqual(kwargs["json"], {"data": 1})
test_init_datalayer
tests/test_chia_client.py::TestChiaRpcClient::test_init_datalayer
PASSED
0.0005s
Test initialization of data_layer service.
Execution Timeline Total: 0.0005s
Setup
Call
Teardown
    def test_init_datalayer(self):
        """Test initialization of data_layer service."""
        with patch("chaimcp.chia_client.load_chia_config", return_value=self.mock_config), \
             patch("chaimcp.chia_client.get_ssl_paths", return_value={"cert":"c", "key":"k"}):
            client = ChiaRpcClient("data_layer")
            self.assertEqual(client.port, 8562)
test_init_defaults
tests/test_chia_client.py::TestChiaRpcClient::test_init_defaults
PASSED
0.0005s
Test initialization with default ports from config.
Execution Timeline Total: 0.0005s
Setup
Call
Teardown
    def test_init_defaults(self):
        """Test initialization with default ports from config."""
        self.assertEqual(self.client.port, 8555)
        
        # Test wallet default
        with patch("chaimcp.chia_client.load_chia_config", return_value=self.mock_config), \
             patch("chaimcp.chia_client.get_ssl_paths", return_value={"cert":"c", "key":"k"}):
            wallet_client = ChiaRpcClient("wallet")
            self.assertEqual(wallet_client.port, 9256)
test_init_explicit_port
tests/test_chia_client.py::TestChiaRpcClient::test_init_explicit_port
PASSED
0.0005s
Test initialization with explicit port override.
Execution Timeline Total: 0.0005s
Setup
Call
Teardown
    def test_init_explicit_port(self):
        """Test initialization with explicit port override."""
        with patch("chaimcp.chia_client.load_chia_config"), \
             patch("chaimcp.chia_client.get_ssl_paths", return_value={"cert":"c", "key":"k"}):
            client = ChiaRpcClient("full_node", port=1234)
            self.assertEqual(client.port, 1234)
test_init_unknown_service
tests/test_chia_client.py::TestChiaRpcClient::test_init_unknown_service
PASSED
0.0005s
Test error when unknown service used without explicit port.
Execution Timeline Total: 0.0005s
Setup
Call
Teardown
    def test_init_unknown_service(self):
        """Test error when unknown service used without explicit port."""
        with patch("chaimcp.chia_client.load_chia_config", return_value=self.mock_config), \
             patch("chaimcp.chia_client.get_ssl_paths", return_value={"cert":"c", "key":"k"}):
            with self.assertRaises(ValueError):
                ChiaRpcClient("unknown_service")
test_wrappers
tests/test_chia_client.py::TestChiaRpcClient::test_wrappers
PASSED
0.0007s
Test convenience wrapper methods.
Execution Timeline Total: 0.0007s
Setup
Call
Teardown
    def test_wrappers(self, mock_get):
        """Test convenience wrapper methods."""
        self.client.get_blockchain_state()
        mock_get.assert_called_with("get_blockchain_state")
        
        self.client.get_network_info()
        mock_get.assert_called_with("get_network_info")
        
        self.client.get_wallets()
        mock_get.assert_called_with("get_wallets")
        
        self.client.get_wallet_balance(123)
        mock_get.assert_called_with("get_wallet_balance", {"wallet_id": 123})
test_get_chia_root_default
tests/test_config.py::TestConfig::test_get_chia_root_default
PASSED
0.0004s
Test getting default CHIA_ROOT when env var is not set.
Execution Timeline Total: 0.0004s
Setup
Call
Teardown
    def test_get_chia_root_default(self):
        """Test getting default CHIA_ROOT when env var is not set."""
        # Note: This relies on the DEFAULT_CHIA_ROOT import in config.py
        # which expands ~/.chia/mainnet. We check if it ends with .chia/mainnet
        root = get_chia_root()
        self.assertTrue(str(root).endswith(".chia/mainnet"))
test_get_chia_root_env_var
tests/test_config.py::TestConfig::test_get_chia_root_env_var
PASSED
0.0003s
Test getting CHIA_ROOT from environment variable.
Execution Timeline Total: 0.0003s
Setup
Call
Teardown
    def test_get_chia_root_env_var(self):
        """Test getting CHIA_ROOT from environment variable."""
        root = get_chia_root()
        self.assertEqual(str(root), "/custom/chia/root")
test_get_letsencrypt_enabled_default
tests/test_config.py::TestConfig::test_get_letsencrypt_enabled_default
PASSED
0.0003s
Test letsencrypt enabled default.
Execution Timeline Total: 0.0003s
Setup
Call
Teardown
    def test_get_letsencrypt_enabled_default(self):
        """Test letsencrypt enabled default."""
        self.assertTrue(get_letsencrypt_enabled())
test_get_letsencrypt_enabled_false
tests/test_config.py::TestConfig::test_get_letsencrypt_enabled_false
PASSED
0.0003s
Test letsencrypt enabled false.
Execution Timeline Total: 0.0003s
Setup
Call
Teardown
    def test_get_letsencrypt_enabled_false(self):
        """Test letsencrypt enabled false."""
        self.assertFalse(get_letsencrypt_enabled())
test_get_mcp_auth_enabled_default
tests/test_config.py::TestConfig::test_get_mcp_auth_enabled_default
PASSED
0.0002s
Test auth enabled default.
Execution Timeline Total: 0.0002s
Setup
Call
Teardown
    def test_get_mcp_auth_enabled_default(self):
        """Test auth enabled default."""
        self.assertTrue(get_mcp_auth_enabled())
test_get_mcp_auth_enabled_false
tests/test_config.py::TestConfig::test_get_mcp_auth_enabled_false
PASSED
0.0002s
Test auth enabled false.
Execution Timeline Total: 0.0002s
Setup
Call
Teardown
    def test_get_mcp_auth_enabled_false(self):
        """Test auth enabled false."""
        self.assertFalse(get_mcp_auth_enabled())
test_get_ssl_paths
tests/test_config.py::TestConfig::test_get_ssl_paths
PASSED
0.0004s
Test SSL path generation.
Execution Timeline Total: 0.0004s
Setup
Call
Teardown
    def test_get_ssl_paths(self, mock_exists):
        """Test SSL path generation."""
        mock_exists.return_value = True
        root = Path("/test/root")
        paths = get_ssl_paths("full_node", root)
        
        base = "/test/root/config/ssl/full_node"
        self.assertEqual(paths["cert"], f"{base}/private_full_node.crt")
        self.assertEqual(paths["key"], f"{base}/private_full_node.key")
        self.assertTrue(paths["ca"].endswith("private_ca.crt"))
test_get_ssl_paths_fallback
tests/test_config.py::TestConfig::test_get_ssl_paths_fallback
PASSED
0.0003s
Test SSL path generation fallback when files don't exist.
Execution Timeline Total: 0.0003s
Setup
Call
Teardown
    def test_get_ssl_paths_fallback(self, mock_exists):
        """Test SSL path generation fallback when files don't exist."""
        mock_exists.return_value = False
        root = Path("/test/root")
        # Should execute the 'pass' block and still return paths
        paths = get_ssl_paths("full_node", root)
        self.assertIn("private_full_node.crt", paths["cert"])
test_get_ssl_paths_implicit_root
tests/test_config.py::TestConfig::test_get_ssl_paths_implicit_root
PASSED
0.0003s
Test SSL path generation with implicit root.
Execution Timeline Total: 0.0003s
Setup
Call
Teardown
    def test_get_ssl_paths_implicit_root(self, mock_exists, mock_get_root):
        """Test SSL path generation with implicit root."""
        mock_exists.return_value = True
        mock_get_root.return_value = Path("/implicit/root")
        
        paths = get_ssl_paths("wallet")
        base = "/implicit/root/config/ssl/wallet"
        self.assertEqual(paths["cert"], f"{base}/private_wallet.crt")
test_load_chia_config_implicit_root
tests/test_config.py::TestConfig::test_load_chia_config_implicit_root
PASSED
0.0013s
Test laoding config using implicit get_chia_root().
Execution Timeline Total: 0.0013s
Setup
Call
Teardown
    def test_load_chia_config_implicit_root(self, mock_get_root, mock_file, mock_exists):
        """Test laoding config using implicit get_chia_root()."""
        mock_get_root.return_value = Path("/implicit/root")
        mock_exists.return_value = True
        config = load_chia_config()
        self.assertEqual(config["wallet"]["rpc_port"], 9256)
        mock_get_root.assert_called_once()
test_load_chia_config_not_found
tests/test_config.py::TestConfig::test_load_chia_config_not_found
PASSED
0.0003s
Test FileNotFoundError when config file is missing.
Execution Timeline Total: 0.0003s
Setup
Call
Teardown
    def test_load_chia_config_not_found(self, mock_exists):
        """Test FileNotFoundError when config file is missing."""
        mock_exists.return_value = False
        with self.assertRaises(FileNotFoundError):
            load_chia_config(Path("/dummy/root"))
test_load_chia_config_success
tests/test_config.py::TestConfig::test_load_chia_config_success
PASSED
0.0017s
Test successfully loading valid YAML config.
Execution Timeline Total: 0.0017s
Setup
Call
Teardown
    def test_load_chia_config_success(self, mock_file, mock_exists):
        """Test successfully loading valid YAML config."""
        mock_exists.return_value = True
        config = load_chia_config(Path("/dummy/root"))
        self.assertEqual(config["full_node"]["rpc_port"], 8555)
test_deployment_manifest
tests/test_deployment.py::TestKubernetesManifests::test_deployment_manifest
PASSED
0.0027s
Execution Timeline Total: 0.0027s
Setup
Call
Teardown
    def test_deployment_manifest(self):
        docs = self.load_yaml("deployment.yaml")
        self.assertIsNotNone(docs, "deployment.yaml not found")
        deployment = docs[0]
        self.assertEqual(deployment['kind'], 'Deployment')
        self.assertEqual(deployment['metadata']['name'], 'chaimcp')
        
        # Check container spec
        containers = deployment['spec']['template']['spec']['containers']
        self.assertTrue(len(containers) > 0)
        chaimcp = containers[0]
        self.assertEqual(chaimcp['image'], 'chaimcp:latest')
        self.assertEqual(chaimcp['image'], 'chaimcp:latest')
        self.assertEqual(chaimcp['ports'][0]['containerPort'], 8000)
        
        # Check config map usage
        self.assertIn('envFrom', chaimcp)
        self.assertEqual(chaimcp['envFrom'][0]['configMapRef']['name'], 'chaimcp-config')
test_ingress_manifest
tests/test_deployment.py::TestKubernetesManifests::test_ingress_manifest
PASSED
0.0017s
Execution Timeline Total: 0.0017s
Setup
Call
Teardown
    def test_ingress_manifest(self):
        docs = self.load_yaml("ingress.yaml")
        self.assertIsNotNone(docs, "ingress.yaml not found")
        ingress = docs[0]
        self.assertEqual(ingress['kind'], 'Ingress')
        self.assertIn("cert-manager.io/cluster-issuer", ingress['metadata']['annotations'])
        self.assertEqual(ingress['spec']['tls'][0]['secretName'], 'chaimcp-tls')
test_issuer_manifest
tests/test_deployment.py::TestKubernetesManifests::test_issuer_manifest
PASSED
0.0015s
Execution Timeline Total: 0.0015s
Setup
Call
Teardown
    def test_issuer_manifest(self):
        docs = self.load_yaml("issuer.yaml")
        self.assertIsNotNone(docs, "issuer.yaml not found")
        issuer = docs[0]
        self.assertEqual(issuer['kind'], 'ClusterIssuer')
        self.assertEqual(issuer['spec']['acme']['privateKeySecretRef']['name'], 'letsencrypt-staging')
test_secret_manifest
tests/test_deployment.py::TestKubernetesManifests::test_secret_manifest
PASSED
0.0010s
Execution Timeline Total: 0.0010s
Setup
Call
Teardown
    def test_secret_manifest(self):
        docs = self.load_yaml("secret.yaml")
        self.assertIsNotNone(docs, "secret.yaml not found")
        secret = docs[0]
        self.assertEqual(secret['kind'], 'Secret')
        self.assertEqual(secret['metadata']['name'], 'chaimcp-secrets')
        self.assertIn('auth-token', secret['stringData'])
test_service_manifest
tests/test_deployment.py::TestKubernetesManifests::test_service_manifest
PASSED
0.0011s
Execution Timeline Total: 0.0011s
Setup
Call
Teardown
    def test_service_manifest(self):
        docs = self.load_yaml("service.yaml")
        self.assertIsNotNone(docs, "service.yaml not found")
        service = docs[0]
        self.assertEqual(service['kind'], 'Service')
        self.assertEqual(service['metadata']['name'], 'chaimcp')
        self.assertEqual(service['spec']['ports'][0]['port'], 80)
        self.assertEqual(service['spec']['ports'][0]['targetPort'], 8000)
test_all_tools_enabled
tests/test_disabled_tools.py::TestDisabledTools::test_all_tools_enabled
PASSED
0.0025s
Verify all tools registered when config is empty.
Execution Timeline Total: 0.0025s
Setup
Call
Teardown
    def test_all_tools_enabled(self, mock_tool):
        """Verify all tools registered when config is empty."""
        mock_decorator = MagicMock()
        mock_tool.return_value = mock_decorator
        
        import chaimcp.main
        
        registered_funcs = []
        for call in mock_decorator.call_args_list:
            func = call.args[0]
            registered_funcs.append(func.__name__)
            
        self.assertIn("get_blockchain_state", registered_funcs)
        self.assertIn("generate_mnemonic", registered_funcs)
test_disabled_tools_logic
tests/test_disabled_tools.py::TestDisabledTools::test_disabled_tools_logic
PASSED
0.0019s
Verify that tools listed in MCP_DISABLED_TOOLS are not registered via mcp.tool().
Execution Timeline Total: 0.0019s
Setup
Call
Teardown
    def test_disabled_tools_logic(self, mock_tool):
        """
        Verify that tools listed in MCP_DISABLED_TOOLS are not registered via mcp.tool().
        """
        # We mock mcp.tool to see if it gets called.
        # Note: mcp.tool() returns a decorator, so we have to handle that.
        mock_decorator = MagicMock()
        mock_tool.return_value = mock_decorator
        
        import chaimcp.main
        
        # get_network_info should be registered (NOT disabled)
        # It's defined as @register_tool() -> calls mcp.tool(name=None)
        # So we expect a call to mock_tool(name=None, description=...)
        # We can't easily check 'name' if it's None in the call, but we can check count?
        
        # Let's capture the tool names passed to register_tool which then calls mcp.tool
        # Be careful: My code calls mcp.tool(name=name, description=description).
        # For get_blockchain_state, name is None.
        
        # But since get_blockchain_state IS disabled, mcp.tool SHOULD NOT be called for it.
        # How do we know which call corresponds to which function?
        # The return value of mcp.tool(...) is the decorator, which is called with the function.
        # mock_decorator(func) -> checks func.__name__
        
        registered_funcs = []
        for call in mock_decorator.call_args_list:
            # mock_decorator is called with (func,)
            func = call.args[0]
            registered_funcs.append(func.__name__)
            
        self.assertNotIn("get_blockchain_state", registered_funcs)
        self.assertNotIn("generate_mnemonic", registered_funcs)
        self.assertIn("get_network_info", registered_funcs)
test_egress_allow_chia
tests/test_k8s_network.py::test_egress_allow_chia
PASSED
34.6761s
Verify chaimcp (or simulated pod) can reach Chia RPC.
Execution Timeline Total: 34.6761s
Setup
Call
Teardown
def test_egress_allow_chia(setup_k8s_env):
    """Verify chaimcp (or simulated pod) can reach Chia RPC."""
    # We simulate the chaimcp app by running a pod with the same label in default ns
    run_kubectl("delete pod test-client", check=False)
    run_kubectl("run test-client --image=busybox --labels=app=chaimcp --restart=Never -- /bin/sh -c 'sleep 3600'")
    run_kubectl("wait --for=condition=Ready pod/test-client --timeout=60s")
    
    try:
        # Try connection to Node RPC
        logger.info("Testing connectivity to connect to chia-node (8555)...")
        res = run_kubectl("exec test-client -- nc -vz chia-node.chia 8555", check=False)
        assert res.returncode == 0, f"Node RPC (8555) failed: {res.stderr}"
        
        # Try connection to Wallet RPC
        logger.info("Testing connectivity to connect to chia-wallet (9256)...")
        res = run_kubectl("exec test-client -- nc -vz chia-wallet.chia 9256", check=False)
        assert res.returncode == 0, f"Wallet RPC (9256) failed: {res.stderr}"

        # Try connection to DataLayer RPC
        logger.info("Testing connectivity to connect to chia-datalayer (8562)...")
        res = run_kubectl("exec test-client -- nc -vz chia-datalayer.chia 8562", check=False)
        assert res.returncode == 0, f"DataLayer RPC (8562) failed: {res.stderr}"
        
    finally:
        run_kubectl("delete pod test-client", check=False)
test_egress_deny_check
tests/test_k8s_network.py::test_egress_deny_check
PASSED
77.4453s
Verify egress is blocked to other destinations (e.g. external network).
Execution Timeline Total: 77.4453s
Setup
Call
Teardown
def test_egress_deny_check(setup_k8s_env):
    """Verify egress is blocked to other destinations (e.g. external network)."""
    run_kubectl("delete pod test-client-deny", check=False)
    run_kubectl("run test-client-deny --image=busybox --labels=app=chaimcp --restart=Never -- /bin/sh -c 'sleep 3600'")
    run_kubectl("wait --for=condition=Ready pod/test-client-deny --timeout=60s")
    
    try:
        # Try connect to google (IP directly to avoid DNS complexity, though DNS allowed)
        # 8.8.8.8 port 53 is DNS (allowed), so try port 80?
        # But our policy allows port 53 UDP/TCP to kube-system.
        # Let's try 1.1.1.1 port 80
        logger.info("Testing denied connectivity to 1.1.1.1:80...")
        res = run_kubectl("exec test-client-deny -- nc -vz -w 3 1.1.1.1 80", check=False)
        assert res.returncode != 0, "Connection succeeded but should have been blocked"
        
    finally:
        run_kubectl("delete pod test-client-deny", check=False)
test_auth_token_env
tests/test_main.py::TestMain::test_auth_token_env
FAILED
0.0010s
Test global auth settings initialization with env var.
Execution Timeline Total: 0.0010s
Setup
Call
Teardown
    def test_auth_token_env(self):
        """Test global auth settings initialization with env var."""
        with patch.dict(os.environ, {"MCP_AUTH_TOKEN": "test-token"}):
            reload(main_module)
            self.assertIsNotNone(main_module.auth_settings)
            self.assertIsNotNone(main_module.token_verifier)
            self.assertEqual(main_module.token_verifier.token, "test-token")
test_env_token_verifier
tests/test_main.py::TestMain::test_env_token_verifier
PASSED
0.0010s
Test validation of tokens.
Execution Timeline Total: 0.0010s
Setup
Call
Teardown
    def test_env_token_verifier(self):
        """Test validation of tokens."""
        verifier = EnvTokenVerifier("secret_token")
        
        # Helper to run async method
        import asyncio
        loop = asyncio.new_event_loop()
        
        valid = loop.run_until_complete(verifier.verify_token("secret_token"))
        self.assertIsNotNone(valid)
        self.assertEqual(valid.token, "secret_token")
        
        invalid = loop.run_until_complete(verifier.verify_token("wrong"))
        self.assertIsNone(invalid)
        loop.close()
test_main_execution
tests/test_main.py::TestMain::test_main_execution
PASSED
0.5095s
Test executing the module as a script (covers __name__ == '__main__').
Execution Timeline Total: 0.5095s
Setup
Call
Teardown
    def test_main_execution(self):
        """Test executing the module as a script (covers __name__ == '__main__')."""
        env = os.environ.copy()
        env["MCP_TRANSPORT"] = "stdio"
        env["PYTHONUNBUFFERED"] = "1" # Ensure output is flushed immediately
        
        try:
            process = subprocess.Popen(
                [sys.executable, "-m", "chaimcp.main"],
                env=env,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True
            )
            try:
                # Wait a bit for startup
                output, error = process.communicate(timeout=2) 
            except subprocess.TimeoutExpired:
                process.kill()
                output, error = process.communicate()
            
            # Combine output for checking
            full_output = output + error
            
            if "ChaiMCP module loaded" not in full_output:
                # Debugging aid
                print(f"STDOUT: {output}")
                print(f"STDERR: {error}")
            
            self.assertIn("ChaiMCP module loaded", full_output)
            
        except Exception as e:
            self.fail(f"Subprocess execution failed: {e}")
test_main_http_prod_config
tests/test_main.py::TestMain::test_main_http_prod_config
FAILED
0.0016s
Test main execution with production config (HTTP, Port 8000, No SSL).
Execution Timeline Total: 0.0016s
Setup
Call
Teardown
    def test_main_http_prod_config(self, mock_exists, mock_mcp, mock_uvicorn):
        """Test main execution with production config (HTTP, Port 8000, No SSL)."""
        mock_exists.return_value = False # Simulate no SSL files
        
        main_module.main()
        
        mock_mcp.streamable_http_app.assert_called_once()
        mock_uvicorn.assert_called_once()
        args, kwargs = mock_uvicorn.call_args
        self.assertEqual(kwargs["port"], 8000)
        self.assertNotIn("ssl_keyfile", kwargs)
test_main_http_ssl
tests/test_main.py::TestMain::test_main_http_ssl
FAILED
0.0010s
Test main execution with HTTP transport and SSL enabled.
Execution Timeline Total: 0.0010s
Setup
Call
Teardown
    def test_main_http_ssl(self, mock_exists, mock_mcp, mock_uvicorn):
        """Test main execution with HTTP transport and SSL enabled."""
        mock_exists.return_value = True # SSL files exist
        
        main_module.main()
        
        mock_mcp.streamable_http_app.assert_called_once()
        mock_uvicorn.assert_called_once()
        args, kwargs = mock_uvicorn.call_args
        self.assertEqual(kwargs["ssl_keyfile"], "k")
        self.assertEqual(kwargs["ssl_certfile"], "c")
test_main_sse_no_ssl
tests/test_main.py::TestMain::test_main_sse_no_ssl
FAILED
0.0010s
Test main execution with SSE transport and no SSL.
Execution Timeline Total: 0.0010s
Setup
Call
Teardown
    def test_main_sse_no_ssl(self, mock_exists, mock_mcp, mock_uvicorn):
        """Test main execution with SSE transport and no SSL."""
        mock_exists.return_value = False # No SSL files
        
        main_module.main()
        
        mock_mcp.sse_app.assert_called_once()
        mock_uvicorn.assert_called_once()
        args, kwargs = mock_uvicorn.call_args
        self.assertEqual(kwargs["port"], 8080)
        self.assertNotIn("ssl_keyfile", kwargs)
test_main_stdio
tests/test_main.py::TestMain::test_main_stdio
FAILED
0.0052s
Test main execution with stdio transport.
Execution Timeline Total: 0.0052s
Setup
Call
Teardown
    def test_main_stdio(self, mock_mcp):
        """Test main execution with stdio transport."""
        main_module.main()
        mock_mcp.run.assert_called_with(transport="stdio")
test_tool_get_blockchain_state_failure
tests/test_main.py::TestMain::test_tool_get_blockchain_state_failure
FAILED
0.0314s
Test get_blockchain_state tool failure path.
Execution Timeline Total: 0.0314s
Setup
Call
Teardown
    def test_tool_get_blockchain_state_failure(self, MockClient):
        """Test get_blockchain_state tool failure path."""
        mock_instance = MockClient.return_value
        mock_instance.get_blockchain_state.return_value = {
            "success": False,
            "error": "RPC Error"
        }
        
        result = get_blockchain_state()
        data = json.loads(result)
        self.assertFalse(data["success"])
        self.assertEqual(data["error"], "RPC Error")
test_tool_get_blockchain_state_success
tests/test_main.py::TestMain::test_tool_get_blockchain_state_success
FAILED
0.0134s
Test get_blockchain_state tool success path.
Execution Timeline Total: 0.0134s
Setup
Call
Teardown
    def test_tool_get_blockchain_state_success(self, MockClient):
        """Test get_blockchain_state tool success path."""
        mock_instance = MockClient.return_value
        mock_instance.get_blockchain_state.return_value = {
            "success": True,
            "blockchain_state": {
                "sync": {"sync_mode": True, "synced": True},
                "peak": {"height": 100},
                "space": 1000,
                "difficulty": 5
            }
        }
        
        result = get_blockchain_state()
        data = json.loads(result)
        # New implementation returns full blockchain_state object inside the response
        self.assertEqual(data["blockchain_state"]["peak"]["height"], 100)
        self.assertTrue(data["blockchain_state"]["sync"]["synced"])
test_tool_get_network_info
tests/test_main.py::TestMain::test_tool_get_network_info
FAILED
0.0137s
Test get_network_info tool.
Execution Timeline Total: 0.0137s
Setup
Call
Teardown
    def test_tool_get_network_info(self, MockClient):
        """Test get_network_info tool."""
        mock_instance = MockClient.return_value
        mock_instance.get_network_info.return_value = {"success": True, "network_name": "mainnet"}
        
        result = get_network_info()
        data = json.loads(result)
        self.assertEqual(data["network_name"], "mainnet")
test_tool_get_wallet_balance_failure
tests/test_main.py::TestMain::test_tool_get_wallet_balance_failure
FAILED
0.0136s
Test get_wallet_balance tool failure.
Execution Timeline Total: 0.0136s
Setup
Call
Teardown
    def test_tool_get_wallet_balance_failure(self, MockClient):
        """Test get_wallet_balance tool failure."""
        mock_instance = MockClient.return_value
        mock_instance.get_wallet_balance.return_value = {
            "success": False,
            "error": "Wallet locked"
        }
        
        result = get_wallet_balance(1)
        data = json.loads(result)
        self.assertFalse(data["success"])
        self.assertEqual(data["error"], "Wallet locked")
test_tool_get_wallet_balance_success
tests/test_main.py::TestMain::test_tool_get_wallet_balance_success
FAILED
0.0132s
Test get_wallet_balance tool success.
Execution Timeline Total: 0.0132s
Setup
Call
Teardown
    def test_tool_get_wallet_balance_success(self, MockClient):
        """Test get_wallet_balance tool success."""
        mock_instance = MockClient.return_value
        mock_instance.get_wallet_balance.return_value = {
            "success": True,
            "wallet_balance": {
                "confirmed_wallet_balance": 1500000000000, # 1.5 XCH
                "spendable_balance": 1500000000000
            }
        }
        
        result = get_wallet_balance(1)
        data = json.loads(result)
        # New implementation returns full wallet_balance object
        self.assertEqual(data["wallet_balance"]["confirmed_wallet_balance"], 1500000000000)
        self.assertEqual(data["wallet_balance"]["spendable_balance"], 1500000000000)
test_npx_launch_help
tests/test_npx_launch.py::TestNpxLaunch::test_npx_launch_help
PASSED
0.4251s
Test that the Node.js wrapper launches successfully with --help.
Execution Timeline Total: 0.4251s
Setup
Call
Teardown
    def test_npx_launch_help(self):
        """Test that the Node.js wrapper launches successfully with --help."""
        
        # Ensure we use the current python environment for the subprocess if possible, 
        # or rely on the wrapper's default behavior.
        # The wrapper spawns 'python3', so we need to make sure 'python3' is available 
        # and has dependencies. Since we are running tests, we assume dependencies are there.
        
        # We also need 'node' available.
        
        try:
            # Activate the virtual environment if we are in one so the wrapper finds deps
            env = os.environ.copy()
            
            # Ensure the python executable used by node matches ours by prepending its dir to PATH
            python_dir = os.path.dirname(sys.executable)
            env["PATH"] = python_dir + os.pathsep + env.get("PATH", "")
            
            # Propagate current sys.path to PYTHONPATH to ensure all libs are found
            # Filter out empty strings which might be current dir
            valid_paths = [p for p in sys.path if p and os.path.isdir(p)]
            env["PYTHONPATH"] = os.pathsep.join(valid_paths) + os.pathsep + env.get("PYTHONPATH", "")

            # Run: node bin/server.js --help
            result = subprocess.run(
                ["node", self.bin_script, "--help"],
                capture_output=True,
                text=True,
                env=env,
                check=False # Check manually
            )
            
            # Check exit code
            self.assertEqual(result.returncode, 0, f"Script failed with stderr: {result.stderr}")
            
            # Check for expected output identifying it's our server
            # The FastMCP server usually prints usage info or "options" on --help
            # The FastMCP server might not print usage info on --help if not configured to do so,
            # or it might just start the server.
            # We check for the startup logs we saw in manual verification.
            output = result.stdout.lower() + result.stderr.lower()
            self.assertIn("starting chaimcp server", output)
            self.assertIn("pythonpath", output)

        except FileNotFoundError:
            self.skipTest("node executable not found")
test_invalid_token
tests/test_server.py::TestAuth::test_invalid_token
PASSED
0.0041s
Execution Timeline Total: 0.0041s
Setup
Call
Teardown
test_valid_token
tests/test_server.py::TestAuth::test_valid_token
PASSED
0.0013s
Execution Timeline Total: 0.0013s
Setup
Call
Teardown
test_get_blockchain_state
tests/test_server.py::TestChaiMCP::test_get_blockchain_state
FAILED
0.0144s
Test get_blockchain_state tool.
Execution Timeline Total: 0.0144s
Setup
Call
Teardown
    def test_get_blockchain_state(self, MockClient):
        """Test get_blockchain_state tool."""
        # Setup mock
        mock_instance = MockClient.return_value
        mock_instance.get_blockchain_state.return_value = {
            "success": True,
            "blockchain_state": {
                "sync": {"sync_mode": False, "synced": True},
                "peak": {"height": 12345},
                "difficulty": 100
            }
        }
        
        # Run tool
        result = get_blockchain_state()
        data = json.loads(result)
        
        # Verify
        self.assertTrue(data["synced"])
        self.assertEqual(data["peak_height"], 12345)
test_get_wallet_balance
tests/test_server.py::TestChaiMCP::test_get_wallet_balance
FAILED
0.0134s
Test get_wallet_balance tool.
Execution Timeline Total: 0.0134s
Setup
Call
Teardown
    def test_get_wallet_balance(self, MockClient):
        """Test get_wallet_balance tool."""
        # Setup mock
        mock_instance = MockClient.return_value
        mock_instance.get_wallet_balance.return_value = {
            "success": True,
            "wallet_balance": {
                "confirmed_wallet_balance": 1500000000000, # 1.5 XCH
                "spendable_balance": 1500000000000
            }
        }
        
        # Run tool
        result = get_wallet_balance(1)
        data = json.loads(result)
        
        # Verify
        self.assertEqual(data["confirmed_balance_xch"], 1.5)