8282716773
Веха СБОРКИ, инкремент 2. AssemblyService.AddMateAsync (API7): top.MateConstraints
→ Add(MateConstraintType) → BaseObject1/2 (грани через FindObjectsByPoint
FirstLevel=false) → ParamValue → Update → RebuildModel. Свой enum MateType
(Coincidence/Distance) → MateConstraintType. MCP-инструмент assembly_add_mate.
Подтверждено спайком и тестами: coincidence сводит грани заподлицо (ширина 120→40),
distance(30) держит зазор 30 (→70); Update=True Valid=True. Решатель может двигать
любой компонент → проверка по ширине габарита.
Ревью Codex учтено: scope урезан до coincidence/distance (только геометрически
проверенные); валидация value по типу (RequireMateValue); полный откат при любой
ошибке после Add (mate.Owner.Delete); возврат AddedMate{Type,Valid}; усиленные
ассерты (Valid, размеры компонентов неизменны). Точечный release RCW отклонён
(конвенция v2-2). Прочие типы (parallel/concentric/angle/…) — будущие инкременты.
Тесты: +14 unit (Mates.Parse/RequiresValue, RequireMateValue), +2 integration
(AssemblyMateTests). Итого 97 unit + 57 integration = 154 зелёных.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
87 lines
3.8 KiB
C#
87 lines
3.8 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)]
|
|
public void RequireMateValue_throws_on_bad_required_value(double value)
|
|
=> Assert.Throws<ArgumentOutOfRangeException>(() => AssemblyValidation.RequireMateValue(true, value));
|
|
|
|
[Fact]
|
|
public void RequireMateValue_accepts_nonnegative_required_value()
|
|
=> Assert.Null(Record.Exception(() => AssemblyValidation.RequireMateValue(true, 30)));
|
|
}
|