test plugin first-party load provenance

This commit is contained in:
Kyle Brown
2026-06-12 22:13:52 +00:00
parent 8c6f3501af
commit 0f633830ca
2 changed files with 132 additions and 1 deletions

View File

@@ -1,6 +1,12 @@
use super::*;
use crate::PluginLoadOutcome;
use crate::manifest::load_plugin_manifest;
use crate::startup_sync::curated_plugins_repo_path;
use crate::test_support::TEST_CURATED_PLUGIN_CACHE_VERSION;
use crate::test_support::TEST_CURATED_PLUGIN_SHA;
use crate::test_support::write_curated_plugin_sha_with;
use crate::test_support::write_file;
use crate::test_support::write_openai_curated_marketplace;
use codex_config::ConfigLayerEntry;
use codex_config::ConfigLayerSource;
use codex_config::ConfigRequirements;
@@ -226,6 +232,131 @@ fn curated_plugin_cache_version_preserves_non_git_sha_versions() {
assert_eq!(curated_plugin_cache_version("0123456"), "0123456");
}
fn curated_plugin_config_layer(temp_dir: &TempDir, plugin_name: &str) -> ConfigLayerStack {
ConfigLayerStack::new(
vec![user_layer(
user_config_path(temp_dir, "config.toml"),
&format!(
r#"[plugins."{plugin_name}@openai-curated"]
enabled = true
"#,
),
)],
ConfigRequirements::default(),
ConfigRequirementsToml::default(),
)
.expect("valid curated plugin config")
}
fn write_curated_cached_plugin_with_developer(
codex_home: &Path,
plugin_name: &str,
version: &str,
developer_name: &str,
) {
let plugin_root = codex_home
.join("plugins/cache/openai-curated")
.join(plugin_name)
.join(version);
write_file(
&plugin_root.join(".codex-plugin/plugin.json"),
&format!(
r#"{{"name":"{plugin_name}","interface":{{"developerName":"{developer_name}"}}}}"#,
),
);
write_file(&plugin_root.join("skills/SKILL.md"), "skill");
}
async fn load_curated_plugin(temp_dir: &TempDir, plugin_name: &str) -> PluginLoadOutcome {
PluginLoadOutcome::from_plugins(
load_plugins_from_layer_stack(
&curated_plugin_config_layer(temp_dir, plugin_name),
HashMap::new(),
&PluginStore::new(temp_dir.path().to_path_buf()),
Some(Product::Codex),
/*prefer_remote_curated_conflicts*/ false,
)
.await,
)
}
#[tokio::test]
async fn load_curated_plugin_prefers_synced_sha_root_over_stale_local_cache() {
let temp_dir = TempDir::new().expect("tempdir");
let curated_root = curated_plugins_repo_path(temp_dir.path());
write_openai_curated_marketplace(&curated_root, &["slack"]);
write_curated_plugin_sha_with(temp_dir.path(), TEST_CURATED_PLUGIN_SHA);
write_curated_cached_plugin_with_developer(temp_dir.path(), "slack", "local", "Third Party");
write_curated_cached_plugin_with_developer(
temp_dir.path(),
"slack",
TEST_CURATED_PLUGIN_CACHE_VERSION,
"OpenAI",
);
let outcome = load_curated_plugin(&temp_dir, "slack").await;
let plugin = outcome.plugins().first().expect("configured plugin");
let synced_root = AbsolutePathBuf::try_from(temp_dir.path().join(format!(
"plugins/cache/openai-curated/slack/{TEST_CURATED_PLUGIN_CACHE_VERSION}"
)))
.expect("synced plugin root");
assert_eq!(plugin.root, synced_root);
assert!(plugin.is_first_party);
assert_eq!(
outcome.effective_first_party_plugin_roots(),
vec![codex_plugin::FirstPartyPluginRoot {
plugin_id: "slack@openai-curated".to_string(),
plugin_root: synced_root,
}]
);
}
#[tokio::test]
async fn load_curated_plugin_with_openai_metadata_without_synced_cache_is_not_first_party() {
let temp_dir = TempDir::new().expect("tempdir");
let curated_root = curated_plugins_repo_path(temp_dir.path());
write_openai_curated_marketplace(&curated_root, &["slack"]);
write_curated_plugin_sha_with(temp_dir.path(), TEST_CURATED_PLUGIN_SHA);
write_curated_cached_plugin_with_developer(temp_dir.path(), "slack", "local", "OpenAI");
let outcome = load_curated_plugin(&temp_dir, "slack").await;
assert!(
!outcome
.plugins()
.first()
.expect("configured plugin")
.is_first_party
);
assert_eq!(outcome.effective_first_party_plugin_roots(), Vec::new());
}
#[tokio::test]
async fn load_curated_plugin_missing_from_synced_marketplace_is_not_first_party() {
let temp_dir = TempDir::new().expect("tempdir");
let curated_root = curated_plugins_repo_path(temp_dir.path());
write_openai_curated_marketplace(&curated_root, &["github"]);
write_curated_plugin_sha_with(temp_dir.path(), TEST_CURATED_PLUGIN_SHA);
write_curated_cached_plugin_with_developer(
temp_dir.path(),
"slack",
TEST_CURATED_PLUGIN_CACHE_VERSION,
"OpenAI",
);
let outcome = load_curated_plugin(&temp_dir, "slack").await;
assert!(
!outcome
.plugins()
.first()
.expect("configured plugin")
.is_first_party
);
assert_eq!(outcome.effective_first_party_plugin_roots(), Vec::new());
}
fn plugin_id() -> PluginId {
PluginId::parse("demo-plugin@test-marketplace").expect("plugin id")
}

View File

@@ -1,8 +1,8 @@
use super::LoadedPlugin;
use super::PluginLoadOutcome;
use crate::OPENAI_CURATED_MARKETPLACE_NAME;
use crate::app_mcp_routing::apply_app_mcp_routing_policy;
use crate::installed_marketplaces::installed_marketplace_roots_from_layer_stack;
use crate::OPENAI_CURATED_MARKETPLACE_NAME;
use crate::is_openai_curated_marketplace_name;
use crate::loader::PluginHookLoadOutcome;
use crate::loader::configured_curated_plugin_ids_from_codex_home;