Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions guards/github-guard/rust-guard/src/labels/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3710,4 +3710,73 @@ mod tests {
integrity
);
}

/// search_repositories with `repositories` key (GraphQL-style) must use
/// `/repositories/{i}` paths, not `/items/{i}`.
#[test]
fn test_search_repositories_repositories_key_uses_correct_paths() {
let ctx = default_ctx();
let tool_args = json!({});
let response = json!({
"totalCount": 2,
"repositories": [
{
"full_name": "owner/public-repo",
"private": false
},
{
"full_name": "owner/private-repo",
"private": true
}
]
});

let result = label_response_paths("search_repositories", &tool_args, &response, &ctx)
.expect("search_repositories with repositories key should produce path labels");

assert_eq!(result.labeled_paths.len(), 2, "both items must be labeled");
assert_eq!(
result.items_path,
Some("/repositories".to_string()),
"items_path must reflect the actual key used"
);
assert_eq!(
result.labeled_paths[0].path, "/repositories/0",
"first item path must use /repositories/ prefix"
);
assert_eq!(
result.labeled_paths[1].path, "/repositories/1",
"second item path must use /repositories/ prefix"
);
}

/// search_repositories with `items` key (REST format) must use `/items/{i}` paths.
#[test]
fn test_search_repositories_items_key_uses_correct_paths() {
let ctx = default_ctx();
let tool_args = json!({});
let response = json!({
"total_count": 1,
"items": [
{
"full_name": "owner/public-repo",
"private": false
}
]
});

let result = label_response_paths("search_repositories", &tool_args, &response, &ctx)
.expect("search_repositories with items key should produce path labels");

assert_eq!(result.labeled_paths.len(), 1);
assert_eq!(
result.items_path,
Some("/items".to_string()),
"items_path must be /items for REST format"
);
assert_eq!(
result.labeled_paths[0].path, "/items/0",
"item path must use /items/ prefix for REST format"
);
}
}
14 changes: 10 additions & 4 deletions guards/github-guard/rust-guard/src/labels/response_paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,14 @@ pub fn label_response_paths(
match tool_name {
// === Repository Search - label by private/public ===
"search_repositories" => {
let items_opt = actual_response.get("items").and_then(|v| v.as_array())
.or_else(|| actual_response.get("repositories").and_then(|v| v.as_array()));
let (items_opt, items_key) =
if let Some(arr) = actual_response.get("items").and_then(|v| v.as_array()) {
(Some(arr), "items")
} else if let Some(arr) = actual_response.get("repositories").and_then(|v| v.as_array()) {
(Some(arr), "repositories")
} else {
(None, "items")
};
if let Some(items) = items_opt {
// Empty search results are server metadata — let lib.rs fallback handle
if items.is_empty() && is_search_result_wrapper(&actual_response) {
Expand Down Expand Up @@ -79,7 +85,7 @@ pub fn label_response_paths(
};

labeled_paths.push(PathLabelEntry {
path: format!("/items/{}", i),
path: format!("/{}/{}", items_key, i),
labels: crate::ResourceLabels {
description: format!("repo:{}", full_name),
secrecy,
Expand All @@ -95,7 +101,7 @@ pub fn label_response_paths(
secrecy: vec![],
integrity: none_integrity("", ctx),
}),
items_path: Some("/items".to_string()),
items_path: Some(format!("/{}", items_key)),
});
}
}
Expand Down
Loading