mirror of
https://github.com/openai/codex.git
synced 2026-11-08 20:40:33 +00:00
Add atomic bulk channel subscription updates (#59998)
## What changed Add `LocalAgentMessageBoard::set_subscriptions` to apply a caller's subscribe and unsubscribe choices for multiple channels in one transaction. Validate all supplied channels before applying changes, and preserve subscriptions for other channels, threads, and agents. Allow this self-only operation on read-only live connections, reject fixed views, and emit no notifications. Share subscription and opt-out updates with the existing single-subscription API. ## Testing Add coverage for bulk updates through a read-only connection, atomic failure when a channel is missing, preservation of other channels and agents, and the absence of notifications. GitOrigin-RevId: 83ed587f9c5deaa3e9de87adb6319f9de0c804b1
This commit is contained in:
@@ -1939,6 +1939,89 @@ async fn silent_appends_preserve_evidence_and_explicit_subscriptions()
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn bulk_subscriptions_are_atomic_and_scoped_to_the_caller_and_supplied_channels() -> Result<()>
|
||||
{
|
||||
use SubscriptionChange::Subscribe;
|
||||
use SubscriptionChange::Unsubscribe;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
let dir = tempfile::tempdir()?;
|
||||
let sqlite = SqliteConfig::new_for_testing(dir.path().to_path_buf().try_into().unwrap());
|
||||
let owner = ThreadId::new();
|
||||
let reviewer = ThreadId::new();
|
||||
let host = Arc::new(Host {
|
||||
clock: AtomicI64::default(),
|
||||
fail_clock: AtomicBool::default(),
|
||||
agent_path_calls: AtomicUsize::default(),
|
||||
members: [
|
||||
(owner, AgentPath::root()),
|
||||
(reviewer, AgentPath::root().join("reviewer").unwrap()),
|
||||
]
|
||||
.into(),
|
||||
active: AtomicBool::new(true),
|
||||
fail_notifications: AtomicBool::default(),
|
||||
notifications: Mutex::default(),
|
||||
});
|
||||
let board = LocalBoard::new(sqlite, owner.into());
|
||||
let writer = board.read_write().open(host.clone()).await?;
|
||||
let reader = board.read_only().open(host.clone()).await?;
|
||||
for name in ["build", "release"] {
|
||||
writer
|
||||
.create_channel(
|
||||
owner,
|
||||
CreateChannelRequest {
|
||||
channel_name: name.into(),
|
||||
description: None,
|
||||
subscription: Subscribe,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
reader
|
||||
.set_subscriptions(
|
||||
reviewer,
|
||||
&BTreeMap::from([("build", Subscribe), ("release", Subscribe)]),
|
||||
)
|
||||
.await?;
|
||||
let both = vec!["build".to_owned(), "release".to_owned()];
|
||||
assert!(
|
||||
reader
|
||||
.set_subscriptions(
|
||||
reviewer,
|
||||
&BTreeMap::from([("build", Unsubscribe), ("missing", Subscribe)])
|
||||
)
|
||||
.await
|
||||
.is_err()
|
||||
);
|
||||
assert_eq!(
|
||||
reader
|
||||
.subscribed_channels(reviewer, PageRequest::default())
|
||||
.await?
|
||||
.results,
|
||||
both
|
||||
);
|
||||
reader
|
||||
.set_subscriptions(reviewer, &BTreeMap::from([("build", Unsubscribe)]))
|
||||
.await?;
|
||||
assert_eq!(
|
||||
reader
|
||||
.subscribed_channels(reviewer, PageRequest::default())
|
||||
.await?
|
||||
.results,
|
||||
vec!["release".to_owned()]
|
||||
);
|
||||
assert_eq!(
|
||||
reader
|
||||
.subscribed_channels(owner, PageRequest::default())
|
||||
.await?
|
||||
.results,
|
||||
both
|
||||
);
|
||||
assert!(host.notifications.lock().unwrap().is_empty());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn local_board_setup_survives_reopen() -> Result<()> {
|
||||
use serde_json::json;
|
||||
|
||||
Reference in New Issue
Block a user