huskies: merge 870
This commit is contained in:
@@ -52,8 +52,8 @@ pub use types::{
|
||||
TestJobCrdt, TestJobView, TokenUsageCrdt, TokenUsageView, subscribe,
|
||||
};
|
||||
pub use write::{
|
||||
migrate_names_from_slugs, migrate_story_ids_to_numeric, name_from_story_id, set_qa_mode,
|
||||
write_item,
|
||||
bump_retry_count, migrate_names_from_slugs, migrate_story_ids_to_numeric, name_from_story_id,
|
||||
set_qa_mode, set_retry_count, write_item,
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -133,7 +133,7 @@ pub fn dump_crdt_state(story_id_filter: Option<&str>) -> CrdtStateDump {
|
||||
_ => None,
|
||||
};
|
||||
let retry_count = match item_crdt.retry_count.view() {
|
||||
JsonValue::Number(n) if n > 0.0 => Some(n as i64),
|
||||
JsonValue::Number(n) => Some(n as i64),
|
||||
_ => None,
|
||||
};
|
||||
let blocked = match item_crdt.blocked.view() {
|
||||
@@ -290,7 +290,7 @@ pub(super) fn extract_item_view(item: &PipelineItemCrdt) -> Option<PipelineItemV
|
||||
_ => None,
|
||||
};
|
||||
let retry_count = match item.retry_count.view() {
|
||||
JsonValue::Number(n) if n > 0.0 => Some(n as i64),
|
||||
JsonValue::Number(n) => Some(n as i64),
|
||||
_ => None,
|
||||
};
|
||||
let blocked = match item.blocked.view() {
|
||||
|
||||
@@ -350,6 +350,51 @@ pub fn write_item(
|
||||
}
|
||||
}
|
||||
|
||||
/// Set `retry_count` to an explicit value for a pipeline item.
|
||||
///
|
||||
/// Pure metadata operation — the item's stage is not changed.
|
||||
/// Call `set_retry_count(story_id, 0)` to reset the counter after a
|
||||
/// stage transition or an explicit unblock.
|
||||
pub fn set_retry_count(story_id: &str, count: i64) {
|
||||
let Some(state_mutex) = get_crdt() else {
|
||||
return;
|
||||
};
|
||||
let Ok(mut state) = state_mutex.lock() else {
|
||||
return;
|
||||
};
|
||||
if let Some(&idx) = state.index.get(story_id) {
|
||||
apply_and_persist(&mut state, |s| {
|
||||
s.crdt.doc.items[idx].retry_count.set(count as f64)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Increment `retry_count` by 1 and return the new value.
|
||||
///
|
||||
/// Pure metadata operation — the item's stage is not changed.
|
||||
/// Returns 0 if the item is not found in the CRDT (no-op in that case).
|
||||
/// Use the returned value to decide whether the story should be blocked.
|
||||
pub fn bump_retry_count(story_id: &str) -> i64 {
|
||||
let Some(state_mutex) = get_crdt() else {
|
||||
return 0;
|
||||
};
|
||||
let Ok(mut state) = state_mutex.lock() else {
|
||||
return 0;
|
||||
};
|
||||
let Some(&idx) = state.index.get(story_id) else {
|
||||
return 0;
|
||||
};
|
||||
let current = match state.crdt.doc.items[idx].retry_count.view() {
|
||||
JsonValue::Number(n) => n as i64,
|
||||
_ => 0,
|
||||
};
|
||||
let new_count = current + 1;
|
||||
apply_and_persist(&mut state, |s| {
|
||||
s.crdt.doc.items[idx].retry_count.set(new_count as f64)
|
||||
});
|
||||
new_count
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::hex;
|
||||
@@ -704,6 +749,71 @@ mod tests {
|
||||
use sqlx::SqlitePool;
|
||||
use sqlx::sqlite::SqliteConnectOptions;
|
||||
|
||||
// ── set_retry_count / bump_retry_count tests ─────────────────────────────
|
||||
|
||||
#[test]
|
||||
fn bump_retry_count_increments_by_one() {
|
||||
init_for_test();
|
||||
write_item(
|
||||
"9001_story_bump_test",
|
||||
"2_current",
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
||||
let v1 = bump_retry_count("9001_story_bump_test");
|
||||
assert_eq!(v1, 1, "first bump should return 1");
|
||||
|
||||
let v2 = bump_retry_count("9001_story_bump_test");
|
||||
assert_eq!(v2, 2, "second bump should return 2");
|
||||
|
||||
let item = read_item("9001_story_bump_test").expect("item must exist");
|
||||
assert_eq!(
|
||||
item.retry_count,
|
||||
Some(2),
|
||||
"CRDT must reflect final bump value"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_retry_count_resets_to_zero() {
|
||||
init_for_test();
|
||||
write_item(
|
||||
"9002_story_set_test",
|
||||
"2_current",
|
||||
None,
|
||||
None,
|
||||
Some(5),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
||||
set_retry_count("9002_story_set_test", 0);
|
||||
|
||||
let item = read_item("9002_story_set_test").expect("item must exist");
|
||||
assert_eq!(
|
||||
item.retry_count,
|
||||
Some(0),
|
||||
"set_retry_count(0) must reset to 0"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bump_returns_zero_for_missing_item() {
|
||||
init_for_test();
|
||||
let result = bump_retry_count("nonexistent_story");
|
||||
assert_eq!(result, 0, "bump on missing item should return 0 (no-op)");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn bug_511_rowid_replay_preserves_field_update_after_list_insert() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
|
||||
Reference in New Issue
Block a user