6b4563a053
Протокол второго прогона (48 вызовов, фаска по всему контуру надписи одной операцией): feature_delete эскиза уносит и смещённую плоскость, созданную ради него, — её id наружу не выдавался, и в дереве оставалась «Смещенная плоскость:N», которую нечем убрать. list_edges отдаёт разбивку по типам и готовую строку индексов; при >60 рёбрах печатает только их. Раньше субагент звал инструмент шесть раз, перебирая type, чтобы убедиться, что не пропущен тип кривой. Навык: исправлено МОЁ ЖЕ утверждение из прошлой итерации — карман subtract опасен для глифов (высота 3 мм), а не для каймы (2 мм, в слой кармана не попадает); запрет на пакетную обработку рёбер текста сужен до скруглений верхних рёбер — фаска по 59 рёбрам нижнего контура строится с первой попытки; добавлены формула кегля через одну пробу и кайма для прямоугольной плашки вторым примитивом.
152 lines
7.1 KiB
C#
152 lines
7.1 KiB
C#
using Kompas.Mcp.Core.Documents;
|
||
using Kompas.Mcp.Core.Modeling;
|
||
using Kompas.Mcp.Core.Query;
|
||
|
||
namespace Kompas.Mcp.Tests.Integration;
|
||
|
||
/// <summary>
|
||
/// Интеграция: удаление операции/эскиза. Без него любая неудачная проба (не тот кегль надписи,
|
||
/// не та глубина) оставалась в дереве навсегда, и единственным выходом было закрыть документ
|
||
/// без сохранения и построить всё заново.
|
||
/// </summary>
|
||
[Trait("Category", "Integration")]
|
||
[Collection(KompasCollection.Name)]
|
||
public sealed class FeatureDeleteTests : IntegrationTestBase
|
||
{
|
||
private readonly DocumentService _docs;
|
||
private readonly PartModeler _modeler;
|
||
private readonly ModelInspectionService _inspection;
|
||
private readonly QueryService _query;
|
||
|
||
public FeatureDeleteTests(KompasFixture fx) : base(fx)
|
||
{
|
||
_docs = new DocumentService(fx.Session, fx.Dispatcher);
|
||
_modeler = new PartModeler(fx.Session, fx.Dispatcher);
|
||
_inspection = new ModelInspectionService(fx.Session, fx.Dispatcher);
|
||
_query = new QueryService(fx.Session, fx.Dispatcher);
|
||
}
|
||
|
||
private async Task<IReadOnlyList<string>> TreeNamesAsync()
|
||
=> (await _inspection.DescribeModelAsync(InspectionText.ModelSections.Tree))
|
||
.Features.Select(f => f.Name).ToList();
|
||
|
||
[Fact]
|
||
public async Task Deleted_extrusion_leaves_the_tree_and_gives_the_material_back()
|
||
{
|
||
await _docs.CreateAsync(KompasDocumentType.Part);
|
||
try
|
||
{
|
||
var plate = await _modeler.OpenSketchAsync(BasePlane.XOY, name: "Контур плиты");
|
||
await _modeler.AddRectangleAsync(plate, 0, 0, 20, 10);
|
||
await _modeler.CloseSketchAsync(plate);
|
||
await _modeler.ExtrudeAsync(plate, depth: 3, name: "Плита");
|
||
|
||
var probe = await _modeler.OpenSketchOnOffsetPlaneAsync(BasePlane.XOY, offset: 3, name: "Проба кегля");
|
||
await _modeler.AddRectangleAsync(probe, 2, 2, 8, 8);
|
||
await _modeler.CloseSketchAsync(probe);
|
||
var probeId = await _modeler.ExtrudeAsync(probe, depth: 5, name: "Неудачная проба");
|
||
await _modeler.RebuildAsync();
|
||
var withProbe = (await _query.GetPartInfoAsync()).Volume;
|
||
|
||
var deleted = await _modeler.DeleteAsync(probeId);
|
||
await _modeler.RebuildAsync();
|
||
|
||
Assert.Contains("Неудачная проба", deleted, StringComparison.Ordinal);
|
||
var names = await TreeNamesAsync();
|
||
Assert.DoesNotContain("Неудачная проба", names);
|
||
Assert.Contains("Плита", names);
|
||
|
||
// 20×10×3 = 600: проба ушла вместе со своим материалом (6×6×5 = 180).
|
||
var after = (await _query.GetPartInfoAsync()).Volume;
|
||
Assert.InRange(after, 594, 606);
|
||
Assert.True(withProbe > after, "проба не добавляла материала — тест ничего не проверяет");
|
||
}
|
||
finally { await _docs.CloseAsync(save: false); }
|
||
}
|
||
|
||
[Fact]
|
||
public async Task Deleting_a_sketch_takes_its_operation_with_it()
|
||
{
|
||
await _docs.CreateAsync(KompasDocumentType.Part);
|
||
try
|
||
{
|
||
var s = await _modeler.OpenSketchAsync(BasePlane.XOY, name: "Контур пробы");
|
||
await _modeler.AddRectangleAsync(s, 0, 0, 20, 10);
|
||
await _modeler.CloseSketchAsync(s);
|
||
await _modeler.ExtrudeAsync(s, depth: 3, name: "Тело пробы");
|
||
await _modeler.RebuildAsync();
|
||
|
||
await _modeler.DeleteAsync(s); // КОМПАС удаляет зависимые каскадом
|
||
await _modeler.RebuildAsync();
|
||
|
||
var names = await TreeNamesAsync();
|
||
Assert.DoesNotContain("Контур пробы", names);
|
||
Assert.DoesNotContain("Тело пробы", names);
|
||
}
|
||
finally { await _docs.CloseAsync(save: false); }
|
||
}
|
||
|
||
[Fact]
|
||
public async Task Deleting_a_sketch_takes_its_offset_plane_with_it()
|
||
{
|
||
await _docs.CreateAsync(KompasDocumentType.Part);
|
||
try
|
||
{
|
||
// Тело нужно, чтобы дерево вообще перечислялось: у детали без тел оно пустое.
|
||
var plate = await _modeler.OpenSketchAsync(BasePlane.XOY, name: "Контур плиты");
|
||
await _modeler.AddRectangleAsync(plate, 0, 0, 20, 10);
|
||
await _modeler.CloseSketchAsync(plate);
|
||
await _modeler.ExtrudeAsync(plate, depth: 3, name: "Плита");
|
||
|
||
// Плоскость создаётся РАДИ эскиза, её id наружу не выдаётся — останься она в дереве,
|
||
// убрать её агенту было бы нечем.
|
||
var s = await _modeler.OpenSketchOnOffsetPlaneAsync(BasePlane.XOY, offset: 5, name: "Проба на смещении");
|
||
await _modeler.AddRectangleAsync(s, 2, 2, 8, 8);
|
||
await _modeler.CloseSketchAsync(s);
|
||
await _modeler.RebuildAsync();
|
||
var before = await TreeNamesAsync();
|
||
Assert.Contains(before, n => n.Contains("мещ", StringComparison.OrdinalIgnoreCase)); // плоскость в дереве есть
|
||
|
||
var deleted = await _modeler.DeleteAsync(s);
|
||
await _modeler.RebuildAsync();
|
||
|
||
Assert.Contains("плоскост", deleted, StringComparison.OrdinalIgnoreCase);
|
||
var names = await TreeNamesAsync();
|
||
Assert.DoesNotContain("Проба на смещении", names);
|
||
Assert.DoesNotContain(names, n => n.Contains("мещ", StringComparison.OrdinalIgnoreCase)); // «Смещенная плоскость:N»
|
||
Assert.Contains("Плита", names);
|
||
}
|
||
finally { await _docs.CloseAsync(save: false); }
|
||
}
|
||
|
||
[Fact]
|
||
public async Task Unknown_id_says_what_can_be_deleted()
|
||
{
|
||
await _docs.CreateAsync(KompasDocumentType.Part);
|
||
try
|
||
{
|
||
var ex = await Assert.ThrowsAsync<KeyNotFoundException>(() => _modeler.DeleteAsync(4242));
|
||
Assert.Contains("4242", ex.Message, StringComparison.Ordinal);
|
||
}
|
||
finally { await _docs.CloseAsync(save: false); }
|
||
}
|
||
|
||
[Fact]
|
||
public async Task Open_sketch_is_closed_before_deletion()
|
||
{
|
||
await _docs.CreateAsync(KompasDocumentType.Part);
|
||
try
|
||
{
|
||
// Эскиз остался в режиме редактирования — удаление обязано с этим справиться само.
|
||
var s = await _modeler.OpenSketchAsync(BasePlane.XOY, name: "Открытая проба");
|
||
await _modeler.AddRectangleAsync(s, 0, 0, 10, 10);
|
||
|
||
await _modeler.DeleteAsync(s);
|
||
await _modeler.RebuildAsync();
|
||
|
||
Assert.DoesNotContain("Открытая проба", await TreeNamesAsync());
|
||
}
|
||
finally { await _docs.CloseAsync(save: false); }
|
||
}
|
||
}
|