Schließt die MCP-Server-Spec ab. "Worksheets" aus der Spec entsprechen im tatsächlichen Datenmodell den .lavorlage-Elternbrief-Vorlagen (LehrerApp.Templating) - es gibt kein separates Arbeitsblatt-Konzept mit Fach/Klassenstufe-Metadaten. Neue Tools list_letter_templates (Read) und render_letter (Read, liefert Base64-PDF, kein DB-Schreibzugriff). upload_worksheet/update_worksheet bewusst nicht umgesetzt: das Seitenlayout ist eine eigene positionsbasierte DSL mit eigenem visuellen Editor (LehrerApp.TemplateDesigner) - ein LLM müsste sie blind erzeugen, mit hohem Risiko für kaputte Layouts. Platzhalter-Logik aus CreateLetterDialogViewModel nach LetterPlaceholderBuilder extrahiert, damit Dialog und MCP-Tool nicht auseinanderdriften. Neuer McpClientRegistrationService trägt den Bridge-Pfad in Claude Desktops claude_desktop_config.json ein (Button in den Einstellungen, nie automatisch), ohne bestehende Fremdeinträge zu verlieren und ohne eine nicht lesbare Konfigurationsdatei zu überschreiben. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
97 lines
3.5 KiB
C#
97 lines
3.5 KiB
C#
using System.Text.Json.Nodes;
|
|
using LehrerApp.Desktop.Services.Mcp;
|
|
using Xunit;
|
|
|
|
namespace LehrerApp.Desktop.Tests;
|
|
|
|
public sealed class McpClientRegistrationServiceTests : IDisposable
|
|
{
|
|
private readonly string _directory = Path.Combine(Path.GetTempPath(), $"lehrerapp-mcpreg-tests-{Guid.NewGuid():N}");
|
|
public McpClientRegistrationServiceTests() => Directory.CreateDirectory(_directory);
|
|
public void Dispose() { if (Directory.Exists(_directory)) Directory.Delete(_directory, true); }
|
|
|
|
private string ConfigPath => Path.Combine(_directory, "claude_desktop_config.json");
|
|
|
|
private string BuildFakeBridge()
|
|
{
|
|
var bridgePath = Path.Combine(_directory, "LehrerApp.McpBridge.exe");
|
|
File.WriteAllText(bridgePath, "fake");
|
|
return bridgePath;
|
|
}
|
|
|
|
[Fact]
|
|
public void Register_OhneBridgeDatei_LiefertFehlerUndSchreibtNichts()
|
|
{
|
|
var service = new McpClientRegistrationService(ConfigPath, Path.Combine(_directory, "fehlt.exe"));
|
|
|
|
var result = service.Register();
|
|
|
|
Assert.False(result.Success);
|
|
Assert.False(File.Exists(ConfigPath));
|
|
}
|
|
|
|
[Fact]
|
|
public void Register_OhneBestehendeKonfiguration_LegtDateiMitEintragAn()
|
|
{
|
|
var bridgePath = BuildFakeBridge();
|
|
var service = new McpClientRegistrationService(ConfigPath, bridgePath);
|
|
|
|
var result = service.Register();
|
|
|
|
Assert.True(result.Success);
|
|
Assert.True(service.IsRegistered());
|
|
var root = JsonNode.Parse(File.ReadAllText(ConfigPath))!;
|
|
Assert.Equal(bridgePath, root["mcpServers"]!["lehrerapp"]!["command"]!.GetValue<string>());
|
|
}
|
|
|
|
[Fact]
|
|
public void Register_BestehendeKonfigurationMitAnderenServern_BleibtErhalten()
|
|
{
|
|
File.WriteAllText(ConfigPath, """{"mcpServers":{"andererServer":{"command":"foo"}},"globalShortcut":"Ctrl+X"}""");
|
|
var bridgePath = BuildFakeBridge();
|
|
var service = new McpClientRegistrationService(ConfigPath, bridgePath);
|
|
|
|
var result = service.Register();
|
|
|
|
Assert.True(result.Success);
|
|
var root = JsonNode.Parse(File.ReadAllText(ConfigPath))!;
|
|
Assert.Equal("foo", root["mcpServers"]!["andererServer"]!["command"]!.GetValue<string>());
|
|
Assert.Equal("Ctrl+X", root["globalShortcut"]!.GetValue<string>());
|
|
Assert.Equal(bridgePath, root["mcpServers"]!["lehrerapp"]!["command"]!.GetValue<string>());
|
|
}
|
|
|
|
[Fact]
|
|
public void Register_KaputteBestehendeKonfiguration_WirdNichtUeberschrieben()
|
|
{
|
|
File.WriteAllText(ConfigPath, "{ das ist kein json");
|
|
var bridgePath = BuildFakeBridge();
|
|
var service = new McpClientRegistrationService(ConfigPath, bridgePath);
|
|
|
|
var result = service.Register();
|
|
|
|
Assert.False(result.Success);
|
|
Assert.Equal("{ das ist kein json", File.ReadAllText(ConfigPath));
|
|
}
|
|
|
|
[Fact]
|
|
public void Unregister_EntferntNurEigenenEintrag()
|
|
{
|
|
File.WriteAllText(ConfigPath, """{"mcpServers":{"andererServer":{"command":"foo"},"lehrerapp":{"command":"bar"}}}""");
|
|
var service = new McpClientRegistrationService(ConfigPath, BuildFakeBridge());
|
|
|
|
var result = service.Unregister();
|
|
|
|
Assert.True(result.Success);
|
|
Assert.False(service.IsRegistered());
|
|
Assert.Contains("andererServer", File.ReadAllText(ConfigPath));
|
|
}
|
|
|
|
[Fact]
|
|
public void IsRegistered_OhneDatei_IstFalse()
|
|
{
|
|
var service = new McpClientRegistrationService(ConfigPath, BuildFakeBridge());
|
|
|
|
Assert.False(service.IsRegistered());
|
|
}
|
|
}
|