Files
kompas3d-mcp/tests/Kompas.Mcp.Tests/Integration/AssemblyMateTests.cs
T
mikhail 8282716773 feat: assembly_add_mate — сопряжения компонентов сборки (API7)
Веха СБОРКИ, инкремент 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>
2026-05-27 13:39:04 +03:00

99 lines
4.0 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); }
}
}