189 lines
7.8 KiB
C#
189 lines
7.8 KiB
C#
using System.IO.Compression;
|
|
using Kompas.Mcp.Core.Interop;
|
|
|
|
namespace Kompas.Mcp.Tests;
|
|
|
|
/// <summary>
|
|
/// Поиск интероп-сборок КОМПАС: приоритет источников, распаковка архива SDK, тексты ошибок.
|
|
/// Настоящие DLL не нужны — провайдер проверяет только наличие файлов с нужными именами.
|
|
/// </summary>
|
|
[Trait("Category", "Unit")]
|
|
public sealed class KompasInteropProviderTests : IDisposable
|
|
{
|
|
private static readonly string[] RequiredFiles =
|
|
{
|
|
"KompasAPI7.dll",
|
|
"Kompas6API5.dll",
|
|
"Kompas6Constants.dll",
|
|
"Kompas6Constants3D.dll",
|
|
};
|
|
|
|
private readonly string _root = Path.Combine(Path.GetTempPath(), "kompas-interop-tests-" + Guid.NewGuid().ToString("N")[..8]);
|
|
|
|
public void Dispose()
|
|
{
|
|
try { if (Directory.Exists(_root)) Directory.Delete(_root, recursive: true); } catch { }
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("KompasAPI7", true)]
|
|
[InlineData("kompas6api5", true)]
|
|
[InlineData("Kompas6API3D5COM", true)]
|
|
[InlineData("Kompas.Mcp.Core", false)]
|
|
[InlineData(null, false)]
|
|
public void IsKompasAssembly_matches_only_interop_names(string? name, bool expected)
|
|
=> Assert.Equal(expected, KompasInteropProvider.IsKompasAssembly(name));
|
|
|
|
[Fact]
|
|
public void Override_directory_wins_over_everything()
|
|
{
|
|
var overrideDir = CreateDirectoryWithAssemblies("override");
|
|
var baseDir = CreateDirectoryWithAssemblies("base");
|
|
|
|
var provider = new KompasInteropProvider(overrideDir, baseDir, Cache, () => throw new Xunit.Sdk.XunitException(
|
|
"поиск установки не должен вызываться при заданном KOMPAS_INTEROP_DIR"));
|
|
|
|
Assert.Equal(overrideDir, provider.ResolveDirectory());
|
|
}
|
|
|
|
[Fact]
|
|
public void Override_directory_without_assemblies_fails_loudly()
|
|
{
|
|
var empty = Path.Combine(_root, "empty");
|
|
Directory.CreateDirectory(empty);
|
|
|
|
var provider = new KompasInteropProvider(empty, MissingDirectory, Cache, () => null);
|
|
|
|
var error = Assert.Throws<KompasInteropException>(() => provider.ResolveDirectory());
|
|
Assert.Contains(KompasInteropProvider.OverrideVariable, error.Message, StringComparison.Ordinal);
|
|
Assert.Contains(empty, error.Message, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void Base_directory_is_used_when_it_holds_assemblies()
|
|
{
|
|
var baseDir = CreateDirectoryWithAssemblies("base");
|
|
|
|
var provider = new KompasInteropProvider(null, baseDir, Cache, () => throw new Xunit.Sdk.XunitException(
|
|
"поиск установки не должен вызываться, когда сборки лежат рядом с сервером"));
|
|
|
|
Assert.Equal(baseDir, provider.ResolveDirectory());
|
|
}
|
|
|
|
[Fact]
|
|
public void Missing_installation_reports_actionable_reason()
|
|
{
|
|
var provider = new KompasInteropProvider(null, MissingDirectory, Cache, () => null);
|
|
|
|
var error = Assert.Throws<KompasInteropException>(() => provider.ResolveDirectory());
|
|
Assert.Contains("KOMPAS.Application", error.Message, StringComparison.Ordinal);
|
|
Assert.Contains(KompasInteropProvider.OverrideVariable, error.Message, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void Installation_without_sdk_component_reports_missing_archive()
|
|
{
|
|
var install = Path.Combine(_root, "KOMPAS-3D v24 Home");
|
|
Directory.CreateDirectory(install);
|
|
|
|
var provider = new KompasInteropProvider(null, MissingDirectory, Cache, () => install);
|
|
|
|
var error = Assert.Throws<KompasInteropException>(() => provider.ResolveDirectory());
|
|
Assert.Contains("SDK", error.Message, StringComparison.Ordinal);
|
|
Assert.Contains("CSharp.zip", error.Message, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void Archive_without_expected_entries_reports_empty_common_folder()
|
|
{
|
|
var install = CreateInstallation("KOMPAS-3D v24 Home", entries: new[] { "Automation/Interop.KGAXLib.dll" });
|
|
|
|
var provider = new KompasInteropProvider(null, MissingDirectory, Cache, () => install);
|
|
|
|
var error = Assert.Throws<KompasInteropException>(() => provider.ResolveDirectory());
|
|
Assert.Contains("Common/", error.Message, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void Assemblies_are_extracted_from_sdk_archive_into_cache()
|
|
{
|
|
var install = CreateInstallation("KOMPAS-3D v24 Home", entries: CommonEntries("readme.txt"));
|
|
|
|
var directory = new KompasInteropProvider(null, MissingDirectory, Cache, () => install).ResolveDirectory();
|
|
|
|
Assert.StartsWith(Cache, directory, StringComparison.Ordinal);
|
|
Assert.All(RequiredFiles, file => Assert.True(File.Exists(Path.Combine(directory, file)), file));
|
|
// В кеш попадают только DLL из Common — не весь архив.
|
|
Assert.False(File.Exists(Path.Combine(directory, "readme.txt")));
|
|
Assert.False(File.Exists(Path.Combine(directory, "Interop.KGAXLib.dll")));
|
|
}
|
|
|
|
[Fact]
|
|
public void Extracted_cache_is_reused_by_the_next_process()
|
|
{
|
|
var install = CreateInstallation("KOMPAS-3D v24 Home", entries: CommonEntries());
|
|
|
|
var first = new KompasInteropProvider(null, MissingDirectory, Cache, () => install).ResolveDirectory();
|
|
var marker = Path.Combine(first, "marker.txt");
|
|
File.WriteAllText(marker, "распаковано первым запуском");
|
|
|
|
var second = new KompasInteropProvider(null, MissingDirectory, Cache, () => install).ResolveDirectory();
|
|
|
|
Assert.Equal(first, second);
|
|
Assert.True(File.Exists(marker), "кеш должен переиспользоваться, а не распаковываться заново");
|
|
}
|
|
|
|
[Fact]
|
|
public void Cache_key_follows_the_sdk_archive()
|
|
{
|
|
var archive = Path.Combine(_root, "CSharp.zip");
|
|
Directory.CreateDirectory(_root);
|
|
File.WriteAllText(archive, "v24");
|
|
var before = KompasInteropProvider.CacheKey(new FileInfo(archive));
|
|
|
|
// Обновление КОМПАС меняет архив — ключ обязан смениться, иначе подхватится старый кеш.
|
|
File.WriteAllText(archive, "v25 — другой размер");
|
|
File.SetLastWriteTimeUtc(archive, DateTime.UnixEpoch.AddDays(1));
|
|
var after = KompasInteropProvider.CacheKey(new FileInfo(archive));
|
|
|
|
Assert.NotEqual(before, after);
|
|
Assert.Equal(16, before.Length);
|
|
}
|
|
|
|
private string Cache => Path.Combine(_root, "cache");
|
|
|
|
private string MissingDirectory => Path.Combine(_root, "no-such-directory");
|
|
|
|
private static string[] CommonEntries(params string[] extra)
|
|
=> RequiredFiles.Select(file => "Common/" + file)
|
|
.Concat(extra.Select(name => "Common/" + name))
|
|
.Append("Automation/Interop.KGAXLib.dll")
|
|
.ToArray();
|
|
|
|
private string CreateDirectoryWithAssemblies(string name)
|
|
{
|
|
var directory = Path.Combine(_root, name);
|
|
Directory.CreateDirectory(directory);
|
|
foreach (var file in RequiredFiles)
|
|
File.WriteAllText(Path.Combine(directory, file), "заглушка");
|
|
|
|
return directory;
|
|
}
|
|
|
|
private string CreateInstallation(string name, string[] entries)
|
|
{
|
|
var install = Path.Combine(_root, name);
|
|
var samples = Path.Combine(install, "SDK", "Samples");
|
|
Directory.CreateDirectory(samples);
|
|
|
|
using var archive = ZipFile.Open(Path.Combine(samples, "CSharp.zip"), ZipArchiveMode.Create);
|
|
foreach (var entry in entries)
|
|
{
|
|
using var writer = new StreamWriter(archive.CreateEntry(entry).Open());
|
|
writer.Write("заглушка");
|
|
}
|
|
|
|
return install;
|
|
}
|
|
}
|