Skip to content

Dhruv - Fix blue square related issues - #2346

Open
DeMoliT1on wants to merge 9 commits into
developmentfrom
dhruv/fix-blue-square-email-issues
Open

DeMoliT1on wants to merge 9 commits into
developmentfrom
dhruv/fix-blue-square-email-issues

Conversation

@DeMoliT1on

@DeMoliT1on DeMoliT1on commented Sep 12, 2026 •

Copy link
Copy Markdown

Description

image

This PR fixes few of the Blue Square assignment issues:

  • New users starting Wednesday or later should not be given Blue Squares
  • Close enough hours email should also remove the blue square mentioned in the email
  • Additionally should also log the appropriate warning visible under User Tracking on Dashboard
  • Add full name to "Administrative details" section

Related PRS (if any):

This is a backend only PR

Main changes explained:

  • Controller Refactor: Extracted helper functions out of warningsController.js into a new warningsHelper.js file. These helper functions support the Warning Logging system i.e automating the process where admins previously had to manually remove close-enough blue squares from the UI, while still properly logging the warning and notifying both the user and the admin via email.
  • User Helper & Email Triggers: Updated userHelper.js to handle new user logic, append full names to Administrative Details, and automate the removal of blue squares for close-enough hours within the updated cron job function.
  • Dependency & Environment Updates: Bumped mongodb-memory-server from ^7.6.3 to ^11.2.0 for compatibility with modern Linux distributions utilizing OpenSSL 3+, and refreshed lockfiles.
  • Sonar Configuration: Modified sonar-project.properties to use sonar.coverage.exclusions instead of a full script exclusion, allowing quality gate security analyses while ignoring irrelevant test script coverage.
  • Monitoring & Test Scripts: Added a monitor config file to structure contact settings for environment variable deployment, and updated testEmailJobs.js to manually test cron job functions modified in this PR.

How to test:

  1. Check into current branch
  2. Log into Dev site with Owner account
  3. Choose a eligible volunteer user account on Dev site
  4. Make sure the account has MISSED_BY_<15% i.e no of hours logged in previous week is 85-99% of the weekly commitment.
  5. Open the user profile page /userprofile/<target_user_id>
  6. Assign a blue square manually on Sunday of current week.
  7. Alternatively you can also testEmailJobs.js script to call the assignBlueSquareForTimeNotMet function. Please make sure to change TARGET_USER_ID to the id from userporfile page ``/userprofile/<target_user_id> before calling the function.
  8. If you have used the script to assign the blue square make sure to change the assignment date to Sunday of current week.
  9. Now call the weeklyAutoReplyEmailFunction from the test script with the same user id.
  10. Refresh user profile on dev site and verify that the blue square has been removed.

Screenshots or videos of changes:

Blue.Square.Fix.Summary.Video.webm

Note:

Since the changes in the PR are related to cron job functions which runs weekly, the test script is the only way to test the changes. Additionally you won't be able to receive any infringement emails since those are only possible on Production.

- Stop Blue Squares for late joiners

- Close enough hours email should also remove the blue square

- Add full name to Administrative Details
@one-community one-community added the High Priority - Please Review First This is an important PR we'd like to get merged as soon as possible label Sep 14, 2026

@shubhamjakhete shubhamjakhete left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Reviewed the changes. The Wednesday-or-later new-user handling is updated correctly, the close-enough-hours flow removes the associated Blue Square, and the user's full name is now included in the Administrative Details section. I did not find any blocking issues with the implementation. Looks good to me.

@Isha2303165 Isha2303165 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Tested the changed backend helpers and all 110 focused tests passed. However, I found issues in the new Blue Square/email flow that should be addressed before approval. During assignBlueSquareForTimeNotMet, the resolved email recipients are showing as Promises, for example: “Email BCCs for blue square assignment: Promise { }”, rather than resolved arrays. This indicates the async CC/BCC helpers may be used without awaiting them.

I also noticed that the new close-enough-hours flow calls getUserRoleByEmail(user), which iterates over user.teams, and also uses user.lastName, while the user projection in weeklyAutoReplyEmailFunction does not appear to include teams or lastName. This could cause the new path to fail with real queried user documents.

