test(pds-server): listRepos-Test unabhängig von der Tabellengröße machen
sync_list_repos_includes_recent_user paginierte von vorn durch listRepos und gab nach 50 Seiten à 50 Zeilen auf. Die repos-Tabelle der Dev-Instanz ist inzwischen auf ~3800 Zeilen gewachsen, der frisch angelegte DID sortierte dahinter — der Test war rot, obwohl der Endpoint korrekt antwortet (manuell mit passendem Cursor verifiziert). Der Cursor startet jetzt unmittelbar *vor* dem Ziel-DID. did_cursor_lt() taugte dafür nicht: es dekrementiert das erste Byte und landet damit vor jedem did:..., also wieder am Tabellenanfang. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013HC9HLrUU1LNwkzp8nkDLX
This commit is contained in:
co-authored by
Claude Opus 5
parent
465a88e4e5
commit
bce4c7862f
@@ -758,8 +758,14 @@ async fn sync_list_repos_includes_recent_user() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let (c, did, _jwt, _cids) = fresh_user_with_records().await;
|
let (c, did, _jwt, _cids) = fresh_user_with_records().await;
|
||||||
// Page through listRepos with a small limit until we see our DID.
|
// Page through listRepos until we see our DID. Start the cursor
|
||||||
let mut cursor: Option<String> = None;
|
// immediately *below* the target rather than at the beginning of
|
||||||
|
// the table: `repos` grows without bound on a long-lived dev
|
||||||
|
// instance (a few thousand rows already), and scanning from the
|
||||||
|
// top made this test fail purely because the DID sorted past the
|
||||||
|
// iteration cap. Anchoring at the DID keeps the cursor round trip
|
||||||
|
// under test while staying independent of table size.
|
||||||
|
let mut cursor: Option<String> = Some(did_cursor_just_before(&did));
|
||||||
let mut found = false;
|
let mut found = false;
|
||||||
for _ in 0..50 {
|
for _ in 0..50 {
|
||||||
let url = match &cursor {
|
let url = match &cursor {
|
||||||
@@ -1022,6 +1028,23 @@ fn urlencode(s: &str) -> String {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The immediate keyset predecessor of `did`: the same string with
|
||||||
|
/// its last byte decremented. `listRepos` filters with `did > cursor`,
|
||||||
|
/// so paging from here puts `did` on the first page regardless of how
|
||||||
|
/// many repos precede it in the table. Unlike [`did_cursor_lt`] — which
|
||||||
|
/// decrements the *first* byte and therefore lands before every
|
||||||
|
/// `did:...` — this stays adjacent to the target.
|
||||||
|
///
|
||||||
|
/// DIDs are ASCII (`did:plc:` + base32), so byte surgery is safe here.
|
||||||
|
fn did_cursor_just_before(did: &str) -> String {
|
||||||
|
let mut bytes = did.as_bytes().to_vec();
|
||||||
|
match bytes.last_mut() {
|
||||||
|
Some(b) if *b > 0 => *b -= 1,
|
||||||
|
_ => return did.to_string(),
|
||||||
|
}
|
||||||
|
String::from_utf8(bytes).unwrap_or_else(|_| did.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
fn did_cursor_lt(did: &str) -> String {
|
fn did_cursor_lt(did: &str) -> String {
|
||||||
let bytes = did.as_bytes();
|
let bytes = did.as_bytes();
|
||||||
let mut prefix = Vec::with_capacity(bytes.len());
|
let mut prefix = Vec::with_capacity(bytes.len());
|
||||||
|
|||||||
Reference in New Issue
Block a user