Files
kompas3d-mcp/tests/Kompas.Mcp.Tests/Integration/AssemblyMateTests.cs
T
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

112 lines
4.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Kompas.Mcp.Core.Assemblies;
using Kompas.Mcp.Core.Documents;
using Kompas.Mcp.Core.Modeling;
using Kompas.Mcp.Core.Query;
namespace Kompas.Mcp.Tests.Integration;
/// <summary>Интеграция: сопряжения компонентов сборки (API7 IMateConstraints3D).</summary>
[Trait("Category", "Integration")]
[Collection(KompasCollection.Name)]
public sealed class AssemblyMateTests : IntegrationTestBase
{
private readonly DocumentService _docs;
private readonly PartModeler _modeler;
private readonly AssemblyService _assembly;
private readonly QueryService _query;
public AssemblyMateTests(KompasFixture fx) : base(fx)
{
_docs = new DocumentService(fx.Session, fx.Dispatcher);
_modeler = new PartModeler(fx.Session, fx.Dispatcher);
_assembly = new AssemblyService(fx.Session, fx.Dispatcher);
_query = new QueryService(fx.Session, fx.Dispatcher);
}
private async Task<string> BuildAndSaveCubeAsync(double side)
{
var path = TestPaths.NewFile(".m3d");
await _docs.CreateAsync(KompasDocumentType.Part);
try
{
var s = await _modeler.OpenSketchAsync(BasePlane.XOY);
await _modeler.AddRectangleAsync(s, 0, 0, side, side);
await _modeler.CloseSketchAsync(s);
await _modeler.ExtrudeAsync(s, depth: side);
await _modeler.RebuildAsync();
await _docs.SaveAsAsync(path);
}
finally { await _docs.CloseAsync(save: false); }
return path;
}
/// <summary>Создать сборку из двух кубов 20³: A в нуле (world x[0,20]), B со сдвигом +100 (x[100,120]).</summary>
private async Task<string> SetupTwoCubesAsync()
{
var cube = await BuildAndSaveCubeAsync(20);
await _docs.CreateAsync(KompasDocumentType.Assembly);
await _assembly.AddComponentAsync(cube, 0, 0, 0);
await _assembly.AddComponentAsync(cube, 100, 0, 0);
return cube;
}
[Fact]
public async Task Coincidence_mate_brings_faces_flush()
{
await SetupTwoCubesAsync();
try
{
var bbBefore = await _query.GetBoundingBoxAsync();
Assert.InRange(bbBefore.MaxX - bbBefore.MinX, 118, 122); // 2 куба врозь ≈ 120
// Совпадение +X грани A (20,10,10) с X гранью B (100,10,10).
var m = await _assembly.AddMateAsync(MateType.Coincidence, 20, 10, 10, 100, 10, 10, value: 0);
Assert.True(m.Valid, "Сопряжение должно быть валидным.");
var bbAfter = await _query.GetBoundingBoxAsync();
Assert.InRange(bbAfter.MaxX - bbAfter.MinX, 38, 42); // кубы соприкоснулись ≈ 40
// Размеры самих компонентов не изменились (двигаются, не деформируются).
var comps = await _query.ListComponentsAsync();
Assert.Equal(2, comps.Count);
Assert.All(comps, c => Assert.InRange(c.SizeX, 19, 21));
}
finally { await _docs.CloseAsync(save: false); }
}
[Fact]
public async Task Distance_mate_holds_faces_apart()
{
await SetupTwoCubesAsync();
try
{
var bbBefore = await _query.GetBoundingBoxAsync();
Assert.InRange(bbBefore.MaxX - bbBefore.MinX, 118, 122);
// Расстояние 30 между +X гранью A и −X гранью B → ширина 20+30+20 = 70.
var m = await _assembly.AddMateAsync(MateType.Distance, 20, 10, 10, 100, 10, 10, value: 30);
Assert.True(m.Valid, "Сопряжение должно быть валидным.");
var bbAfter = await _query.GetBoundingBoxAsync();
Assert.InRange(bbAfter.MaxX - bbAfter.MinX, 66, 74); // ≈ 70
var comps = await _query.ListComponentsAsync();
Assert.All(comps, c => Assert.InRange(c.SizeX, 19, 21));
}
finally { await _docs.CloseAsync(save: false); }
}
[Fact]
public async Task Mate_with_point_off_any_face_throws()
{
await SetupTwoCubesAsync();
try
{
// Вторая точка — в пустоте (далеко от обоих кубов): грань не найдена → понятная ошибка.
await Assert.ThrowsAsync<InvalidOperationException>(
() => _assembly.AddMateAsync(MateType.Coincidence, 20, 10, 10, 1000, 1000, 1000, value: 0));
}
finally { await _docs.CloseAsync(save: false); }
}
}