The test run also logs a caught error, “TypeError: original.toObject is not a function”, from weeklyAutoReplyEmailFunction even though the suite reports passing. Please address these runtime issues and add or adjust tests so the full success path completes without errors.

Image Image

@Isha2303165 Isha2303165 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Rechecked the latest updates after my previous review. All 110 focused tests across 3 suites passed, and the backend build completed successfully with 767 files compiled. The previously observed issues also appear resolved: Blue Square email BCC values are now resolved correctly instead of remaining pending Promises, the weekly auto-reply query includes the required lastName and teams fields, and the previous toObject runtime error did not recur during testing. No blocking issues found in the updated changes. Approved.

Image

@AaditTrivedi AaditTrivedi left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Reviewed by Aadit Trivedi.

How I verified
Checked out the PR locally (Node 22), installed with npm ci from package-lock.json, ran the three affected suites, and read the changes in userHelper.js against the description and @Isha2303165's review. I did not run the cron flow end to end against a database.

Note for other reviewers: installing with yarn makes userHelper.spec.js and getInfringementEmailBody.test.js fail to load (htmlparser2 is ESM-only). The same failure happens on development, so it is pre-existing and not caused by this PR. yarn.lock is out of sync with package.json on development too. Use npm ci to run these tests.

Verified working

  • Tests: userHelper.spec.js, warningsHelper.spec.js, and getInfringementEmailBody.test.js pass 110/110. Screenshot below.
  • @Isha2303165's three issues are resolved:
    1. No Promise { <pending> } recipients appear in the test output.
    2. The weeklyAutoReplyEmailFunction user query now includes lastName and teams (line 1299), which the close-enough path uses.
    3. No toObject is not a function error appears in the test output.
  • Wednesday rule: both checks moved from > 1 to > 2, so only start dates of Wednesday or later get a pass, matching the description.
  • The user's full name appears in the Administrative Details section of the email.

