Files
kompas3d-mcp/tests/Kompas.Mcp.Tests/ModelSectionsTests.cs
T
mikhail 4e3d367637
ci / build (push) Successful in 28s
Каталог инструментов сжат под агента: 84 → 56
Инструменты были нарезаны по способу вызова, а не по смыслу: четыре отверстия,
двойники *_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 теперь падает, если в публикуемом навыке всплывёт слитое имя.
2026-07-31 11:52:30 +03:00

72 lines
3.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.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);
}
}