Projektarten: Mail T16 Repo zuordnen

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-09-30 18:31:07 +00:00
1 parent 08958dee77
commit b8a530958f
3 files changed
+261

No files matched your search

+121
View File
@@ -0,0 +1,121 @@
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 shouldSendAssignMail, dueReminder, sendRepoAssignMail
let renderMail, setMailTransportForTests, getDocument
const NOW = new Date('2026-09-30T15:00:00Z')
const hoursAgo = (h) => new Date(NOW.getTime() - h * 3600_000).toISOString()
const KENSO = { $id: 'k', displayName: 'Kenso Grimm', shortcode: 'KNSO', email: 'kenso@webklar.com', $createdAt: '2026-01-01T00:00:00.000Z' }
const projectDoc = (extra = {}) => ({
$id: 'p1',
repoFullName: 'WEBklar/amin-friseur',
giteaRepoUrl: 'https://git.webklar.com/WEBklar/amin-friseur',
...extra,
})
before(async () => {
fake = await startFakeAppwrite({
seed: {
employees: [KENSO],
websiteProjects: [projectDoc(), projectDoc({ $id: 'p2' }), projectDoc({ $id: 'p3' })],
},
})
Object.assign(process.env, { APPWRITE_ENDPOINT: fake.endpoint, APPWRITE_API_KEY: 'test-key' })
;({ shouldSendAssignMail, dueReminder, sendRepoAssignMail } = await import('../server/services/repoAssignMail.js'))
;({ renderMail } = await import('../server/services/mailTemplates.js'))
;({ setMailTransportForTests } = await import('../server/services/mailer.js'))
;({ getDocument } = await import('../server/services/appwriteAdmin.js'))
})
after(async () => {
setMailTransportForTests?.(null)
await fake.close()
})
const doc = (id) => getDocument(process.env.APPWRITE_COLLECTION_WEBSITE_PROJECTS || 'websiteProjects', id)
test('shouldSendAssignMail: nur neu angelegt, < 24 h, ohne Art, < 3 Versuche', () => {
const repo = { created_at: hoursAgo(1) }
assert.equal(shouldSendAssignMail({}, repo, { now: NOW, createdNow: true }), true)
assert.equal(shouldSendAssignMail({}, repo, { now: NOW, createdNow: false }), false)
assert.equal(shouldSendAssignMail({}, { created_at: hoursAgo(25) }, { now: NOW, createdNow: true }), false)
assert.equal(shouldSendAssignMail({ kind: 'website' }, repo, { now: NOW, createdNow: true }), false)
assert.equal(shouldSendAssignMail({ assignMailAttempts: 3 }, repo, { now: NOW, createdNow: true }), false)
assert.equal(shouldSendAssignMail({ assignMailAttempts: 2 }, repo, { now: NOW, createdNow: true }), true)
assert.equal(shouldSendAssignMail({ assignMailSentAt: hoursAgo(1) }, repo, { now: NOW, createdNow: true }), false)
assert.equal(shouldSendAssignMail({}, {}, { now: NOW, createdNow: true }), false, 'ohne created_at keine Mail')
})
test('dueReminder: genau nach 24 h, ohne Art, ohne frühere Erinnerung', () => {
const base = { assignMailSentAt: hoursAgo(25) }
assert.equal(dueReminder(base, { now: NOW }), true)
assert.equal(dueReminder({ assignMailSentAt: hoursAgo(23) }, { now: NOW }), false)
assert.equal(dueReminder({}, { now: NOW }), false)
assert.equal(dueReminder({ ...base, kind: 'app' }, { now: NOW }), false)
assert.equal(dueReminder({ ...base, assignReminderSentAt: hoursAgo(1) }, { now: NOW }), false)
})
test('T16: rendert ohne Probleme, Betreff mit und ohne Erinnerung', () => {
const values = {
betreff_vorsatz: '', vorname: 'Kenso', repo: 'WEBklar/x', repo_link: 'https://git.webklar.com/WEBklar/x',
angelegt_von: 'Kenso Grimm', angelegt_am: '30.09.2026 17:00', zuordnen_link: 'https://ticket.webklar.com/projects/zuordnen/p1',
}
const plain = renderMail('T16', values)
assert.deepEqual(plain.problems, [])
assert.equal(plain.subject, 'Neues Repo zuordnen: WEBklar/x')
assert.doesNotMatch(plain.html, /einzige Erinnerung/)
const rem = renderMail('T16', { ...values, betreff_vorsatz: 'Erinnerung: ' }, { erinnerung: true })
assert.equal(rem.subject, 'Erinnerung: Neues Repo zuordnen: WEBklar/x')
assert.match(rem.html, /einzige Erinnerung/)
})
test('sendRepoAssignMail: Mail mit exaktem Link, setzt assignMailSentAt + Versuche', async () => {
const sink = mailSink()
setMailTransportForTests(sink)
const r = await sendRepoAssignMail(projectDoc({ $id: 'p1', $createdAt: '2026-09-30T14:46:00Z' }), { giteaLogin: '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].subject, 'Neues Repo zuordnen: WEBklar/amin-friseur')
assert.match(sink.sent[0].html, /href="https:\/\/ticket\.webklar\.com\/projects\/zuordnen\/p1"/)
assert.match(sink.sent[0].html, /30\.09\.2026 16:46/)
const d = await doc('p1')
assert.ok(d.assignMailSentAt)
assert.equal(d.assignMailAttempts, 1)
})
test('sendRepoAssignMail: Erinnerung setzt assignReminderSentAt', async () => {
const sink = mailSink()
setMailTransportForTests(sink)
const r = await sendRepoAssignMail(projectDoc({ $id: 'p1', assignMailAttempts: 1 }), { giteaLogin: 'knso', reminder: true })
assert.deepEqual(r, { sent: true })
assert.match(sink.sent[0].subject, /^Erinnerung: Neues Repo zuordnen/)
const d = await doc('p1')
assert.ok(d.assignReminderSentAt)
assert.equal(d.assignMailAttempts, 2)
})
test('sendRepoAssignMail: unbekannter Login → no_employee, kein Zähler', async () => {
const sink = mailSink()
setMailTransportForTests(sink)
const r = await sendRepoAssignMail(projectDoc({ $id: 'p2' }), { giteaLogin: 'fremd' })
assert.deepEqual(r, { sent: false, reason: 'no_employee' })
assert.equal(sink.sent.length, 0)
const d = await doc('p2')
assert.equal(d.assignMailAttempts, undefined)
assert.equal(d.assignMailSentAt, undefined)
})
test('sendRepoAssignMail: SMTP-Fehler zählt Versuch, setzt kein SentAt, wirft nie', async () => {
setMailTransportForTests({ sendMail: async () => { throw new Error('SMTP kaputt') } })
const r = await sendRepoAssignMail(projectDoc({ $id: 'p3', assignMailAttempts: 1 }), { giteaLogin: 'knso' })
assert.equal(r.sent, false)
const d = await doc('p3')
assert.equal(d.assignMailAttempts, 2)
assert.equal(d.assignMailSentAt, undefined)
})