alumni_lookup

Email Testing Guide — QA Checklist

Version: 1.0
Last Updated: January 2026
Purpose: Comprehensive guide for testing all emails in the Alumni Lookup and Champion Portal applications


Table of Contents

  1. Development Email Setup
  2. Champion Portal Emails
  3. Lookup Portal Emails
  4. Devise/Authentication Emails
  5. Testing Checklist
  6. Troubleshooting

1. Development Email Setup

Letter Opener (Development)

All emails in development are captured by Letter Opener and opened in your browser automatically.

View past emails:

tmp/letter_opener/

Clear all development emails:

rm -rf tmp/letter_opener/*

Staging/Production Email Delivery

Environment Delivery Method View Sent Emails
Development Letter Opener (browser) tmp/letter_opener/
Staging Postmark Postmark Dashboard
Production Postmark Postmark Dashboard

2. Champion Portal Emails

2.1 Notification Mailer (Cp::NotificationMailer)

These emails are triggered by the notification system (Phase 8.3).

Immediate Notification

Purpose: Send critical notifications immediately (e.g., messages, mentions, new events)

Trigger:

# Happens automatically when notification is created with digest_preference: :immediate
# Or manually:
notification = Cp::Notification.last
Cp::NotificationMailer.immediate_notification(notification).deliver_now

Testing Steps:

  1. Log in as a Champion with digest_preference: "immediate"
  2. Have another user send them a message
  3. Verify email is sent immediately with:
    • Correct notification content
    • Working “View” button link
    • “Manage notification preferences” link in footer

Daily Digest

Purpose: Bundle multiple notifications into a single daily email (sent at 7am)

Trigger:

# Rails console - simulate daily digest
champion = Cp::Champion.find_by(email: "test@example.com")
notifications = champion.notifications.pending_email.limit(10)
Cp::NotificationMailer.daily_digest(champion, notifications, unread_threads: champion.unread_message_threads).deliver_now

Testing Steps:

  1. Create 3+ notifications for a Champion with digest_preference: "daily"
  2. Run the rake task or console command above
  3. Verify email contains:
    • Grouped notifications by category
    • Unread messages count (if any)
    • Links to each notification source

Rake Tasks:

# Preview what would be sent (doesn't send email)
bin/rake "notifications:digests:preview_daily[email@example.com]"

# Send to a specific champion
bin/rake "notifications:digests:send_daily_to[email@example.com]"

# Send to all eligible champions
bin/rake notifications:digests:send_daily

Weekly Digest

Purpose: Weekly summary email with notifications, events, and stats (sent Monday 7am)

Trigger:

champion = Cp::Champion.find_by(email: "test@example.com")
notifications = champion.notifications.where("created_at > ?", 7.days.ago)
upcoming_events = Cp::Event.upcoming.limit(5)
week_stats = { new_champions: 5, messages_received: 3, posts_created: 2 }
Cp::NotificationMailer.weekly_digest(champion, notifications, upcoming_events: upcoming_events, week_stats: week_stats).deliver_now

Testing Steps:

  1. Champion must have digest_preference: "weekly"
  2. Run the command above
  3. Verify email shows:
    • Weekly notification summary
    • Upcoming events section
    • Community stats

Rake Tasks:

# Preview what would be sent (doesn't send email)
bin/rake "notifications:digests:preview_weekly[email@example.com]"

# Send to a specific champion
bin/rake "notifications:digests:send_weekly_to[email@example.com]"

# Send to all eligible champions (bypasses Monday check)
bin/rake notifications:digests:send_weekly

Verification Pending Notification

Purpose: Notify Champion their verification is being processed

Trigger:

champion = Cp::Champion.find(id)
notification = champion.notifications.create!(
  notifiable_type: "Cp::Champion",
  notifiable_id: champion.id,
  category: "account",
  title: "Verification in Progress"
)
Cp::NotificationMailer.verification_pending_notification(notification).deliver_now

Testing Steps:

  1. This is auto-sent when Champion completes signup
  2. Verify email contains:
    • “We’re reviewing your account”
    • Expected timeline
    • What to expect next

Verification Approved Notification

Purpose: Welcome email when Champion is verified

Trigger:

# Happens automatically when admin verifies a Champion
# Or manually:
champion = Cp::Champion.find(id)
notification = champion.notifications.where(category: "account").last
Cp::NotificationMailer.verification_approved_notification(notification).deliver_now

Testing Steps:

  1. In Admin: Verify a pending Champion
  2. Verify welcome email contains:
    • Celebratory message 🎉
    • Link to directory
    • Getting started tips

Support Reply Notification

Purpose: Notify Champion when staff replies to their support thread

Trigger:

notification = Cp::Notification.where(category: "support").last
Cp::NotificationMailer.support_reply_notification(notification).deliver_now

Testing Steps:

  1. Create a support thread as a CL
  2. Have staff reply from Lookup Portal
  3. Verify Champion receives email with:
    • Reply preview
    • Link to thread

Community Leader Assigned Notification

Purpose: Notify Champion when they’re assigned as a Community Leader

Trigger:

notification = Cp::Notification.where(category: "community_leader").last
Cp::NotificationMailer.community_leader_assigned_notification(notification).deliver_now

Testing Steps:

  1. In Admin: Assign a Champion as CL for a community
  2. Verify they receive email with:
    • Community name
    • CL responsibilities
    • Link to CL dashboard

2.2 Community Mailer (Cp::CommunityMailer)

Join Request Submitted (to Engagement Team)

Purpose: Notify staff when a Champion requests to join a community

Trigger:

join_request = Cp::JoinRequest.last
Cp::CommunityMailer.join_request_submitted(join_request).deliver_now

Testing Steps:

  1. Log in as Champion
  2. Request to join a private community
  3. Verify Engagement Team receives email with:
    • Champion name and community
    • One-click approve/deny links

Join Request Approved

Purpose: Notify Champion their join request was approved

Trigger:

join_request = Cp::JoinRequest.last
Cp::CommunityMailer.join_request_approved(join_request).deliver_now

Testing Steps:

  1. Approve a pending join request
  2. Verify Champion receives email with:
    • Welcome message
    • Link to community

Join Request Denied

Purpose: Notify Champion their join request was denied

Trigger:

join_request = Cp::JoinRequest.where(status: "denied").last
Cp::CommunityMailer.join_request_denied(join_request).deliver_now

Testing Steps:

  1. Deny a pending join request
  2. Verify Champion receives polite denial email

Member Removed

Purpose: Notify Champion when removed from a community

Trigger:

membership = Cp::CommunityMembership.last
Cp::CommunityMailer.member_removed(membership).deliver_now

2.3 Moderation Mailer (Cp::ModerationMailer)

Content Auto-Hidden

Purpose: Notify Community Leader when content is auto-hidden due to flags

Trigger:

leader = Cp::Champion.community_leaders.first
content = Cp::BoardPost.hidden.last
community = content.board.community
Cp::ModerationMailer.content_auto_hidden(leader: leader, content: content, community: community, flag_count: 3).deliver_now

Testing Steps:

  1. Flag a post/comment multiple times until auto-hidden
  2. Verify CL receives email with:
    • Content preview
    • Flag summary
    • Link to moderation queue

Content Escalated

Purpose: Notify Engagement Team when CL escalates content

Trigger:

content = Cp::BoardPost.last
community = content.board.community
escalated_by = Cp::Champion.community_leaders.first
Cp::ModerationMailer.content_escalated(content: content, community: community, escalated_by: escalated_by, notes: "Needs review").deliver_now

Testing Steps:

  1. As CL, escalate flagged content
  2. Verify Engagement Team receives email with:
    • CL notes
    • Content preview
    • Link to content

2.4 Support Thread Mailer (Cp::SupportThreadMailer)

New Thread Notification (to Staff)

Purpose: Notify support responders when CL creates support thread

Trigger:

support_thread = Cp::SupportThread.last
Cp::SupportThreadMailer.new_thread_notification(support_thread).deliver_now

Testing Steps:

  1. As CL, create a support thread
  2. Verify staff with can_support_respond: true receive email

Staff Reply Notification (to Champion)

Purpose: Notify CL when staff replies to their support thread

Trigger:

thread = Cp::SupportThread.last
message = thread.messages.last
Cp::SupportThreadMailer.staff_reply_notification(thread, message).deliver_now

Testing Steps:

  1. Staff replies to support thread in Lookup Portal
  2. Verify CL receives notification email

2.5 Admin Notification Mailer (Cp::AdminNotificationMailer)

New Champion Signup

Purpose: Notify admins when new Champion signs up

Trigger:

champion = Cp::Champion.last
Cp::AdminNotificationMailer.new_champion_signup(champion).deliver_now

Testing Steps:

  1. Complete Champion signup flow
  2. Verify admins receive notification with:
    • Champion details
    • Link to verification queue

Weekly Admin Digest

Purpose: Weekly summary of Champion Portal activity for staff

Trigger:

user = User.admin.first
Cp::AdminNotificationMailer.weekly_digest(user, period_start: 1.week.ago, period_end: Time.current).deliver_now

Testing Steps:

  1. Run digest command
  2. Verify email shows:
    • New signups count
    • Verification stats
    • Activity metrics

2.6 Champion Mailer (Cp::ChampionMailer)

Champion Invite

Purpose: Invite someone to become a Champion

Trigger:

inviter = Cp::Champion.verified.first
Cp::ChampionMailer.invite(inviter: inviter, recipient_email: "friend@example.com", personal_note: "Join us!").deliver_now

Testing Steps:

  1. Use “Invite a Champion” feature
  2. Verify invite email contains:
    • Inviter’s name
    • Personal note
    • Signup link

2.7 Message Mailer (MessageMailer)

New Message Notification

Purpose: Notify Champion of new direct message

Trigger:

message = Cp::Message.last
recipient = message.thread.participants.where.not(champion_id: message.sender_id).first.champion
MessageMailer.new_message_notification(message: message, recipient: recipient).deliver_now

Testing Steps:

  1. Send a message to another Champion
  2. Verify recipient receives email with:
    • Sender name
    • Message preview
    • Reply link

2.8 Feedback Mailer (Cp::FeedbackMailer)

Beta Feedback

Purpose: Send user feedback to the team

Trigger:

champion = Cp::Champion.first
Cp::FeedbackMailer.beta_feedback(champion: champion, feedback_type: "bug", message: "Test feedback", page_url: "/dashboard").deliver_now

Testing Steps:

  1. Use the feedback widget in Champion Portal
  2. Submit bug/feature/question feedback
  3. Verify team receives email with:
    • Feedback type
    • User message
    • Page URL
    • Champion info

2.9 Champion Signup Mailer (ChampionSignupMailer)

Welcome Email

Purpose: Welcome new Champion after signup (legacy flow)

Trigger:

signup = ChampionSignup.last
ChampionSignupMailer.welcome_email(signup).deliver_now

Admin Notification

Purpose: Notify admins of new signup (legacy flow)

Trigger:

signup = ChampionSignup.last
ChampionSignupMailer.admin_notification(signup).deliver_now

3. Lookup Portal Emails

User Mailer (UserMailer)

OTP Email

Purpose: Send one-time verification code

Trigger:

user = User.first
UserMailer.otp_email(user).deliver_now

Testing Steps:

  1. Trigger OTP verification flow
  2. Verify email contains:
    • 6-digit code
    • Expiration info

4. Devise/Authentication Emails

Password Reset

Trigger: Click “Forgot Password” on login page

Confirmation Email

Trigger: New user signup (if confirmable enabled)

Unlock Instructions

Trigger: Account locked after failed attempts


5. Testing Checklist

Pre-Release Email QA

For each email type, verify:

Check Description
Subject Line Clear, correct, includes relevant context
From Address Correct domain (alumnichampions.com vs alumnilookup.com)
Reply-To Set correctly (usually alumni@belmont.edu)
Recipient Correct person receives the email
Content All personalization tokens render correctly
Links All links work and go to correct domain
Mobile Email renders well on mobile
Footer Includes unsubscribe/preferences link

Quick Smoke Test

Run this in console to test all mailers compile:

# Test all mailers can be instantiated (doesn't send)
[
  -> { Cp::NotificationMailer },
  -> { Cp::CommunityMailer },
  -> { Cp::ModerationMailer },
  -> { Cp::SupportThreadMailer },
  -> { Cp::AdminNotificationMailer },
  -> { Cp::ChampionMailer },
  -> { Cp::FeedbackMailer },
  -> { MessageMailer },
  -> { ChampionSignupMailer },
  -> { UserMailer }
].each { |m| puts "#{m.call.name}: OK" }

6. Troubleshooting

Email Not Sending

  1. Check delivery method:
    Rails.application.config.action_mailer.delivery_method
    # Should be :letter_opener in dev, :postmark in prod
    
  2. Check Postmark API key:
    ENV['POSTMARK_API_TOKEN'].present?
    
  3. Check logs:
    tail -f log/development.log | grep -i mail
    

Check default_url_options in the mailer. Champion Portal mailers should use:

{ host: "alumnichampions.com", protocol: "https" }

Missing Environment Variables

Required for production:


Quick Reference: Console Commands

# Find recent emails by type
Cp::Notification.where(category: "message").order(created_at: :desc).limit(5)

# Check Champion's notification preferences
champion = Cp::Champion.find_by(email: "...")
champion.notification_settings

# Preview an email in console
mail = Cp::NotificationMailer.immediate_notification(Cp::Notification.last)
puts mail.body.encoded

Quick Reference: Rake Tasks

Note: In zsh, you must quote rake tasks with brackets to avoid glob expansion errors.

# List all champions with digest notifications configured
bin/rake notifications:digest_recipients

# Preview digests (doesn't send email)
bin/rake "notifications:digests:preview_daily[email@example.com]"
bin/rake "notifications:digests:preview_weekly[email@example.com]"

# Send digests to specific champion
bin/rake "notifications:digests:send_daily_to[email@example.com]"
bin/rake "notifications:digests:send_weekly_to[email@example.com]"

# Send digests to all eligible champions
bin/rake notifications:digests:send_daily
bin/rake notifications:digests:send_weekly

Document maintained by Engineering team. Update when new mailers are added.