Issues found

  1. Scope is much larger than described. The description lists userHelper.js and the test script, but the PR also moves about 250 lines from warningsController.js into a new warningsHelper.js, bumps mongodb-memory-server from ^7.6.3 to ^11.2.0 (four major versions, affecting every test that uses it), rewrites large parts of both lockfiles, and edits sonar-project.properties. Please document these or move them to separate PRs.
  2. sonar-project.properties adds src/scripts/** to sonar.exclusions. That excludes every script in the repo from the quality gate, including the test script this PR edits, so its issues are hidden rather than fixed. Please revert this and address the issues directly.
  3. Jae's name and email are hardcoded in monitorData (jae@onecommunityglobal.org). Please read the monitor contact from configuration or an existing lookup rather than code.
  4. Conflict with #2369: this PR moves filterWarnings out of warningsController.js, and #2369 modifies filterWarnings in that same file to return tracker descriptions. Whichever merges second needs to carry the other's changes over, or the description field will be lost. Worth coordinating with @AnthonyWeathers.

Pointers, not blocking

  • The warning tracker query (currentWarnings.find({ activeWarning: true }, ...)) runs inside the per-user loop, so it repeats for every qualifying user. It returns the same data each time and can be fetched once before the loop.
  • if (typeof currentWarningDescriptions !== 'undefined') is always true right after the await, so the check and its comment can be removed.
  • Pre-existing, not from this PR: around line 380 the sort compares a.lastName with b.lastname (lowercase n), so the second name always ends in "undefined."

Evidence
Test run, 110/110 passing with npm ci:
image

Required action
Please revert the Sonar exclusion, document or split out the extra changes, move the monitor contact out of the code, and coordinate with #2369. Happy to re-review once updated.

@manavkheni1 manavkheni1 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Reviewed by Manav. Pulled the branch locally (npm ci, per @AaditTrivedi's note about npm install failing on htmlparser2), and independently verified the following:

Tests: Ran the same three suites (userHelper.spec.js, warningsHelper.spec.js, getInfringementEmailBody.test.js) — 110/110 passing, no Promise { } values, no toObject is not a function errors.

I also confirmed @AaditTrivedi's four outstanding concerns are still present and should be addressed before merge:

  • mongodb-memory-server bumped from ^7.6.3 to ^11.2.0 (4 major versions) — unrelated to this PR's stated scope
  • sonar-project.properties now excludes src/scripts/** from the quality gate, hiding rather than fixing issues in the test script
  • Hardcoded email "jae@onecommunityglobal.org" at userHelper.js line 173, inside getUserRoleByEmail — should come from config or an existing lookup instead
  • Merge conflict risk with #2369, which also modifies filterWarnings/warningsController.js

Agreeing changes should be addressed before this merges. Screenshot below showing my test run.

Image Image

…config

* Update sonar-project.properties to use sonar.coverage.exclusions for src/scripts instead of full exclusion
* Fix a pre-existing case-sensitivity sorting bug using lastName instead of lastname in userHelper.js
* Optimize performance by moving the currentWarningDescriptions query outside of the user loop in userHelper.js
* Simplify warningId lookup using optional chaining and remove redundant undefined checks
* Replace hardcoded monitor contact details in monitorData with MONITOR_CONTACT_CONFIG
@DeMoliT1on

Copy link
Copy Markdown
Author

Reviewed by Aadit Trivedi.

How I verified Checked out the PR locally (Node 22), installed with npm ci from package-lock.json, ran the three affected suites, and read the changes in userHelper.js against the description and @Isha2303165's review. I did not run the cron flow end to end against a database.

Note for other reviewers: installing with yarn makes userHelper.spec.js and getInfringementEmailBody.test.js fail to load (htmlparser2 is ESM-only). The same failure happens on development, so it is pre-existing and not caused by this PR. yarn.lock is out of sync with package.json on development too. Use npm ci to run these tests.

Verified working

  • Tests: userHelper.spec.js, warningsHelper.spec.js, and getInfringementEmailBody.test.js pass 110/110. Screenshot below.

  • @Isha2303165's three issues are resolved:

    1. No Promise { <pending> } recipients appear in the test output.
    2. The weeklyAutoReplyEmailFunction user query now includes lastName and teams (line 1299), which the close-enough path uses.
    3. No toObject is not a function error appears in the test output.
  • Wednesday rule: both checks moved from > 1 to > 2, so only start dates of Wednesday or later get a pass, matching the description.

  • The user's full name appears in the Administrative Details section of the email.

Issues found

  1. Scope is much larger than described. The description lists userHelper.js and the test script, but the PR also moves about 250 lines from warningsController.js into a new warningsHelper.js, bumps mongodb-memory-server from ^7.6.3 to ^11.2.0 (four major versions, affecting every test that uses it), rewrites large parts of both lockfiles, and edits sonar-project.properties. Please document these or move them to separate PRs.
  2. sonar-project.properties adds src/scripts/** to sonar.exclusions. That excludes every script in the repo from the quality gate, including the test script this PR edits, so its issues are hidden rather than fixed. Please revert this and address the issues directly.
  3. Jae's name and email are hardcoded in monitorData (jae@onecommunityglobal.org). Please read the monitor contact from configuration or an existing lookup rather than code.
  4. Conflict with Anthony/Feature-Add-InfoModal-For-Tracker-List #2369: this PR moves filterWarnings out of warningsController.js, and Anthony/Feature-Add-InfoModal-For-Tracker-List #2369 modifies filterWarnings in that same file to return tracker descriptions. Whichever merges second needs to carry the other's changes over, or the description field will be lost. Worth coordinating with @AnthonyWeathers.

Pointers, not blocking

  • The warning tracker query (currentWarnings.find({ activeWarning: true }, ...)) runs inside the per-user loop, so it repeats for every qualifying user. It returns the same data each time and can be fetched once before the loop.
  • if (typeof currentWarningDescriptions !== 'undefined') is always true right after the await, so the check and its comment can be removed.
  • Pre-existing, not from this PR: around line 380 the sort compares a.lastName with b.lastname (lowercase n), so the second name always ends in "undefined."

Evidence Test run, 110/110 passing with npm ci: image

Required action Please revert the Sonar exclusion, document or split out the extra changes, move the monitor contact out of the code, and coordinate with #2369. Happy to re-review once updated.

@AaditTrivedi,

Thanks for the detailed review! Here is what I've updated:

  • Scope and Documentation: Updated the PR description to comprehensively document the warningsController refactor (extracting helper functions), the mongodb-memory-server version bump (to ensure compatibility with newer linux distributions which uses OpenSSL 3+), lockfile updates, and sonar-project.properties modifications.
  • Sonar Qube Configuration: Reverted the complete exclusion in sonar-project.properties to use sonar.coverage.exclusions instead. This ensures that irrelevant test script code coverage enforcement is ignored while keeping security and quality gate analyses fully active.
  • Monitor Contact Configuration: Created a new monitor config file to house the contact details. While placeholder values are temporarily used, this establishes the necessary structure so the DevOps team can transition them into environment variables for deployments.
  • Conflict Resolution with Anthony/Feature-Add-InfoModal-For-Tracker-List #2369: Coordinating with @AnthonyWeathers to handle the overlapping changes for filterwarnings and ensure no description fields are lost during merge order.
  • Non-Blocking Issues: Resolved all pointers and non-blocking issues, including optimizing the warning tracker query outside the loop, removing redundant undefined checks, and fixing the pre-existing case-sensitivity sort bug.

@vidiyala99 vidiyala99 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Reviewed d2b0143 (no review on this head yet). I ran the PR's suites offline (no DB, no .env): userHelper.spec.js, warningsHelper.spec.js and getInfringementEmailBody.test.js pass 110/110. I also started this branch's backend and checked Dashboard > Show Trackers as Admin: 67 GET /api/warnings/:id calls succeeded and the trackers render as before (including the "Blu Sq Rmvd - Hrs Close Enoug" row), so the warningsHelper extraction looks behavior-preserving. The Wednesday start-day rule and the admin Name: line match the description, and the earlier review points (hoisted warning query, lastName typo) are addressed.

I did not run testEmailJobs.js against the shared dev DB, because of point 1. Instead I wrote three small mocked unit tests (below, collapsed); each one passing demonstrates one of the first three issues:

  1. Targeted test runs are not isolated. With targetUserId set, assignBlueSquareForTimeNotMet scopes only the active-user query (userHelper.js:886-888). The inactive-user block (:954-962) still runs processWeeklySummariesByUserId for every inactive user, which pushes an empty summary with $slice: 4 and drops their oldest stored summary, and deleteOldTimeOffRequests() (:938) still deletes expired time-off requests for everyone. Following test step 7 on the shared dev DB therefore changes data for users who are not the target (proof P1). Please skip both blocks when a target is given, and consider a dry-run flag for the script.
  2. Auto-removal also removes blue squares earned for a missing summary. A user at 85 to 99 percent of their hours with no summary still resolves to MISSED_HOURS_BY_<15% (resolveAutoReplyTemplate has no summary input; only metHours && !hasSummary is skipped at :1325), and the new code then pulls their blue square (:1347-1349), whose description cites both reasons (proof P2). Please only remove it when the summary was submitted.
  3. The $pull matches on date only. $pull: { infringements: { date: assignmentDate } } removes every blue square dated that Sunday, including manually assigned ones, although the comment says "system-assigned" (proof P3). Adding manuallyAssigned: { $ne: true } (the field exists on the schema) would match the intent. The current test steps use a manual blue square, so they pass for the wrong reason.
  4. Not idempotent. Each run pushes another warning, and on the fourth-plus path a second run would pull the re-issued blue square (same date) and issue another. infringementCount is also left stale after the pull in the blue and yellow cases.
  5. Contradictory emails in production. A fourth-offense user gets "issue blue square" (:1412), then "New Infringement Assigned" (:1446), then the "close enough for us to remove this blue square" template (:1460). These new sends also ignore the email/cc/bcc overrides, and sendEmailToUser is not awaited.
  6. The monitor contact is still hardcoded: monitorContactConfig.js holds literals, and getUserRoleByEmail (:174) and sendBlueSquareEmail (:1174-1176) still embed the address. The mongodb-memory-server bump and lockfile churn are also still in, and the conflict with #2369 remains.
Proof tests (mocked models, no DB). Save as src/helpers/__tests__/PREP-2346.proof.spec.js and run npx jest src/helpers/__tests__/PREP-2346.proof.spec.js
const mongoose = require('mongoose');
const moment = require('moment-timezone');

/* =======================
   MOCKS (MUST COME FIRST)
   ======================= */

