4e3d367637
ci / build (push) Successful in 28s
Инструменты были нарезаны по способу вызова, а не по смыслу: четыре отверстия, двойники *_index, одиннадцать sketch_add_*. Агент платил за это дважды — 40 КБ описаний в каждой сессии и лишние round-trip'ы, а каждый вызов это ещё и шанс сбиться. Теперь инструмент называет ОПЕРАЦИЮ, вариант задаётся параметром, объект выбирается индексом или точкой одним и тем же инструментом. Проверка построения приходит сама. Каждая мутирующая операция дописывает к ответу итог validate_part (AutoValidation): Create()/Update()==true не значит успех, а правило навыка «проверяй после каждого шага» удваивало число вызовов. Выключается через set_auto_validate или KOMPAS_MCP_AUTOVALIDATE=0 — сбой самой проверки уходит в примечание и никогда не превращает удачную операцию в ошибку. Эскиз: 16 инструментов → 3. sketch_create(plane|faceIndex|x,y,z, entities[], autoClose) строит контур целиком; пакет выполняется за ОДИН заход на STA-поток (PartModeler.AddEntitiesAsync), ошибка называет позицию примитива в списке. Слияния: hole(type=simple|counterbore|countersink|conic), extrude/revolve(mode), pattern(kind), mirror (без featureIds — всё тело), document_save(path?), set_variable как upsert (разведочный вызов «есть ли такая» больше не нужен), list_faces/list_edges(index?) вместо отдельных describe_*, get_part_info и get_bounding_box — в describe_model(sections), где незапрошенные разделы вообще не читаются из модели. Селектор index|point: fillet_edge/chamfer_edge принимают edgeIndices списком — одна операция дерева на все рёбра; для операций API7, умеющих только точку, индекс переводится в точку через ModelInspectionService.FaceCenterPointAsync (середина параметрической области грани). Схема слитого инструмента не запрещает неверную комбинацию полей — это делает валидация, и её сообщение называет type и недостающий параметр. Тесты: 291 unit (+41) и 136 integration (+5), интеграционные — на живом КОМПАС. Новые проверяют ровно рискованные места: точка-из-индекса лежит на грани и по ней создаётся эскиз, мульти-ребёрное скругление даёт один узел дерева и убыль объёма, upsert создаёт и затем меняет переменную с формулой, пакет сообщает позицию сбоя. PluginSkillsTests теперь падает, если в публикуемом навыке всплывёт слитое имя.
72 lines
3.1 KiB
C#
72 lines
3.1 KiB
C#
using Kompas.Mcp.Core.Query;
|
||
|
||
namespace Kompas.Mcp.Tests;
|
||
|
||
/// <summary>Разделы «паспорта» модели: разбор списка и фильтрация рендера.</summary>
|
||
[Trait("Category", "Unit")]
|
||
public sealed class ModelSectionsTests
|
||
{
|
||
private static ModelDescription Sample() => new()
|
||
{
|
||
Name = "Деталь",
|
||
DocumentTypeName = "деталь",
|
||
IsEmpty = false,
|
||
Box = new BoundingBox { MinX = 0, MinY = 0, MinZ = 0, MaxX = 40, MaxY = 20, MaxZ = 15 },
|
||
Mass = new PartInfo { Volume = 12000, Mass = 0.0942, Area = 3400, CenterX = 20, CenterY = 10, CenterZ = 7.5 },
|
||
Bodies = [new BodyInfo { Index = 0, IsSolid = true, FaceCount = 6 }],
|
||
FaceTypes = ["plane", "plane", "cylinder"],
|
||
EdgeTypes = ["line", "circle"],
|
||
Features = [new FeatureInfo { Index = 0, Name = "Эскиз:1", Kind = "sketch", Detail = "" }],
|
||
Imported = false,
|
||
Variables = [new VariableInfo { Name = "width", Expression = "40", Value = 40, External = false, Information = false }],
|
||
};
|
||
|
||
[Theory]
|
||
[InlineData(null)]
|
||
[InlineData("")]
|
||
[InlineData("all")]
|
||
[InlineData("box,mass,bodies,topology,tree,variables")]
|
||
public void All_sections_by_default(string? spec)
|
||
=> Assert.Equal(InspectionText.ModelSections.All, InspectionText.ParseSections(spec));
|
||
|
||
[Fact]
|
||
public void Subset_is_parsed_case_and_separator_tolerant()
|
||
{
|
||
var parsed = InspectionText.ParseSections(" Box; mass ");
|
||
|
||
Assert.Equal(InspectionText.ModelSections.Box | InspectionText.ModelSections.Mass, parsed);
|
||
}
|
||
|
||
[Fact]
|
||
public void Unknown_section_lists_the_allowed_ones()
|
||
{
|
||
var ex = Assert.Throws<ArgumentException>(() => InspectionText.ParseSections("box,колбаса"));
|
||
|
||
Assert.Contains("колбаса", ex.Message, StringComparison.Ordinal);
|
||
Assert.Contains("topology", ex.Message, StringComparison.Ordinal);
|
||
}
|
||
|
||
[Fact]
|
||
public void Render_keeps_only_requested_sections()
|
||
{
|
||
var text = InspectionText.Render(Sample(), InspectionText.ModelSections.Box | InspectionText.ModelSections.Mass);
|
||
|
||
Assert.Contains("Габарит", text, StringComparison.Ordinal);
|
||
Assert.Contains("МЦХ", text, StringComparison.Ordinal);
|
||
Assert.DoesNotContain("Топология", text, StringComparison.Ordinal);
|
||
Assert.DoesNotContain("Дерево построения", text, StringComparison.Ordinal);
|
||
Assert.DoesNotContain("Переменные", text, StringComparison.Ordinal);
|
||
}
|
||
|
||
[Fact]
|
||
public void Box_section_carries_coordinates_not_only_sizes()
|
||
{
|
||
// get_bounding_box слит в describe_model — координаты габарита не должны потеряться.
|
||
var text = InspectionText.Render(Sample(), InspectionText.ModelSections.Box);
|
||
|
||
Assert.Contains("40.00 × 20.00 × 15.00 мм", text, StringComparison.Ordinal);
|
||
Assert.Contains("X 0.00…40.00", text, StringComparison.Ordinal);
|
||
Assert.Contains("Z 0.00…15.00", text, StringComparison.Ordinal);
|
||
}
|
||
}
|