Skip to main content
Glama
test_cli.py10.6 kB
"""Tests for CLI options using Click's testing utilities.""" import os import pytest from click.testing import CliRunner from nextcloud_mcp_server.app import run @pytest.fixture def runner(): """Create a Click CLI runner.""" return CliRunner() @pytest.fixture def clean_env(monkeypatch): """Clean environment variables before each test.""" env_vars = [ "NEXTCLOUD_HOST", "NEXTCLOUD_USERNAME", "NEXTCLOUD_PASSWORD", "NEXTCLOUD_OIDC_CLIENT_ID", "NEXTCLOUD_OIDC_CLIENT_SECRET", "NEXTCLOUD_OIDC_SCOPES", "NEXTCLOUD_OIDC_TOKEN_TYPE", "NEXTCLOUD_MCP_SERVER_URL", "NEXTCLOUD_PUBLIC_ISSUER_URL", ] for var in env_vars: monkeypatch.delenv(var, raising=False) def test_help_message_displays_all_options(runner): """Test that help message includes all new CLI options.""" result = runner.invoke(run, ["--help"]) assert result.exit_code == 0 # Check for new options assert "--nextcloud-host" in result.output assert "--nextcloud-username" in result.output assert "--nextcloud-password" in result.output assert "--oauth-scopes" in result.output assert "--oauth-token-type" in result.output assert "--public-issuer-url" in result.output # Check for existing options assert "--oauth-client-id" in result.output assert "--oauth-client-secret" in result.output assert "--mcp-server-url" in result.output def test_token_type_accepts_valid_values(runner, clean_env): """Test that --oauth-token-type accepts bearer and jwt (case insensitive).""" # Test lowercase bearer result = runner.invoke(run, ["--oauth-token-type", "bearer", "--help"]) assert result.exit_code == 0 # Test lowercase jwt result = runner.invoke(run, ["--oauth-token-type", "jwt", "--help"]) assert result.exit_code == 0 # Test uppercase (should work with case_sensitive=False) result = runner.invoke(run, ["--oauth-token-type", "Bearer", "--help"]) assert result.exit_code == 0 result = runner.invoke(run, ["--oauth-token-type", "JWT", "--help"]) assert result.exit_code == 0 def test_token_type_rejects_invalid_values(runner, clean_env): """Test that --oauth-token-type rejects invalid values.""" result = runner.invoke(run, ["--oauth-token-type", "invalid"]) assert result.exit_code != 0 assert "Invalid value" in result.output def test_cli_options_set_environment_variables(runner, clean_env, monkeypatch): """Test that CLI options set environment variables correctly.""" # We need to mock the actual server startup to avoid connection errors # Store the env vars that get set captured_env = {} def mock_get_app(*args, **kwargs): # Capture environment variables after they're set by CLI captured_env.update( { "NEXTCLOUD_HOST": os.environ.get("NEXTCLOUD_HOST"), "NEXTCLOUD_USERNAME": os.environ.get("NEXTCLOUD_USERNAME"), "NEXTCLOUD_PASSWORD": os.environ.get("NEXTCLOUD_PASSWORD"), "NEXTCLOUD_OIDC_SCOPES": os.environ.get("NEXTCLOUD_OIDC_SCOPES"), "NEXTCLOUD_OIDC_TOKEN_TYPE": os.environ.get( "NEXTCLOUD_OIDC_TOKEN_TYPE" ), "NEXTCLOUD_PUBLIC_ISSUER_URL": os.environ.get( "NEXTCLOUD_PUBLIC_ISSUER_URL" ), "NEXTCLOUD_MCP_SERVER_URL": os.environ.get("NEXTCLOUD_MCP_SERVER_URL"), } ) # Raise an exception to stop execution before uvicorn.run raise SystemExit(0) # Patch get_app to capture env vars monkeypatch.setattr("nextcloud_mcp_server.app.get_app", mock_get_app) _ = runner.invoke( run, [ "--nextcloud-host", "https://test.example.com", "--nextcloud-username", "testuser", "--nextcloud-password", "testpass", "--oauth-scopes", "openid nc:read", "--oauth-token-type", "jwt", "--public-issuer-url", "https://public.example.com", "--mcp-server-url", "http://test:8000", ], ) # Verify environment variables were set assert captured_env["NEXTCLOUD_HOST"] == "https://test.example.com" assert captured_env["NEXTCLOUD_USERNAME"] == "testuser" assert captured_env["NEXTCLOUD_PASSWORD"] == "testpass" assert captured_env["NEXTCLOUD_OIDC_SCOPES"] == "openid nc:read" assert captured_env["NEXTCLOUD_OIDC_TOKEN_TYPE"] == "jwt" assert captured_env["NEXTCLOUD_PUBLIC_ISSUER_URL"] == "https://public.example.com" assert captured_env["NEXTCLOUD_MCP_SERVER_URL"] == "http://test:8000" def test_cli_options_override_environment_variables(runner, monkeypatch): """Test that CLI options override environment variables.""" # Set environment variables monkeypatch.setenv("NEXTCLOUD_HOST", "https://from-env.example.com") monkeypatch.setenv("NEXTCLOUD_USERNAME", "envuser") monkeypatch.setenv("NEXTCLOUD_OIDC_SCOPES", "openid") monkeypatch.setenv("NEXTCLOUD_OIDC_TOKEN_TYPE", "bearer") captured_env = {} def mock_get_app(*args, **kwargs): captured_env.update( { "NEXTCLOUD_HOST": os.environ.get("NEXTCLOUD_HOST"), "NEXTCLOUD_USERNAME": os.environ.get("NEXTCLOUD_USERNAME"), "NEXTCLOUD_OIDC_SCOPES": os.environ.get("NEXTCLOUD_OIDC_SCOPES"), "NEXTCLOUD_OIDC_TOKEN_TYPE": os.environ.get( "NEXTCLOUD_OIDC_TOKEN_TYPE" ), } ) raise SystemExit(0) monkeypatch.setattr("nextcloud_mcp_server.app.get_app", mock_get_app) # Provide CLI options that should override env vars _ = runner.invoke( run, [ "--nextcloud-host", "https://from-cli.example.com", "--nextcloud-username", "cliuser", "--oauth-scopes", "openid nc:write", "--oauth-token-type", "jwt", ], ) # Verify CLI options overrode env vars assert captured_env["NEXTCLOUD_HOST"] == "https://from-cli.example.com" assert captured_env["NEXTCLOUD_USERNAME"] == "cliuser" assert captured_env["NEXTCLOUD_OIDC_SCOPES"] == "openid nc:write" assert captured_env["NEXTCLOUD_OIDC_TOKEN_TYPE"] == "jwt" def test_environment_variables_used_when_cli_not_provided(runner, monkeypatch): """Test that environment variables are used when CLI options not provided.""" # Set environment variables monkeypatch.setenv("NEXTCLOUD_HOST", "https://from-env.example.com") monkeypatch.setenv("NEXTCLOUD_USERNAME", "envuser") monkeypatch.setenv("NEXTCLOUD_PASSWORD", "envpass") monkeypatch.setenv("NEXTCLOUD_OIDC_SCOPES", "openid email") monkeypatch.setenv("NEXTCLOUD_OIDC_TOKEN_TYPE", "jwt") monkeypatch.setenv("NEXTCLOUD_PUBLIC_ISSUER_URL", "https://public-env.example.com") captured_env = {} def mock_get_app(*args, **kwargs): captured_env.update( { "NEXTCLOUD_HOST": os.environ.get("NEXTCLOUD_HOST"), "NEXTCLOUD_USERNAME": os.environ.get("NEXTCLOUD_USERNAME"), "NEXTCLOUD_PASSWORD": os.environ.get("NEXTCLOUD_PASSWORD"), "NEXTCLOUD_OIDC_SCOPES": os.environ.get("NEXTCLOUD_OIDC_SCOPES"), "NEXTCLOUD_OIDC_TOKEN_TYPE": os.environ.get( "NEXTCLOUD_OIDC_TOKEN_TYPE" ), "NEXTCLOUD_PUBLIC_ISSUER_URL": os.environ.get( "NEXTCLOUD_PUBLIC_ISSUER_URL" ), } ) raise SystemExit(0) monkeypatch.setattr("nextcloud_mcp_server.app.get_app", mock_get_app) # Don't provide any CLI options - should use env vars _ = runner.invoke(run, []) # Verify env vars were used assert captured_env["NEXTCLOUD_HOST"] == "https://from-env.example.com" assert captured_env["NEXTCLOUD_USERNAME"] == "envuser" assert captured_env["NEXTCLOUD_PASSWORD"] == "envpass" assert captured_env["NEXTCLOUD_OIDC_SCOPES"] == "openid email" assert captured_env["NEXTCLOUD_OIDC_TOKEN_TYPE"] == "jwt" assert ( captured_env["NEXTCLOUD_PUBLIC_ISSUER_URL"] == "https://public-env.example.com" ) def test_default_values(runner, clean_env, monkeypatch): """Test that default values are used when neither CLI nor env vars provided.""" captured_env = {} def mock_get_app(*args, **kwargs): captured_env.update( { "NEXTCLOUD_OIDC_SCOPES": os.environ.get("NEXTCLOUD_OIDC_SCOPES"), "NEXTCLOUD_OIDC_TOKEN_TYPE": os.environ.get( "NEXTCLOUD_OIDC_TOKEN_TYPE" ), "NEXTCLOUD_MCP_SERVER_URL": os.environ.get("NEXTCLOUD_MCP_SERVER_URL"), } ) raise SystemExit(0) monkeypatch.setattr("nextcloud_mcp_server.app.get_app", mock_get_app) # Don't provide CLI options or env vars - should use defaults _ = runner.invoke(run, []) # Verify default values assert captured_env["NEXTCLOUD_OIDC_SCOPES"] == ( "openid profile email " "notes:read notes:write " "calendar:read calendar:write " "todo:read todo:write " "contacts:read contacts:write " "cookbook:read cookbook:write " "deck:read deck:write " "tables:read tables:write " "files:read files:write " "sharing:read sharing:write" ) assert captured_env["NEXTCLOUD_OIDC_TOKEN_TYPE"] == "bearer" assert captured_env["NEXTCLOUD_MCP_SERVER_URL"] == "http://localhost:8000" def test_oauth_token_type_case_normalization(runner, clean_env, monkeypatch): """Test that token type is normalized correctly regardless of input case.""" captured_env = {} def mock_get_app(*args, **kwargs): captured_env["NEXTCLOUD_OIDC_TOKEN_TYPE"] = os.environ.get( "NEXTCLOUD_OIDC_TOKEN_TYPE" ) raise SystemExit(0) monkeypatch.setattr("nextcloud_mcp_server.app.get_app", mock_get_app) # Test uppercase JWT runner.invoke(run, ["--oauth-token-type", "JWT"]) assert captured_env["NEXTCLOUD_OIDC_TOKEN_TYPE"] in ["JWT", "jwt"] # Test mixed case Bearer captured_env.clear() runner.invoke(run, ["--oauth-token-type", "Bearer"]) assert captured_env["NEXTCLOUD_OIDC_TOKEN_TYPE"] in ["Bearer", "bearer"]

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/No-Smoke/nextcloud-mcp-comprehensive'

If you have feedback or need assistance with the MCP directory API, please join our Discord server