Files
kompas3d-mcp/tests/Kompas.Mcp.Tests/InspectionTextTests.cs
T
mikhail 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.
2026-07-31 14:51:12 +03:00

214 lines
10 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>Юнит-тесты чистого рендера/группировки структурного «паспорта». 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);
}
}