db management fix

This commit is contained in:
root
2026-05-22 17:21:52 -04:00
parent bae4942276
commit 48ecde2a51
13 changed files with 294 additions and 7 deletions
+70
View File
@@ -0,0 +1,70 @@
import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest'
vi.mock('../lib/prisma', () => ({
prisma: {
employee: {
findFirst: vi.fn(),
create: vi.fn(),
},
company: {
findUniqueOrThrow: vi.fn(),
},
},
}))
vi.mock('./notificationService', () => ({
sendTransactionalEmail: vi.fn().mockResolvedValue(undefined),
}))
import crypto from 'crypto'
import { prisma } from '../lib/prisma'
import { sendTransactionalEmail } from './notificationService'
import { inviteEmployee } from './teamService'
describe('teamService inviteEmployee', () => {
const originalDashboardUrl = process.env.DASHBOARD_URL
const originalPublicDashboardUrl = process.env.NEXT_PUBLIC_DASHBOARD_URL
beforeEach(() => {
vi.clearAllMocks()
process.env.DASHBOARD_URL = 'http://localhost:3001'
process.env.NEXT_PUBLIC_DASHBOARD_URL = ''
vi.spyOn(crypto, 'randomBytes').mockReturnValue(Buffer.from('token-123'))
vi.spyOn(crypto, 'randomUUID').mockReturnValue('uuid-123')
vi.mocked(prisma.employee.findFirst).mockResolvedValue(null as any)
vi.mocked(prisma.company.findUniqueOrThrow).mockResolvedValue({ name: 'Atlas Cars' } as any)
vi.mocked(prisma.employee.create).mockResolvedValue({
id: 'emp_1',
firstName: 'Aya',
lastName: 'Benali',
email: 'aya@example.com',
role: 'AGENT',
isActive: true,
} as any)
})
afterAll(() => {
process.env.DASHBOARD_URL = originalDashboardUrl
process.env.NEXT_PUBLIC_DASHBOARD_URL = originalPublicDashboardUrl
vi.restoreAllMocks()
})
it('builds invite reset links under the dashboard base path', async () => {
await inviteEmployee('company_1', 'owner_1', {
firstName: 'Aya',
lastName: 'Benali',
email: 'aya@example.com',
role: 'AGENT',
})
expect(sendTransactionalEmail).toHaveBeenCalledWith(
expect.objectContaining({
to: 'aya@example.com',
html: expect.stringContaining('http://localhost:3001/dashboard/reset-password?token=746f6b656e2d313233'),
text: expect.stringContaining('http://localhost:3001/dashboard/reset-password?token=746f6b656e2d313233'),
}),
)
})
})
+15 -1
View File
@@ -27,6 +27,18 @@ export interface TeamMember {
invitationStatus?: 'accepted' | 'pending' | 'revoked'
}
function ensureDashboardBasePath(baseUrl: string) {
try {
const url = new URL(baseUrl)
const pathname = url.pathname.replace(/\/$/, '')
if (!pathname.endsWith('/dashboard')) url.pathname = `${pathname}/dashboard`
return url.toString().replace(/\/$/, '')
} catch {
const trimmed = baseUrl.replace(/\/$/, '')
return trimmed.endsWith('/dashboard') ? trimmed : `${trimmed}/dashboard`
}
}
function buildInviteEmail(resetUrl: string, companyName: string, firstName: string, role: EmployeeRole) {
const greetingName = firstName.trim() || 'there'
@@ -84,7 +96,9 @@ export async function inviteEmployee(companyId: string, inviterId: string, paylo
},
})
const dashboardUrl = process.env.DASHBOARD_URL ?? 'http://localhost:3001'
const dashboardUrl = ensureDashboardBasePath(
process.env.DASHBOARD_URL ?? process.env.NEXT_PUBLIC_DASHBOARD_URL ?? 'http://localhost:3001/dashboard',
)
const resetUrl = `${dashboardUrl}/reset-password?token=${rawToken}`
const message = buildInviteEmail(resetUrl, company.name, payload.firstName, payload.role)