Skip to content
This repository was archived by the owner on Nov 14, 2025. It is now read-only.

Commit e903f90

Browse files
committed
Add tests to increase code coverage for utilities, resources, prompts, and tools modules
1 parent 4be33a6 commit e903f90

File tree

5 files changed

+348
-0
lines changed

5 files changed

+348
-0
lines changed

tests/client_tests.rs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,3 +220,116 @@ async fn test_get_quality_gate_success() {
220220
assert_eq!(failing_condition.metric_key, "new_maintainability_rating");
221221
assert_eq!(failing_condition.actual_value, "2");
222222
}
223+
224+
#[tokio::test]
225+
async fn test_list_projects_success() {
226+
// Start a mock server
227+
let mock_server = MockServer::start().await;
228+
229+
// Setup mock response for projects endpoint with the correct endpoint path
230+
Mock::given(method("GET"))
231+
.and(path("/api/components/search"))
232+
.and(query_param("qualifiers", "TRK"))
233+
.respond_with(ResponseTemplate::new(200).set_body_string(
234+
r#"{
235+
"paging": {
236+
"pageIndex": 1,
237+
"pageSize": 50,
238+
"total": 2
239+
},
240+
"components": [
241+
{
242+
"key": "test-project-1",
243+
"name": "Test Project 1",
244+
"qualifier": "TRK"
245+
},
246+
{
247+
"key": "test-project-2",
248+
"name": "Test Project 2",
249+
"qualifier": "TRK"
250+
}
251+
]
252+
}"#,
253+
))
254+
.expect(1)
255+
.mount(&mock_server)
256+
.await;
257+
258+
// Create client with mock server URL
259+
let client = SonarQubeClient::new(SonarQubeConfig {
260+
base_url: mock_base_url(&mock_server),
261+
token: mock_token(),
262+
organization: None,
263+
});
264+
265+
// Call function and verify results
266+
let projects = client.list_projects(None, None, None).await.unwrap();
267+
268+
// Verify response data
269+
assert_eq!(projects.paging.page_index, 1);
270+
assert_eq!(projects.paging.page_size, 50);
271+
assert_eq!(projects.paging.total, 2);
272+
assert_eq!(projects.components.len(), 2);
273+
274+
// Verify project details
275+
let first_project = &projects.components[0];
276+
assert_eq!(first_project.key, "test-project-1");
277+
assert_eq!(first_project.name, "Test Project 1");
278+
279+
let second_project = &projects.components[1];
280+
assert_eq!(second_project.key, "test-project-2");
281+
assert_eq!(second_project.name, "Test Project 2");
282+
}
283+
284+
#[tokio::test]
285+
async fn test_list_projects_with_organization() {
286+
// Start a mock server
287+
let mock_server = MockServer::start().await;
288+
289+
// Setup mock response for projects endpoint with organization using the correct endpoint
290+
Mock::given(method("GET"))
291+
.and(path("/api/components/search"))
292+
.and(query_param("qualifiers", "TRK"))
293+
.and(query_param("organization", "my-org"))
294+
.respond_with(ResponseTemplate::new(200).set_body_string(
295+
r#"{
296+
"paging": {
297+
"pageIndex": 1,
298+
"pageSize": 100,
299+
"total": 1
300+
},
301+
"components": [
302+
{
303+
"key": "my-org-project",
304+
"name": "Organization Project",
305+
"qualifier": "TRK"
306+
}
307+
]
308+
}"#,
309+
))
310+
.expect(1)
311+
.mount(&mock_server)
312+
.await;
313+
314+
// Create client with mock server URL
315+
let client = SonarQubeClient::new(SonarQubeConfig {
316+
base_url: mock_base_url(&mock_server),
317+
token: mock_token(),
318+
organization: None, // We'll override this in the method call
319+
});
320+
321+
// Call function with organization override
322+
let projects = client
323+
.list_projects(None, None, Some("my-org"))
324+
.await
325+
.unwrap();
326+
327+
// Verify response data
328+
assert_eq!(projects.paging.total, 1);
329+
assert_eq!(projects.components.len(), 1);
330+
331+
// Verify project details
332+
let project = &projects.components[0];
333+
assert_eq!(project.key, "my-org-project");
334+
assert_eq!(project.name, "Organization Project");
335+
}

tests/prompts_tests.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
mod helpers;
2+
3+
use sonarqube_mcp_server::mcp::prompts::*;
4+
use sonarqube_mcp_server::mcp::types::*;
5+
6+
#[tokio::test]
7+
async fn test_prompts_list() {
8+
// Call prompts_list function
9+
let result = prompts_list(None).await.unwrap();
10+
11+
// Verify next_cursor is None
12+
assert!(result.next_cursor.is_none());
13+
14+
// The prompts list may be empty in this implementation
15+
// If there are prompts, verify they have required fields
16+
for prompt in &result.prompts {
17+
assert!(!prompt.name.is_empty());
18+
// Check optional fields if they exist
19+
if let Some(description) = &prompt.description {
20+
assert!(!description.is_empty());
21+
}
22+
}
23+
}
24+
25+
#[tokio::test]
26+
async fn test_prompts_get() {
27+
// Create a request with a prompt name
28+
let request = GetPromptRequest {
29+
name: "test-prompt".to_string(),
30+
arguments: None,
31+
};
32+
33+
// Call prompts_get function
34+
let result = prompts_get(request).await.unwrap();
35+
36+
// Verify the response content
37+
assert!(!result.description.is_empty());
38+
assert!(result.description.contains("test-prompt"));
39+
40+
// For now, messages is None since we don't have defined prompts
41+
assert!(result.messages.is_none());
42+
}

tests/resources_tests.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
mod helpers;
2+
3+
use sonarqube_mcp_server::mcp::resources::*;
4+
use sonarqube_mcp_server::mcp::types::*;
5+
use url::Url;
6+
7+
#[tokio::test]
8+
async fn test_resources_list() {
9+
// Call resources_list function
10+
let result = resources_list(None).await.unwrap();
11+
12+
// Verify the response contains resources
13+
assert!(
14+
!result.resources.is_empty(),
15+
"Resources list should not be empty"
16+
);
17+
18+
// Verify next_cursor is None
19+
assert!(result.next_cursor.is_none());
20+
21+
// Verify the resources have required fields
22+
for resource in &result.resources {
23+
assert!(!resource.name.is_empty());
24+
assert!(resource.uri.to_string().len() > 0);
25+
}
26+
}
27+
28+
#[tokio::test]
29+
async fn test_resource_read() {
30+
// Create a request with a URI
31+
let request = ReadResourceRequest {
32+
uri: Url::parse("resource:logs").unwrap(),
33+
meta: None,
34+
};
35+
36+
// Call resource_read function
37+
let result = resource_read(request).await.unwrap();
38+
39+
// Verify the response content
40+
assert_eq!(result.content.uri.to_string(), "resource:logs");
41+
assert_eq!(result.content.mime_type, Some("text/plain".to_string()));
42+
assert!(result.content.text.is_some());
43+
assert!(result.content.blob.is_none());
44+
45+
// Verify the text content
46+
let text = result.content.text.unwrap();
47+
assert!(!text.is_empty());
48+
}

tests/tools_tests.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ mod helpers;
33
use helpers::{load_fixture, mock_base_url, mock_token, test_project_key};
44
use sonarqube_mcp_server::mcp::sonarqube::client::SonarQubeClient;
55
use sonarqube_mcp_server::mcp::sonarqube::types::*;
6+
use sonarqube_mcp_server::mcp::tools::tools_list;
67
use wiremock::{
78
matchers::{method, path, query_param},
89
Mock, MockServer, ResponseTemplate,
@@ -192,3 +193,78 @@ async fn test_sonarqube_get_metrics_tool_with_error() {
192193
_ => panic!("Expected ProjectNotFound error, got: {:?}", result),
193194
}
194195
}
196+
197+
#[tokio::test]
198+
async fn test_tools_list() {
199+
// Call tools_list function
200+
let result = tools_list(None).await.unwrap();
201+
202+
// Verify the response contains tools
203+
assert!(!result.tools.is_empty(), "Tools list should not be empty");
204+
205+
// Verify the response contains SonarQube tools
206+
let sonarqube_tools = result
207+
.tools
208+
.iter()
209+
.filter(|t| t.name.starts_with("sonarqube"));
210+
assert!(
211+
sonarqube_tools.count() > 0,
212+
"Should contain SonarQube tools"
213+
);
214+
215+
// Verify next_cursor is None
216+
assert!(result.next_cursor.is_none());
217+
}
218+
219+
#[tokio::test]
220+
#[ignore] // This is a placeholder test that requires setting the global client
221+
async fn test_sonarqube_list_projects_tool() {
222+
// Start a mock server
223+
let mock_server = MockServer::start().await;
224+
225+
// Setup mock response for projects endpoint
226+
Mock::given(method("GET"))
227+
.and(path("/api/components/search"))
228+
.and(query_param("qualifiers", "TRK"))
229+
.respond_with(ResponseTemplate::new(200).set_body_string(
230+
r#"{
231+
"paging": {
232+
"pageIndex": 1,
233+
"pageSize": 100,
234+
"total": 2
235+
},
236+
"components": [
237+
{
238+
"key": "test-project-1",
239+
"name": "Test Project 1",
240+
"qualifier": "TRK"
241+
},
242+
{
243+
"key": "test-project-2",
244+
"name": "Test Project 2",
245+
"qualifier": "TRK"
246+
}
247+
]
248+
}"#,
249+
))
250+
.mount(&mock_server)
251+
.await;
252+
253+
// Create client with mock server URL and set the global client for tools
254+
let _client = SonarQubeClient::new(SonarQubeConfig {
255+
base_url: mock_base_url(&mock_server),
256+
token: mock_token(),
257+
organization: None,
258+
});
259+
260+
// Since we can't easily set the global client in tests, we'll skip this test for now
261+
// This is a placeholder to show how the test would be structured
262+
263+
// The actual implementation would require setting the global SONARQUBE_CLIENT
264+
// and calling the sonarqube_list_projects function with a request
265+
266+
// Ideally, we would:
267+
// 1. Set the global client
268+
// 2. Call sonarqube_list_projects
269+
// 3. Verify the response contains expected projects
270+
}

tests/utilities_tests.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
mod helpers;
2+
3+
use sonarqube_mcp_server::mcp::types::*;
4+
use sonarqube_mcp_server::mcp::utilities::*;
5+
6+
#[tokio::test]
7+
async fn test_initialize() {
8+
// Create a proper initialize request
9+
let request = InitializeRequest {
10+
protocol_version: "1.0".to_string(),
11+
capabilities: ClientCapabilities::default(),
12+
client_info: Implementation {
13+
name: "test-client".to_string(),
14+
version: "1.0.0".to_string(),
15+
},
16+
};
17+
18+
// Call initialize function
19+
let result = initialize(request).await.unwrap();
20+
21+
// Verify response fields contain expected values
22+
// Note: The actual name may vary depending on the application configuration
23+
assert!(!result.server_info.name.is_empty());
24+
assert!(!result.protocol_version.is_empty());
25+
26+
// Check capabilities
27+
assert!(result.capabilities.prompts.is_some());
28+
assert!(result.capabilities.tools.is_some());
29+
}
30+
31+
#[tokio::test]
32+
async fn test_ping() {
33+
let request = PingRequest {};
34+
let result = ping(request).await.unwrap();
35+
36+
// Empty result should be returned
37+
assert_eq!(
38+
serde_json::to_string(&result).unwrap(),
39+
serde_json::to_string(&EmptyResult {}).unwrap()
40+
);
41+
}
42+
43+
#[tokio::test]
44+
async fn test_logging_set_level() {
45+
let request = SetLevelRequest {
46+
level: "debug".to_string(),
47+
};
48+
49+
let result = logging_set_level(request).await.unwrap();
50+
51+
// LoggingResponse should be returned
52+
assert_eq!(
53+
serde_json::to_string(&result).unwrap(),
54+
serde_json::to_string(&LoggingResponse {}).unwrap()
55+
);
56+
}
57+
58+
#[tokio::test]
59+
async fn test_roots_list() {
60+
let result = roots_list(None).await.unwrap();
61+
62+
// Verify the response contains at least one root
63+
assert!(!result.roots.is_empty());
64+
65+
// Verify the first root has a name and URL
66+
let first_root = &result.roots[0];
67+
assert!(!first_root.name.is_empty());
68+
assert!(!first_root.url.is_empty());
69+
}

0 commit comments

Comments
 (0)