jest.mock('../../models/userProfile', () => ({
  findById: jest.fn(),
  find: jest.fn(),
  aggregate: jest.fn(),
  updateOne: jest.fn().mockResolvedValue({}),
  findByIdAndUpdate: jest.fn((id, update, cb) => {
    if (typeof cb === 'function') cb(null);
    return Promise.resolve({});
  }),
}));

jest.mock('../../models/badge', () => ({
  find: jest.fn(),
  findOne: jest.fn(),
}));

jest.mock('../../models/team', () => ({
  aggregate: jest.fn(),
}));

jest.mock('../../utilities/timeUtils');
jest.mock('../../utilities/emailSender');
jest.mock('../dashboardhelper', () =>
  jest.fn(() => ({
    laborthisweek: jest.fn().mockResolvedValue([{ timeSpent_hrs: 36 }]),
  })),
);
jest.mock('../../models/BlueSquareEmailAssignment', () => ({
  find: jest.fn().mockImplementation(() => ({
    populate: jest.fn().mockImplementation(() => ({
      exec: jest.fn().mockResolvedValue([
        {
          email: 'bcc-test@example.com',
          assignedTo: { isActive: true },
        },
      ]),
    })),
  })),
}));
jest.mock('../../models/timeOffRequest', () => ({
  deleteMany: jest.fn(),
  find: jest.fn(),
}));

