refactor(frontend,plugins): normalise les DTOs review backend et corrige le mock (#120)

- plugin.ts: normalizeReview() garantit issues/installable définis même si backend omet ces champs
- mock/index.ts: MockPluginGateway.uninstall() retourne removalOutcome pour cohérence avec le domaine
- domain/index.ts: PluginUninstallResult ajoute removalOutcome optionnel
- Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-01 19:12:16 +02:00
parent 5baf5821d4
commit a88307c5ff
4 changed files with 42 additions and 3 deletions

View File

@ -3523,7 +3523,7 @@ export class MockPluginGateway implements PluginGateway {
throw err; throw err;
} }
this.plugins.splice(idx, 1); this.plugins.splice(idx, 1);
return { pluginId, restartRequired: true }; return { pluginId, removalOutcome: "removed", restartRequired: true };
} }
async listRuntimeContributions(): Promise<PluginRuntimeContributionCatalog> { async listRuntimeContributions(): Promise<PluginRuntimeContributionCatalog> {

View File

@ -23,4 +23,28 @@ describe("TauriPluginGateway invoke payloads", () => {
}, },
}); });
}); });
it("normalizes backend review DTOs for the Plugins panel view-model", async () => {
invoke.mockResolvedValueOnce({
id: "dev.acme.gitgraph",
displayName: "Git Graph",
publisher: "Acme",
version: "1.2.3",
description: "Graph",
sourceKind: "archive",
sourceLabel: "/tmp/gitgraph.ideaplug",
contentHash: "abc",
trustLevel: "full",
contributionSummary: { topLevelMenus: 1, menuItems: 2, layouts: 3, mcpServers: 4 },
contributes: { menus: [], menuItems: [], layouts: [], mcpServers: [] },
});
const review = await new TauriPluginGateway().reviewPackage({
sourceKind: "archive",
path: "/tmp/gitgraph.ideaplug",
});
expect(review.issues).toEqual([]);
expect(review.installable).toBe(true);
});
}); });

View File

@ -21,15 +21,29 @@ import type {
} from "@/domain"; } from "@/domain";
import type { PluginGateway, ReviewPluginPackageInput } from "@/ports"; import type { PluginGateway, ReviewPluginPackageInput } from "@/ports";
type TauriPluginReviewDto = Omit<PluginReview, "issues" | "installable"> & {
issues?: PluginReview["issues"];
installable?: boolean;
};
function normalizeReview(review: TauriPluginReviewDto): PluginReview {
return {
...review,
issues: review.issues ?? [],
installable: review.installable ?? true,
};
}
export class TauriPluginGateway implements PluginGateway { export class TauriPluginGateway implements PluginGateway {
listPlugins(): Promise<PluginAdmin[]> { listPlugins(): Promise<PluginAdmin[]> {
return invoke<PluginAdmin[]>("plugin_list_plugins"); return invoke<PluginAdmin[]>("plugin_list_plugins");
} }
reviewPackage(input: ReviewPluginPackageInput): Promise<PluginReview> { async reviewPackage(input: ReviewPluginPackageInput): Promise<PluginReview> {
return invoke<PluginReview>("plugin_review_package", { const review = await invoke<TauriPluginReviewDto>("plugin_review_package", {
input: { sourceKind: input.sourceKind, path: input.path }, input: { sourceKind: input.sourceKind, path: input.path },
}); });
return normalizeReview(review);
} }
installFromArchive(path: string): Promise<PluginInstallResult> { installFromArchive(path: string): Promise<PluginInstallResult> {

View File

@ -1730,6 +1730,7 @@ export interface PluginInstallResult {
/** Outcome of `uninstall`. */ /** Outcome of `uninstall`. */
export interface PluginUninstallResult { export interface PluginUninstallResult {
pluginId: string; pluginId: string;
removalOutcome?: "removed" | "tombstoned" | "notFound";
restartRequired: boolean; restartRequired: boolean;
} }