Gmail Listener ждущий определенные письма

Есть набор тестов, которые выполняют различные оплаты.
Подтверждение оплаты происходит с помощью одноразового пароля. Пароль отправляется на тестовый телефон, откуда форвардится на емеил.
Тест, доходит до момента подтверждения оплаты и вызывает метод, который подключается к почте и ждет/ищет необходимое письмо с кодом подтверждения.
Время в течение которого может прийти код подтверждения от 5-10 секунд до 1-2 минут.

Я написал метод, который работает с почтой. Но! он мне очень сильно не нравится, написан откровенно топорно. Проблема в том, что не хватает умений и знаний как правильно написать, чтобы он работал стабильно.

Основные проблемы НЕстабильности 2:

  1. Если код смс задерживается и приходит дольше чем ~30 сек, метод работы с gmail находится в этот момент в while цикле ожидания нового письма, но, отваливается с ошибкой gmail connection failure, через 30-35 сек.
  2. Если запускать тесты скопом, то часто происходит ситуация, что 1ый тест отработал успешно, запустился второй тест. 2ой тест доходит до лисенера почты и тут приходит письмо не с кодом, а с отчетом успешной оплаты по первому тесту, метод видит письмо, не находит в нем нужную фразу и отваливается.

Код лисенера почты под катом. Объясните пожалуйста, как написать стабильный лисенер для почты ?

public class GmailImap {

    private final String email_id = "";
    private final String email_pass = "";

    private String digitCode;

    public String getDigitCode() {
        Properties properties = new Properties();
        properties.put("mail.store.protocol", "imaps");
        properties.put("mail.imaps.host", "imap.gmail.com");
        properties.put("mail.imaps.port", "993");
        properties.put("mail.imap.connectiontimeout", "120000");
        properties.put("mail.imap.timeout", "120000");
        try {
            // Connect to gmail and get store and inbox
            Session session = Session.getDefaultInstance(properties, null);
            Store store = session.getStore("imaps");
            System.out.println("Connection initiated...");
            store.connect(email_id, email_pass);
            System.out.println("Connection is ready...");
            Folder inbox = store.getFolder("inbox");
            inbox.open(Folder.READ_WRITE);

            // Make unseen flag for inbox mails
            Flags seen = new Flags(Flags.Flag.SEEN);
            FlagTerm unseenFlagTerm = new FlagTerm(seen, false);
            Message[] unseenMessages = inbox.search(unseenFlagTerm);

            // Delete all prev unseen mails
            int messageCount = unseenMessages.length;
            System.out.println("crap messages: " + messageCount);
            for (int i = 0; i < messageCount; i++) {
                Message msg = unseenMessages[i];
                inbox.setFlags(new Message[]{msg}, new Flags(Flags.Flag.DELETED), true);
            }

            unseenMessages = inbox.search(unseenFlagTerm);
            messageCount = unseenMessages.length;
            System.out.println("new messages: " + messageCount);

            // Wait for mail

            Long startTime = System.currentTimeMillis();
            do {
                inbox = store.getFolder("inbox");
                inbox.open(Folder.READ_WRITE);
                unseenMessages = inbox.search(unseenFlagTerm);
                messageCount = unseenMessages.length;
                System.out.println((System.currentTimeMillis() - startTime));
            } while ((messageCount == 0) && ((System.currentTimeMillis() - startTime) < 120000));

            unseenMessages = inbox.search(unseenFlagTerm);
            messageCount = unseenMessages.length;
            System.out.println(messageCount);

            String messageContent;
            unseenMessages = inbox.search(unseenFlagTerm);
            messageContent = unseenMessages[0].getContent().toString();
            System.out.println(messageContent);

            // get company digitCode
            if (messageContent.contains("condition_1") {
                Pattern p = Pattern.compile("\\d{6}");
                Matcher m = p.matcher(messageContent);
                while (m.find()) {
                    System.out.println(m.group());
                    digitCode = m.group();
                }
            }

            // get external digitCode
            if (messageContent.contains("condition_2")) {
                Pattern p = Pattern.compile("\\d{5}");
                Matcher m = p.matcher(messageContent);
                while (m.find()) {
                    System.out.println(m.group());
                    digitCode = m.group();
                }
            }

            inbox.setFlags(new Message[]{unseenMessages[0]}, new Flags(Flags.Flag.DELETED), true);
            inbox.close(true);
            store.close();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NoSuchProviderException e) {
            e.printStackTrace();
        } catch (MessagingException e) {
            e.printStackTrace();
        }
        return digitCode;
    }
}

по 2му. А есть возможность при форварде с телефона добавлять (в тему письма или куда то еще) какой то идентификатор который генерится специально для теста? И тогда можно фильтровать емейлы по нему.

По 1му - а почему отваливается сам gmail? У меня реализован тест на gmail и он иногда ожидает до 20 минут. ( иногда и больше) Сам использую python для этого.

форвард осуществляю с помощью https://cofp.ru/smsforwarder/quick_start.php
тут нет возможности добавить параметр. Есть только возможность настройки фильтров. Проблема апликухи в том, что стандартно дают 4 фильтра. Я их настроил, но различных писем много и мне 4 фильтров мало. Больше фильтров - в платной версии. Проблема только в том, что кнопка купить премиум версию - не работает в апликухе)))

Перепробовал около 10-15 различных апликух для пересылки смс, основные проблемы были с тем, что апликухи слишком долго перенаправляли смски и не всегда вообще перенаправляли. Эта оказалась самой шустрой и стабильной в плане пересылки смс на почту.

Я пытался разобраться почему gmail вываливает connection failure, но так и не понял. Единственное подозрение на мой цикл while в котором я постоянно дергаю store и обновляю inbox со store. Возможно в цикле это происходит слишком много раз и gmail сервер меня просто выкидывает из-за кол-ва запросов. Но, это лишь мои догадки. Разобраться в причине этой ошибки я не смог =(((

а можешь код в теме вставить через

код

так будет легче его читать.

1 лайк

готово.

Я для такого же теста использовала методы из этой статьи: https://angiejones.tech/test-automation-to-verify-email/
Чтобы подождать письмо, используем Awaitility (надо добавить депенденси в Грэдл или Мэвен). Можно регулировать длительность, у нас 15 сек.:

private void waitForEmailOrPhoneLinkToBeReceived(String email, String subject, String content) {
        await()
                .pollInSameThread()
                .ignoreExceptions()
                .atMost(15, TimeUnit.SECONDS)
                .with()
                .pollDelay(1, TimeUnit.SECONDS)
                .and()
                .pollInterval(1, TimeUnit.SECONDS)
                .until(() -> isNewMessageReceived(email, subject, content));
    }
2 лайка

А нельзя подключить БД, или нужно именно проверять с gmail?

2 лайка

Надо вообще напрямую в БД смотреть, согласен. Зачем эти велосипеды с каким-то тестовым телефоном, потом ещё и с почтой.
Код отправился? Смотрим в БД какой код, вставляем. Всё.

ребят, я не настолько новичок, чтобы не подумать об этом варианте.
Проблема в том, что смс с кодом отправляют различные банки и сторонние сервисы, к которым доступа нет и быть не может никогда.

Ну ок, перекладывайте код с тестового телефона в БД/куда-то ещё. На кой фиг тут почту юзать ещё, к тому же хрен знает когда там это сообщение дойдет и дойдет ли вообще?

хотя бы в 2 словах, каким образом с телефона записывать код в БД ?
если подключаться напрямую к телефону через Апиум, например, то тогда и БД не очень нужно на сколько я понимаю, можно парсить смски на телефоне. Об этом варианте я тоже думал, но отказался, т.к. не знаю как подключиться к телефону, если тесты запускаются на удаленной виртуалке. С Апиумом работал очень немного и знаю только, что телефон должен быть подключен к компу для доступа. Могу и ошибаться к-но.

1 лайк