Привет, Хабр!
Сегодня поговорим на специфическую тему: автоматизация тестирования ПО для терминалов самообслуживания 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.
Тула в отдельном потоке общается с контроллером по последовательному порту и при необходимости имитирует ситуации приема якобы «денег». Также необходимо иметь какой-то пул тестовых аккаунтов или провайдеров, платежи по которым не запроцессятся по-настоящему, а в какой-то момент отобьются. Либо, для более сложных случаев, деньги действительно проходят через реальный процессинг, но попадают на специальные счета, с которых потом опять же и будут списываться для платежных тестов.
Такой вот круговорот денег в природе.

Принтер чеков (наверху) и купюроприемник (внизу).
Софт для терминалов и банкоматов должен быть предельно устойчив к различным нештатным ситуациям — отсутствию интернета, проблемам с купюроприемником и т.д. Поскольку такой софт запускается на терминале с очень высоким приоритетом и правами, он по сути перехватывает все управление ОС. Нельзя так просто свернуть его или закрыть по Alt-F4. Софт умеет перезагружать сам себя и терминал, в т.ч. В цикле. Необходимо также проверить подобные стрессовые сценарии, например, отсутствие связи с платежным процессингом. Терминал реагирует на это периодическими перезагрузками, нужно проверять, что он делает это корректно и корректно всякий раз загружает терминальный софт.
Установленный терминальный софт централизованно обновляется с помощью платежного процессинга. Процессинг планирует апдейт конкретного терминала и определяет для него профиль обновления, то есть какие файлы и откуда необходимо грузить. Для обновления необходимо выполнить ряд процедур в БД процессинга, а после этого мониторить терминал. Проверять по логам, что началась загрузка необходимых файлов и что обновление было успешно завершено и терминальный софт поднялся.
Чистая установка с нуля сложнее. Обычно «в полях» ее делает человек, просто копируя установочный файл на терминал и вводя настройки и авторизационные данные в формы установщика, который представляет из себя обычно WinAPI приложение со стандартными Windows-контроллерами (TextBox, CheckBox и т.д.).
В наших автотестах для сценария чистой установки на терминал подкладывается Python-скрипт, который запускает установщик, с помощью библиотеки pywinauto взаимодействует с контроллами и пишет собственный установочный лог. После окончания первого этапа установки скрипт завершается, а терминальный софт проходит авторизацию в процессинге, получает пути к профилю установки и выкачивает все необходимые файлы. Здесь уже нужно в реалтайме мониторить логи на терминале через SSH. Все нештатные ситуации во время установки (например, неверные авторизационные данные), пишутся в него, необходимо читать эти логи в онлайн-режиме и при необходимости считать тест чистой установки проваленным.

Чистая установка с нуля MarATL
Мы обзорно пробежались по основным техническим аспектам создания автотестов для терминального софта. Не погружаясь глубоко в технику, разобрали большую задачу на отдельные аспекты. Задавайте в комментариях вопросы, если тема интересна, можно осветить в отдельной статье подробнее некоторые моменты. Спасибо за внимание!
Сегодня поговорим на специфическую тему: автоматизация тестирования ПО для терминалов самообслуживания 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
Заключение
Мы обзорно пробежались по основным техническим аспектам создания автотестов для терминального софта. Не погружаясь глубоко в технику, разобрали большую задачу на отдельные аспекты. Задавайте в комментариях вопросы, если тема интересна, можно осветить в отдельной статье подробнее некоторые моменты. Спасибо за внимание!
