Files
kompas3d-mcp/tests/Kompas.Mcp.Tests/Integration/FeatureDeleteTests.cs
T
mikhail 6b4563a053 Итерация 2: плоскость уходит с эскизом, list_edges одним вызовом
Протокол второго прогона (48 вызовов, фаска по всему контуру надписи одной операцией):

feature_delete эскиза уносит и смещённую плоскость, созданную ради него, — её id наружу
не выдавался, и в дереве оставалась «Смещенная плоскость:N», которую нечем убрать.

list_edges отдаёт разбивку по типам и готовую строку индексов; при >60 рёбрах печатает
только их. Раньше субагент звал инструмент шесть раз, перебирая type, чтобы убедиться,
что не пропущен тип кривой.

Навык: исправлено МОЁ ЖЕ утверждение из прошлой итерации — карман subtract опасен для
глифов (высота 3 мм), а не для каймы (2 мм, в слой кармана не попадает); запрет на пакетную
обработку рёбер текста сужен до скруглений верхних рёбер — фаска по 59 рёбрам нижнего
контура строится с первой попытки; добавлены формула кегля через одну пробу и кайма
для прямоугольной плашки вторым примитивом.
2026-07-31 21:20:10 +03:00

152 lines
7.1 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.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); }
}
}