/* =======================
   IMPORTS AFTER MOCKS
   ======================= */

const userProfile = require('../../models/userProfile');
const currentWarnings = require('../../models/currentWarnings');
const warningsHelper = require('../warningsHelper');
const userHelperFactory = require('../userHelper');
const emailSender = require('../../utilities/emailSender');
const { COMPANY_TZ } = require('../../constants/company');
const timeOffRequest = require('../../models/timeOffRequest');

const { weeklyAutoReplyEmailFunction, assignBlueSquareForTimeNotMet } = userHelperFactory();

/* ===== PREP-2346 proof tests (reviewer-only, not part of PR) ===== */
describe('PREP-2346 proofs', () => {
  const WARNING_DESC = 'Blu Sq Rmvd - Hrs Close Enoug';
  const assignmentDate = moment().tz(COMPANY_TZ).startOf('week').format('YYYY-MM-DD');

  beforeEach(() => {
    jest.clearAllMocks();
    emailSender.mockResolvedValue(true);
  });

  it('P1: targeted assignBlueSquareForTimeNotMet still sweeps ALL inactive users weeklySummaries', async () => {
    userProfile.find
      .mockResolvedValueOnce([]) // target user query
      .mockResolvedValueOnce([{ _id: new mongoose.Types.ObjectId() }, { _id: new mongoose.Types.ObjectId() }]); // inactive
    userProfile.findByIdAndUpdate.mockClear();
    await assignBlueSquareForTimeNotMet({
      targetUserId: new mongoose.Types.ObjectId(),
      ccOverride: ['cc@x.com'],
      bccOverride: ['bcc@x.com'],
    });
    expect(userProfile.find).toHaveBeenNthCalledWith(2, { isActive: false }, '_id');
    const summaryShifts = userProfile.findByIdAndUpdate.mock.calls.filter(
      (c) => c[1] && c[1].$push && c[1].$push.weeklySummaries,
    );
    expect(summaryShifts.length).toBe(2); // both inactive users had summaries shifted ($slice: 4 drops oldest)
    expect(summaryShifts[0][1].$push.weeklySummaries.$slice).toBe(4);
    expect(timeOffRequest.deleteMany).toHaveBeenCalled(); // global time-off cleanup also runs
  });

  it('P2: 85-99% hours user WITHOUT a weekly summary still gets the blue square pulled', async () => {
    jest.spyOn(timeOffRequest, 'find').mockResolvedValue([]);
    jest.spyOn(currentWarnings, 'find').mockReturnValue({
      sort: jest.fn().mockResolvedValue([{ warningTitle: WARNING_DESC, _id: 'w1' }]),
    });
    jest.spyOn(warningsHelper, 'filterWarnings').mockReturnValue({ sendEmail: null, size: 0 });
    jest.spyOn(warningsHelper, 'sendEmailToUser').mockImplementation(() => {});
    const user = {
      _id: '60c72b2f9b1d8b2bad709999',
      email: 'nosummary@example.com',
      firstName: 'No',
      lastName: 'Summary',
      weeklycommittedHours: 40, // dashboardhelper mock returns 36h = 90% of 40
      missedHours: 0,
      startDate: '2022-01-01',
      weeklySummaryOption: 'Required',
      weeklySummaryNotReq: false,
      weeklySummaries: [{ summary: '' }, { summary: '' }], // NO summary last week
      infringements: [
        { date: assignmentDate, description: 'System auto-assigned infringement for two reasons: not meeting weekly volunteer time commitment as well as not submitting a weekly summary.' },
      ],
      teams: [],
      warnings: [],
    };
    userProfile.find.mockResolvedValueOnce([user]);
    userProfile.findByIdAndUpdate.mockReset();
    userProfile.findByIdAndUpdate.mockResolvedValue(user);
    await weeklyAutoReplyEmailFunction({ targetUserId: user._id, bccOverride: ['b@x.com'] });
    expect(userProfile.findByIdAndUpdate).toHaveBeenCalledWith(user._id, {
      $pull: { infringements: { date: assignmentDate } },
    });
    const body = emailSender.mock.calls.map((c) => c[2]).join(' ');
    expect(body).toContain('close enough to your total hours for us to remove this blue square');
  });

  it('P3: $pull is by date only, so a MANUAL blue square on the same Sunday is removed too', async () => {
    jest.spyOn(timeOffRequest, 'find').mockResolvedValue([]);
    jest.spyOn(currentWarnings, 'find').mockReturnValue({ sort: jest.fn().mockResolvedValue([]) });
    jest.spyOn(warningsHelper, 'filterWarnings').mockReturnValue({ sendEmail: null, size: 0 });
    const user = {
      _id: '60c72b2f9b1d8b2bad708888', email: 'm@example.com', firstName: 'M', lastName: 'A',
      weeklycommittedHours: 40, missedHours: 0, startDate: '2022-01-01',
      weeklySummaryOption: 'Required', weeklySummaries: [{}, { summary: 'ok' }],
      infringements: [
        { date: assignmentDate, description: 'system hours' },
        { date: assignmentDate, description: 'manual: missed video call', manuallyAssigned: true },
      ],
      teams: [], warnings: [],
    };
    userProfile.find.mockResolvedValueOnce([user]);
    userProfile.findByIdAndUpdate.mockReset();
    userProfile.findByIdAndUpdate.mockResolvedValue(user);
    await weeklyAutoReplyEmailFunction({ targetUserId: user._id, bccOverride: ['b@x.com'] });
    const pull = userProfile.findByIdAndUpdate.mock.calls.find((c) => c[1].$pull);
    expect(pull[1].$pull.infringements).toEqual({ date: assignmentDate }); // no manuallyAssigned / description filter
  });
});

