From 7ab79b491ee2470e0f44eba395d9fdaed602811e Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Tue, 3 Nov 2026 15:02:51 +0100 Subject: [PATCH] Checked every JSON type of every options of the app in the trim smoke test --- app/TrimSmokeTest/JsonContractWalker.cs | 5 +- app/TrimSmokeTest/JsonRoots.cs | 79 ++++++++----------------- app/TrimSmokeTest/Program.cs | 5 +- app/TrimSmokeTest/TrimSmokeTest.csproj | 10 ++++ 4 files changed, 43 insertions(+), 56 deletions(-) diff --git a/app/TrimSmokeTest/JsonContractWalker.cs b/app/TrimSmokeTest/JsonContractWalker.cs index 73af51fb2..392087164 100644 --- a/app/TrimSmokeTest/JsonContractWalker.cs +++ b/app/TrimSmokeTest/JsonContractWalker.cs @@ -45,7 +45,10 @@ public static class JsonContractWalker return; } - foreach (var property in typeInfo.Properties) + // + // System.Text.Json never meets the type of an ignored property, e.g., one marked with JsonIgnore: + // + foreach (var property in typeInfo.Properties.Where(property => property is not { Get: null, Set: null, AssociatedParameter: null })) Visit(property.PropertyType, $"{path} → {property.Name}", options, visited, failures); if (typeInfo.KeyType is { } keyType) diff --git a/app/TrimSmokeTest/JsonRoots.cs b/app/TrimSmokeTest/JsonRoots.cs index 909f3d79b..578232cb3 100644 --- a/app/TrimSmokeTest/JsonRoots.cs +++ b/app/TrimSmokeTest/JsonRoots.cs @@ -1,65 +1,38 @@ using System.Text.Json; - -using AIStudio.Agents; -using AIStudio.Agents.AssistantAudit; -using AIStudio.Assistants.Builder; -using AIStudio.Assistants.PromptOptimizer; -using AIStudio.Assistants.SWOTAnalysis; -using AIStudio.Assistants.VisualBriefing; -using AIStudio.Chat; -using AIStudio.Settings; -using AIStudio.Settings.DataModel; -using AIStudio.Settings.DataModel.PreviousModels; -using AIStudio.Tools; -using AIStudio.Tools.DataBackup; +using System.Text.Json.Serialization; namespace AIStudio.TrimSmokeTest; /// -/// The types which AI Studio reads or writes as JSON on their own, not only as part of another type. +/// The types which AI Studio reads or writes as JSON, each with the options it uses for them. /// /// -/// Whatever these types hold, i.e., the types of their properties, the elements of their collections, and -/// their derived types, gets checked along with them, so only the outermost types belong here. Add a type -/// whenever AI Studio starts to store a new kind of file or to read a new kind of model answer.

-/// -/// The answers of models are read with options of their own, which are private and only change names and -/// leniency. The default options build the same contracts for the types of .NET they hold. A type which is -/// private to its parser, such as the DTO of a slide deck, is out of reach here. +/// The options of AI Studio each name the context which knows their types, so the types registered in that +/// context are exactly those which System.Text.Json gets to read or write with these options. Whatever these +/// types hold, i.e., the types of their properties, the elements of their collections, and their derived +/// types, gets checked along with them. A new type, or new options, need nothing here: they are found +/// through their context. ///
public static class JsonRoots { - public static readonly IReadOnlyList ALL = - [ - // Chats, in the workspaces, among the temporary chats, and in chat archives: - new(typeof(ChatThread), WorkspaceBehaviour.JSON_OPTIONS), + /// + /// Returns every type registered in a context of the options of AI Studio, together with these options. + /// + /// + /// Options which several members share are checked once. Like AppJsonOptions.FindAll, this needs the + /// static state of the app to be filled before. + /// + public static IReadOnlyList FindAll() => AppJsonOptions.FindAll().Values + .Distinct(ReferenceEqualityComparer.Instance) + .Cast() + .SelectMany(options => options.TypeInfoResolverChain + .OfType() + .SelectMany(RegisteredTypes) + .Select(type => new JsonRoot(type, options))) + .ToList(); - // The settings, and their older versions, which the migrations still read: - new(typeof(Data), SettingsManager.JSON_OPTIONS), - new(typeof(DataV1V3), SettingsManager.JSON_OPTIONS), - new(typeof(DataV4), SettingsManager.JSON_OPTIONS), - new(typeof(DataV5), SettingsManager.JSON_OPTIONS), - - // The projects of the Visual Briefing assistant: - new(typeof(VisualBriefingManifest), VisualBriefingJson.Persistence), - new(typeof(VisualBriefingBuildRecord), VisualBriefingJson.Persistence), - new(typeof(VisualBriefingEvidenceArtifact), VisualBriefingJson.Persistence), - new(typeof(VisualBriefingPlanArtifact), VisualBriefingJson.Persistence), - new(typeof(VisualBriefingContentArtifact), VisualBriefingJson.Persistence), - new(typeof(VisualBriefingPresentationArtifact), VisualBriefingJson.Persistence), - - // Backups and their restore: - new(typeof(DataBackupManifest), DataBackupFormat.JSON_OPTIONS), - new(typeof(DataRestorePlan), DataBackupFormat.JSON_OPTIONS), - new(typeof(DataRestoreReport), DataBackupFormat.JSON_OPTIONS), - - // Answers of models: - new(typeof(LuaResponse), JsonSerializerOptions.Default), - new(typeof(LauncherTextsResponse), JsonSerializerOptions.Default), - new(typeof(PromptOptimizationResult), JsonSerializerOptions.Default), - new(typeof(RetrievalContextValidationResult), JsonSerializerOptions.Default), - new(typeof(AssistantAuditResult), JsonSerializerOptions.Default), - new(typeof(SwotCategoryAnalysisResult), JsonSerializerOptions.Default), - new(typeof(SwotModerationResult), JsonSerializerOptions.Default), - ]; + private static IEnumerable RegisteredTypes(JsonSerializerContext context) => context.GetType() + .GetCustomAttributesData() + .Where(attribute => attribute.AttributeType == typeof(JsonSerializableAttribute)) + .Select(attribute => (Type)attribute.ConstructorArguments[0].Value!); } \ No newline at end of file diff --git a/app/TrimSmokeTest/Program.cs b/app/TrimSmokeTest/Program.cs index 3309fc329..44a531924 100644 --- a/app/TrimSmokeTest/Program.cs +++ b/app/TrimSmokeTest/Program.cs @@ -15,14 +15,15 @@ public static class Program // AIStudio.Program.LOGGER_FACTORY = NullLoggerFactory.Instance; + var roots = JsonRoots.FindAll(); var failures = new List(); - foreach (var root in JsonRoots.ALL) + foreach (var root in roots) failures.AddRange(JsonContractWalker.FindBrokenContracts(root)); failures.AddRange(JsonRoundTrips.FindLosses()); if (failures.Count is 0) { - Console.WriteLine($"- All {JsonRoots.ALL.Count} JSON roots can be read and written."); + Console.WriteLine($"- All {roots.Count} JSON roots can be read and written."); return 0; } diff --git a/app/TrimSmokeTest/TrimSmokeTest.csproj b/app/TrimSmokeTest/TrimSmokeTest.csproj index 02b77017b..5a39621af 100644 --- a/app/TrimSmokeTest/TrimSmokeTest.csproj +++ b/app/TrimSmokeTest/TrimSmokeTest.csproj @@ -21,6 +21,16 @@ + + + + +