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; /// Интеграция: сопряжения компонентов сборки (API7 IMateConstraints3D). [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 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; } /// Создать сборку из двух кубов 20³: A в нуле (world x[0,20]), B со сдвигом +100 (x[100,120]). private async Task 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( () => _assembly.AddMateAsync(MateType.Coincidence, 20, 10, 10, 1000, 1000, 1000, value: 0)); } finally { await _docs.CloseAsync(save: false); } } }