test(memory-sync/github): assert involves: query qualifier (#2664)

This commit is contained in:
obchain
2026-05-26 15:25:16 +05:30
committed by GitHub
parent 1aee7ded8d
commit 844a26c0c0
2 changed files with 62 additions and 6 deletions
@@ -197,12 +197,7 @@ impl ComposioProvider for GitHubProvider {
};
// Build the base search query.
let query = match &state.cursor {
Some(cursor) => {
format!("involves:{login} updated:>{cursor}")
}
None => format!("involves:{login}"),
};
let query = build_search_query(&login, state.cursor.as_deref());
let mut total_fetched: usize = 0;
let mut total_persisted: usize = 0;
@@ -423,3 +418,21 @@ impl GitHubProvider {
})
}
}
/// Build the GitHub Search-Issues query for an incremental sync.
///
/// `involves:` is GitHub's logical-OR over `author`, `assignee`, `mentions`,
/// and `commenter`, so the result set covers every item the connected user
/// has standing in — not only items explicitly assigned to them. When a
/// cursor from a prior sync is present, an `updated:>{cursor}` clause is
/// appended so the next page request only returns items changed since.
///
/// Kept as a free function (rather than inline in `sync()`) so the query
/// contract — specifically the `involves:` qualifier — can be asserted by
/// unit tests without spinning up the full sync pipeline.
pub(super) fn build_search_query(login: &str, cursor: Option<&str>) -> String {
match cursor {
Some(cursor) => format!("involves:{login} updated:>{cursor}"),
None => format!("involves:{login}"),
}
}
@@ -1,5 +1,6 @@
//! Unit tests for the GitHub Composio provider.
use super::provider::build_search_query;
use super::sync::{
extract_issue_id, extract_issue_title, extract_issue_updated_at, extract_issues,
extract_user_login,
@@ -179,3 +180,45 @@ fn default_impl_matches_new() {
b.curated_tools().map(<[_]>::len),
);
}
// ── build_search_query ──────────────────────────────────────────────────────
//
// Regression coverage for #2418: the GitHub Memory Provider must scope the
// periodic sync to `involves:{login}` — GitHub's logical-OR over `author`,
// `assignee`, `mentions`, and `commenter` — rather than the narrower
// `assignee:{login}`. Without these assertions the qualifier could silently
// regress to assignee-only and lose author / mention / commenter coverage
// for OSS contributors who are rarely explicitly assigned.
#[test]
fn build_search_query_uses_involves_qualifier_without_cursor() {
let query = build_search_query("octocat", None);
assert_eq!(query, "involves:octocat");
}
#[test]
fn build_search_query_does_not_fall_back_to_assignee_qualifier() {
let query = build_search_query("octocat", None);
assert!(
!query.contains("assignee:"),
"query must not use the narrower assignee-only qualifier (see #2418): {query}"
);
assert!(query.starts_with("involves:"));
}
#[test]
fn build_search_query_appends_updated_clause_when_cursor_present() {
let query = build_search_query("octocat", Some("2026-05-25T00:00:00Z"));
assert_eq!(
query,
"involves:octocat updated:>2026-05-25T00:00:00Z",
"cursor must be threaded through as an updated:> clause so incremental syncs only refetch changed items"
);
}
#[test]
fn build_search_query_interpolates_login_verbatim() {
let query = build_search_query("Hyphen-User_99", Some("2026-01-02T03:04:05Z"));
assert!(query.contains("involves:Hyphen-User_99"));
assert!(query.contains("updated:>2026-01-02T03:04:05Z"));
}