Projektarten: T16 Versuchszähler je Mail (R7)

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:11 +00:00
1 parent b8a530958f
commit de4b6f3a35
2 files changed
+20 -2

No files matched your search

+3
View File
@@ -33,6 +33,7 @@ export function shouldSendAssignMail(project, repo, { now = new Date(), createdN
/** Erinnerung faellig: Mail ging vor >= 24 h, Art fehlt noch, noch keine Erinnerung. */
export function dueReminder(project, { now = new Date() } = {}) {
if (project?.kind || project?.assignReminderSentAt) return false
if (Number(project?.assignMailAttempts || 0) >= MAX_ATTEMPTS) return false
const sent = time(project?.assignMailSentAt)
if (sent === null) return false
return new Date(now).getTime() - sent >= DAY_MS
@@ -73,6 +74,8 @@ export async function sendRepoAssignMail(project, { giteaLogin, reminder = false
await updateDocument(config.collections.websiteProjects, project.$id, patch)
return { sent: false, reason: err.message }
}
// assignMailAttempts zaehlt die Versuche der aktuellen Mail: nach Erfolg zurueck auf 0 (R7)
patch.assignMailAttempts = 0
patch[reminder ? 'assignReminderSentAt' : 'assignMailSentAt'] = new Date().toISOString()
await updateDocument(config.collections.websiteProjects, project.$id, patch)
return { sent: true }
+17 -2
View File
@@ -58,6 +58,8 @@ test('dueReminder: genau nach 24 h, ohne Art, ohne frühere Erinnerung', () => {
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)
assert.equal(dueReminder({ ...base, assignMailAttempts: 3 }, { now: NOW }), false)
assert.equal(dueReminder({ ...base, assignMailAttempts: 2 }, { now: NOW }), true)
})
test('T16: rendert ohne Probleme, Betreff mit und ohne Erinnerung', () => {
@@ -86,7 +88,7 @@ test('sendRepoAssignMail: Mail mit exaktem Link, setzt assignMailSentAt + Versuc
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)
assert.equal(d.assignMailAttempts, 0, 'Erfolg setzt den Zähler zurück')
})
test('sendRepoAssignMail: Erinnerung setzt assignReminderSentAt', async () => {
@@ -97,7 +99,7 @@ test('sendRepoAssignMail: Erinnerung setzt assignReminderSentAt', async () => {
assert.match(sink.sent[0].subject, /^Erinnerung: Neues Repo zuordnen/)
const d = await doc('p1')
assert.ok(d.assignReminderSentAt)
assert.equal(d.assignMailAttempts, 2)
assert.equal(d.assignMailAttempts, 0)
})
test('sendRepoAssignMail: unbekannter Login → no_employee, kein Zähler', async () => {
@@ -119,3 +121,16 @@ test('sendRepoAssignMail: SMTP-Fehler zählt Versuch, setzt kein SentAt, wirft n
assert.equal(d.assignMailAttempts, 2)
assert.equal(d.assignMailSentAt, undefined)
})
test('fehlgeschlagene Erinnerung zählt hoch und endet nach 3 Versuchen', async () => {
setMailTransportForTests({ sendMail: async () => { throw new Error('SMTP kaputt') } })
let project = projectDoc({ $id: 'p2', assignMailSentAt: hoursAgo(25), assignMailAttempts: 0 })
for (let i = 1; i <= 3; i += 1) {
assert.equal(dueReminder(project, { now: NOW }), true)
const r = await sendRepoAssignMail(project, { giteaLogin: 'knso', reminder: true })
assert.equal(r.sent, false)
project = { ...project, ...(await doc('p2')) }
assert.equal(project.assignMailAttempts, i)
}
assert.equal(dueReminder(project, { now: NOW }), false)
})