Skip to content

fix(slack): Normalize email casing in linkExistingSlackMembers - #58

Merged
tylermenezes merged 1 commit into
mainfrom
detail/bug-fix/fix-slack-normalize-email-casing-in-linkexistingsl-c676f4
Sep 18, 2026
Merged

tylermenezes merged 1 commit into
mainfrom
detail/bug-fix/fix-slack-normalize-email-casing-in-linkexistingsl-c676f4

Conversation

@detail-app

@detail-app detail-app Bot commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Detail bug report: View on Detail

Bug

linkExistingSlackMembers (the hourly Slack-sync cron fallback) matched apply-time DB emails against Slack's users.list profile.email using JavaScript's case-sensitive in operator on an un-normalized lookup, and its previousParticipants Prisma-in copy was similarly case-sensitive. Any student/mentor whose apply-time email casing differs from the casing Slack holds (e.g. DB First.Last@Company.com vs Slack first.last@company.com) was silently never matched, leaving slackId null indefinitely — so downstream consumers that filter on slackId treated the user as absent from the workspace. This was inconsistent with every other cross-system email join in the codebase (postmark.ts, Attio sync), which normalize casing.

Fix

  • Added normalizeEmail(email) = email.toLowerCase().trim() and applied it to both sides of every comparison in src/slack/linkExistingSlackMembers.ts: the searchStudents/searchMentors lookup keys, the users.list member filter and row-id lookup, and the previousParticipants in-memory match/lookup/delete.
  • The previousParticipants Prisma findMany now passes email: { in: [...], mode: 'insensitive' } so prior-event rows with differing casing are found on a default case-sensitive Postgres collation.
  • Added an optional deps?: { prisma?, slack? } injection seam to linkExistingSlackMembers (defaults to Container.get(PrismaClient) + getSlackClientForEvent(event)), so the cron path is unit-testable without a live DB / Slack workspace. The sole runtime caller (slackSync.ts) is unchanged and backward-compatible.

mode: 'insensitive' is supported by the installed Prisma client's StringFilter and was verified against a real Postgres 14 instance (default en_US.utf8 collation).

Testing

  • Added tests/testLinkExistingSlackMembers.ts (5 node:test cases): normalizeEmail lowercase+trim, student case-mismatch linking (the reported bug), mentor case-mismatch linking, whitespace-divergent linking (guards the .trim() half), and the previousParticipants case-insensitive copy — which also asserts the Prisma query carries mode: 'insensitive' and that the prior copy removes the row from the search set before users.list runs.
  • Offline unit tests, the existing tests/testSlackReporting.ts, npx tsc --skipLibCheck --noEmit, and npm run build all pass. (No lint is available — the repo's pinned @typescript-eslint parser fails under the installed typescript@5.2.2, independently of this change.)
  • End-to-end verification against a real Postgres 14 container: applied the Labs schema via prisma db push (the migration chain hits a pre-existing ordering quirk on fresh DBs), seeded an event/project plus case-mismatched, exact-case, whitespace-divergent, and already-linked rows, then drove the real fixed function against the live DB with a mock users.list returning lowercased casings. All affected rows acquired the expected slackId; the already-linked row was untouched. A data-layer join demonstration confirmed the case-sensitive join (the bug) matches 1/4 of the seeded null rows while the case-insensitive join (the fix) matches 4/4.
  • The real-Slack users.list capture could not be run: no Slack token is available in this environment (env | grep -iE SLACK is empty; no .env), so the run would be rejected by Slack. It was substituted with a mock users.list payload modeling the lowercased profile.email casings Slack returns; the Postgres half was fully exercised.

Automatic Fixes PRs can be configured here.

@detail-app
detail-app Bot requested a review from tylermenezes September 18, 2026 02:54
@tylermenezes
tylermenezes merged commit fbdfcd0 into main Sep 18, 2026
1 check passed
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