Filter plugin-install test analytics by event type (#45515)

## Why

The analytics test helper returned the first analytics request, which could contain unrelated events. Plugin-install assertions need to select the expected event even when it arrives in a later request or shares a batch with other events.

## What changed

Update `wait_for_plugin_analytics_payload` to accept an event type, scan requests until matching events appear, and return the payload with only those events. Update callers to request `codex_plugin_installed` or `codex_plugin_install_failed` as appropriate.

## Testing

Add a regression test that sends an unrelated analytics request followed by a mixed batch and verifies that the helper returns only the expected plugin-install failure event.

GitOrigin-RevId: 41290c63ea1c2a3da1efa78b81222b8da76bcbff
This commit is contained in:
zm-oai
2026-09-14 21:03:49 +00:00
committed by copyberry
parent ef8b356c22
commit fd5bf3b059
@@ -587,7 +587,7 @@ async fn plugin_install_tracks_analytics_when_remote_detail_fetch_fails() -> Res
assert_eq!(err.error.code, -32600);
assert!(err.error.message.contains("failed with status 404"));
let payload = wait_for_plugin_analytics_payload(&server).await?;
let payload = wait_for_plugin_analytics_payload(&server, "codex_plugin_install_failed").await?;
let event_params = &payload["events"][0]["event_params"];
assert_eq!(
payload["events"][0]["event_type"],
@@ -659,7 +659,7 @@ async fn plugin_install_tracks_analytics_when_remote_install_is_rate_limited() -
/*expected_count*/ 1,
)
.await?;
let payload = wait_for_plugin_analytics_payload(&server).await?;
let payload = wait_for_plugin_analytics_payload(&server, "codex_plugin_install_failed").await?;
let event_params = &payload["events"][0]["event_params"];
assert_eq!(
payload["events"][0]["event_type"],
@@ -734,7 +734,7 @@ async fn plugin_install_rejects_remote_plugin_disabled_by_admin_before_download(
.join("plugins/cache/openai-curated-remote/linear")
.exists()
);
let payload = wait_for_plugin_analytics_payload(&server).await?;
let payload = wait_for_plugin_analytics_payload(&server, "codex_plugin_install_failed").await?;
let event_params = &payload["events"][0]["event_params"];
assert_eq!(
payload["events"][0]["event_type"],
@@ -783,7 +783,7 @@ async fn plugin_install_rejects_remote_plugin_not_available() -> Result<()> {
/*expected_count*/ 0,
)
.await?;
let payload = wait_for_plugin_analytics_payload(&server).await?;
let payload = wait_for_plugin_analytics_payload(&server, "codex_plugin_install_failed").await?;
let event_params = &payload["events"][0]["event_params"];
assert_eq!(
payload["events"][0]["event_type"],
@@ -872,7 +872,8 @@ async fn plugin_install_tracks_analytics_when_marketplace_file_cannot_be_read()
.contains("failed to read marketplace file")
);
let payload = wait_for_plugin_analytics_payload(&analytics_server).await?;
let payload =
wait_for_plugin_analytics_payload(&analytics_server, "codex_plugin_install_failed").await?;
let event_params = &payload["events"][0]["event_params"];
assert_eq!(
payload["events"][0]["event_type"],
@@ -1026,7 +1027,8 @@ async fn plugin_install_tracks_analytics_event() -> Result<()> {
timeout(DEFAULT_TIMEOUT, mcp.read_response(request_id)).await??;
assert_eq!(response.apps_needing_auth, Vec::<AppSummary>::new());
let payload = wait_for_plugin_analytics_payload(&analytics_server).await?;
let payload =
wait_for_plugin_analytics_payload(&analytics_server, "codex_plugin_installed").await?;
assert_eq!(
payload,
json!({
@@ -1094,7 +1096,8 @@ async fn plugin_install_failure_tracks_analytics_event() -> Result<()> {
.await??;
assert_eq!(err.error.code, -32600);
let payload = wait_for_plugin_analytics_payload(&analytics_server).await?;
let payload =
wait_for_plugin_analytics_payload(&analytics_server, "codex_plugin_install_failed").await?;
let event_params = &payload["events"][0]["event_params"];
assert_eq!(
payload["events"][0]["event_type"],
@@ -1151,7 +1154,7 @@ async fn plugin_install_tracks_remote_plugin_analytics_event() -> Result<()> {
json!({"install_attempt_id": INSTALL_ATTEMPT_ID})
);
let payload = wait_for_plugin_analytics_payload(&server).await?;
let payload = wait_for_plugin_analytics_payload(&server, "codex_plugin_installed").await?;
assert_eq!(
payload,
json!({
@@ -1229,7 +1232,7 @@ async fn plugin_install_preserves_status_when_remote_bundle_error_body_is_too_la
/*expected_count*/ 0,
)
.await?;
let payload = wait_for_plugin_analytics_payload(&server).await?;
let payload = wait_for_plugin_analytics_payload(&server, "codex_plugin_install_failed").await?;
let event_params = &payload["events"][0]["event_params"];
assert_eq!(
payload["events"][0]["event_type"],
@@ -2762,22 +2765,32 @@ async fn mount_backend_analytics_events(server: &MockServer) {
.await;
}
async fn wait_for_plugin_analytics_payload(server: &MockServer) -> Result<serde_json::Value> {
async fn wait_for_plugin_analytics_payload(
server: &MockServer,
event_type: &str,
) -> Result<serde_json::Value> {
timeout(DEFAULT_TIMEOUT, async {
loop {
let Some(requests) = server.received_requests().await else {
tokio::time::sleep(Duration::from_millis(25)).await;
continue;
};
if let Some(request) = requests.iter().find(|request| {
for request in requests.iter().filter(|request| {
request.method == "POST"
&& request
.url
.path()
.ends_with("/codex/analytics-events/events")
}) {
return serde_json::from_slice(&request.body)
.map_err(|err| anyhow::anyhow!("invalid analytics payload: {err}"));
let mut payload: serde_json::Value = serde_json::from_slice(&request.body)
.map_err(|err| anyhow::anyhow!("invalid analytics payload: {err}"))?;
let events = payload["events"]
.as_array_mut()
.ok_or_else(|| anyhow::anyhow!("analytics payload missing events array"))?;
events.retain(|event| event["event_type"] == event_type);
if !events.is_empty() {
return Ok(payload);
}
}
tokio::time::sleep(Duration::from_millis(25)).await;
}
@@ -2785,6 +2798,36 @@ async fn wait_for_plugin_analytics_payload(server: &MockServer) -> Result<serde_
.await?
}
#[tokio::test]
async fn plugin_analytics_wait_ignores_unrelated_requests_and_batched_events() -> Result<()> {
let server = MockServer::start().await;
mount_backend_analytics_events(&server).await;
let client = HttpClientBuilder::new().build_direct()?;
let expected = json!({
"event_type": "codex_plugin_install_failed",
"event_params": {"plugin_id": "sample-plugin@debug"}
});
for events in [
json!([{"event_type": "codex_turn_event"}]),
json!([{"event_type": "codex_thread_initialized"}, expected.clone()]),
] {
client
.post(format!(
"{}/backend-api/codex/analytics-events/events",
server.uri()
))
.json(&json!({"events": events}))
.send()
.await?
.error_for_status()?;
}
assert_eq!(
wait_for_plugin_analytics_payload(&server, "codex_plugin_install_failed").await?,
json!({"events": [expected]})
);
Ok(())
}
async fn oauth_discovery_request_count(server: &MockServer) -> usize {
server
.received_requests()