118 lines
5.3 KiB
JavaScript
118 lines
5.3 KiB
JavaScript
import { test, before, after } from 'node:test'
|
||
import assert from 'node:assert/strict'
|
||
import { startFakeAppwrite } from './helpers/fakeAppwrite.js'
|
||
import { mailSink } from './support/mailSink.js'
|
||
|
||
let fake
|
||
let findEmployeeForPusher, buildPushTimeMail, sendPushTimeMail
|
||
let renderMail, verifyPushTimeToken, setMailTransportForTests
|
||
|
||
const KENSO = { $id: 'k', displayName: 'Kenso Grimm', shortcode: 'KNSO', email: 'kenso@webklar.com', $createdAt: '2026-01-01T00:00:00.000Z' }
|
||
const ANDREJ = { $id: 'a', displayName: 'Andrej Stevanovski', shortcode: 'ANDJ', email: '', $createdAt: '2026-01-02T00:00:00.000Z' }
|
||
|
||
const ticket = { $id: 't1', woid: '100118', customerName: 'Amin Friseur', topic: 'Website Amin Friseur', details: 'Neue Seite', status: 'Occupied' }
|
||
const worksheet = { $id: 'ws9', woid: '100118', serviceType: 'GIT' }
|
||
const commits = [
|
||
{ id: 'abc1234def', message: 'Startseite: Hero neu\n\nlanger Text' },
|
||
{ id: 'def5678abc', message: '<script>x</script> & Co' },
|
||
]
|
||
const input = (extra = {}) => ({
|
||
ticket, worksheet, repoFullName: 'WEBklar/amin-friseur', branch: 'main', commits,
|
||
pushedAt: new Date('2026-09-30T14:46:00Z'), ...extra,
|
||
})
|
||
|
||
before(async () => {
|
||
fake = await startFakeAppwrite({ seed: { employees: [ANDREJ, KENSO] } })
|
||
Object.assign(process.env, {
|
||
APPWRITE_ENDPOINT: fake.endpoint,
|
||
APPWRITE_API_KEY: 'test-key',
|
||
PUSH_TIME_SECRET: 'z'.repeat(40),
|
||
})
|
||
;({ findEmployeeForPusher, buildPushTimeMail, sendPushTimeMail } = await import('../server/services/pushTimeMail.js'))
|
||
;({ renderMail } = await import('../server/services/mailTemplates.js'))
|
||
;({ verifyPushTimeToken } = await import('../server/services/pushTimeToken.js'))
|
||
;({ setMailTransportForTests } = await import('../server/services/mailer.js'))
|
||
})
|
||
|
||
after(async () => {
|
||
setMailTransportForTests?.(null)
|
||
await fake.close()
|
||
})
|
||
|
||
test('findEmployeeForPusher: Kürzel, voller Name, ohne E-Mail, unbekannt', () => {
|
||
const employees = [ANDREJ, KENSO]
|
||
assert.equal(findEmployeeForPusher({ username: 'knso' }, employees)?.$id, 'k')
|
||
assert.equal(findEmployeeForPusher({ full_name: ' kenso grimm ' }, employees)?.$id, 'k')
|
||
assert.equal(findEmployeeForPusher({ login: 'ANDJ' }, employees), null)
|
||
assert.equal(findEmployeeForPusher({ username: 'fremd' }, employees), null)
|
||
assert.equal(findEmployeeForPusher({}, employees), null)
|
||
})
|
||
|
||
test('buildPushTimeMail: Betreff, Links, Escaping, gültiger Token', () => {
|
||
const mail = buildPushTimeMail({ employee: KENSO, ...input() })
|
||
assert.equal(mail.template, 'T15')
|
||
const out = renderMail('T15', mail.values)
|
||
assert.deepEqual(out.problems, [])
|
||
assert.equal(out.subject, '[Zeit] #100118 Amin Friseur – 2 Commit(s)')
|
||
assert.match(out.html, /Hallo Kenso,/)
|
||
assert.match(out.html, /In Arbeit/)
|
||
assert.match(out.html, /30\.09\.2026 16:46/)
|
||
for (const m of [15, 30, 45, 60, 90, 120, 180]) {
|
||
assert.match(out.html, new RegExp(`/zeit/[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\?min=${m}"`))
|
||
}
|
||
assert.match(out.html, /\/zeit\/[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+"/, 'Andere Zeit ohne min')
|
||
assert.match(out.html, /https:\/\/ticket\.webklar\.com\/tickets\?woid=100118/)
|
||
assert.match(out.html, /abc1234 Startseite: Hero neu/)
|
||
assert.doesNotMatch(out.html, /langer Text/)
|
||
assert.match(out.html, /<script>x<\/script> & Co/)
|
||
assert.doesNotMatch(out.html, /<script>/)
|
||
|
||
const token = mail.values.link_andere.split('/zeit/')[1]
|
||
assert.deepEqual(verifyPushTimeToken(token), { worksheetId: 'ws9', employeeId: 'k' })
|
||
})
|
||
|
||
test('buildPushTimeMail: 12 Commits → 10 Zeilen + Rest', () => {
|
||
const many = Array.from({ length: 12 }, (_, i) => ({ id: `${String(i).padStart(7, '0')}ffff`, message: `Commit ${i}` }))
|
||
const { values } = buildPushTimeMail({ employee: KENSO, ...input({ commits: many }) })
|
||
const lines = values.commits.split('\n')
|
||
assert.equal(lines.length, 11)
|
||
assert.equal(lines.at(-1), '… und 2 weitere')
|
||
assert.equal(values.anzahl_commits, 12)
|
||
})
|
||
|
||
test('buildPushTimeMail: lange Beschreibung gekürzt, ohne Kunde', () => {
|
||
const { values } = buildPushTimeMail({
|
||
employee: KENSO,
|
||
...input({ ticket: { ...ticket, customerName: '', details: 'x'.repeat(400), status: 'Weird' } }),
|
||
})
|
||
assert.equal(values.ticket_beschreibung.length, 301)
|
||
assert.ok(values.ticket_beschreibung.endsWith('…'))
|
||
assert.equal(values.kunde, 'ohne Kunde')
|
||
assert.equal(values.ticket_status, 'Weird')
|
||
})
|
||
|
||
test('sendPushTimeMail: an den Pusher, ohne BCC', async () => {
|
||
const sink = mailSink()
|
||
setMailTransportForTests(sink)
|
||
const r = await sendPushTimeMail({ ...input(), pusher: { username: 'knso' } })
|
||
assert.deepEqual(r, { sent: true })
|
||
assert.equal(sink.sent.length, 1)
|
||
assert.deepEqual(sink.sent[0].to.map((t) => t.address), ['kenso@webklar.com'])
|
||
assert.equal(sink.sent[0].from.name, 'WEBklar Kundenportal')
|
||
assert.equal(sink.sent[0].bcc, undefined)
|
||
})
|
||
|
||
test('sendPushTimeMail: unbekannter Pusher → keine Mail', async () => {
|
||
const sink = mailSink()
|
||
setMailTransportForTests(sink)
|
||
const r = await sendPushTimeMail({ ...input(), pusher: { username: 'fremd' } })
|
||
assert.equal(r.sent, false)
|
||
assert.equal(sink.sent.length, 0)
|
||
})
|
||
|
||
test('sendPushTimeMail: Versandfehler wird geschluckt', async () => {
|
||
setMailTransportForTests({ sendMail: async () => { throw new Error('SMTP kaputt') } })
|
||
const r = await sendPushTimeMail({ ...input(), pusher: { username: 'knso' } })
|
||
assert.equal(r.sent, false)
|
||
})
|