Files
codex/codex-rs/connectors/src/snapshot_tests.rs
T
jif 4e0f863df3 Add a connector declaration snapshot (#29851)
## Why

Connector declarations currently enter Codex through broad plugin
capability summaries, then MCP setup, turn tooling, and `app/list` each
reconstruct the same information. That makes executor-selected
connectors difficult to add without coupling connector behavior to the
host plugin loader.

This PR introduces a small connector-owned value that later stack layers
can populate before thread startup.

## What changed

- Move the pure app-declaration parser into `codex-connectors`,
preserving declaration order and category cleanup while leaving
host-side validation and deduplication unchanged.
- Add an immutable `ConnectorSnapshot` with ordered connector IDs and
plugin display-name provenance.
- Adapt the existing local-plugin capability summaries into that
snapshot at current consumer boundaries.
- Use the snapshot for MCP tool provenance, turn connector inventory,
and `app/list`.
- Keep the crate API narrow: no test-only snapshot accessors are
exposed.

The externally visible behavior is unchanged. Connector tools still come
from the orchestrator-owned `/ps/mcp` server, and local plugin
enablement remains owned by the existing plugin loader.

## Stack scope

This is the foundation only. It does not read selected executor packages
or change thread startup. #29852 adds the executor-backed declaration
reader, and #29856 composes selected declarations into a thread
snapshot.
2026-06-24 23:24:01 +01:00

48 lines
1.4 KiB
Rust

use codex_plugin::AppConnectorId;
use pretty_assertions::assert_eq;
use super::ConnectorSnapshot;
use super::PluginConnectorSource;
#[test]
fn snapshot_merges_sources_in_order_and_dedupes_provenance() {
let host_source = source("host", "Zulu", &["calendar", "calendar"]);
let host = ConnectorSnapshot::from_plugin_sources([
source("skills", "Skills only", &[]),
host_source.clone(),
]);
let selected = ConnectorSnapshot::from_plugin_sources([
source("selected-a", "Alpha", &["drive", "calendar"]),
source("selected-b", "Alpha", &["calendar"]),
]);
let merged = host.merged_with(&selected);
assert_eq!(host.sources, vec![host_source]);
assert_eq!(
merged.connector_ids(),
&[
AppConnectorId("calendar".to_string()),
AppConnectorId("drive".to_string()),
]
);
assert_eq!(
merged.plugin_display_names_for_connector_id("calendar"),
&["Alpha".to_string(), "Zulu".to_string()]
);
assert_eq!(
merged.plugin_display_names_for_connector_id("missing"),
&[] as &[String]
);
}
fn source(id: &str, display_name: &str, connector_ids: &[&str]) -> PluginConnectorSource {
PluginConnectorSource::from_connector_ids(
id,
display_name,
connector_ids
.iter()
.map(|id| AppConnectorId((*id).to_string())),
)
}