"No","テストID","テスト種別","要件カテゴリ","テストケース名","テスト条件","測定項目","合格基準","優先度","備考"
"1","NFT-001","性能テスト","レスポンス","API一般リクエストレート制限検証","通常クライアントから1分間に3000リクエストを送信","リクエスト成功率/スロットル発生数","3000req/分以内はスロットルなし、超過時は429応答","高","rack_attack.rb: throttle('req/ip', limit: 3000, period: 1.minute)"
"2","NFT-002","性能テスト","レスポンス","ログインスロットリング検証（IP別）","同一IPから5分間に6回以上ログイン試行","スロットル発生タイミング","5回/5分を超えた時点でスロットル","高","rack_attack.rb: throttle('login/ip', limit: 5, period: 5.minutes)"
"3","NFT-003","性能テスト","レスポンス","ログインスロットリング検証（メール別）","同一メールで15分間に11回以上ログイン試行","スロットル発生タイミング","10回/15分を超えた時点でスロットル","高","rack_attack.rb: throttle('login/email', limit: 10, period: 15.minutes)"
"4","NFT-004","性能テスト","レスポンス","パスワードリセットスロットリング検証","同一IPから30分間に6回以上パスワードリセット要求","スロットル発生タイミング","5回/30分を超えた時点でスロットル","高","rack_attack.rb: throttle('reset_password/ip', limit: 5, period: 30.minutes)"
"5","NFT-005","性能テスト","レスポンス","MFA認証スロットリング検証","1分間に6回以上MFA認証試行","スロットル発生タイミング","5回/分を超えた時点でスロットル","高","rack_attack.rb: throttle('mfa_verification/ip', limit: 5, period: 1.minute)"
"6","NFT-006","性能テスト","レスポンス","Widget API会話作成スロットリング検証","同一IPから12時間に7回以上会話作成","スロットル発生タイミング","6回/12時間を超えた時点でスロットル","中","rack_attack.rb: throttle('api/v1/widget/conversations', limit: 6, period: 12.hours)"
"7","NFT-007","性能テスト","レスポンス","レポートAPI個別ユーザースロットリング検証","同一ユーザーから1分間に100リクエスト超過","スロットル発生タイミング","100req/分を超えた時点でスロットル","中","rack_attack.rb: RATE_LIMIT_REPORTS_API_USER_LEVEL=100"
"8","NFT-008","性能テスト","レスポンス","レポートAPIアカウントレベルスロットリング検証","同一アカウントから1分間に1000リクエスト超過","スロットル発生タイミング","1000req/分を超えた時点でスロットル","中","rack_attack.rb: RATE_LIMIT_REPORTS_API_ACCOUNT_LEVEL=1000"
"9","NFT-009","性能テスト","レスポンス","連絡先検索APIスロットリング検証","同一アカウントから1分間に100リクエスト超過","スロットル発生タイミング","100req/分を超えた時点でスロットル","中","rack_attack.rb: RATE_LIMIT_CONTACT_SEARCH=100"
"10","NFT-010","性能テスト","同時接続","Pumaスレッド処理検証","RAILS_MAX_THREADS設定（デフォルト5）で同時リクエスト","スレッド使用率/応答時間","設定スレッド数内で安定処理","中","puma.rb: max_threads_count=ENV.fetch('RAILS_MAX_THREADS', 5)"
"11","NFT-011","性能テスト","同時接続","Pumaワーカープロセス検証","WEB_CONCURRENCY設定でのワーカー数検証","プロセス生成/メモリ使用量","設定ワーカー数が正常起動","中","puma.rb: workers=ENV.fetch('WEB_CONCURRENCY', 0)"
"12","NFT-012","性能テスト","タイムアウト","Webhookタイムアウト検証","外部Webhook呼び出し時のタイムアウト","タイムアウト発生時間","デフォルト5秒、設定可能","中","lib/webhooks/trigger.rb: timeout=GlobalConfig.get_value('WEBHOOK_TIMEOUT')"
"13","NFT-013","性能テスト","タイムアウト","Redis接続タイムアウト検証","Redis接続プールのタイムアウト設定","接続タイムアウト発生","1秒でタイムアウト","中","config/initializers/01_redis.rb: timeout: 1"
"14","NFT-014","性能テスト","キャッシュ","静的ファイルキャッシュ検証","静的ファイルへのリクエスト","Cache-Controlヘッダー","max-age=1年が設定される","低","production.rb: public_file_server.headers={'Cache-Control'=>'public, max-age=31536000'}"
"15","NFT-015","セキュリティテスト","認証","パスワード複雑性検証","パスワード設定時の複雑性要件","バリデーション結果","大文字1/小文字1/数字1/特殊文字1が必要","高","secure_password.rb: password_required_uppercase_count=1, etc."
"16","NFT-016","セキュリティテスト","認証","パスワード長検証","6文字未満/128文字超のパスワード設定試行","バリデーション結果","6-128文字の範囲で許可","高","devise.rb: config.password_length=6..128"
"17","NFT-017","セキュリティテスト","認証","パスワードハッシュ強度検証","パスワード保存時のbcryptストレッチ回数","ハッシュ処理時間/セキュリティレベル","本番環境で11回ストレッチ","高","devise.rb: config.stretches=11"
"18","NFT-018","セキュリティテスト","認証","MFA有効化・無効化検証","二要素認証の有効化/無効化処理","MFA状態の正確な切り替え","OTPシークレット暗号化保存、バックアップコード生成","高","user.rb: encrypts :otp_secret, :otp_backup_codes"
"19","NFT-019","セキュリティテスト","認証","MFAバックアップコード検証","バックアップコードによるMFA認証","認証成功/コード無効化","使用済みコードは再利用不可","高","mfa/authentication_service.rb"
"20","NFT-020","セキュリティテスト","認証","パスワードリセットトークン有効期限検証","6時間経過後のパスワードリセットトークン使用","トークン有効性","6時間で無効化","高","devise.rb: config.reset_password_within=6.hours"
"21","NFT-021","セキュリティテスト","認証","メール再確認検証","メールアドレス変更時の再確認要求","確認プロセス","変更時に再確認必要","中","devise.rb: config.reconfirmable=true"
"22","NFT-022","セキュリティテスト","暗号化","機密データ暗号化検証（Email Channel）","IMAPパスワード/SMTPパスワードの保存","暗号化状態","Chatwoot.encryption_configured?時に暗号化","高","channel/email.rb: encrypts :imap_password, :smtp_password"
"23","NFT-023","セキュリティテスト","暗号化","OTPシークレット暗号化検証","OTPシークレットの保存と取得","暗号化状態/決定論的暗号化","deterministic:trueで暗号化","高","user.rb: encrypts :otp_secret, deterministic: true"
"24","NFT-024","セキュリティテスト","ログ","機密パラメータフィルタリング検証","機密データを含むリクエストのログ出力","ログ内容","password/secret/token等がマスク","高","filter_parameter_logging.rb: filter_parameters"
"25","NFT-025","セキュリティテスト","認可","Punditポリシー基本検証","各リソースへのアクセス権限検証","認可結果","ポリシーに基づく適切なアクセス制御","高","app/policies/application_policy.rb"
"26","NFT-026","セキュリティテスト","認可","アカウントユーザーロール検証","agent/administratorロールの権限差異","実行可能操作","ロールに応じた適切な権限付与","高","account_user.rb: enum role: {agent: 0, administrator: 1}"
"27","NFT-027","セキュリティテスト","入力検証","文字列長制限検証","text型20000文字/string型255文字超の入力","バリデーション結果","制限超過時にエラー","高","application_record.rb: validates_column_content_length"
"28","NFT-028","セキュリティテスト","入力検証","ファイルアップロードサイズ検証","MAXIMUM_FILE_UPLOAD_SIZE超のファイルアップロード","バリデーション結果","制限超過時にエラー（デフォルト40MB）","高","attachment.rb: validate_file_size"
"29","NFT-029","セキュリティテスト","入力検証","ファイルタイプ検証","許可されていないMIMEタイプのアップロード","バリデーション結果","ACCEPTABLE_FILE_TYPES以外は拒否","高","attachment.rb: validate_file_content_type"
"30","NFT-030","セキュリティテスト","通信","FORCE_SSL設定検証","HTTP接続時のリダイレクト動作","プロトコル","FORCE_SSL=trueでHTTPSへリダイレクト","高","production.rb: config.force_ssl=ENV.fetch('FORCE_SSL', false)"
"31","NFT-031","セキュリティテスト","通信","CORSポリシー検証","異なるオリジンからのAPIリクエスト","CORSヘッダー","設定に応じた適切なCORS応答","中","cors.rb: Rack::Cors設定"
"32","NFT-032","セキュリティテスト","ブルートフォース","Super Adminログイン保護検証","5分間に6回以上のSuper Adminログイン試行","スロットル発生","5回/5分を超えた時点でスロットル","高","rack_attack.rb: throttle('super_admin_login/ip', limit: 5, period: 5.minutes)"
"33","NFT-033","セキュリティテスト","ブルートフォース","アカウント作成保護検証","30分間に6回以上のアカウント作成試行","スロットル発生","5回/30分を超えた時点でスロットル","高","rack_attack.rb: throttle('accounts/ip', limit: 5, period: 30.minutes)"
"34","NFT-034","可用性テスト","ヘルスチェック","ヘルスチェックエンドポイント検証","/healthエンドポイントへのリクエスト","応答状態/スロットル除外","常にスロットル対象外、正常応答","高","rack_attack.rb: safelist('health check')"
"35","NFT-035","可用性テスト","排他制御","分散ロック機能検証","Redis::LockManagerによる排他制御","ロック取得/解放","1秒タイムアウトで自動解放","中","lib/redis/lock_manager.rb: LOCK_TIMEOUT=1.second"
"36","NFT-036","可用性テスト","排他制御","MutexApplicationJobロック検証","同時実行を防ぐジョブのロック機構","ジョブ排他実行","ロック取得失敗時はLockAcquisitionError","中","mutex_application_job.rb: with_lock"
"37","NFT-037","可用性テスト","スケジュールジョブ","定期バージョンチェック検証","Internal::CheckNewVersionsJobの実行","実行間隔","毎日12:00 UTCに実行","低","schedule.yml: cron='0 12 */1 * *'"
"38","NFT-038","可用性テスト","スケジュールジョブ","古い通知削除検証","Notification::RemoveOldNotificationJobの実行","削除対象/残存数","1ヶ月以上の通知削除、ユーザー毎に300件まで","低","schedule.yml: cron='30 22 * * *'"
"39","NFT-039","可用性テスト","スケジュールジョブ","会話自動解決検証","TriggerScheduledItemsJobの実行","解決対象","5分毎に実行","中","schedule.yml: cron='*/5 * * * *'"
"40","NFT-040","可用性テスト","スケジュールジョブ","自動割り当て検証","Inboxes::BulkAutoAssignmentJobの実行","割り当て処理","15分毎に実行","中","schedule.yml: cron='*/15 * * * *'"
"41","NFT-041","可用性テスト","エラー処理","Sentryエラー監視検証","エラー発生時のSentry通知","エラー送信","staging/productionで有効","中","sentry.rb: enabled_environments=['staging', 'production']"
"42","NFT-042","可用性テスト","エラー処理","Rack::Timeoutタイムアウト除外検証","タイムアウトエラーのSentry除外","エラー送信除外","RequestTimeoutExceptionは送信除外","中","sentry.rb: excluded_exceptions+=['Rack::Timeout::RequestTimeoutException']"
"43","NFT-043","可用性テスト","WebSocket","ActionCable Redis接続検証","WebSocket接続のRedis設定","接続安定性","GCP Memorystore対応設定","中","actioncable.rb: REDIS_DISABLE_CLIENT_COMMAND"
"44","NFT-044","スケーラビリティテスト","Redisプール","Alfred Redisプール検証","REDIS_ALFRED_SIZE設定でのプールサイズ","プール使用率","デフォルト5、設定可能","中","01_redis.rb: alfred_size=ENV.fetch('REDIS_ALFRED_SIZE', 5)"
"45","NFT-045","スケーラビリティテスト","Redisプール","Velma Redisプール検証","Rack Attack用Redisプール","プール使用率","固定サイズ5","中","01_redis.rb: ConnectionPool.new(size: 5)"
"46","NFT-046","スケーラビリティテスト","バックグラウンド処理","Sidekiqキュー処理検証","scheduled_jobs/housekeeping/purgableキューの処理","キュー処理遅延","キュー別に適切な処理","中","schedule.yml: queue設定"
"47","NFT-047","保守性テスト","ログ","Lograge構造化ログ検証","LOGRAGE_ENABLED=true時のログ出力","ログフォーマット","JSON形式で出力","低","lograge.rb: formatter=Lograge::Formatters::Json.new"
"48","NFT-048","保守性テスト","ログ","Sidekiqログ検証","本番環境でのSidekiqログ出力","ログフォーマット/レベル","JSON形式、LOG_LEVEL設定に従う","低","sidekiq.rb: formatter=Sidekiq::Logger::Formatters::JSON.new"
"49","NFT-049","保守性テスト","ログ","Rack Attackブロックログ検証","スロットル発生時のログ出力","ログ内容","IP/パス/ユーザー識別子/アカウントIDを記録","中","rack_attack.rb: ActiveSupport::Notifications.subscribe"
"50","NFT-050","保守性テスト","トレーシング","Datadog APM検証","DD_TRACE_AGENT_URL設定時のトレーシング","トレース送信","Rails計装が有効","低","datadog.rb: c.tracing.instrument :rails"
"51","NFT-051","保守性テスト","トレーシング","Sentryトランザクション検証","ENABLE_SENTRY_TRANSACTIONS設定時のトレーシング","トレース送信","10%サンプルレート","低","sentry.rb: traces_sample_rate=0.1"
"52","NFT-052","保守性テスト","監査","デシリアライズエラー処理検証","ActiveJob::DeserializationError発生時","エラー処理","ジョブを破棄しログ出力","中","application_job.rb: discard_on ActiveJob::DeserializationError"
"53","NFT-053","互換性テスト","API","APIバージョニング検証","v1/v2 APIエンドポイントの動作","応答互換性","両バージョンが正常動作","中","routes.rb: namespace :v1, :v2"
"54","NFT-054","互換性テスト","認証","OmniAuth Google OAuth2検証","Google OAuth2によるSSO認証","認証フロー","正常に認証完了","中","user.rb: omniauth_providers: [:google_oauth2, :saml]"
"55","NFT-055","互換性テスト","認証","OmniAuth SAML検証","SAML認証によるSSO","認証フロー","正常に認証完了","中","user.rb: omniauth_providers: [:google_oauth2, :saml]"