02: the three mocked proof tests pass on d2b0143; each pass demonstrates issue 1, 2 or 3

Image

03a: targetUserId only scopes the active-user query; time-off cleanup and the inactive sweep stay global

Image

03b: the near-miss path skips only met-hours users without a summary, and the pull is keyed on date alone

Image

01: the PR's own suites pass 110/110 (working as intended)

Image

04 (Admin, light): Show Trackers works on this branch's backend (working as intended)

Image

@adit24dhaya adit24dhaya left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Re-reviewed the current head d2b0143 after the remediation commit.

I verified the latest changes locally without a database: the three affected suites passed (3/3 suites, 110/110 tests), and npm run build compiled 775 files successfully. I also confirmed that the Sonar exclusion is now limited to coverage, the warning query was hoisted, and the lastName sort typo was corrected.

Changes are still required before merge. The monitor contact was moved to monitorContactConfig.js, but the name and email remain hardcoded there, so the original configurability concern is not resolved. More importantly, the current head still has the same production-behavior problems documented with mocked proofs in the existing same-head review: targeted runs can mutate unrelated users/global data, the close-enough path can remove a blue square when the summary is missing, and the date-only $pull can remove manual infringements. I confirmed those paths remain present in userHelper.js.

I’m not adding duplicate inline comments because the existing review on this exact commit already provides reproducible proof tests and precise locations. Please address those current-head findings and add regression coverage for the targeted/dry-run boundary and selective infringement removal.

@DeMoliT1on

Copy link
Copy Markdown
Author

@vidiyala99 @adit24dhaya Have included the required changes for the PR. Also would like to add that the monitor contact config is temporarily hardcoded, and the responsible DevOps team can convert it to env variables since I do not have permission to add the same for Dev/Production, hence have left a note in the config file.

I have also coordinated with @AnthonyWeathers in regards to #2369 that once one of the PR gets merged, the other would incorporate the required changes while handle the merge conflicts.

@DeMoliT1on
DeMoliT1on force-pushed the dhruv/fix-blue-square-email-issues branch from 56952f2 to bd27455 Compare October 3, 2026 23:06
@sonarqubecloud

sonarqubecloud Bot commented Oct 3, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@RichaSapre RichaSapre left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Tested PR #2346 locally using npm ci. The three affected test suites passed with 110/110 tests, and npm run build completed successfully with 775 files compiled.
I reviewed the updated Blue Square assignment and weekly auto-reply flows, including the new handling for close-enough hours, warning creation, infringement updates, and targeted-user testing.

I found one remaining correctness issue: manually assigned Blue Squares are still considered eligible by hasTodayBlueSquare. Although the later $pull excludes manually assigned infringements, the function can still create a “Removed Blue Square” warning and send the removal email while the manually assigned square remains. Please restrict this flow to system-assigned squares and add a regression test.

Image

Comment thread src/helpers/userHelper.js
const userAfterPull = await userProfile.findByIdAndUpdate(
user._id,
{
$pull: { infringements: { date: assignmentDate, manuallyAssigned: { $ne: true } } },

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This now filters manually assigned infringements out of the $pull, but hasTodayBlueSquare still treats a manually assigned square as eligible for the close-enough flow. The function can therefore add the “Removed Blue Square” warning and send the removal email even though the manually assigned square remains. Could we require a system-assigned square here as well and add a regression test for this case?

@AaditTrivedi AaditTrivedi left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Re-reviewed by Aadit Trivedi.

Follow-up to my earlier review, checking the commits from Sep 28 and Oct 3 ("sonar exclusions, optimize warning queries, and extract monitor config" and "Address reviewer comments") against the diff with development.

Fixed since my review

  1. Sonar: src/scripts/** is no longer in sonar.exclusions; it is now only in sonar.coverage.exclusions, so scripts are still analyzed for issues. That addresses my concern.
  2. The monitor contact is now read from the new src/config/monitorContactConfig.js instead of being hardcoded in the warning logic.

Still open

  1. mongodb-memory-server is still bumped from ^7.6.3 to ^11.2.0. If the upgrade is needed for these tests, please explain why in the description; otherwise please move it to a separate PR, since it affects every test that uses an in-memory database.
  2. A new hardcoded recipient was added: const recipients = ['jae@onecommunityglobal.org']; (around line 174 of userHelper.js). Since the monitor contact now lives in monitorContactConfig.js, please read this from the same config. The other occurrences of that address in the file were already on development, so they are outside this PR.

Pointer, not blocking

  • Please confirm coordination with #2369, since both PRs still change filterWarnings.

Required action
Please address the two open points. The rest of my earlier review is resolved.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

High Priority - Please Review First This is an important PR we'd like to get merged as soon as possible

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants