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 теперь падает, если в публикуемом навыке всплывёт слитое имя.
122 lines
4.4 KiB
C#
122 lines
4.4 KiB
C#
using Kompas.Mcp.Core.Modeling;
|
|
|
|
namespace Kompas.Mcp.Tests;
|
|
|
|
/// <summary>
|
|
/// Разбор пакета примитивов эскиза. Схема слитого инструмента не может запретить неверную
|
|
/// комбинацию полей — её ловит этот разбор, и сообщение должно называть недостающий параметр
|
|
/// и позицию в списке, иначе агент не исправится с первой попытки.
|
|
/// </summary>
|
|
[Trait("Category", "Unit")]
|
|
public sealed class SketchEntityInputsTests
|
|
{
|
|
private static SketchEntityInput Circle() => new()
|
|
{
|
|
Type = "circle", CenterX = 0, CenterY = 0, Radius = 5,
|
|
};
|
|
|
|
[Fact]
|
|
public void Empty_input_gives_empty_list()
|
|
{
|
|
Assert.Empty(SketchEntityInputs.Parse(null));
|
|
Assert.Empty(SketchEntityInputs.Parse([]));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("line", SketchEntityKind.Line)]
|
|
[InlineData("LINE", SketchEntityKind.Line)]
|
|
[InlineData(" rect ", SketchEntityKind.Rectangle)]
|
|
[InlineData("rectangle", SketchEntityKind.Rectangle)]
|
|
[InlineData("arc3", SketchEntityKind.Arc3Points)]
|
|
[InlineData("arc3points", SketchEntityKind.Arc3Points)]
|
|
[InlineData("axis", SketchEntityKind.Axis)]
|
|
public void Type_names_and_aliases_are_recognized(string type, SketchEntityKind expected)
|
|
{
|
|
var e = new SketchEntityInput
|
|
{
|
|
Type = type,
|
|
Points = [new SketchPoint(0, 0), new SketchPoint(10, 10), new SketchPoint(20, 0)],
|
|
};
|
|
|
|
Assert.Equal(expected, SketchEntityInputs.Parse([e])[0].Kind);
|
|
}
|
|
|
|
[Fact]
|
|
public void Unknown_type_lists_the_allowed_ones()
|
|
{
|
|
var e = new SketchEntityInput { Type = "spiral" };
|
|
|
|
var ex = Assert.Throws<ArgumentException>(() => SketchEntityInputs.Parse([e]));
|
|
Assert.Contains("spiral", ex.Message, StringComparison.Ordinal);
|
|
Assert.Contains("polyline", ex.Message, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void Error_names_the_position_in_the_batch()
|
|
{
|
|
var bad = new SketchEntityInput { Type = "circle", CenterX = 0, CenterY = 0 }; // без radius
|
|
|
|
var ex = Assert.Throws<ArgumentException>(() => SketchEntityInputs.Parse([Circle(), Circle(), bad]));
|
|
Assert.Contains("entities[2]", ex.Message, StringComparison.Ordinal);
|
|
Assert.Contains("radius", ex.Message, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void Line_requires_two_points()
|
|
{
|
|
var e = new SketchEntityInput { Type = "line", Points = [new SketchPoint(0, 0)] };
|
|
|
|
var ex = Assert.Throws<ArgumentException>(() => SketchEntityInputs.Parse([e]));
|
|
Assert.Contains("points", ex.Message, StringComparison.Ordinal);
|
|
Assert.Contains("минимум 2", ex.Message, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void Arc_requires_center_radius_and_angles()
|
|
{
|
|
var e = new SketchEntityInput { Type = "arc", CenterX = 0, CenterY = 0, Radius = 10, StartAngle = 0 };
|
|
|
|
var ex = Assert.Throws<ArgumentException>(() => SketchEntityInputs.Parse([e]));
|
|
Assert.Contains("endAngle", ex.Message, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void Polygon_requires_vertex_count()
|
|
{
|
|
var e = new SketchEntityInput { Type = "polygon", CenterX = 0, CenterY = 0, Radius = 10 };
|
|
|
|
var ex = Assert.Throws<ArgumentException>(() => SketchEntityInputs.Parse([e]));
|
|
Assert.Contains("vertexCount", ex.Message, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void Defaults_are_applied_for_omitted_flags()
|
|
{
|
|
var e = new SketchEntityInput { Type = "arc", CenterX = 1, CenterY = 2, Radius = 3, StartAngle = 0, EndAngle = 90 };
|
|
|
|
var parsed = SketchEntityInputs.Parse([e])[0];
|
|
|
|
Assert.True(parsed.CounterClockwise); // умолчание — против часовой
|
|
Assert.True(parsed.Inscribed);
|
|
Assert.False(parsed.Closed);
|
|
Assert.Equal(0, parsed.Angle);
|
|
}
|
|
|
|
[Fact]
|
|
public void Points_keep_their_order()
|
|
{
|
|
var e = new SketchEntityInput
|
|
{
|
|
Type = "polyline",
|
|
Points = [new SketchPoint(0, 0), new SketchPoint(10, 0), new SketchPoint(10, 5)],
|
|
Closed = true,
|
|
};
|
|
|
|
var parsed = SketchEntityInputs.Parse([e])[0];
|
|
|
|
Assert.True(parsed.Closed);
|
|
Assert.Equal(3, parsed.Points.Count);
|
|
Assert.Equal((10.0, 5.0), parsed.Points[2]);
|
|
}
|
|
}
|