feat(slack): autoFlagPreExistingSlackAccess helper
Best-effort lookup of an activating member's email in the Slack workspace. On a hit, flips slackInvited:true and stamps slackInvitedAt without sending a fresh invite. Races against a 3s timeout and swallows all errors so activation never blocks on Slack. - Promotes SlackService.findUserByEmail from private to public so the helper can call it without a wrapper. - New activity-log action: slack_access_auto_detected (actor = subject). - Idempotent: short-circuits when slackInvited is already true. Callers wired in next commit.
This commit is contained in:
parent
2f6a92ac61
commit
b1d8cb1966
4 changed files with 263 additions and 1 deletions
67
tests/server/models/member-slack-fields.test.js
Normal file
67
tests/server/models/member-slack-fields.test.js
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
// Spec: docs/specs/wave-based-slack-onboarding.md
|
||||
// Test plan: docs/specs/wave-based-slack-onboarding-tests.md §1
|
||||
//
|
||||
// SCAFFOLD: `describe.skip` until the schema migration lands. Tests use the
|
||||
// schema's path metadata only — no DB connection required.
|
||||
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import mongoose from 'mongoose'
|
||||
import Member from '../../../server/models/member.js'
|
||||
|
||||
describe.skip('Member schema — Slack fields (post-migration)', () => {
|
||||
it('does not define slackInviteStatus (1.2)', () => {
|
||||
expect(Member.schema.path('slackInviteStatus')).toBeUndefined()
|
||||
})
|
||||
|
||||
it('defines slackInvited as Boolean with default false (1.1)', () => {
|
||||
const path = Member.schema.path('slackInvited')
|
||||
expect(path).toBeDefined()
|
||||
expect(path.instance).toBe('Boolean')
|
||||
expect(path.defaultValue).toBe(false)
|
||||
})
|
||||
|
||||
it('defines slackInvitedAt as an optional Date (1.3)', () => {
|
||||
const path = Member.schema.path('slackInvitedAt')
|
||||
expect(path).toBeDefined()
|
||||
expect(path.instance).toBe('Date')
|
||||
expect(path.isRequired).toBeFalsy()
|
||||
})
|
||||
|
||||
it('retains slackUserId as String (1.4)', () => {
|
||||
const path = Member.schema.path('slackUserId')
|
||||
expect(path).toBeDefined()
|
||||
expect(path.instance).toBe('String')
|
||||
})
|
||||
|
||||
it('does not auto-stamp slackInvitedAt via pre-save hook (1.5)', () => {
|
||||
// Constructing a doc and flipping slackInvited should NOT set slackInvitedAt
|
||||
// — call sites are responsible (project convention).
|
||||
const doc = new Member({
|
||||
email: 't@example.com',
|
||||
name: 'T',
|
||||
circle: 'community',
|
||||
contributionAmount: 0
|
||||
})
|
||||
doc.slackInvited = true
|
||||
expect(doc.slackInvitedAt).toBeUndefined()
|
||||
})
|
||||
|
||||
it('new member defaults: slackInvited false, slackInvitedAt unset (1.1)', () => {
|
||||
const doc = new Member({
|
||||
email: 'new@example.com',
|
||||
name: 'New',
|
||||
circle: 'community',
|
||||
contributionAmount: 0
|
||||
})
|
||||
expect(doc.slackInvited).toBe(false)
|
||||
expect(doc.slackInvitedAt).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
// Sanity: import doesn't introduce mongoose connection side-effects.
|
||||
describe('mongoose import sanity', () => {
|
||||
it('imports without error', () => {
|
||||
expect(mongoose).toBeDefined()
|
||||
expect(Member).toBeDefined()
|
||||
})
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue