fix(runtime): isolate agent state by project (#101)
This commit is contained in:
@ -5,7 +5,7 @@ use std::time::Duration;
|
||||
|
||||
use domain::{
|
||||
AgentId, AgentInbox, InboxError, InboxItem, InboxItemKind, InboxReceiptStatus, InboxSource,
|
||||
InputMediator, Ticket, TicketId,
|
||||
InputMediator, ProjectId, RuntimeAgentKey, Ticket, TicketId,
|
||||
};
|
||||
use infrastructure::{
|
||||
start_background_ready_inbox_bridge, BackgroundTaskReadyToDeliver, InMemoryMailbox,
|
||||
@ -26,6 +26,14 @@ fn agent(n: u128) -> AgentId {
|
||||
AgentId::from_uuid(Uuid::from_u128(n))
|
||||
}
|
||||
|
||||
fn project_id(n: u128) -> ProjectId {
|
||||
ProjectId::from_uuid(Uuid::from_u128(n))
|
||||
}
|
||||
|
||||
fn key(project: u128, target: AgentId) -> RuntimeAgentKey {
|
||||
RuntimeAgentKey::new(project_id(project), target)
|
||||
}
|
||||
|
||||
fn ticket_id(n: u128) -> TicketId {
|
||||
TicketId::from_uuid(Uuid::from_u128(n))
|
||||
}
|
||||
@ -70,7 +78,7 @@ fn completion_item(id: u128, target: AgentId, task: u128) -> InboxItem {
|
||||
|
||||
fn make_busy(inbox: &MediatedInbox, target: AgentId) {
|
||||
let ticket = Ticket::new(ticket_id(900), "active", "active turn");
|
||||
let _pending = inbox.enqueue(target, ticket);
|
||||
let _pending = inbox.enqueue(key(1, target), ticket);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -80,12 +88,12 @@ fn enqueue_message_while_busy_is_queued_not_rejected() {
|
||||
make_busy(&inbox, target);
|
||||
|
||||
let receipt = inbox
|
||||
.enqueue_message(target, user_item(1, target, "queued"))
|
||||
.enqueue_message(key(1, target), user_item(1, target, "queued"))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(receipt.status, InboxReceiptStatus::Queued);
|
||||
assert_eq!(receipt.depth, 2);
|
||||
let snapshot = inbox.snapshot(target);
|
||||
let snapshot = inbox.snapshot(key(1, target));
|
||||
assert_eq!(snapshot.depth, 2);
|
||||
assert_eq!(snapshot.items.len(), 1);
|
||||
assert_eq!(snapshot.items[0].body, "queued");
|
||||
@ -98,12 +106,16 @@ fn inbox_fifo_drains_two_entrants_in_order() {
|
||||
let first = user_item(1, target, "first");
|
||||
let second = user_item(2, target, "second");
|
||||
|
||||
inbox.enqueue_message(target, first.clone()).unwrap();
|
||||
inbox.enqueue_message(target, second.clone()).unwrap();
|
||||
inbox
|
||||
.enqueue_message(key(1, target), first.clone())
|
||||
.unwrap();
|
||||
inbox
|
||||
.enqueue_message(key(1, target), second.clone())
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(inbox.dequeue_next(target), Some(first));
|
||||
assert_eq!(inbox.dequeue_next(target), Some(second));
|
||||
assert_eq!(inbox.dequeue_next(target), None);
|
||||
assert_eq!(inbox.dequeue_next(key(1, target)), Some(first));
|
||||
assert_eq!(inbox.dequeue_next(key(1, target)), Some(second));
|
||||
assert_eq!(inbox.dequeue_next(key(1, target)), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -113,7 +125,7 @@ fn overflow_normal_message_returns_inbox_full() {
|
||||
make_busy(&inbox, target);
|
||||
|
||||
let err = inbox
|
||||
.enqueue_message(target, user_item(1, target, "overflow"))
|
||||
.enqueue_message(key(1, target), user_item(1, target, "overflow"))
|
||||
.unwrap_err();
|
||||
|
||||
assert_eq!(
|
||||
@ -132,12 +144,12 @@ fn overflow_completion_is_deferred_not_lost_or_enqueued() {
|
||||
make_busy(&inbox, target);
|
||||
|
||||
let receipt = inbox
|
||||
.enqueue_message(target, completion_item(1, target, 42))
|
||||
.enqueue_message(key(1, target), completion_item(1, target, 42))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(receipt.status, InboxReceiptStatus::Deferred);
|
||||
assert_eq!(receipt.depth, 1);
|
||||
assert!(inbox.snapshot(target).items.is_empty());
|
||||
assert!(inbox.snapshot(key(1, target)).items.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -146,13 +158,13 @@ fn snapshot_exposes_queue_depth_and_items() {
|
||||
let target = agent(1);
|
||||
|
||||
inbox
|
||||
.enqueue_message(target, user_item(1, target, "first"))
|
||||
.enqueue_message(key(1, target), user_item(1, target, "first"))
|
||||
.unwrap();
|
||||
inbox
|
||||
.enqueue_message(target, user_item(2, target, "second"))
|
||||
.enqueue_message(key(1, target), user_item(2, target, "second"))
|
||||
.unwrap();
|
||||
|
||||
let snapshot = inbox.snapshot(target);
|
||||
let snapshot = inbox.snapshot(key(1, target));
|
||||
assert_eq!(snapshot.agent_id, target);
|
||||
assert_eq!(snapshot.depth, 2);
|
||||
assert_eq!(
|
||||
@ -170,19 +182,38 @@ fn drain_removes_only_one_item_at_a_time() {
|
||||
let inbox = inbox(10);
|
||||
let target = agent(1);
|
||||
inbox
|
||||
.enqueue_message(target, user_item(1, target, "first"))
|
||||
.enqueue_message(key(1, target), user_item(1, target, "first"))
|
||||
.unwrap();
|
||||
inbox
|
||||
.enqueue_message(target, user_item(2, target, "second"))
|
||||
.enqueue_message(key(1, target), user_item(2, target, "second"))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(inbox.dequeue_next(target).unwrap().body, "first");
|
||||
assert_eq!(inbox.dequeue_next(key(1, target)).unwrap().body, "first");
|
||||
|
||||
let snapshot = inbox.snapshot(target);
|
||||
let snapshot = inbox.snapshot(key(1, target));
|
||||
assert_eq!(snapshot.depth, 1);
|
||||
assert_eq!(snapshot.items[0].body, "second");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn same_agent_id_in_distinct_projects_has_isolated_inbox_queues() {
|
||||
let inbox = inbox(10);
|
||||
let target = agent(1);
|
||||
let p1 = key(1, target);
|
||||
let p2 = key(2, target);
|
||||
let first = user_item(1, target, "project one");
|
||||
let second = user_item(2, target, "project two");
|
||||
|
||||
inbox.enqueue_message(p1, first.clone()).unwrap();
|
||||
inbox.enqueue_message(p2, second.clone()).unwrap();
|
||||
|
||||
assert_eq!(inbox.snapshot(p1).depth, 1);
|
||||
assert_eq!(inbox.snapshot(p2).depth, 1);
|
||||
assert_eq!(inbox.dequeue_next(p1), Some(first));
|
||||
assert_eq!(inbox.snapshot(p1).depth, 0);
|
||||
assert_eq!(inbox.snapshot(p2).items, vec![second]);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn ready_bridge_enqueues_background_completion_item() {
|
||||
let inbox = Arc::new(inbox(10));
|
||||
@ -192,13 +223,13 @@ async fn ready_bridge_enqueues_background_completion_item() {
|
||||
|
||||
tx.send(BackgroundTaskReadyToDeliver {
|
||||
task_id: task_id(42),
|
||||
project_id: domain::ProjectId::from_uuid(Uuid::from_u128(7)),
|
||||
project_id: project_id(7),
|
||||
owner_agent_id: target,
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
tokio::time::sleep(Duration::from_millis(20)).await;
|
||||
let snapshot = inbox.snapshot(target);
|
||||
let snapshot = inbox.snapshot(key(7, target));
|
||||
assert_eq!(snapshot.depth, 1);
|
||||
assert_eq!(snapshot.items[0].kind, InboxItemKind::BackgroundCompletion);
|
||||
assert_eq!(
|
||||
|
||||
Reference in New Issue
Block a user