fix(plugin-sdk): aligne le manifeste hello-plugin sur le schéma backend + fixture de test

Le manifeste hello-plugin était généré par un SDK obsolète/désaligné avec le
schéma backend actuel (champ ideaPluginManifestVersion manquant, displayName,
trustLevel, shape de contributes), ce qui faisait échouer l'installation avec
« invalid input: missing field ideaPluginManifestVersion ».

- SDK (manifest.ts/js, index.ts) aligné sur le schéma backend courant.
- hello-plugin (exemple + fixture) régénéré avec le manifeste valide.
- Base de tests fonctionnels plugins : fixture réelle versionnée
  (reference-minimal) + test d'installation/chargement bout en bout
  (plugin_install_load.rs), pour couvrir install/load/catalog depuis un
  dossier fixture réel.

Le sous-repo git imbriqué et vide (sdk/IdeaSDK/.git, 0 commit) a été
neutralisé pour que les sources du SDK soient suivies normalement dans ce
dépôt plutôt que comme gitlink vide.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-29 10:00:44 +02:00
parent c1ff98086c
commit 6270f98e54
22 changed files with 1113 additions and 0 deletions

View File

@ -0,0 +1,3 @@
export function activate(ctx) {
ctx.commands?.registerCommand("dev.idea.fixtures.reference-minimal.hello", () => "hello");
}

View File

@ -0,0 +1,33 @@
{
"ideaPluginManifestVersion": 1,
"id": "dev.idea.fixtures.reference-minimal",
"displayName": "Reference Minimal Plugin",
"publisher": "IdeA QA",
"version": "0.1.0",
"description": "Minimal fixture for the real plugin install/load loop.",
"engines": {
"idea": ">=0.1.0 <1.0.0"
},
"main": "dist/index.js",
"trustLevel": "full",
"capabilities": [
"ui"
],
"contributes": {
"menus": [
{
"id": "dev.idea.fixtures.reference-minimal.menu",
"label": "Reference",
"topLevel": true
}
],
"menuItems": [
{
"id": "dev.idea.fixtures.reference-minimal.hello.item",
"targetMenuId": "dev.idea.fixtures.reference-minimal.menu",
"label": "Say Hello",
"command": "dev.idea.fixtures.reference-minimal.hello"
}
]
}
}

View File

@ -0,0 +1,92 @@
use std::fs;
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use application::{
InstallPluginFromDirectory, JsonPluginManifestValidator, ListPluginRuntimeContributions,
ListPlugins,
};
use infrastructure::{
ExternalMcpPluginSupervisor, FsPluginPackageStore, FsPluginRegistryStore,
TokioBroadcastEventBus,
};
static TEMP_COUNTER: AtomicU64 = AtomicU64::new(0);
fn temp_dir(label: &str) -> PathBuf {
let n = TEMP_COUNTER.fetch_add(1, Ordering::SeqCst);
let path = std::env::temp_dir().join(format!(
"idea-plugin-functional-{label}-{}-{n}",
std::process::id()
));
let _ = fs::remove_dir_all(&path);
fs::create_dir_all(&path).unwrap();
path
}
fn fixture_path(name: &str) -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("tests")
.join("fixtures")
.join("plugins")
.join(name)
}
#[tokio::test]
async fn installs_reference_fixture_and_loads_runtime_catalog() {
let app_data = temp_dir("app-data");
let fixture = fixture_path("reference-minimal");
let packages = Arc::new(FsPluginPackageStore::new(&app_data));
let registry = Arc::new(FsPluginRegistryStore::new(&app_data));
let validator = Arc::new(JsonPluginManifestValidator::new("0.3.0"));
let events = Arc::new(TokioBroadcastEventBus::new());
let mcp = Arc::new(ExternalMcpPluginSupervisor::new());
let install = InstallPluginFromDirectory::new(
packages.clone(),
registry.clone(),
validator.clone(),
events,
mcp.clone(),
);
let result = install
.execute(fixture.to_string_lossy().into_owned())
.await
.unwrap();
assert_eq!(result.plugin.id, "dev.idea.fixtures.reference-minimal");
assert_eq!(result.plugin.display_name, "Reference Minimal Plugin");
assert_eq!(result.review.contribution_summary.top_level_menus, 1);
assert_eq!(result.review.contribution_summary.menu_items, 1);
assert!(result.restart_required);
assert!(app_data
.join("plugins/installed/dev.idea.fixtures.reference-minimal/idea-plugin.json")
.is_file());
let admin = ListPlugins::new(packages.clone(), registry.clone(), validator.clone())
.execute()
.await
.unwrap();
assert_eq!(admin.len(), 1);
assert_eq!(admin[0].id, "dev.idea.fixtures.reference-minimal");
assert_eq!(
admin[0].lifecycle_state,
domain::PluginLifecycleState::Enabled
);
let catalog = ListPluginRuntimeContributions::new(packages, registry, validator)
.execute()
.await
.unwrap();
assert_eq!(catalog.plugins.len(), 1);
let plugin = &catalog.plugins[0];
assert_eq!(plugin.id, "dev.idea.fixtures.reference-minimal");
assert!(plugin
.bundle_url
.starts_with("idea-plugin://dev.idea.fixtures.reference-minimal/0.1.0/"));
assert_eq!(plugin.contributes.menus.len(), 1);
assert_eq!(plugin.contributes.menu_items.len(), 1);
let _ = fs::remove_dir_all(app_data);
}