Skip to content

Manual Test Runbook — A5: HubSpot discovery trigger

Owner: Sagar  |  Time: ~45 min  |  Requires: HubSpot sandbox + GitHub token + Postmark/email service

Purpose

Verify that the A5 Custom Code Action dispatches the G4 discovery workflow and sends an offer email when a HubSpot Deal moves to "Qualified".

Prerequisites

  • HubSpot sandbox account with a Deals pipeline.
  • GitHub token (fine-grained PAT with actions:write on snowops-automation repo).
  • Postmark account (or another email provider configured).
  • Sandbox Deal has these custom properties created:
  • discovery_tenant_id (single-line text)
  • discovery_subscription_id (single-line text)
  • discovery_subscription_display_name (single-line text, optional)
  • discovery_status (single-line text, default null)
  • discovery_triggered_at (datetime)
  • contact_email (single-line text, mapped from primary contact)
  • contact_firstname (single-line text, mapped from primary contact)
  • company_name (single-line text)

Steps

1. Unit test suite

cd apps/crm-automations
npm ci
npm test
  • All 4 test suites pass, including discovery-trigger/action.test.ts (6 tests).

2. Create HubSpot Private App secret

In HubSpot sandbox:

  • Settings → Integrations → Private Apps.
  • Create/use app with scopes:
  • crm.objects.deals.read
  • crm.objects.deals.write
  • crm.objects.contacts.read
  • Copy token → store as HUBSPOT_TOKEN_A5.

3. Set up GitHub fine-grained PAT

On github.com:

  • Settings → Developer settings → Personal access tokens → Fine-grained tokens.
  • Create token with:
  • Repository: snowops/snowops-automation
  • Permissions: actions:write
  • Copy → store as GITHUB_PAT_A5.

4. Set up Postmark (or test email service)

  • Create a Postmark account (free tier is fine for testing).
  • Verify the sender domain (test emails can go to any address).
  • Copy server token → store as POSTMARK_TOKEN_A5.

5. Create the Custom Code Action in HubSpot

Navigate to Automation → Custom Code:

  • Create action Discovery Trigger (A5).
  • Language: Node.js 20.
  • Input fields:
  • dealId (type: number, required)
  • Output fields:
  • triggered (boolean)
  • skip_reason (string, nullable)
  • workflow_dispatched (boolean)
  • email_sent (boolean)
  • Secrets in Private App settings:
  • HUBSPOT_PRIVATE_APP_TOKEN → from step 2.
  • GITHUB_DISPATCH_TOKEN → from step 3.
  • EMAIL_API_TOKEN → Postmark server token from step 4.
  • EMAIL_FROM → sender email (e.g., no-reply@snowops.io).
  • GH_OWNERsnowops
  • GH_REPOsnowops-automation
  • GH_WORKFLOW_FILEdiscovery-run.yml
  • GH_WORKFLOW_REFmain (or a test branch)

Paste the compiled code from dist/discovery-trigger/action.js (entry point: main export).

6. Create a test Deal in HubSpot

  • Create a Deal: "Test Co — Cloud Secure".
  • Fill in custom properties:
  • discovery_tenant_id: 11111111-2222-3333-4444-555555555555 (valid GUID format)
  • discovery_subscription_id: aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee (valid GUID format)
  • discovery_subscription_display_name: Test Co Sandbox
  • contact_email: alice@testco.com (your test email address)
  • contact_firstname: Alice
  • company_name: Test Co
  • Note the Deal ID.

7. Create a test workflow

In HubSpot Workflows:

  • Trigger: Deal stage changed to Qualified.
  • Action: Run Custom Code → Discovery Trigger (A5).
  • Input mapping:
  • dealId ← Deal ID (built-in).

8. Move the Deal to Qualified

  • In HubSpot, open the Deal from step 6.
  • Change deal stage to Qualified.
  • Workflow enrolls the deal.
  • Workflow runs the action.
  • Wait 5–10 seconds for the action to execute.

9. Verify the GitHub workflow dispatch

  • Open snowops-automation Actions.
  • Look for a new run of discovery-run workflow.
  • Run inputs should show:
  • client_name: test-co
  • tenant_id: (the GUID you filled in)
  • subscription_id: (the GUID you filled in)
  • subscription_display_name: Test Co Sandbox
  • hubspot_deal_id: (the Deal ID from step 6)
  • environment: discovery-default

10. Verify the offer email

  • Check your test email inbox (or Postmark logs if testing internally).
  • Email should:
  • From: the sender email you configured.
  • To: alice@testco.com.
  • Subject: "SnowOps free Azure discovery audit for Test Co".
  • Body mentions:
    • "Hi Alice," greeting.
    • Links to G0 bootstrap docs.
    • Booking URL.
    • Explanation of the three IDs needed (tenant, subscription, SP).

11. Verify HubSpot Deal was patched

  • Refresh the Deal in HubSpot.
  • Check that:
  • discovery_status = triggered.
  • discovery_triggered_at = timestamp (ISO format).

12. Test idempotency

  • Re-run the workflow on the same Deal (e.g., re-enroll or trigger manually if HubSpot allows).
  • The action should return:
  • triggered: false
  • skip_reason: "already_triggered"
  • No GitHub dispatch happens.
  • No email sent.

13. Test validation failures

Create additional test Deals with missing/invalid properties:

  • Missing discovery_tenant_id:
  • Verify skip_reason: missing_or_invalid_tenant_id.
  • No dispatch, no email.
  • Invalid GUID format in discovery_subscription_id:
  • Same skip behavior.
  • Missing contact_email:
  • skip_reason: missing_contact_email.

Pass criteria

  • Unit tests all pass (6 discovery-trigger tests).
  • Custom Code Action created and available in HubSpot workflows.
  • Deal move to Qualified triggers the workflow.
  • GitHub discovery-run workflow is dispatched with correct inputs.
  • Offer email is received at the contact's email address.
  • Deal custom properties are updated (discovery_status, discovery_triggered_at).
  • Idempotent: re-firing does not re-dispatch or re-email.
  • Validation failures are handled gracefully (skip_reason returned).

Failure modes & escalation

Symptom Action
GitHub dispatch 404s Check PAT token has actions:write scope on correct repo. Verify GH_OWNER, GH_REPO, GH_WORKFLOW_FILE secrets are correct.
Email fails to send Check Postmark token is valid and from address is verified. Look at Postmark logs.
Deal not enrolled in workflow Check workflow trigger is set to stage change → Qualified. Manually trigger workflow if HubSpot UI allows.
Custom Code Action not available Ensure it's committed to the Private App. Reload HubSpot page.
Invalid email address format not rejected Check regex in isPlausibleEmail() — it requires user@domain.ext format.

Sign-off

  • Tester: ___  |  Date: _  |  Result: PASS / FAIL / N/A
  • Notes: