using System.Text.Json;
using AIStudio.Assistants.SWOTAnalysis;
using AIStudio.Chat;
using AIStudio.Settings;
using AIStudio.Settings.DataModel;
using AIStudio.Settings.DataModel.PreviousModels;
using AIStudio.Tools;
using AIStudio.Tools.Databases.ProvenanceStore;
using AIStudio.Tools.DataBackup;
namespace AIStudio.Tests.Serialization;
///
/// The types which AI Studio reads or writes as JSON on their own, for the tests which compare the contracts of
/// System.Text.Json.
///
///
/// Only the outermost types belong here: whatever they hold gets checked along with them. Add a type whenever
/// AI Studio starts to store a new kind of file or to exchange a new kind of message, with the options it uses
/// for them. What AI Studio sends is listed apart from what it receives: only what it reads has to keep the
/// defaults of a new object.
///
public static partial class JsonTestRoots
{
///
/// The data AI Studio keeps on the disk of the user.
///
public static IEnumerable Stored()
{
TestHost.PrepareStaticState();
// Chats, in the workspaces, among the temporary chats, and in chat archives:
yield return Case(new(typeof(ChatThread), WorkspaceBehaviour.JSON_OPTIONS));
// The settings, and their older versions, which the migrations still read:
yield return Case(new(typeof(Data), SettingsManager.JSON_OPTIONS));
yield return Case(new(typeof(DataV1V3), SettingsManager.JSON_OPTIONS));
yield return Case(new(typeof(DataV4), SettingsManager.JSON_OPTIONS));
yield return Case(new(typeof(DataV5), SettingsManager.JSON_OPTIONS));
//
// The values of managed settings, which get written and read on their own, as the type of their
// setting. That type is often an interface, such as ISet, which the settings themselves
// never use:
//
foreach (var valueType in ManagedSettingValueTypes())
yield return Case(new(valueType, SettingsManager.JSON_OPTIONS));
// The SWOT analysis, which a chat stores as the presentation of a message:
yield return Case(new(typeof(SwotAnalysisResult), SwotAnalysisContentPresentation.JSON_OPTIONS), "read");
yield return Case(new(typeof(SwotAnalysisResult), SwotAnalysisContentPresentation.WRITE_OPTIONS), "written");
// Backups and their restore:
yield return Case(new(typeof(DataBackupManifest), DataBackupFormat.JSON_OPTIONS));
yield return Case(new(typeof(DataRestorePlan), DataBackupFormat.JSON_OPTIONS));
yield return Case(new(typeof(DataRestoreReport), DataBackupFormat.JSON_OPTIONS));
// The text around a passage which the provenance store keeps:
yield return Case(new(typeof(IReadOnlyList), SqliteProvenanceStoreClientImplementation.JSON_OPTIONS));
yield return Case(new(typeof(List), SqliteProvenanceStoreClientImplementation.JSON_OPTIONS));
}
private static TestCaseData Case(JsonTestRoot root, string? purpose = null)
{
//
// The same System.Text.Json does with the first call of Serialize or Deserialize. Only then, the
// options hand out contracts:
//
root.Options.MakeReadOnly(populateMissingResolver: true);
return new TestCaseData(root).SetArgDisplayNames(purpose is null ? root.ToString() : $"{root}, {purpose}");
}
///
/// Names the case after its type together with the last part of the namespace, which tells apart types of the
/// same name, e.g., the lists of models of two providers.
///
private static TestCaseData CaseWithNamespace(Type type, JsonSerializerOptions options)
{
var typeNamespace = type.Namespace!;
return Case(new(type, options), typeNamespace[(typeNamespace.LastIndexOf('.') + 1)..]);
}
///
/// Returns the classes and structs of AI Studio which are the given type.
///
///
/// A converter which writes a value as the type it is of meets these types without any property naming them.
/// Listing them all here keeps a new one from going unchecked.
///
private static IEnumerable ImplementationsOf(Type type) => typeof(Program).Assembly.GetTypes()
.Where(candidate => candidate is { IsAbstract: false, IsInterface: false, IsGenericTypeDefinition: false } && type.IsAssignableFrom(candidate))
.OrderBy(candidate => candidate.FullName, StringComparer.Ordinal);
private static IEnumerable ManagedSettingValueTypes()
{
//
// Creating the settings registers every managed setting, just like the start of the app does:
//
_ = new Data();
return ManagedConfiguration.RegisteredSettings
.Select(setting => setting.GetType().GetGenericArguments()[1])
.Distinct()
.OrderBy(JsonContractDescription.TypeName, StringComparer.Ordinal);
}
}