using System.Text.Json; using System.Text.Json.Serialization; using Microsoft.VisualStudio.TestTools.UnitTesting; using YMhut.Box.Core.Tools; namespace YMhut.Box.Tests; [TestClass] public sealed class ToolWebPayloadSerializationTests { private static readonly JsonSerializerOptions Options = CreateOptions(); [TestMethod] public void ToolPagePayloadSerializesRulesParametersAndActions() { var catalog = new ToolCatalog(); var module = catalog.GetById("json_formatter")!; var spec = ToolPageSpecCatalog.For(module); var experiences = new ToolResultExperienceCatalog(catalog); Assert.IsNotEmpty(spec.Rules); var payload = new ToolPageWebPayload( module.Id, module.Metadata.Name, module.Metadata.Description, spec, experiences.GetRequiredPage(module.Id), new ToolInputState( """{"name":"YMhut"}""", [new ToolInputField("input", "Input", "textarea", """{"name":"YMhut"}""")], new Dictionary(StringComparer.OrdinalIgnoreCase) { ["mode"] = "format" }, new Dictionary(StringComparer.OrdinalIgnoreCase) { ["hint"] = "Enter values or choose options, then run the tool." }), "Light", "zh-CN", new ToolResultPrivacyPolicy(["update.ymhut.cn"]), new ToolPageRuntimeMetadata(DateTimeOffset.UnixEpoch, AutoRun: false, OfflineCapable: true, "Dev", "\uE943"), ["run", "copyResult", "showRaw"]); var json = JsonSerializer.Serialize(payload, Options); var roundTrip = JsonSerializer.Deserialize(json, Options); Assert.IsNotNull(roundTrip); Assert.AreEqual(module.Id, roundTrip.ToolId); Assert.IsNotEmpty(roundTrip.Spec.Rules); Assert.IsNotEmpty(roundTrip.Spec.Parameters); Assert.AreEqual("format", roundTrip.Input.Rules["mode"]); CollectionAssert.Contains(roundTrip.AvailableActions.ToList(), "copyResult"); } [TestMethod] public void ToolResultPayloadSerializesRepresentativeBlockKinds() { var catalog = new ToolCatalog(); var module = catalog.GetById("hotboard")!; var experiences = new ToolResultExperienceCatalog(catalog); var blocks = new[] { ToolResultBlock.KeyValue("Details", [new("Key", "Value")]), ToolResultBlock.Table("Table", [new[] { "Name", "Value" }, new[] { "YMhut", "Box" }]), ToolResultBlock.List(ToolResultBlockKind.RankedList, "Ranking", [new("1", "First", "Trend", "ok", "https://example.com")]), ToolResultBlock.List(ToolResultBlockKind.NewsList, "News", [new("1", "Headline", "Source", "info", "https://example.com/news")]), ToolResultBlock.File("File", "report.txt", @"C:\Temp\report.txt"), ToolResultBlock.Link("Link", "YMhut", "https://example.com"), ToolResultBlock.Media(ToolResultBlockKind.Media, "Media", "video", "https://example.com/video.mp4"), ToolResultBlock.Json("JSON", """{"ok":true}"""), ToolResultBlock.List(ToolResultBlockKind.Status, "Status", [new("OK", "Ready", "", "ok", "")]) }; var document = new ToolResultDocument( module.Id, ToolResultKind.RankedList, "raw output", blocks, new Dictionary(StringComparer.OrdinalIgnoreCase) { ["displayProfile"] = "cards" }, "raw output", "test", "ok"); var payload = new ToolResultWebPayload( module.Id, module.Metadata.Name, document, experiences.GetRequired(module.Id).ExperienceId, "Dark", "en-US", new ToolResultPrivacyPolicy(["update.ymhut.cn"]), new ToolResultRuntimeMetadata(42, DateTimeOffset.UnixEpoch, Cached: false, "test", new Dictionary()), experiences.GetRequired(module.Id)); var json = JsonSerializer.Serialize(payload, Options); var roundTrip = JsonSerializer.Deserialize(json, Options); Assert.IsNotNull(roundTrip); Assert.HasCount(blocks.Length, roundTrip.ResultDocument.Blocks); CollectionAssert.Contains(roundTrip.ResultDocument.Blocks.Select(block => block.Kind).ToList(), ToolResultBlockKind.RankedList); CollectionAssert.Contains(roundTrip.ResultDocument.Blocks.Select(block => block.Kind).ToList(), ToolResultBlockKind.Media); StringAssert.Contains(json, nameof(ToolResultBlockKind.RankedList)); } private static JsonSerializerOptions CreateOptions() { var options = new JsonSerializerOptions(JsonSerializerDefaults.Web); options.Converters.Add(new JsonStringEnumConverter()); return options; } }