メールOTPを取得してjmeterで負荷をかける

ワンタイムパスワードを使った認証の画面の性能テストをする際に、受信したメールからワンタイムパスワードを取得して認証するという操作が必要です。今回はメールからワンタイムパスワードを取得して、取得したワンタイムパスワードをhttpのbodyデータに乗せるところまでを検証したのでその記録となります。

## 取得方法
gmailの内容を取得する方法は私が観測した限り以下の２つがあります。
・OAuthを使ったgmail APIを使って取得
・IMAPサーバーから取得

今回はIMAPサーバーから取得で検証しました。
（OAuthを使ったgmail APIだと無料枠があるとはいえ従量課金になるので、また今度検証します。）

## アプリパスワードの設定
下記を参考にアプリパスワードを設定します。
https://support.google.com/accounts/answer/185833

## OTPを取得するpythonを作成
pythonの環境がない場合は構築します。
参考：https://qiita.com/million_hundred/items/3787f6c61a4fb38d6a89


OTPを取得するpythonコードを書きます。
件名：OTP
メール本文：Your OTP is 123456
というメールが届くという前提でコードを書いています。
```python get_otp.py
import imaplib
import email 
from email.header import decode_header
import re

# メールサーバーの情報
username = "example@gmail.com"
password = "aplicationPassword"
mail_server = "imap.gmail.com"
try:
    # IMAPサーバーに接続
    mail = imaplib.IMAP4_SSL(mail_server)
    mail.login(username, password)
    mail.select("inbox")

    # 最新のメールを取得
    result, data = mail.search(None, 'SUBJECT "OTP"')
    mail_ids = data[0].split()

    if not mail_ids:
        print("OTPに関するメールが見つかりません")
    else:
        latest_email_id = mail_ids[-1]
        
        # メールの内容を取得
        result, msg_data = mail.fetch(latest_email_id, "(RFC822)")
        raw_email = msg_data[0][1]
        msg = email.message_from_bytes(raw_email)

        # 件名OTPを取得
        subject, encoding = decode_header(msg["Subject"])[0]
        if isinstance(subject, bytes):
            subject = subject.decode(encoding if encoding else "utf-8")

        # メール本文を取得
        if msg.is_multipart():
            for part in msg.walk():
                if part.get_content_type() == "text/plain":
                    body = part.get_payload(decode=True).decode("utf-8", errors="ignore")
                    break
        else:
            body = msg.get_payload(decode=True).decode("utf-8", errors="ignore")

        # メール本文から、Your OTP is 123456のような6桁のOTPコードを取得
        otp_match = re.search(r"Your OTP is (\d{6})", body)
        if otp_match:
            print( otp_match.group(1))
        else:
            print("OTPコードが見つかりません")

    mail.logout()

except imaplib.IMAP4.error as e:
    print("IMAPエラー:", e)
except Exception as e:
    print("エラーが発生しました:", e)

```

## pythonコードの配置
get_opt.pyをjmeterのbin直下に置きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920773/9cb92459-a393-4e1c-bf65-0ab63735f299.png)

## JmeterのBeanShellSamplerの作成
JmeterでBeanShellSamplerを作成して以下のようにスクリプトを書きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920773/08596310-6f8c-45a8-9d5f-fe324bc645ea.png)


```java
String command = "python get_otp.py";
Process proc = Runtime.getRuntime().exec(command);
BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
String otp = reader.readLine(); 
vars.put("otp", otp); 
log.info("OTP: " + otp);
```

## http request bodyの設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920773/e351f5bc-24d0-481e-9423-8989570df341.png)
テンプレートリテラル${}で変数を囲ってあげると、当該コンポーネントより前に宣言された変数の値を取り出すことができます。

## メールの作成
自分から自分あてにワンタイムパスワードのメールを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920773/8e9adb49-dfa8-4cc4-90ac-aa8ec99bc131.png)


## Jmeterの実行
上のスタートボタンを実行してView Results Treeから実行結果を見てみます。
otpにメールから取得した値が設定できたことが確認できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920773/ab91e435-600a-4e27-9053-0b34997ca621.png)

## 参考
https://qiita.com/HirumaT/items/75b45e67f9b08bb9c2fa
https://www.skyarch.net/blog/apache-jmeter%E3%81%A8beanshell%E3%81%A7%E6%9F%94%E8%BB%9F%E3%81%AA%E8%B2%A0%E8%8D%B7%E3%83%86%E3%82%B9%E3%83%88%E3%82%92%E3%81%97%E3%82%88%E3%81%86%EF%BC%81/