Files
mikhail 3fe06f61dc feat: assembly_add_mate — упрочнение по ревью Codex реализации
#1: Valid==false → ошибка (откат), а не «успех с valid=false»; косвенно отсекает
грани одного компонента (вырожденный мат). #6/#7: distance value строго > 0
(нулевой зазор = coincidence) + unit-кейс value=0 бросает. #8: негативный
интеграционный тест — точка в пустоте → грань не найдена → ошибка.
#2/#3/#4 оставлены по обоснованию (best-effort откол, Add??throw, первый IFace —
консистентно с HoleService). #9 точечный release RCW отклонён (конвенция v2-2).

Тесты: 98 unit + 58 integration = 156 зелёных.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 13:45:35 +03:00

88 lines
3.9 KiB
C#

using Kompas.Mcp.Core.Assemblies;
namespace Kompas.Mcp.Tests;
[Trait("Category", "Unit")]
public sealed class AssemblyValidationTests
{
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
public void NormalizeComponentPath_throws_on_empty(string? path)
=> Assert.Throws<ArgumentException>(() => AssemblyValidation.NormalizeComponentPath(path));
[Theory]
[InlineData(@"C:\parts\bolt.step")]
[InlineData(@"C:\parts\bolt.txt")]
[InlineData(@"C:\parts\bolt")]
public void NormalizeComponentPath_throws_on_wrong_extension(string path)
=> Assert.Throws<ArgumentException>(() => AssemblyValidation.NormalizeComponentPath(path));
[Theory]
[InlineData(@"C:\parts\bolt.m3d", ".m3d")]
[InlineData(@"C:\parts\frame.a3d", ".a3d")]
[InlineData(@"C:\parts\bolt.M3D", ".m3d")] // регистр не важен
[InlineData(" C:\\parts\\bolt.a3d ", ".a3d")] // пробелы обрезаются
public void NormalizeComponentPath_accepts_m3d_and_a3d(string path, string expectedExt)
{
var result = AssemblyValidation.NormalizeComponentPath(path);
Assert.True(Path.IsPathFullyQualified(result), "Должен вернуться абсолютный путь.");
Assert.EndsWith(expectedExt, result, StringComparison.OrdinalIgnoreCase);
}
[Theory]
[InlineData(double.NaN, 0, 0)]
[InlineData(0, double.PositiveInfinity, 0)]
[InlineData(0, 0, double.NegativeInfinity)]
public void RequireFinitePosition_throws_on_non_finite(double x, double y, double z)
=> Assert.Throws<ArgumentException>(() => AssemblyValidation.RequireFinitePosition(x, y, z));
[Fact]
public void RequireFinitePosition_accepts_finite()
=> Assert.Null(Record.Exception(() => AssemblyValidation.RequireFinitePosition(50, -10, 0)));
// ── Сопряжения (Mates) ───────────────────────────────────────────────
[Theory]
[InlineData("coincidence", MateType.Coincidence)]
[InlineData("совпадение", MateType.Coincidence)]
[InlineData("DISTANCE", MateType.Distance)]
[InlineData(" расстояние ", MateType.Distance)]
public void Mates_Parse_maps_known(string text, MateType expected)
=> Assert.Equal(expected, Mates.Parse(text));
[Theory]
[InlineData("parallel")] // пока не поддержан
[InlineData("сопряжение")]
[InlineData("")]
public void Mates_Parse_throws_on_unknown(string text)
=> Assert.Throws<ArgumentException>(() => Mates.Parse(text));
// Корректность ToConstraintType проверяется интеграционно (coincidence даёт прилегание,
// distance — зазор), а не unit-ассертом: КОМПАС-enum встроен (EmbedInteropTypes) и не виден
// тестовому проекту через границу сборки.
[Theory]
[InlineData(MateType.Distance, true)]
[InlineData(MateType.Coincidence, false)]
public void Mates_RequiresValue(MateType type, bool expected)
=> Assert.Equal(expected, Mates.RequiresValue(type));
[Fact]
public void RequireMateValue_ignores_value_when_not_required()
=> Assert.Null(Record.Exception(() => AssemblyValidation.RequireMateValue(false, double.NaN)));
[Theory]
[InlineData(double.NaN)]
[InlineData(double.PositiveInfinity)]
[InlineData(-1)]
[InlineData(0)] // нулевой зазор вырождается в coincidence → ошибка
public void RequireMateValue_throws_on_bad_required_value(double value)
=> Assert.Throws<ArgumentOutOfRangeException>(() => AssemblyValidation.RequireMateValue(true, value));
[Fact]
public void RequireMateValue_accepts_positive_required_value()
=> Assert.Null(Record.Exception(() => AssemblyValidation.RequireMateValue(true, 30)));
}