feat: unit tests#70
Open
HarishChandran3304 wants to merge 13 commits into
Open
Conversation
There was a problem hiding this comment.
Pull request overview
This pull request adds an integration/unit test suite for the frappe_gmail_thread app, introduces a GitHub Actions workflow to run those tests in CI, and includes a small bug fix in the Google Settings on_update doc event handler found while writing tests.
Changes:
- Add extensive IntegrationTestCase coverage for Gmail Account, Gmail Thread sync/lifecycle, OAuth, Pub/Sub callback, activity timeline helpers, and parsing helpers.
- Add a
unit-tests.ymlGitHub Actions workflow to provision bench + site and runbench run-tests --app frappe_gmail_thread. - Fix Google Settings doc event logic to read the realtime flag from the Google Settings doc rather than the iterated Gmail Account doc.
Reviewed changes
Copilot reviewed 14 out of 18 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| frappe_gmail_thread/tests/utils/test_helpers.py | Adds tests for helper functions (email parsing, thread lookup, attachments, inline image rewriting). |
| frappe_gmail_thread/tests/utils/init.py | Test package initializer for utils tests. |
| frappe_gmail_thread/tests/tasks/test_sync.py | Adds tests for the scheduled sync task enqueue behavior. |
| frappe_gmail_thread/tests/tasks/test_daily.py | Adds tests for daily Pub/Sub enabling task behavior and error logging. |
| frappe_gmail_thread/tests/tasks/init.py | Test package initializer for tasks tests. |
| frappe_gmail_thread/tests/doc_events/test_google_settings.py | Adds tests validating Google Settings on_update behavior (enable/disable pubsub/no-op). |
| frappe_gmail_thread/tests/doc_events/init.py | Test package initializer for doc events tests. |
| frappe_gmail_thread/tests/api/test_pubsub.py | Adds tests for the Pub/Sub callback endpoint behavior (gating, enqueue, malformed payload logging). |
| frappe_gmail_thread/tests/api/test_oauth.py | Adds tests for OAuth URL generation, callback permissioning, token exchange, and pubsub enable/disable behavior. |
| frappe_gmail_thread/tests/api/test_gmail.py | Adds tests for is_gmail_configured behavior. |
| frappe_gmail_thread/tests/api/test_activity.py | Adds tests for timeline integration helpers (attachments refresh, link/unlink/relink). |
| frappe_gmail_thread/tests/api/init.py | Test package initializer for API tests. |
| frappe_gmail_thread/tests/init.py | Introduces shared test helpers (users, gmail account/thread factories, as_user). |
| frappe_gmail_thread/frappe_gmail_thread/doctype/gmail_thread/test_gmail_thread.py | Replaces placeholder test with comprehensive Gmail Thread lifecycle/sync/permission tests. |
| frappe_gmail_thread/frappe_gmail_thread/doctype/gmail_account/test_gmail_account.py | Replaces placeholder test with comprehensive Gmail Account lifecycle and sync enqueue tests. |
| frappe_gmail_thread/doc_events/google_settings.py | Fixes on_update to use doc.custom_gmail_sync_in_realtime instead of reading from the iterated account doc. |
| .semgrepignore | Minor comment spelling normalization and formatting touch-up. |
| .github/workflows/unit-tests.yml | Adds CI workflow to set up bench/site and run the app’s test suite. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
PR to add unit tests for
frappe_gmail_threadand set up the missing unit-tests CI workflow.Also fixes one source bug found while writing tests for the Google Settings
on_updatedoc event.ref: App Spec
Test Cases
Gmail Account lifecycle
before_insertauto-fillslinked_userandemail_idfrom the session uservalidatethrows whengmail_enabled=1but Google Settings is disabled, missing client_id, or missing client_secretvalidateis silent whengmail_enabled=0on_trashcallsdisable_pubsubonly when enabled, has refresh_token, and topic is configured; silent otherwisebefore_savere-validates Google Settings whengmail_enabledtoggles onbefore_savetriggerssync_labels+enable_pubsubwhen refresh_token changes and account is enabledbefore_savetriggersdisable_pubsubwhen refresh_token changes and account is not enabledbefore_saveresetslast_historyidto 0 when labels changesync_labels_apienqueues the sync job, is idempotent, and honoursreset_historyidflagGmail Thread lifecycle
msgprintwarning fires when another thread already references the same docall_subjectsis regenerated as a sorted, deduplicated, whitespace-stripped join of subject_of_first_mail and every email's subjectdocsharewith every involved user except the ownerGmail thread sync
threads().list()whenlast_historyid=0history().list()whenlast_historyidis non-zerogmail_enabled=0,refresh_tokenis empty, or no labels enabledlast_historyidis updated to the max history_id observednotFoundfrom the history API resetslast_historyidto 0frappe.log_errorand the loop continuesAlreadyExistsErrorfromcreate_new_emailis swallowed and processing continuesGmail Threadis created with subject_of_first_mail, creation, and owner from the emailpublish_realtimefires on incremental sync for threads with a referenceLabel sync
sync_labelsappends new labels matched on label_idshould_save=Truesaves the account;should_save=Falseleaves it untouchedPermission model
get_permission_query_conditionsreturns an empty string for AdministratorInvolved Usermembership or ownershiphas_permissionreturns True for Administrator on any ptypeInvolved UserOAuth flow
get_auth_urlthrowsDoesNotExistErrorfor a missing Gmail Accountget_auth_urlthrowsPermissionErrorwhen the session user lacks write or is not the linked usercallbackthrows when the session user has no Gmail Account or lacks write permissionauthorize_accesssaves refresh_token + authorization_code on successful exchangecheck_gmail_objectthrows when the authorized email does not match linked_usercheck_gmail_objectthrows a clear authorization-expired error oninvalid_grantget_access_tokenraises ValidationError when refresh_token is missingRealtime sync via Pub/Sub
enable_pubsubis a no-op when gmail_enabled=0 or realtime is offenable_pubsubthrows when refresh_token or topic is missingenable_pubsubcallsusers().watch()with enabled labels + SENT (without duplicating)disable_pubsubis a no-op when gmail_enabled=0 or realtime is still ondisable_pubsubcallsusers().stop()when account enabled and realtime turned offpubsub.callbackreturns "OK" without enqueuing when Google Settings.enable=0, realtime=0, or no topicpubsub.callbackdecodes the base64 envelope, looks up the System User, and enqueues a sync jobfrappe.log_errorand "OK" is still returnedActivity timeline integration
get_attachments_datarefreshes each attachment's file_url from the File doctypeget_linked_gmail_threadsreturns one timeline entry per email for every matching threadrelink_gmail_threadupdates reference fields and savesunlink_gmail_threadclears reference fields and savesEmail parsing helpers
html_to_textreturns BeautifulSoup-extracted text with space separator and stripped whitespacepop_down_quoted_repliesstrips\nOn ... wrote:from text bodiespop_down_quoted_repliesrewraps Apple, Outlook, and Yahoo quote containers asdiv.gmail_quoteset_to_and_ccpopulates.to/.cc/.bccby parsing the corresponding headersfind_gmail_threadmatches bygmail_thread_idor walksmessage_idsfor the parent threadcreate_new_emailraisesAlreadyExistsErrorfor a duplicateemail_message_idand appends linked_user to the existing threadsent_or_receivedbased on whether the sender matches a non-Website Userprocess_attachmentscreates private File records and renames file names >= 140 chars to<uuid>.<ext>replace_inline_imagesrewritescid:references to the matching File's unique_urlScheduled tasks
tasks.sync.sync_emailsenqueues a sync job for every enabled account with a refresh_token; idempotenttasks.daily.enable_pubsub_everydayreturns silently when Google Settings is disabled, realtime is off, or topic is missingenable_pubsubfor every enabled Gmail Account; per-account failures are logged with title="PubSub Error" and the loop continuesGoogle Settings doc event
on_updatecallsenable_pubsubfor every Gmail Account when Google Settings.custom_gmail_sync_in_realtime is truthydisable_pubsubwhen the flag is falsyConfiguration check
is_gmail_configuredreturns{configured: False, "Gmail Account not found"}when the session user has no accountPermissionErrorwhen the session user lacks read permission{configured: False, "Please configure Gmail"}whengmail_enabled=0{configured: True}when enabled with a refresh_token and the linked_user matches the session userRelevant Technical Choices
unittest.mockpatches at the consumer module's import sites; tests verify what the app passes to those layers, not the round-trip with Google.on_updatefix (gdoctodoc) lands as a separate commit on top of an intent-driven failing test, so the bug is reproducible from history.Testing Instructions
Additional Information:
Screenshot/Screencast
Checklist
Fixes #