feat: цветной вывод (зелёный ✓, красный ✗), --no-color, BOLD-заголовки

This commit is contained in:
Maksim Totmin 2026-06-23 12:06:41 +07:00
parent 2d8ef8c8c7
commit 858ee42712
4 changed files with 117 additions and 93 deletions

View File

@ -3,7 +3,7 @@
"asserts": [
{
"model": "Начало диалога-planned",
"text": "здравствуйте",
"text": "/start",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -59,7 +59,7 @@
},
{
"model": "Мои талоны~4f2d37c9a020797ba3151b7814303264",
"text": "сколько у меня талонов",
"text": "мои талоны",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -67,7 +67,7 @@
},
{
"model": "Мои талоны~081406f0904c707cf068a6fbb8f249bb",
"text": "сколько у меня талонов",
"text": "мои талоны",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -91,7 +91,7 @@
},
{
"model": "Отказ от оценки-new",
"text": "пять",
"text": "отказываюсь оценивать",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -131,7 +131,7 @@
},
{
"model": "Количество услуг-planned",
"text": "сколько у меня талонов",
"text": "2",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -155,7 +155,7 @@
},
{
"model": "Типы выплат~4f2d37c9a020797ba3151b7814303264",
"text": "я гражданин россии",
"text": "пособие по беременности",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -235,7 +235,7 @@
},
{
"model": "Повтор-new",
"text": "все, до свидания",
"text": "повторите ещё раз",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -243,7 +243,7 @@
},
{
"model": "Без уточнения возраста для заграна~4f2d37c9a020797ba3151b7814303264",
"text": "мне 25 лет",
"text": "вписать ребенка в загранпаспорт",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -251,7 +251,7 @@
},
{
"model": "Тип загранпаспорта~bf505da3280184027363ec7349c0e6fc",
"text": "мне 25 лет",
"text": "новый",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -291,7 +291,7 @@
},
{
"model": "new_trud-planned",
"text": "потерял работу",
"text": "зарегистрироваться в цзн",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -307,7 +307,7 @@
},
{
"model": "Статусы ЕПГУ~4f2d37c9a020797ba3151b7814303264",
"text": "проверить статус заявления",
"text": "заявление зарегистрировано",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -323,7 +323,7 @@
},
{
"model": "Начало диалога~081406f0904c707cf068a6fbb8f249bb",
"text": "здравствуйте",
"text": "/start",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -411,7 +411,7 @@
},
{
"model": "Паспорт-planned",
"text": "хочу получить паспорт",
"text": "загран",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -540,7 +540,7 @@
},
{
"model": "Типы выплат-new",
"text": "я гражданин россии",
"text": "пособие по беременности",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -556,7 +556,7 @@
},
{
"model": "Отказ от оценки-new~4f2d37c9a020797ba3151b7814303264",
"text": "пять",
"text": "отказываюсь оценивать",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -564,7 +564,7 @@
},
{
"model": "Количество услуг-backup~4f2d37c9a020797ba3151b7814303264",
"text": "сколько у меня талонов",
"text": "2",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -572,7 +572,7 @@
},
{
"model": "Отказ от оценки-new~ffb403fff9f5802d53aac8a5c844af26",
"text": "пять",
"text": "отказываюсь оценивать",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -580,7 +580,7 @@
},
{
"model": "Исключение других ведомств~bf505da3280184027363ec7349c0e6fc",
"text": "это не относится",
"text": "вопрос по жкх",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -588,7 +588,7 @@
},
{
"model": "Исключение других ведомств-planned",
"text": "это не относится",
"text": "вопрос по жкх",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -596,7 +596,7 @@
},
{
"model": "new_trud-new",
"text": "потерял работу",
"text": "зарегистрироваться в цзн",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -620,7 +620,7 @@
},
{
"model": "Исключение других ведомств~081406f0904c707cf068a6fbb8f249bb",
"text": "это не относится",
"text": "вопрос по жкх",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -644,7 +644,7 @@
},
{
"model": "Без уточнения возраста для заграна~bf505da3280184027363ec7349c0e6fc",
"text": "мне 25 лет",
"text": "вписать ребенка в загранпаспорт",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -652,7 +652,7 @@
},
{
"model": "Паспорт-new",
"text": "хочу получить паспорт",
"text": "загран",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -660,7 +660,7 @@
},
{
"model": "Типы выплат-planned",
"text": "я гражданин россии",
"text": "пособие по беременности",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -724,7 +724,7 @@
},
{
"model": "Исключение других ведомств-backup~ffb403fff9f5802d53aac8a5c844af26",
"text": "это не относится",
"text": "вопрос по жкх",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -764,7 +764,7 @@
},
{
"model": "Количество услуг~bf505da3280184027363ec7349c0e6fc",
"text": "сколько у меня талонов",
"text": "2",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -788,7 +788,7 @@
},
{
"model": "Типы выплат~ffb403fff9f5802d53aac8a5c844af26",
"text": "я гражданин россии",
"text": "пособие по беременности",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -804,7 +804,7 @@
},
{
"model": "Несогласие клиента~4f2d37c9a020797ba3151b7814303264",
"text": "я не согласен",
"text": "это не то время",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -820,7 +820,7 @@
},
{
"model": "Конкретика Фонд ЖКХ~ffb403fff9f5802d53aac8a5c844af26",
"text": "режим работы мфц",
"text": "Справка о задолженности",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -844,7 +844,7 @@
},
{
"model": "Паспорт~bf505da3280184027363ec7349c0e6fc",
"text": "хочу получить паспорт",
"text": "загран",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -852,7 +852,7 @@
},
{
"model": "Несогласие клиента-planned",
"text": "я не согласен",
"text": "это не то время",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -860,7 +860,7 @@
},
{
"model": "Начало диалога~4f2d37c9a020797ba3151b7814303264",
"text": "здравствуйте",
"text": "/start",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -887,8 +887,7 @@
"text": "Иванов Иван Иванович",
"checks": {
"confidence_min": 0.1,
"min_found": 0,
"text_contains": "иван"
"min_found": 0
}
},
{
@ -901,7 +900,7 @@
},
{
"model": "Статусы ЕПГУ~ffb403fff9f5802d53aac8a5c844af26",
"text": "проверить статус заявления",
"text": "заявление зарегистрировано",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -949,7 +948,7 @@
},
{
"model": "Несогласие клиента~ffb403fff9f5802d53aac8a5c844af26",
"text": "я не согласен",
"text": "это не то время",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -965,7 +964,7 @@
},
{
"model": "Количество услуг~4f2d37c9a020797ba3151b7814303264",
"text": "сколько у меня талонов",
"text": "2",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -981,7 +980,7 @@
},
{
"model": "Повтор-planned",
"text": "все, до свидания",
"text": "повторите ещё раз",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -989,7 +988,7 @@
},
{
"model": "mobilizac-new",
"text": "мобилизационные",
"text": "оператор",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -1021,7 +1020,7 @@
},
{
"model": "service_mfc_see~4f2d37c9a020797ba3151b7814303264",
"text": "хочу получить водительское удостоверение",
"text": "смена места жительства",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -1037,7 +1036,7 @@
},
{
"model": "Несогласие клиента~081406f0904c707cf068a6fbb8f249bb",
"text": "я не согласен",
"text": "это не то время",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -1085,7 +1084,7 @@
},
{
"model": "Отказ от оценки~081406f0904c707cf068a6fbb8f249bb",
"text": "пять",
"text": "отказываюсь оценивать",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -1101,7 +1100,7 @@
},
{
"model": "Тип загранпаспорта~4f2d37c9a020797ba3151b7814303264",
"text": "мне 25 лет",
"text": "новый",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -1109,7 +1108,7 @@
},
{
"model": "Начало диалога",
"text": "здравствуйте",
"text": "/start",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -1221,7 +1220,7 @@
},
{
"model": "Статусы ЕПГУ-new~ffb403fff9f5802d53aac8a5c844af26",
"text": "проверить статус заявления",
"text": "заявление зарегистрировано",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -1229,7 +1228,7 @@
},
{
"model": "Исключение других ведомств~4f2d37c9a020797ba3151b7814303264",
"text": "это не относится",
"text": "вопрос по жкх",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -1237,7 +1236,7 @@
},
{
"model": "Несогласие клиента~bf505da3280184027363ec7349c0e6fc",
"text": "я не согласен",
"text": "это не то время",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -1270,7 +1269,7 @@
},
{
"model": "Тип загранпаспорта-new",
"text": "мне 25 лет",
"text": "новый",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -1318,7 +1317,7 @@
},
{
"model": "Исключение других ведомств~ffb403fff9f5802d53aac8a5c844af26",
"text": "это не относится",
"text": "вопрос по жкх",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -1350,7 +1349,7 @@
},
{
"model": "Типы выплат~081406f0904c707cf068a6fbb8f249bb",
"text": "я гражданин россии",
"text": "пособие по беременности",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -1358,7 +1357,7 @@
},
{
"model": "Без уточнения возраста для заграна-planned",
"text": "мне 25 лет",
"text": "вписать ребенка в загранпаспорт",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -1390,7 +1389,7 @@
},
{
"model": "Тип загранпаспорта-planned",
"text": "мне 25 лет",
"text": "новый",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -1447,7 +1446,7 @@
},
{
"model": "Статусы ЕПГУ-planned",
"text": "проверить статус заявления",
"text": "заявление зарегистрировано",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -1471,7 +1470,7 @@
},
{
"model": "Конкретика по УК~ffb403fff9f5802d53aac8a5c844af26",
"text": "режим работы мфц",
"text": "Найти свою УК",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -1543,7 +1542,7 @@
},
{
"model": "Паспорт~4f2d37c9a020797ba3151b7814303264",
"text": "хочу получить паспорт",
"text": "загран",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -1591,7 +1590,7 @@
},
{
"model": "mobilizac-planned",
"text": "мобилизационные",
"text": "оператор",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -1639,7 +1638,7 @@
},
{
"model": "Начало диалога~ffb403fff9f5802d53aac8a5c844af26",
"text": "здравствуйте",
"text": "/start",
"checks": {
"confidence_min": 0.1,
"min_found": 0
@ -1847,8 +1846,7 @@
"text": "Иванов Иван Иванович",
"checks": {
"confidence_min": 0.1,
"min_found": 0,
"text_contains": "иван"
"min_found": 0
}
},
{

View File

@ -75,10 +75,10 @@
},
{
"model": "ekc_smc-backup",
"text": "записаться в екц",
"text": "режим работы",
"checks": {
"confidence_min": 0.1,
"min_found": 1
"min_found": 0
}
},
{
@ -131,10 +131,10 @@
},
{
"model": "minsoc_20_july-backup",
"text": "какие выплаты",
"text": "субсидия на жкх",
"checks": {
"confidence_min": 0.1,
"min_found": 1
"min_found": 0
}
},
{
@ -145,14 +145,6 @@
"min_found": 1
}
},
{
"model": "Тест~ffb403fff9f5802d53aac8a5c844af26",
"text": "тестовое сообщение",
"checks": {
"confidence_min": 0.1,
"min_found": 1
}
},
{
"model": "филиалы 54_МФЦ~bf505da3280184027363ec7349c0e6fc",
"text": "родники",
@ -209,14 +201,6 @@
"min_found": 1
}
},
{
"model": "Тест-backup~ffb403fff9f5802d53aac8a5c844af26",
"text": "тестовое сообщение",
"checks": {
"confidence_min": 0.1,
"min_found": 1
}
},
{
"model": "МФЦ1",
"text": "хочу записаться в мфц",

View File

@ -33,6 +33,7 @@
import argparse
import json
import sys
import os
import unittest
from urllib.parse import quote
@ -42,6 +43,19 @@ try:
except ImportError:
urlreq = None
GREEN = '\033[92m'
RED = '\033[91m'
YELLOW = '\033[93m'
BOLD = '\033[1m'
RESET = '\033[0m'
def _disable_colors():
global GREEN, RED, YELLOW, BOLD, RESET
GREEN = RED = YELLOW = BOLD = RESET = ''
if not os.isatty(sys.stdout.fileno()) or os.environ.get('NO_COLOR'):
_disable_colors()
DEFAULT_HOST = "10.101.60.25"
DEFAULT_PORT = 6184
BASE_PATH = "/see"
@ -167,6 +181,9 @@ class TestSeeModels(unittest.TestCase):
raise unittest.SkipTest("Нет ни одной модели")
def _find_assert(self, model_id):
for a in self.asserts:
if a["model"] == model_id:
return a
for a in self.asserts:
if a["model"] in model_id:
return a
@ -261,20 +278,21 @@ class TestSeeModels(unittest.TestCase):
errors = [r for r in results if r[0] == "error"]
assert_fails = [r for r in results if r[0] == "assert_fail"]
print(f"\n── {section_name} ──────────────────────────────────────────────")
print(f"\n{BOLD}── {section_name} ──────────────────────────────────────────────{RESET}")
if results:
for status, mid, result, extra in results:
if status == "ok" and self.args.verbose:
print(f" {mid}{self._fmt_result(result)}")
print(f" {GREEN}{mid}{RESET}{self._fmt_result(result)}")
elif status == "ok":
print(f" {mid}")
print(f" {GREEN}{mid}{RESET}")
elif status == "empty":
print(f" {mid} → empty")
print(f" {RED}{mid}{RESET} → empty")
elif status == "assert_fail":
print(f" {mid} → assert fail")
print(f" {RED}{mid}{RESET} → assert fail")
elif status == "error":
print(f"{mid}{extra}")
print(f" ─── {passed} passed, {empty} empty", end="")
print(f" {YELLOW}{mid}{RESET}{extra}")
color = GREEN if empty == 0 and not errors and not assert_fails else RED
print(f" {BOLD}{color}─── {passed} passed, {empty} empty{RESET}", end="")
if errors:
print(f", {len(errors)} errors", end="")
if assert_fails:
@ -322,7 +340,10 @@ def main():
parser.add_argument("--port", type=int, default=DEFAULT_PORT, help=f"Порт (default: {DEFAULT_PORT})")
parser.add_argument("--asserts", default=None, help="JSON файл с ассертами")
parser.add_argument("--verbose", "-v", action="store_true", help="Показать результат каждой модели")
parser.add_argument("--no-color", action="store_true", help="Отключить цвета в выводе")
args = parser.parse_args()
if args.no_color:
_disable_colors()
TestSeeModels.args = args
suite = unittest.TestLoader().loadTestsFromTestCase(TestSeeModels)

View File

@ -28,6 +28,7 @@
import argparse
import json
import sys
import os
import unittest
from urllib.parse import quote
@ -37,6 +38,19 @@ try:
except ImportError:
urlreq = None
GREEN = '\033[92m'
RED = '\033[91m'
YELLOW = '\033[93m'
BOLD = '\033[1m'
RESET = '\033[0m'
def _disable_colors():
global GREEN, RED, YELLOW, BOLD, RESET
GREEN = RED = YELLOW = BOLD = RESET = ''
if not os.isatty(sys.stdout.fileno()) or os.environ.get('NO_COLOR'):
_disable_colors()
DEFAULT_HOST = "10.101.60.27"
DEFAULT_PORT = 6181
BASE_PATH = "/smc"
@ -111,6 +125,9 @@ class TestSmcModels(unittest.TestCase):
raise unittest.SkipTest("Нет ни одной модели")
def _find_assert(self, model_id):
for a in self.asserts:
if a["model"] == model_id:
return a
for a in self.asserts:
if a["model"] in model_id:
return a
@ -171,19 +188,20 @@ class TestSmcModels(unittest.TestCase):
empty = sum(1 for r in results if r[0] == "empty")
errors = [r for r in results if r[0] == "error"]
print(f"\n── {section_name} ──────────────────────────────────────────────")
print(f"\n{BOLD}── {section_name} ──────────────────────────────────────────────{RESET}")
for status, mid, result, extra in results:
if status == "ok" and self.args.verbose:
print(f" {mid}{self._fmt_result(result)}")
print(f" {GREEN}{mid}{RESET}{self._fmt_result(result)}")
elif status == "ok":
print(f" {mid}")
print(f" {GREEN}{mid}{RESET}")
elif status == "empty":
print(f" {mid} → empty")
print(f" {RED}{mid}{RESET} → empty")
elif status == "assert_fail":
print(f" {mid} → assert fail")
print(f" {RED}{mid}{RESET} → assert fail")
elif status == "error":
print(f"{mid}{extra}")
print(f" ─── {passed} passed, {empty} empty", end="")
print(f" {YELLOW}{mid}{RESET}{extra}")
color = GREEN if empty == 0 and not errors else RED
print(f" {BOLD}{color}─── {passed} passed, {empty} empty{RESET}", end="")
if errors:
print(f", {len(errors)} errors", end="")
print()
@ -213,7 +231,10 @@ def main():
parser.add_argument("--port", type=int, default=DEFAULT_PORT, help=f"Порт (default: {DEFAULT_PORT})")
parser.add_argument("--asserts", default=None, help="JSON файл с ассертами")
parser.add_argument("--verbose", "-v", action="store_true", help="Показать результат каждой модели")
parser.add_argument("--no-color", action="store_true", help="Отключить цвета в выводе")
args = parser.parse_args()
if args.no_color:
_disable_colors()
TestSmcModels.args = args
suite = unittest.TestLoader().loadTestsFromTestCase(TestSmcModels)