В этом материале
Одинаковая последовательность байтов должна давать одинаковые события независимо от разбиения сетевого чтения.
Проверять свойство, а не удачный пример
Обычный тест передаёт парсеру целые строки data и подтверждает один корректный ответ. Реальная сеть может разрезать строку посередине символа или объединить несколько событий в одно чтение. Property test меняет только границы поступления байтов и сохраняет исходный поток. Проверяемое свойство: разобранные события, их порядок и финальное состояние одинаковы для всех выбранных разбиений. Это сильнее набора удобных вручную нарезанных chunks, но не доказывает корректность при любых повреждённых данных. Сначала нужен независимый ожидаемый результат, иначе две одинаково ошибочные реализации будут успешно подтверждать друг друга на каждом запуске.
Создать канонический байтовый корпус
Корпус содержит комментарий, несколько data-строк одного события, пустую строку-разделитель и текст с многобайтовыми символами. Спецификация SSE определяет обработку строк и формирование событий; отдельный JSON payload подчиняется RFC 8259. Эти уровни следует проверять отдельно. Нельзя считать каждый JSON объект сетевым пакетом или требовать, чтобы разделитель события совпадал с концом чтения. В manifest фиксируют сами байты и ожидаемые события после корректной нормализации SSE. Повреждённый JSON помещают в другой случай с явно ожидаемой ошибкой приложения, а не смешивают с допустимой фрагментацией транспорта.
Генерировать границы с бюджетом
Для короткого образца перебирают одиночные разрезы, затем добавляют воспроизводимые случайные разбиения. Отдельные случаи ставят границы внутри UTF-8 последовательности, между CR и LF, внутри имени поля и непосредственно перед разделителем события. Число вариантов ограничивают заранее, потому что полный перебор всех разбиений быстро становится непрактичным. Документация TextDecoder.decode объясняет потоковый режим декодирования: состояние должно сохраняться между чтениями и корректно завершаться в конце. Генератор сохраняет seed, размеры фрагментов и исходный fixture. Эти данные позволяют повторить сбой без хранения реального пользовательского содержимого или полного сетевого дампа.
Уменьшать контрпример осмысленно
При обнаружении различия уменьшают количество разрезов и удаляют лишние события, сохраняя нарушение. Затем пробуют сокращать payload с сохранением необходимых многобайтовых символов. Простое удаление произвольных байтов может превратить корректный вход в другой класс ошибки, поэтому shrinker должен учитывать условия свойства. Например, минимальный пример может показать потерю символа только при разрыве перед последним байтом UTF-8. Отчёт содержит ожидаемые и фактические события, последний принятый offset и причину остановки. Не следует сразу исправлять decoder по внешнему виду повреждённой строки: сначала надо исключить двойную нормализацию или потерю фрагмента тестовой обвязкой.
Отдельно мутировать некорректный поток
После проверки фрагментации добавляют случаи обрезанного UTF-8, незавершённого события и недопустимого JSON. Для них заранее определяют политику отказа; равенство с корректным потоком уже не является целью. Ставят ограничения длины события, памяти и времени выполнения, чтобы генератор не стал источником неограниченной нагрузки. Полезны клиент streaming и проверка потока vLLM. Результат описывает устойчивость конкретного парсера к исследованным границам и мутациям. Он не подтверждает правильность finish_reason, usage, повторов запросов или поведение промежуточного прокси: эти свойства требуют самостоятельных интеграционных сценариев.