Привет!
Сегодня поговорим на специфическую тему: автоматизация тестирования ПО для терминалов самообслуживания QIWI.
В теме автоматизации тестирования есть области, которые исхожены вдоль и поперек несколько раз, например, тестирование веб-сервисов. Для таких областей существуют отдельные инструменты, паттерны и best practices. Выдумывать ничего не нужно, риски минимальны, берешь и делаешь.
Бывают и обратные ситуации. Предметная область специфична, подглядеть готовые решения не у кого, инструментов нет, технологический стек продукта своеобразен. Приходится глубоко погружаться в предметную область, из палок и эээ… других подручных материалов мастерить себе инструменты и попутно собирать много-много грабель разной величины и убойной силы.
Вот о чем-то таком и хотелось рассказать сегодня. Статья подойдет тем, кто занимается разработкой и тестированием софта для банковских терминалов или автоматов самообслуживания. А также тем, кто хочет расширить свой технический кругозор примерами «а еще бывает вот так».
QIWI-терминал в 2020. На заднем фоне можно увидеть его начинку.
Проблема
Первый терминал QIWI появился в 2004 году и тогда умел пополнять счет мобильного телефона. С тех пор много воды утекло, и АСО (автоматы самообслуживания) сильно изменились. Теперь с помощью них можно пополнять банковские карты, осуществлять переводы, оплачивать услуги разных поставщиков (провайдеров) — ЖКХ, штрафы ГИБДД, кредитные организации, наши собственные продукты QIWI (QIWI-кошелек, карта беспроцентной рассрочки Совесть). Терминалы QIWI умеют также работать в режиме постаматов (автоматизированных пунктов выдачи для интернет-магазинов).
Что из себя представляет терминал?
Терминал АСО с точки зрения железа — обычный десктоп со специфической периферией, такой как купюро- и монетоприемники, принтеры чеков, диспенсеры, POS-терминалы (прием пластиковых карт) и т.д.
И тут первая проблема.
Вот эта известнейшая фотография была сделана в округе Сонома в Калифорнии. Говорят, что это натуральные цвета, без всякой обработки. Кстати, именно на территории округа Сонома располагалась самая южная русская колония в Америке.
Да, это она. Операционная система, поддержку которой прекратили в 2014 году, Windows XP. Версию POSReady тянули аж до 2019 года, но… Тут не все так просто.
Дело в том, что терминалы не являются собственностью компании QIWI. Они принадлежат партнерам, т.н. Агентам, ИП и юридическим лицам, которые заключают с QIWI договор, закупают терминалы, арендуют площади в ТЦ и получают практически пассивный доход.
Соответственно, 90% парка терминалов QIWI (а их более 100к по России и зарубежным странам) работают под управлением Windows XP на самом разном железе — от 512 Мб до 4 Гб оперативной памяти, самые разные ЦПУ (от Pentium 4 глубоких нулевых годов до более-менее современных Core i5) и разное качество и скорость интернета. Терминалы разного возраста, какие-то из них регулярно апгрейдят, а какие-то не апгрейдили очень давно (работает — не трожь!). Наша же задача — регулярно поставлять свежие обновления терминального ПО (оно называется MarATL) и сохранять совместимость со всем этим многообразием начинки и периферии.
Операционная система с истекшей поддержкой
Представим себе простенькую схему автоматизации тестирования. У нас есть CI-сервер, например, TeamCity или Jenkins. Наш терминальный софт собирается из сырцов по событию (коммиту или мёржу), собранный бинарный файл куда-то выкладывается. Происходит автомагическая установка софта, запускаются ночные функциональные автотесты… Эээ, стоп! А куда происходит установка софта? И как?
Как я говорил выше, 90 процентов терминалов работают под управлением Windows XP, поддержка которой завершилась в 2014 году. Это означает, что эта ОС давно уже не соответствует политикам безопасности, для нее не выпускают обновления, под нее нет свежего работающего софта и даже родная майкрософтская Visual Studio компилирует C++ для нее только после плясок с бубном, а точнее с версией toolchain для сборщика MSBuild.
В общем, запускать на самом терминале или виртуалке с Windows XP тесты и вообще какой-то софт — идея очень плохая. Buildagent TeamCity на XP не поднимешь, в Ansible playbook такую машину не настроишь, контейнеров там тоже нет. Шаблонов виртуализации для Windows XP тоже не вот чтобы много. В любой крупной компании, думающей об информационной безопасности, машину с Windows XP будут держать подальше от корпоративной сети или тем более домена. То есть все взаимодействие с терминалом (или виртуалкой, притворяющейся им) должно происходить по удаленке, на самом терминале должен быть минимум стороннего софта.
Решение
Некоторые люди удивляются, когда слышат о связке OpenSSH и Windows XP. В современных версия ОС от Майкрософт поддержка SSH включена прямо в систему. В уже не очень современных (Windows 7), есть установщики SSH с помощью PowerShell. C Windows XP ситуация чуть хуже, но только чуть. Есть древняя версия установщика, которая работает на ней без дополнительного софта.
SSH — старый, добрый, надежный способ удаленного управления хостом, общеизвестная технология. Нужно реализовать такие классы SSH-коннекторов, которые смогут параллельно делать несколько вещей. Например, в режиме онлайн подгружать свежие логи с терминала, выполнять какие-то команды (копирование файлов, запуск скриптов, выдача прав, получение списка процессов, мониторинг системного времени терминала и т.д.).
С мониторингом системного времени особенно интересно. Стандартными средствами командной строки Windows XP выдает время только в форматированном виде часы-минуты-секунды и т.д., никакого вам UNIX-time. Засада, например, в том, что XP уже давно не получает обновлений таймзон, а наше российское правительство известно постоянными играми с отменами летнего или зимнего времени.
Например, стандартный Windows XP SP3 в таймзоне московского времени показывает время на час раньше реального московского времени. Соответственно, таймстемпы логов терминала в любом случае не совпадают со временем на тестовом контуре.
Платежи-платежи-платежи…
Интерфейс QIWI-терминала — это по сути веб, который запускается на браузерном движке, как правило довольно старом. По протоколу websocket он взаимодействует с MarATL, собственно терминальным софтом. При выборе какого-то платежа на терминале (например, оплаты сотовой связи) с помощью серии команд выбирается платежный провайдер, выясняются суммы комиссий, ограничения по приему купюр, создается платеж на конкретного провайдера, который потом через терминальный софт отправляется в процессинг платежей.
Взаимодействовать с веб-интерфейсом по удаленке — так себе идея, тем более тесты интерфейса — другая задача. Можно создать тестовую веб-страницу, которая будет подкладываться на время тестов вместо стандартной index.html. На странице выполняется JS-сценарий, который работает с терминальным софтом, а с другой стороны создает у себя вебсокет-клиент, смотрящий наружу, в сторону хоста, на котором запускаются тесты.
На стороне автотестов нужно реализовать вебсокет-сервер, ожидающий на старте подключения клиента с терминала и отправляющий команды, который в неизменном виде через тестовую страницу пробрасываются в терминальный софт. Ответы софта также отправляются на тестовый вебсокет-сервер.
То есть можно описать сет команд для платежного теста в виде JSON, в котором последовательно описано, какие команды нужно отправить и какой ответ на них ждать.
Тестовая страница интерфейса. Выводит на экран команды, проходящие через нее.
А где же деньги?
Самая коварная техническая задача — имитировать периферийные устройства терминала. Принтер чеков имитируется тривиально, в ОС создается виртуальный принтер, печатающий данные в файл. Можно достать с терминала этот чек (или его копию, формируемую самим терминальным софтом) и раскодировать, попутно проверив, например, что в этом чеке присутствуют все необходимые поля.
Сложнее с устройствами, принимающими деньги — купюро- и монетоприемниками. Понятно, что если мы хотим протестировать сценарий платежа, нам нужно как-то эмулировать присутствие купюроприемника на терминале, поддержать функционал приема купюр, возврата, имитации разных проблем (например, возврат мятой купюры).
Большинство устройств приема денег работают по протоколу CashCode NET. Этот протокол определяет сценарии взаимодействия купюроприемника и контроллера (в нашем случае, контроллер — это наше терминальное ПО).
Пример взаимодействия контроллера и валидатора купюр по протоколу CashCode. С определенной периодичностью контроллер запрашивает статусы устройства.
Минимальный размер команды CashCode — 6 байт. Описание команд можно посмотреть в спецификации протокола CashCode NET.
Стандартный купюроприемник подключается к терминалу через COM-порт. Существуют сторонние утилиты, позволяющие создавать на машине виртуальный последовательный порт, например, VSPE. Поддерживаются даже сценарии проброса этого порта через TCP-соединение.
Наш случай проще, нам необходима тула, умеющая цепляться к порту средствами WinAPI и имеющая произвольный удобный интерфейс, например, самый простой stdin/stdout, который можно подцепить с помощью SSH.
Тула в отдельном потоке общается с контроллером по последовательному порту и при необходимости имитирует ситуации приема якобы «денег». Также необходимо иметь какой-то пул тестовых аккаунтов или провайдеров, платежи по которым не запроцессятся по-настоящему, а в какой-то момент отобьются. Либо, для более сложных случаев, деньги действительно проходят через реальный процессинг, но попадают на специальные счета, с которых потом опять же и будут списываться для платежных тестов.
Такой вот круговорот денег в природе.
Принтер чеков (наверху) и купюроприемник (внизу).
24/7
Софт для терминалов и банкоматов должен быть предельно устойчив к различным нештатным ситуациям — отсутствию интернета, проблемам с купюроприемником и т.д. Поскольку такой софт запускается на терминале с очень высоким приоритетом и правами, он по сути перехватывает все управление ОС. Нельзя так просто свернуть его или закрыть по Alt-F4. Софт умеет перезагружать сам себя и терминал, в т.ч. В цикле. Необходимо также проверить подобные стрессовые сценарии, например, отсутствие связи с платежным процессингом. Терминал реагирует на это периодическими перезагрузками, нужно проверять, что он делает это корректно и корректно всякий раз загружает терминальный софт.
Установка с нуля и обновление
Установленный терминальный софт централизованно обновляется с помощью платежного процессинга. Процессинг планирует апдейт конкретного терминала и определяет для него профиль обновления, то есть какие файлы и откуда необходимо грузить. Для обновления необходимо выполнить ряд процедур в БД процессинга, а после этого мониторить терминал. Проверять по логам, что началась загрузка необходимых файлов и что обновление было успешно завершено и терминальный софт поднялся.
Чистая установка с нуля сложнее. Обычно «в полях» ее делает человек, просто копируя установочный файл на терминал и вводя настройки и авторизационные данные в формы установщика, который представляет из себя обычно WinAPI приложение со стандартными Windows-контроллерами (TextBox, CheckBox и т.д.).
В наших автотестах для сценария чистой установки на терминал подкладывается Python-скрипт, который запускает установщик, с помощью библиотеки pywinauto взаимодействует с контроллами и пишет собственный установочный лог. После окончания первого этапа установки скрипт завершается, а терминальный софт проходит авторизацию в процессинге, получает пути к профилю установки и выкачивает все необходимые файлы. Здесь уже нужно в реалтайме мониторить логи на терминале через SSH. Все нештатные ситуации во время установки (например, неверные авторизационные данные), пишутся в него, необходимо читать эти логи в онлайн-режиме и при необходимости считать тест чистой установки проваленным.
Чистая установка с нуля MarATL
Заключение
Мы обзорно пробежались по основным техническим аспектам создания автотестов для терминального софта. Не погружаясь глубоко в технику, разобрали большую задачу на отдельные аспекты. Задавайте в комментариях вопросы, если тема интересна, можно осветить в отдельной статье подробнее некоторые моменты. Спасибо за внимание!
Автор: Oleg Savinov