4f4dd082d9
set_part_info задаёт наименование и обозначение детали: без него в дереве стоит безликая «Деталь», и она же уходит в штамп чертежа и в спецификацию. describe_model показывает наименование в шапке, а безымянную деталь помечает явно. Имя файла этого не заменяет — это разные свойства. document_save рапортовал «Сохранено», ничего не записав: SaveAs возвращает void и молча отказывает, если файл с этим именем уже открыт в КОМПАС другим документом. Теперь путь и наличие файла проверяются после записи, а Save сверяет флаг Changed. Освободить занятое имя нечем не было — document_close получил режим all. Текст исключения доходил до клиента только у McpException, всё остальное подменялось на «An error occurred invoking». То есть подсказки в наших сообщениях агент не видел вовсе. Фильтр CallTool в Program.cs возвращает IsError с реальной причиной (ToolErrorText разворачивает AggregateException и склеивает вложенные причины). Там же — регистрация PrimitiveService.
214 lines
10 KiB
C#
214 lines
10 KiB
C#
using Kompas.Mcp.Core.Query;
|
||
|
||
namespace Kompas.Mcp.Tests;
|
||
|
||
/// <summary>Юнит-тесты чистого рендера/группировки структурного «паспорта». COM не требуется.</summary>
|
||
[Trait("Category", "Unit")]
|
||
public sealed class InspectionTextTests
|
||
{
|
||
// ── Детект пустой модели по габариту ──────────────────────────────────
|
||
[Fact]
|
||
public void IsEmptyGabarit_true_for_sentinel()
|
||
=> Assert.True(InspectionText.IsEmptyGabarit(double.MaxValue, double.MaxValue, double.MaxValue));
|
||
|
||
[Fact]
|
||
public void IsEmptyGabarit_true_for_all_zero()
|
||
=> Assert.True(InspectionText.IsEmptyGabarit(0, 0, 0));
|
||
|
||
[Fact]
|
||
public void IsEmptyGabarit_false_for_real_part()
|
||
=> Assert.False(InspectionText.IsEmptyGabarit(40, 20, 15));
|
||
|
||
// ── Группировка типов ────────────────────────────────────────────────
|
||
[Fact]
|
||
public void GroupTypes_orders_by_count_desc()
|
||
{
|
||
var s = InspectionText.GroupTypes(new[] { "plane", "cylinder", "plane", "plane", "cylinder" });
|
||
Assert.Equal("3 plane, 2 cylinder", s);
|
||
}
|
||
|
||
[Fact]
|
||
public void GroupTypes_empty_returns_dash()
|
||
=> Assert.Equal("—", InspectionText.GroupTypes(Array.Empty<string>()));
|
||
|
||
// ── Наименование/обозначение детали в заголовке ──────────────────────
|
||
private static ModelDescription Desc(string partName, string partMarking) => new()
|
||
{
|
||
Name = "part.m3d", PartName = partName, PartMarking = partMarking,
|
||
IsEmpty = true, DocumentTypeName = "деталь",
|
||
Bodies = [], FaceTypes = [], EdgeTypes = [], Features = [], Variables = [], Imported = false,
|
||
};
|
||
|
||
[Fact]
|
||
public void Part_without_a_name_is_flagged_with_the_way_to_set_it()
|
||
{
|
||
var s = InspectionText.PartIdentityText(Desc("", ""));
|
||
|
||
Assert.Contains("без наименования", s, StringComparison.Ordinal);
|
||
Assert.Contains("set_part_info", s, StringComparison.Ordinal);
|
||
}
|
||
|
||
[Fact]
|
||
public void Part_identity_shows_both_name_and_marking()
|
||
{
|
||
var s = InspectionText.PartIdentityText(Desc("Значок декоративный", "ОМЕ.741124.001"));
|
||
|
||
Assert.Contains("«Значок декоративный»", s, StringComparison.Ordinal);
|
||
Assert.Contains("«ОМЕ.741124.001»", s, StringComparison.Ordinal);
|
||
}
|
||
|
||
[Fact]
|
||
public void Part_identity_omits_the_empty_marking()
|
||
{
|
||
var s = InspectionText.PartIdentityText(Desc("Значок декоративный", " "));
|
||
|
||
Assert.Contains("наименование", s, StringComparison.Ordinal);
|
||
Assert.DoesNotContain("обозначение", s, StringComparison.Ordinal);
|
||
}
|
||
|
||
// ── Детект импорта без истории ───────────────────────────────────────
|
||
private static FeatureInfo Feat(int i, string name, string kind) => new() { Index = i, Name = name, Kind = kind };
|
||
|
||
[Fact]
|
||
public void IsImported_true_for_bare_origin_and_body()
|
||
{
|
||
var fs = new[] { Feat(0, "Начало координат", "origin"), Feat(1, "Body1", "other") };
|
||
Assert.True(InspectionText.IsImported(fs, bodyCount: 1));
|
||
}
|
||
|
||
[Fact]
|
||
public void IsImported_false_when_tree_has_edit_history()
|
||
{
|
||
// Импорт, отредактированный move_face: origin + тело + 5 узлов-правок (>2) — это уже история.
|
||
var fs = new[]
|
||
{
|
||
Feat(0, "Начало координат", "origin"), Feat(1, "Body1", "other"),
|
||
Feat(2, "Смещенная плоскость:1", "other"), Feat(3, "Разрезать:1", "other"),
|
||
Feat(4, "Переместить грани:1", "other"), Feat(5, "Булева операция:1", "other"),
|
||
};
|
||
Assert.False(InspectionText.IsImported(fs, bodyCount: 1));
|
||
}
|
||
|
||
[Fact]
|
||
public void IsImported_false_for_native_part_with_operations()
|
||
{
|
||
var fs = new[]
|
||
{
|
||
Feat(0, "Начало координат", "origin"), Feat(1, "Эскиз:1", "sketch"),
|
||
Feat(2, "Элемент выдавливания:1", "extrusion"), Feat(3, "Скругление:1", "fillet"),
|
||
};
|
||
Assert.False(InspectionText.IsImported(fs, bodyCount: 1));
|
||
}
|
||
|
||
[Fact]
|
||
public void IsImported_false_without_body()
|
||
=> Assert.False(InspectionText.IsImported(new[] { Feat(0, "Начало координат", "origin") }, bodyCount: 0));
|
||
|
||
// ── Рендер: родная параметрическая деталь ────────────────────────────
|
||
[Fact]
|
||
public void Render_native_part_shows_tree_with_params()
|
||
{
|
||
var d = new ModelDescription
|
||
{
|
||
Name = "demo",
|
||
IsEmpty = false,
|
||
Box = new BoundingBox { MinX = -20, MinY = -10, MinZ = 0, MaxX = 20, MaxY = 10, MaxZ = 15 },
|
||
Mass = new PartInfo { Volume = 11961, Mass = 0.094, Area = 3370, CenterX = 0, CenterY = 0, CenterZ = 7.5 },
|
||
Bodies = new[] { new BodyInfo { Index = 0, IsSolid = true, FaceCount = 7 } },
|
||
FaceTypes = new[] { "plane", "plane", "cylinder" },
|
||
EdgeTypes = new[] { "line", "line" },
|
||
Imported = false,
|
||
Features = new[]
|
||
{
|
||
new FeatureInfo { Index = 0, Name = "Начало координат", Kind = "origin" },
|
||
new FeatureInfo { Index = 1, Name = "Эскиз:1", Kind = "sketch" },
|
||
new FeatureInfo { Index = 2, Name = "Элемент выдавливания:1", Kind = "extrusion", Detail = "глубина 15.00 мм" },
|
||
new FeatureInfo { Index = 3, Name = "Скругление:1", Kind = "fillet", Detail = "R3.00 мм" },
|
||
},
|
||
Variables = Array.Empty<VariableInfo>(),
|
||
};
|
||
|
||
var text = InspectionText.Render(d);
|
||
|
||
Assert.Contains("Документ: «demo»", text);
|
||
Assert.Contains("Габарит (Д×Ш×В): 40.00 × 20.00 × 15.00 мм", text);
|
||
Assert.Contains("Тела: 1", text);
|
||
Assert.Contains("граней 3 (2 plane, 1 cylinder)", text);
|
||
Assert.Contains("Дерево построения (4", text);
|
||
Assert.Contains("«Элемент выдавливания:1» — глубина 15.00 мм", text);
|
||
Assert.Contains("«Скругление:1» — R3.00 мм", text);
|
||
Assert.Contains("Переменные: нет", text);
|
||
}
|
||
|
||
// ── Рендер: импортированная модель (флаг Imported) ───────────────────
|
||
[Fact]
|
||
public void Render_imported_model_notes_no_history()
|
||
{
|
||
var d = new ModelDescription
|
||
{
|
||
Name = "top_spacer",
|
||
IsEmpty = false,
|
||
Box = new BoundingBox { MinX = 0, MinY = 0, MinZ = 0, MaxX = 18, MaxY = 8, MaxZ = 39 },
|
||
Mass = new PartInfo { Volume = 2463, Mass = 0.019, Area = 2053, CenterX = 0, CenterY = 0, CenterZ = 21 },
|
||
Bodies = new[] { new BodyInfo { Index = 0, IsSolid = true, FaceCount = 61 } },
|
||
FaceTypes = new[] { "plane", "cylinder" },
|
||
EdgeTypes = new[] { "line" },
|
||
Imported = true,
|
||
Features = new[]
|
||
{
|
||
new FeatureInfo { Index = 0, Name = "Начало координат", Kind = "origin" },
|
||
new FeatureInfo { Index = 1, Name = "Body1", Kind = "body" },
|
||
},
|
||
Variables = Array.Empty<VariableInfo>(),
|
||
};
|
||
|
||
var text = InspectionText.Render(d);
|
||
|
||
Assert.Contains("импортирован", text.ToLowerInvariant());
|
||
Assert.DoesNotContain("Дерево построения (", text); // не показываем как параметрическое дерево
|
||
}
|
||
|
||
// ── Рендер: пустая модель ────────────────────────────────────────────
|
||
[Fact]
|
||
public void Render_empty_model()
|
||
{
|
||
var d = new ModelDescription
|
||
{
|
||
Name = "empty", IsEmpty = true,
|
||
Bodies = Array.Empty<BodyInfo>(), FaceTypes = Array.Empty<string>(), EdgeTypes = Array.Empty<string>(),
|
||
Imported = false, Features = Array.Empty<FeatureInfo>(), Variables = Array.Empty<VariableInfo>(),
|
||
};
|
||
|
||
var text = InspectionText.Render(d);
|
||
|
||
Assert.Contains("Модель пуста", text);
|
||
}
|
||
|
||
// ── Рендер переменных ────────────────────────────────────────────────
|
||
[Fact]
|
||
public void Render_variables_listed_with_flags()
|
||
{
|
||
var d = new ModelDescription
|
||
{
|
||
Name = "p", IsEmpty = false,
|
||
Box = new BoundingBox { MinX = 0, MinY = 0, MinZ = 0, MaxX = 1, MaxY = 1, MaxZ = 1 },
|
||
Mass = new PartInfo { Volume = 1, Mass = 1, Area = 6, CenterX = 0, CenterY = 0, CenterZ = 0 },
|
||
Bodies = new[] { new BodyInfo { Index = 0, IsSolid = true, FaceCount = 6 } },
|
||
FaceTypes = new[] { "plane" }, EdgeTypes = new[] { "line" },
|
||
Imported = false, Features = Array.Empty<FeatureInfo>(),
|
||
Variables = new[]
|
||
{
|
||
new VariableInfo { Name = "L", Expression = "80", Value = 80, External = true, Information = false },
|
||
new VariableInfo { Name = "v", Expression = "L/2", Value = 40, External = false, Information = true },
|
||
},
|
||
};
|
||
|
||
var text = InspectionText.Render(d);
|
||
|
||
Assert.Contains("Переменные (2):", text);
|
||
Assert.Contains("L = 80", text);
|
||
Assert.Contains("[внешняя]", text);
|
||
Assert.Contains("[инфо]", text);
|
||
}
|
||
}
|