mirror of
https://github.com/openai/codex.git
synced 2026-10-18 17:18:52 +00:00
Expose plugin installation interstitial requirements (#33896)
## What changed - Add nullable `mustShowInstallationInterstitial` metadata to app-server `PluginSummary` responses. - Preserve the remote service value through catalog, installed-plugin cache, list, read, and share-list paths. - Return `null` for local plugins and remote responses that omit the field so clients can fail closed when the policy is unavailable. ## Testing - Cover `true`, `false`, and missing values across remote summary paths and app-server plugin endpoints. GitOrigin-RevId: db79702ee2bfa800a9910d64d933bcabecb3ce70
This commit is contained in:
+7
@@ -14779,6 +14779,13 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"mustShowInstallationInterstitial": {
|
||||
"default": null,
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
+7
@@ -11136,6 +11136,13 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"mustShowInstallationInterstitial": {
|
||||
"default": null,
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -534,6 +534,13 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"mustShowInstallationInterstitial": {
|
||||
"default": null,
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -534,6 +534,13 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"mustShowInstallationInterstitial": {
|
||||
"default": null,
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -684,6 +684,13 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"mustShowInstallationInterstitial": {
|
||||
"default": null,
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -490,6 +490,13 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"mustShowInstallationInterstitial": {
|
||||
"default": null,
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -25,7 +25,7 @@ localVersion: string | null, name: string,
|
||||
/**
|
||||
* Remote sharing context associated with this plugin when available.
|
||||
*/
|
||||
shareContext: PluginShareContext | null, source: PluginSource, installed: boolean, enabled: boolean, installPolicy: PluginInstallPolicy, installPolicySource: PluginInstallPolicySource | null, authPolicy: PluginAuthPolicy,
|
||||
shareContext: PluginShareContext | null, source: PluginSource, installed: boolean, enabled: boolean, installPolicy: PluginInstallPolicy, installPolicySource: PluginInstallPolicySource | null, mustShowInstallationInterstitial: boolean | null, authPolicy: PluginAuthPolicy,
|
||||
/**
|
||||
* Availability state for installing and using the plugin.
|
||||
*/
|
||||
|
||||
@@ -625,6 +625,8 @@ pub struct PluginSummary {
|
||||
pub enabled: bool,
|
||||
pub install_policy: PluginInstallPolicy,
|
||||
pub install_policy_source: Option<PluginInstallPolicySource>,
|
||||
#[serde(default)]
|
||||
pub must_show_installation_interstitial: Option<bool>,
|
||||
pub auth_policy: PluginAuthPolicy,
|
||||
/// Availability state for installing and using the plugin.
|
||||
#[serde(default)]
|
||||
|
||||
@@ -3680,6 +3680,7 @@ fn plugin_share_list_response_serializes_share_items() {
|
||||
enabled: false,
|
||||
install_policy: PluginInstallPolicy::Available,
|
||||
install_policy_source: Some(PluginInstallPolicySource::WorkspaceSetting),
|
||||
must_show_installation_interstitial: None,
|
||||
auth_policy: PluginAuthPolicy::OnUse,
|
||||
availability: PluginAvailability::Available,
|
||||
interface: None,
|
||||
@@ -3703,6 +3704,7 @@ fn plugin_share_list_response_serializes_share_items() {
|
||||
"enabled": false,
|
||||
"installPolicy": "AVAILABLE",
|
||||
"installPolicySource": "WORKSPACE_SETTING",
|
||||
"mustShowInstallationInterstitial": null,
|
||||
"authPolicy": "ON_USE",
|
||||
"availability": "AVAILABLE",
|
||||
"interface": null,
|
||||
@@ -3731,6 +3733,7 @@ fn plugin_summary_defaults_missing_availability_to_available() {
|
||||
assert_eq!(summary.availability, PluginAvailability::Available);
|
||||
assert_eq!(summary.local_version, None);
|
||||
assert_eq!(summary.share_context, None);
|
||||
assert_eq!(summary.must_show_installation_interstitial, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -215,7 +215,7 @@ Example with notification opt-out:
|
||||
- `marketplace/add` — add a remote plugin marketplace from an HTTP(S) Git URL, SSH Git URL, or GitHub `owner/repo` shorthand, then persist it into the user marketplace config. Returns the installed root path plus whether the marketplace was already present.
|
||||
- `marketplace/remove` — remove a configured marketplace by name from the user marketplace config, and delete its installed marketplace root when one exists.
|
||||
- `marketplace/upgrade` — upgrade all configured Git plugin marketplaces, or one named marketplace when `marketplaceName` is provided. Returns selected marketplace names, upgraded roots, and per-marketplace errors.
|
||||
- `plugin/list` — list discovered plugin marketplaces and plugin state, including effective marketplace install/auth policy metadata, nullable remote install-policy provenance in `installPolicySource` (`WORKSPACE_SETTING` or `IMPLICIT_CANONICAL_APP`), the remote marketplace `version` and locally materialized `localVersion` when available, plugin `availability` (`AVAILABLE` by default or `DISABLED_BY_ADMIN` for remote plugins blocked upstream), fail-open `marketplaceLoadErrors` entries for marketplace files that could not be parsed or loaded, and best-effort `featuredPluginIds` for the official curated marketplace. Clients can explicitly request the remote `workspace-directory`, `shared-with-me`, or `created-by-me-remote` marketplace kinds. `interface.category` uses the marketplace category when present; otherwise it falls back to the plugin manifest category (**under development; do not call from production clients yet**).
|
||||
- `plugin/list` — list discovered plugin marketplaces and plugin state, including effective marketplace install/auth policy metadata, nullable remote install-policy provenance in `installPolicySource` (`WORKSPACE_SETTING` or `IMPLICIT_CANONICAL_APP`), the remote marketplace `version` and locally materialized `localVersion` when available, plugin `availability` (`AVAILABLE` by default or `DISABLED_BY_ADMIN` for remote plugins blocked upstream), fail-open `marketplaceLoadErrors` entries for marketplace files that could not be parsed or loaded, and best-effort `featuredPluginIds` for the official curated marketplace. Every `PluginSummary` returned by plugin list, installed, read, and share-list methods includes `mustShowInstallationInterstitial`: remote service values preserve `true` or `false`, while local plugins and remote responses that omit the policy return `null`. Clients should fail closed when the value is `null`. Clients can explicitly request the remote `workspace-directory`, `shared-with-me`, or `created-by-me-remote` marketplace kinds. `interface.category` uses the marketplace category when present; otherwise it falls back to the plugin manifest category (**under development; do not call from production clients yet**).
|
||||
- `plugin/installed` — list installed plugin rows plus any explicitly requested local install-suggestion plugin names, without fetching the broader remote catalog. Remote rows include nullable `installPolicySource`; local rows return `null`. Mention surfaces can use this narrower view when they need plugin mention payloads rather than plugin-page discovery data (**under development; do not call from production clients yet**).
|
||||
- `plugin/read` — read one plugin by `marketplacePath` plus `pluginName`, returning marketplace info, a list-style `summary`, manifest descriptions/interface metadata, and bundled skills/hooks/apps/MCP server names. Remote plugin details can include scheduled task summaries from the catalog; `scheduledTasks: null` means the metadata is unavailable, while an empty array means the catalog found no scheduled tasks. Remote plugin details expose the canonical `shareUrl` supplied by the remote catalog when available; it is `null` for local plugins or when the catalog omits it. This field is separate from `summary.shareContext`, which continues to describe user and workspace sharing state. Returned plugin skills include their current `enabled` state after local config filtering; bundled hooks are returned as lightweight declaration summaries keyed for correlation with `hooks/list`. Use `plugin/install`'s `appsNeedingAuth` to drive post-install authentication and `app/list`'s `isAccessible` to determine current connector accessibility (**under development; do not call from production clients yet**).
|
||||
- `plugin/skill/read` — read remote plugin skill markdown on demand by `remoteMarketplaceName`, `remotePluginId`, and `skillName`. This lets clients preview uninstalled remote plugin skills without downloading the plugin bundle.
|
||||
|
||||
@@ -167,6 +167,7 @@ fn convert_configured_marketplace_plugin_to_plugin_summary(
|
||||
source: marketplace_plugin_source_to_info(plugin.source),
|
||||
install_policy: plugin.policy.installation.into(),
|
||||
install_policy_source: None,
|
||||
must_show_installation_interstitial: None,
|
||||
auth_policy: plugin.policy.authentication.into(),
|
||||
availability: PluginAvailability::Available,
|
||||
interface: plugin.interface.map(local_plugin_interface_to_info),
|
||||
@@ -1091,6 +1092,7 @@ impl PluginRequestProcessor {
|
||||
enabled: outcome.plugin.enabled,
|
||||
install_policy: outcome.plugin.policy.installation.into(),
|
||||
install_policy_source: None,
|
||||
must_show_installation_interstitial: None,
|
||||
auth_policy: outcome.plugin.policy.authentication.into(),
|
||||
availability: PluginAvailability::Available,
|
||||
interface: outcome.plugin.interface.map(local_plugin_interface_to_info),
|
||||
@@ -2181,6 +2183,7 @@ fn remote_plugin_summary_to_info(summary: RemoteCatalogPluginSummary) -> PluginS
|
||||
enabled: summary.enabled,
|
||||
install_policy: summary.install_policy,
|
||||
install_policy_source: summary.install_policy_source,
|
||||
must_show_installation_interstitial: summary.must_show_installation_interstitial,
|
||||
auth_policy: summary.auth_policy,
|
||||
availability: summary.availability,
|
||||
interface: summary.interface,
|
||||
|
||||
@@ -253,6 +253,8 @@ enabled = true
|
||||
let mut global_installed_body: serde_json::Value = serde_json::from_str(
|
||||
&remote_installed_plugin_body("", "1.2.3", /*enabled*/ true),
|
||||
)?;
|
||||
global_installed_body["plugins"][0]["must_show_installation_interstitial"] =
|
||||
serde_json::json!(false);
|
||||
let mut remote_only = global_installed_body["plugins"][0].clone();
|
||||
remote_only["id"] = serde_json::json!("plugins~Plugin_11111111111111111111111111111111");
|
||||
remote_only["name"] = serde_json::json!("remote-only");
|
||||
@@ -310,16 +312,24 @@ enabled = true
|
||||
remote_marketplace
|
||||
.plugins
|
||||
.iter()
|
||||
.map(|plugin| (plugin.id.clone(), plugin.install_policy_source))
|
||||
.map(|plugin| {
|
||||
(
|
||||
plugin.id.clone(),
|
||||
plugin.install_policy_source,
|
||||
plugin.must_show_installation_interstitial,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
vec![
|
||||
(
|
||||
"linear@openai-curated-remote".to_string(),
|
||||
Some(PluginInstallPolicySource::WorkspaceSetting),
|
||||
Some(false),
|
||||
),
|
||||
(
|
||||
"remote-only@openai-curated-remote".to_string(),
|
||||
Some(PluginInstallPolicySource::WorkspaceSetting),
|
||||
Some(false),
|
||||
),
|
||||
]
|
||||
);
|
||||
@@ -499,6 +509,7 @@ async fn plugin_list_keeps_valid_marketplaces_when_another_marketplace_fails_to_
|
||||
enabled: false,
|
||||
install_policy: PluginInstallPolicy::Available,
|
||||
install_policy_source: None,
|
||||
must_show_installation_interstitial: None,
|
||||
auth_policy: PluginAuthPolicy::OnInstall,
|
||||
availability: codex_app_server_protocol::PluginAvailability::Available,
|
||||
interface: None,
|
||||
@@ -797,6 +808,7 @@ async fn plugin_list_uses_alternate_discoverable_manifest_and_keeps_undiscoverab
|
||||
enabled: false,
|
||||
install_policy: PluginInstallPolicy::Available,
|
||||
install_policy_source: None,
|
||||
must_show_installation_interstitial: None,
|
||||
auth_policy: PluginAuthPolicy::OnInstall,
|
||||
availability: codex_app_server_protocol::PluginAvailability::Available,
|
||||
interface: Some(codex_app_server_protocol::PluginInterface {
|
||||
@@ -838,6 +850,7 @@ async fn plugin_list_uses_alternate_discoverable_manifest_and_keeps_undiscoverab
|
||||
enabled: false,
|
||||
install_policy: PluginInstallPolicy::Available,
|
||||
install_policy_source: None,
|
||||
must_show_installation_interstitial: None,
|
||||
auth_policy: PluginAuthPolicy::OnInstall,
|
||||
availability: codex_app_server_protocol::PluginAvailability::Available,
|
||||
interface: None,
|
||||
@@ -1753,6 +1766,7 @@ async fn plugin_list_includes_remote_marketplaces_when_remote_plugin_enabled() -
|
||||
"scope": "GLOBAL",
|
||||
"installation_policy": "AVAILABLE",
|
||||
"installation_policy_source": "IMPLICIT_CANONICAL_APP",
|
||||
"must_show_installation_interstitial": true,
|
||||
"authentication_policy": "ON_USE",
|
||||
"status": "ENABLED",
|
||||
"release": {
|
||||
@@ -1793,6 +1807,7 @@ async fn plugin_list_includes_remote_marketplaces_when_remote_plugin_enabled() -
|
||||
"scope": "GLOBAL",
|
||||
"installation_policy": "AVAILABLE",
|
||||
"installation_policy_source": "WORKSPACE_SETTING",
|
||||
"must_show_installation_interstitial": false,
|
||||
"authentication_policy": "ON_USE",
|
||||
"status": "ENABLED",
|
||||
"release": {
|
||||
@@ -1924,6 +1939,10 @@ async fn plugin_list_includes_remote_marketplaces_when_remote_plugin_enabled() -
|
||||
remote_marketplace.plugins[0].install_policy_source,
|
||||
Some(PluginInstallPolicySource::ImplicitCanonicalApp)
|
||||
);
|
||||
assert_eq!(
|
||||
remote_marketplace.plugins[0].must_show_installation_interstitial,
|
||||
Some(true)
|
||||
);
|
||||
assert_eq!(
|
||||
remote_marketplace.plugins[0].availability,
|
||||
codex_app_server_protocol::PluginAvailability::Available
|
||||
@@ -1963,6 +1982,10 @@ async fn plugin_list_includes_remote_marketplaces_when_remote_plugin_enabled() -
|
||||
cached_catalog["plugins"][0]["installation_policy_source"],
|
||||
serde_json::json!("IMPLICIT_CANONICAL_APP")
|
||||
);
|
||||
assert_eq!(
|
||||
cached_catalog["plugins"][0]["must_show_installation_interstitial"],
|
||||
serde_json::json!(true)
|
||||
);
|
||||
assert_eq!(
|
||||
cached_catalog["plugins"][0]["release"]["interface"]["default_prompts"],
|
||||
serde_json::json!(["Create a Linear issue", "Review my Linear projects"])
|
||||
@@ -2057,6 +2080,10 @@ async fn plugin_list_uses_cached_global_remote_catalog_and_refreshes_it() -> Res
|
||||
remote_marketplace.plugins[0].id,
|
||||
"linear@openai-curated-remote"
|
||||
);
|
||||
assert_eq!(
|
||||
remote_marketplace.plugins[0].must_show_installation_interstitial,
|
||||
None
|
||||
);
|
||||
wait_for_remote_plugin_request_count(&server, "/ps/plugins/list", /*expected_count*/ 1).await?;
|
||||
wait_for_cached_remote_catalog_plugin_ids(codex_home.path(), &[cached_remote_plugin_id])
|
||||
.await?;
|
||||
|
||||
@@ -157,6 +157,7 @@ apps = true
|
||||
"scope": "GLOBAL",
|
||||
"installation_policy": "AVAILABLE",
|
||||
"installation_policy_source": "IMPLICIT_CANONICAL_APP",
|
||||
"must_show_installation_interstitial": true,
|
||||
"authentication_policy": "ON_USE",
|
||||
"release": {
|
||||
"version": "1.2.1",
|
||||
@@ -309,6 +310,10 @@ apps = true
|
||||
response.plugin.summary.install_policy_source,
|
||||
Some(PluginInstallPolicySource::ImplicitCanonicalApp)
|
||||
);
|
||||
assert_eq!(
|
||||
response.plugin.summary.must_show_installation_interstitial,
|
||||
Some(true)
|
||||
);
|
||||
assert_eq!(
|
||||
response
|
||||
.plugin
|
||||
|
||||
@@ -184,6 +184,7 @@ async fn plugin_share_save_uploads_local_plugin() -> Result<()> {
|
||||
enabled: true,
|
||||
install_policy: PluginInstallPolicy::Available,
|
||||
install_policy_source: None,
|
||||
must_show_installation_interstitial: Some(false),
|
||||
auth_policy: PluginAuthPolicy::OnUse,
|
||||
availability: codex_app_server_protocol::PluginAvailability::Available,
|
||||
interface: Some(expected_plugin_interface()),
|
||||
@@ -613,6 +614,7 @@ async fn plugin_share_list_returns_created_workspace_plugins() -> Result<()> {
|
||||
enabled: true,
|
||||
install_policy: PluginInstallPolicy::Available,
|
||||
install_policy_source: None,
|
||||
must_show_installation_interstitial: Some(false),
|
||||
auth_policy: PluginAuthPolicy::OnUse,
|
||||
availability: codex_app_server_protocol::PluginAvailability::Available,
|
||||
interface: Some(expected_plugin_interface()),
|
||||
@@ -1230,6 +1232,7 @@ async fn plugin_share_delete_removes_created_workspace_plugin() -> Result<()> {
|
||||
enabled: true,
|
||||
install_policy: PluginInstallPolicy::Available,
|
||||
install_policy_source: None,
|
||||
must_show_installation_interstitial: Some(false),
|
||||
auth_policy: PluginAuthPolicy::OnUse,
|
||||
availability: codex_app_server_protocol::PluginAvailability::Available,
|
||||
interface: Some(expected_plugin_interface()),
|
||||
@@ -1357,6 +1360,7 @@ fn remote_plugin_json(plugin_id: &str) -> serde_json::Value {
|
||||
}
|
||||
],
|
||||
"installation_policy": "AVAILABLE",
|
||||
"must_show_installation_interstitial": false,
|
||||
"authentication_policy": "ON_USE",
|
||||
"release": {
|
||||
"version": "0.1.0",
|
||||
|
||||
@@ -782,6 +782,7 @@ fn remote_installed_plugin_in_marketplace(
|
||||
enabled: true,
|
||||
install_policy: codex_app_server_protocol::PluginInstallPolicy::Available,
|
||||
install_policy_source: None,
|
||||
must_show_installation_interstitial: None,
|
||||
auth_policy: codex_app_server_protocol::PluginAuthPolicy::OnUse,
|
||||
availability: codex_app_server_protocol::PluginAvailability::Available,
|
||||
interface: None,
|
||||
|
||||
@@ -156,6 +156,7 @@ pub struct RemoteInstalledPlugin {
|
||||
pub enabled: bool,
|
||||
pub install_policy: PluginInstallPolicy,
|
||||
pub install_policy_source: Option<PluginInstallPolicySource>,
|
||||
pub must_show_installation_interstitial: Option<bool>,
|
||||
pub auth_policy: PluginAuthPolicy,
|
||||
pub availability: PluginAvailability,
|
||||
pub interface: Option<PluginInterface>,
|
||||
@@ -174,6 +175,7 @@ pub struct RemotePluginSummary {
|
||||
pub enabled: bool,
|
||||
pub install_policy: PluginInstallPolicy,
|
||||
pub install_policy_source: Option<PluginInstallPolicySource>,
|
||||
pub must_show_installation_interstitial: Option<bool>,
|
||||
pub auth_policy: PluginAuthPolicy,
|
||||
pub availability: PluginAvailability,
|
||||
pub interface: Option<PluginInterface>,
|
||||
@@ -572,6 +574,8 @@ struct RemotePluginDirectoryItem {
|
||||
share_principals: Option<Vec<RemotePluginDirectorySharePrincipal>>,
|
||||
installation_policy: PluginInstallPolicy,
|
||||
installation_policy_source: Option<RemotePluginInstallPolicySource>,
|
||||
#[serde(default)]
|
||||
must_show_installation_interstitial: Option<bool>,
|
||||
authentication_policy: PluginAuthPolicy,
|
||||
#[serde(rename = "status", default)]
|
||||
availability: PluginAvailability,
|
||||
@@ -1079,6 +1083,7 @@ pub fn group_remote_installed_plugins_by_marketplaces(
|
||||
enabled: plugin.enabled,
|
||||
install_policy: plugin.install_policy,
|
||||
install_policy_source: plugin.install_policy_source,
|
||||
must_show_installation_interstitial: plugin.must_show_installation_interstitial,
|
||||
auth_policy: plugin.auth_policy,
|
||||
availability: plugin.availability,
|
||||
interface: plugin.interface.clone(),
|
||||
@@ -1516,6 +1521,7 @@ fn build_remote_plugin_summary(
|
||||
install_policy_source: plugin
|
||||
.installation_policy_source
|
||||
.and_then(RemotePluginInstallPolicySource::into_protocol),
|
||||
must_show_installation_interstitial: plugin.must_show_installation_interstitial,
|
||||
auth_policy: plugin.authentication_policy,
|
||||
availability: plugin.availability,
|
||||
interface: remote_plugin_interface_to_info(plugin),
|
||||
@@ -1598,6 +1604,7 @@ fn remote_installed_plugin_to_cache_entry(
|
||||
install_policy_source: plugin
|
||||
.installation_policy_source
|
||||
.and_then(RemotePluginInstallPolicySource::into_protocol),
|
||||
must_show_installation_interstitial: plugin.must_show_installation_interstitial,
|
||||
auth_policy: plugin.authentication_policy,
|
||||
availability: plugin.availability,
|
||||
interface: remote_plugin_interface_to_info(plugin),
|
||||
|
||||
@@ -649,6 +649,7 @@ async fn list_remote_plugin_shares_fetches_created_workspace_plugins() {
|
||||
enabled: false,
|
||||
install_policy: PluginInstallPolicy::Available,
|
||||
install_policy_source: None,
|
||||
must_show_installation_interstitial: None,
|
||||
auth_policy: PluginAuthPolicy::OnUse,
|
||||
availability: PluginAvailability::Available,
|
||||
interface: Some(expected_plugin_interface()),
|
||||
@@ -689,6 +690,7 @@ async fn list_remote_plugin_shares_fetches_created_workspace_plugins() {
|
||||
enabled: true,
|
||||
install_policy: PluginInstallPolicy::Available,
|
||||
install_policy_source: None,
|
||||
must_show_installation_interstitial: None,
|
||||
auth_policy: PluginAuthPolicy::OnUse,
|
||||
availability: PluginAvailability::Available,
|
||||
interface: Some(expected_plugin_interface()),
|
||||
|
||||
@@ -81,6 +81,64 @@ fn installation_policy_source_is_preserved_across_remote_summary_paths() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn installation_interstitial_requirement_is_preserved_across_remote_summary_paths() {
|
||||
let mut directory_plugin = directory_plugin("plugin-linear", "linear");
|
||||
directory_plugin.must_show_installation_interstitial = Some(true);
|
||||
let marketplace = build_remote_marketplace(
|
||||
REMOTE_GLOBAL_MARKETPLACE_NAME,
|
||||
REMOTE_GLOBAL_MARKETPLACE_DISPLAY_NAME,
|
||||
vec![directory_plugin.clone()],
|
||||
Vec::new(),
|
||||
/*include_installed_only*/ false,
|
||||
)
|
||||
.expect("marketplace should be valid")
|
||||
.expect("marketplace should not be empty");
|
||||
assert_eq!(
|
||||
marketplace
|
||||
.plugins
|
||||
.into_iter()
|
||||
.map(|plugin| plugin.must_show_installation_interstitial)
|
||||
.collect::<Vec<_>>(),
|
||||
vec![Some(true)]
|
||||
);
|
||||
|
||||
directory_plugin.must_show_installation_interstitial = Some(false);
|
||||
let installed_plugin = remote_installed_plugin_to_cache_entry(&RemotePluginInstalledItem {
|
||||
plugin: directory_plugin,
|
||||
enabled: true,
|
||||
disabled_skill_names: Vec::new(),
|
||||
})
|
||||
.expect("installed plugin should be valid");
|
||||
let marketplaces = group_remote_installed_plugins_by_marketplaces(
|
||||
&[installed_plugin],
|
||||
&[REMOTE_GLOBAL_MARKETPLACE_NAME],
|
||||
);
|
||||
assert_eq!(
|
||||
marketplaces
|
||||
.into_iter()
|
||||
.flat_map(|marketplace| marketplace.plugins)
|
||||
.map(|plugin| plugin.must_show_installation_interstitial)
|
||||
.collect::<Vec<_>>(),
|
||||
vec![Some(false)]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_installation_interstitial_requirement_deserializes_to_none() {
|
||||
let plugin = directory_plugin("plugin-linear", "linear");
|
||||
let mut plugin_json = serde_json::to_value(plugin).expect("plugin should serialize");
|
||||
plugin_json
|
||||
.as_object_mut()
|
||||
.expect("plugin should serialize to an object")
|
||||
.remove("must_show_installation_interstitial");
|
||||
|
||||
let plugin: RemotePluginDirectoryItem =
|
||||
serde_json::from_value(plugin_json).expect("missing requirement should deserialize");
|
||||
|
||||
assert_eq!(plugin.must_show_installation_interstitial, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_installation_policy_source_maps_to_none() {
|
||||
let plugin = directory_plugin("plugin-linear", "linear");
|
||||
@@ -126,6 +184,7 @@ fn directory_plugin(id: &str, name: &str) -> RemotePluginDirectoryItem {
|
||||
share_principals: None,
|
||||
installation_policy: PluginInstallPolicy::Available,
|
||||
installation_policy_source: None,
|
||||
must_show_installation_interstitial: None,
|
||||
authentication_policy: PluginAuthPolicy::OnUse,
|
||||
availability: PluginAvailability::Available,
|
||||
release: RemotePluginReleaseResponse {
|
||||
|
||||
@@ -174,6 +174,7 @@ mod tests {
|
||||
enabled: true,
|
||||
install_policy: PluginInstallPolicy::Available,
|
||||
install_policy_source: None,
|
||||
must_show_installation_interstitial: None,
|
||||
auth_policy: PluginAuthPolicy::OnInstall,
|
||||
availability: PluginAvailability::Available,
|
||||
interface: None,
|
||||
|
||||
@@ -1342,6 +1342,7 @@ pub(super) fn plugins_test_summary(
|
||||
enabled,
|
||||
install_policy,
|
||||
install_policy_source: None,
|
||||
must_show_installation_interstitial: None,
|
||||
auth_policy: PluginAuthPolicy::OnInstall,
|
||||
availability: PluginAvailability::Available,
|
||||
interface: Some(plugins_test_interface(
|
||||
@@ -1372,6 +1373,7 @@ pub(super) fn plugins_test_remote_summary(
|
||||
enabled: true,
|
||||
install_policy: PluginInstallPolicy::Available,
|
||||
install_policy_source: None,
|
||||
must_show_installation_interstitial: None,
|
||||
auth_policy: PluginAuthPolicy::OnInstall,
|
||||
availability: PluginAvailability::Available,
|
||||
interface: Some(plugins_test_interface(
|
||||
|
||||
Reference in New Issue
Block a user