3fe06f61dc
#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>
112 lines
4.6 KiB
C#
112 lines
4.6 KiB
C#
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); }
|
||
}
|
||
}
|