fix(ticket116): isoler crash plugin hello-plugin + erreur explicite UI

This commit is contained in:
2026-07-31 14:17:56 +02:00
parent e042ced724
commit d4e61a86f0
9 changed files with 417 additions and 64 deletions

View File

@ -302,7 +302,113 @@ fn open_folder(path: &PathBuf) -> Result<(), ErrorDto> {
#[cfg(test)]
mod tests {
use super::block_on_protocol_future;
use super::{asset_allowed, block_on_protocol_future};
use std::sync::Mutex;
use async_trait::async_trait;
use domain::ports::{
PluginManifestBytes, PluginManifestError, PluginPackageStore, PluginRegistryError,
PluginRegistryStore, PluginStoreError,
};
use domain::{
ContentHash, LocalPath, PluginId, PluginInstallSource, PluginLifecycleState,
PluginRegistry, PluginRegistryEntry, RelativePath, RemovalOutcome, StagedPluginPackage,
};
use http::StatusCode;
struct FakeRegistry {
registry: Mutex<PluginRegistry>,
}
#[async_trait]
impl PluginRegistryStore for FakeRegistry {
async fn load_registry(&self) -> Result<PluginRegistry, PluginRegistryError> {
Ok(self.registry.lock().unwrap().clone())
}
async fn save_registry(
&self,
registry: &PluginRegistry,
) -> Result<(), PluginRegistryError> {
*self.registry.lock().unwrap() = registry.clone();
Ok(())
}
}
struct FakePackages {
manifest: Vec<u8>,
}
#[async_trait]
impl PluginPackageStore for FakePackages {
async fn list_installed(&self) -> Result<Vec<domain::PluginPackageRef>, PluginStoreError> {
Ok(Vec::new())
}
async fn read_manifest(
&self,
_package: &domain::PluginPackageRef,
) -> Result<PluginManifestBytes, PluginStoreError> {
Ok(PluginManifestBytes {
bytes: self.manifest.clone(),
})
}
async fn install_from_archive(
&self,
_archive: &LocalPath,
) -> Result<StagedPluginPackage, PluginStoreError> {
Err(PluginStoreError::Invalid("not used".to_owned()))
}
async fn install_from_directory(
&self,
_dir: &LocalPath,
) -> Result<StagedPluginPackage, PluginStoreError> {
Err(PluginStoreError::Invalid("not used".to_owned()))
}
async fn commit_install(
&self,
_staged: StagedPluginPackage,
_plugin_id: &PluginId,
) -> Result<domain::PluginPackageRef, PluginStoreError> {
Err(PluginStoreError::Invalid("not used".to_owned()))
}
async fn remove_package(
&self,
_plugin_id: &PluginId,
) -> Result<RemovalOutcome, PluginStoreError> {
Ok(RemovalOutcome::NotFound)
}
fn bundle_url(
&self,
plugin_id: &PluginId,
entry: &RelativePath,
hash: &ContentHash,
) -> Result<domain::PluginBundleUrl, PluginStoreError> {
Ok(domain::PluginBundleUrl::new(format!(
"idea-plugin://{}/current/{}/{}",
plugin_id.as_str(),
hash.as_str(),
entry.as_str()
)))
}
}
struct RejectingValidator;
impl domain::ports::PluginManifestValidator for RejectingValidator {
fn validate(
&self,
_bytes: &[u8],
_package: &domain::PluginPackageRef,
) -> Result<domain::PluginManifest, PluginManifestError> {
Err(PluginManifestError::Invalid("broken manifest".to_owned()))
}
}
#[tokio::test(flavor = "multi_thread")]
async fn protocol_future_can_be_waited_inside_tauri_runtime() {
@ -310,4 +416,43 @@ mod tests {
assert_eq!(value, 42);
}
#[tokio::test]
async fn asset_allowed_confines_invalid_active_manifest_to_forbidden() {
let plugin_id = PluginId::new("dev.acme.gitgraph").unwrap();
let hash = ContentHash::new("abc123").unwrap();
let registry = FakeRegistry {
registry: Mutex::new(PluginRegistry {
version: 1,
plugins: vec![PluginRegistryEntry {
id: plugin_id.clone(),
lifecycle_state: PluginLifecycleState::Enabled,
source: PluginInstallSource::Directory {
path_label: "/source/plugin".to_owned(),
},
content_hash: hash.clone(),
restart_required: false,
error: None,
}],
}),
};
let packages = FakePackages {
manifest: br#"{"broken":true}"#.to_vec(),
};
let rel = RelativePath::new("dist/index.js").unwrap();
let err = asset_allowed(
&plugin_id,
hash.as_str(),
&rel,
&registry,
&packages,
&RejectingValidator,
)
.await
.unwrap_err();
assert_eq!(err.0, StatusCode::FORBIDDEN);
assert!(err.1.contains("broken manifest"));
}
}