Skip to content

Tested email analytics database queries - #29890

Open
EvanHahn wants to merge 4 commits into
mainfrom
email-analytics-queries-di-and-tests
Open

Tested email analytics database queries#29890
EvanHahn wants to merge 4 commits into
mainfrom
email-analytics-queries-di-and-tests

Conversation

@EvanHahn

@EvanHahn EvanHahn commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

no ref

I recommend reviewing this one commit at a time.

This change should have no user impact.

This change:

  • tests email analytics database queries
  • stops using a global reference to the database, and injects the dependencies (DI)

In addition to automated testing, I also manually tested this. I sent a newsletter, opened it in Gmail, and verified that I saw the open appear in the newsletter's analytics.

We'll do some DI/testing soon.
This was written entirely by GPT-5.6 Sol (Medium thinking) with the
following prompt:

> Write full unit tests for
> `ghost/core/core/server/services/email-analytics/lib/queries.ts`. The
> test should create a Knex database with in-memory SQLite—similar to
> how
> `ghost/core/test/unit/server/services/automations/automations-repository.test.ts`
> does it—and then test everything against that Knex database.
>
> Before you go off and write everything, let's test a single method. I
> want to make sure we're on the right track! I think `getJobData` is
> probably the simplest, so test it. Make sure to test the case where
> it's defined and not defined.
>
> `ghost/core/core/server/data/schema/schema.js:1098-1108` has the
> schema for `jobs`, which you may wish to refer to. You may also wish
> to refer to the jobs that are set up as part of email analytics.
>
> This should be a test-only change.
I continued the conversation from the previous commit with the
following:

> Great work. I love what you've written. Now add unit tests for the
> rest of the public methods. Make sure to test edge cases.

I did a *very* cursory scan of the results, and asked it to "Verify that
the test schema matches the real schema", which it did.
@nx-cloud

nx-cloud Bot commented Aug 11, 2026

Copy link
Copy Markdown

🤖 Nx Cloud AI Fix

Ensure the fix-ci command is configured to always run in your CI pipeline to get automatic fixes in future runs. For more information, please see https://nx.dev/ci/features/self-healing-ci


View your CI Pipeline Execution ↗ for commit 21357f9

Command Status Duration Result
nx run ghost:test:ci:integration ✅ Succeeded 3m 11s View ↗
nx run ghost:test:integration ✅ Succeeded 3m 9s View ↗
nx run ghost:test:legacy ✅ Succeeded 2m 58s View ↗
nx run ghost:test:e2e ✅ Succeeded 2m 46s View ↗
nx run ghost-monorepo:lint:boundaries ✅ Succeeded 23s View ↗
nx run-many -t lint -p ghost,ghost-monorepo ✅ Succeeded 20s View ↗
nx run-many -t test:unit -p ghost ✅ Succeeded 30s View ↗
nx run @tryghost/admin:build ✅ Succeeded 4s View ↗
nx run-many --target=build --projects=tag:publi... ✅ Succeeded <1s View ↗

💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗


☁️ Nx Cloud last updated this comment at 2026-08-11 20:56:29 UTC

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The email analytics query module now exports a Queries class that receives a Knex connection. Service initialization creates one instance and passes it to newsletter and automation analytics. Query methods use the injected connection. Member aggregation normalizes count results. Integration stubs and fixtures now target constructed Queries instances. A SQLite-backed unit test suite covers query operations and aggregation behavior.

Possibly related PRs

Suggested reviewers: cmraible

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly describes the added email analytics query tests, which are a primary part of the changeset.
Description check ✅ Passed The description accurately covers the query tests, dependency injection changes, and manual verification.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch email-analytics-queries-di-and-tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
ghost/core/core/server/services/email-analytics/lib/queries.ts (1)

172-180: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Set created_at when inserting a missing job.

When no job exists, this insert omits created_at. The jobs schema in ghost/core/test/unit/server/services/email-analytics/queries.test.ts requires that column. The insert fails, the catch block hides the error, and the timestamp is not saved. Add a test for this fallback path.

Proposed fix
                 await this.#knex('jobs').insert({
                     id: new ObjectID().toHexString(),
                     name: jobName,
                     [updateField]: date.toISOString(), // force to iso string for sqlite
+                    created_at: new Date(),
                     updated_at: date.toISOString(), // force to iso string for sqlite
                     status: status
                 });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ghost/core/core/server/services/email-analytics/lib/queries.ts` around lines
172 - 180, Update the fallback insert in the job update method to include the
required created_at timestamp, using the existing date value consistently with
the other timestamp fields. Add a unit test in the relevant queries test suite
covering insertion when the job is missing and verifying created_at is
persisted.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@ghost/core/core/server/services/email-analytics/lib/queries.ts`:
- Around line 172-180: Update the fallback insert in the job update method to
include the required created_at timestamp, using the existing date value
consistently with the other timestamp fields. Add a unit test in the relevant
queries test suite covering insertion when the job is missing and verifying
created_at is persisted.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f0a0a44b-63b2-49f0-b24c-d57b95695b7f

📥 Commits

Reviewing files that changed from the base of the PR and between 94c80d0 and 21357f9.

📒 Files selected for processing (8)
  • ghost/core/core/server/services/email-analytics/email-analytics-service-wrapper.js
  • ghost/core/core/server/services/email-analytics/index.ts
  • ghost/core/core/server/services/email-analytics/lib/queries.ts
  • ghost/core/test/integration/services/email-analytics/automation-email-analytics.test.js
  • ghost/core/test/integration/services/email-service/newsletter-email-event-storage.test.js
  • ghost/core/test/integration/services/mailgun-email-suppression-list.test.js
  • ghost/core/test/unit/server/services/email-analytics/queries.test.ts
  • ghost/core/test/utils/fixture-utils.js

@codecov

codecov Bot commented Aug 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 75.00000% with 16 lines in your changes missing coverage. Please review.
✅ Project coverage is 75.46%. Comparing base (94c80d0) to head (21357f9).

Files with missing lines Patch % Lines
...ore/server/services/email-analytics/lib/queries.ts 71.42% 16 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #29890      +/-   ##
==========================================
- Coverage   75.46%   75.46%   -0.01%     
==========================================
  Files        1606     1606              
  Lines      140769   140787      +18     
  Branches    17466    17460       -6     
==========================================
+ Hits       106238   106248      +10     
- Misses      33483    33491       +8     
  Partials     1048     1048              
Flag Coverage Δ
e2e-tests 77.61% <75.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@EvanHahn

Copy link
Copy Markdown
Contributor Author

CodeRabbit caught a legit bug. Fixed in #29895.

@EvanHahn
EvanHahn requested a review from cmraible August 11, 2026 21:15
EvanHahn added a commit that referenced this pull request Aug 11, 2026
ref #29890 (review)

Big picture
-----------

We sometimes failed to create the job row for the "missing" job. This
could cause:

- unnecessary extra fetches from Mailgun
- events to be dropped entirely, if the site was off for awhile

Details
-------

There are four jobs for email analytics:

1. opened
2. non-opened
3. scheduled
4. missing

Before this change, `setJobTimestamp` failed to upsert the job row. For
the first three job types, it turned out that was fine, because they
were already created by previous code paths. But not for the "missing"
job!

Before this change, we'd only create the "missing" job row when:

- no missing events were found
- there was an error processing the batch

Now, we create it at the right time as intended.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant