Öffnungszeiten: Mails K24–K27 heißen jetzt K27–K30, Feld k24At heißt einschaltMailAt
K24–K26 sind seit dem Webshop-Livegang (main) vergeben; der Mail-Lader sucht die Vorlage über den Dateianfang, doppelte Nummern hätten die falsche Mail geschickt. Das Feld ist noch nicht in der Produktion angelegt (zeiten-setup.mjs lief noch nicht), daher jetzt ein sprechender Name. Spec und Plan behalten die alten Nummern (Hinweis oben im Dokument). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ea4457e6a4
commit
c7cd436ec2
34 files changed
+359
-354
No files matched your search
@@ -7,13 +7,13 @@ import { useTestEnv } from './support/testEnv.js'
|
||||
import { ZEITEN_DATETIMES } from '../server/services/zeiten/schema.js'
|
||||
|
||||
// Abo-Abgleich von Öffnungszeiten live (Aufgabe 21, Spec 5 "Abo-Abgleich" Schritte 1 bis 4 und Z11): gleicheZeitenAboAb(now) koppelt die im
|
||||
// Wartungs-Abo enthaltenen Zeiten (unlockedBy 'abo') an das Abo: Abo-Ende erkennen (aboEndeAt, K26), nach 30 Tagen abschalten (K27, Ersatz-Ticket),
|
||||
// bei Rückkehr des Abos wieder einschalten (K24 "wieder", Neubau nach dem Abschalten), 30 Tage nach dem Abschalten die Daten löschen.
|
||||
// Wartungs-Abo enthaltenen Zeiten (unlockedBy 'abo') an das Abo: Abo-Ende erkennen (aboEndeAt, K29), nach 30 Tagen abschalten (K30, Ersatz-Ticket),
|
||||
// bei Rückkehr des Abos wieder einschalten (K27 "wieder", Neubau nach dem Abschalten), 30 Tage nach dem Abschalten die Daten löschen.
|
||||
// Aufbau wie termineAboAbgleich.test.js: Appwrite-Nachbau mit Schema, Mails in der Senke, feste Zeit JETZT; Tage zählen nach Berliner Kalendertagen.
|
||||
// Datumsfelder wie in Appwrite: UTC mit "+00:00", Unlesbares oder '' wäre dort ein 400 (leeren geht nur mit null)
|
||||
const DATETIMES = {
|
||||
...ZEITEN_DATETIMES,
|
||||
portalFeatures: ['orderedAt', 'activatedAt', 'disabledAt', 'consentAt', 'aboEndeMailAt', 'aboEndeAt', 'k24At'],
|
||||
portalFeatures: ['orderedAt', 'activatedAt', 'disabledAt', 'consentAt', 'aboEndeMailAt', 'aboEndeAt', 'einschaltMailAt'],
|
||||
}
|
||||
const aw = await startAppwriteMock({
|
||||
schema: {
|
||||
@@ -122,7 +122,7 @@ const EIN_PLAN = () => ({
|
||||
})
|
||||
|
||||
/**
|
||||
* Funktion "Öffnungszeiten live" von kunde für Projekt nr, wie nach dem Einschalten im Wartungs-Abo: aktiv, Ticket geschlossen, K24 gesendet,
|
||||
* Funktion "Öffnungszeiten live" von kunde für Projekt nr, wie nach dem Einschalten im Wartungs-Abo: aktiv, Ticket geschlossen, K27 gesendet,
|
||||
* Zeiten gespeichert (daten, ersatzHash leer). Das Projekt "Praxis Muster" und das Einbau-Ticket legt die Hilfe mit an.
|
||||
*/
|
||||
async function legeFunktion(nr = 1, over = {}, { kunde = KUNDE, projekt = 'Praxis Muster', zeiten = true } = {}) {
|
||||
@@ -140,7 +140,7 @@ async function legeFunktion(nr = 1, over = {}, { kunde = KUNDE, projekt = 'Praxi
|
||||
siteKey: `abcdefghijklmno${'pqrstuv'[nr]}`,
|
||||
ticketId: `t_${nr}`,
|
||||
einbau: 'angestossen',
|
||||
k24At: ZUVOR,
|
||||
einschaltMailAt: ZUVOR,
|
||||
activatedAt: ZUVOR,
|
||||
...over,
|
||||
})
|
||||
@@ -167,9 +167,9 @@ async function laufe(now = JETZT) {
|
||||
const ERGEBNIS_LEER = { geprueft: 0, aboEnde: 0, abgeschaltet: 0, wiederEin: 0, geloescht: 0, mails: 0, fehler: 0 }
|
||||
const ergebnis = (over) => ({ ...ERGEBNIS_LEER, ...over })
|
||||
|
||||
const K26 = /^Dein Wartungs-Abo ist beendet/
|
||||
const K27 = 'Öffnungszeiten live ist abgeschaltet'
|
||||
const K24 = 'Öffnungszeiten live ist eingeschaltet'
|
||||
const K29 = /^Dein Wartungs-Abo ist beendet/
|
||||
const K30 = 'Öffnungszeiten live ist abgeschaltet'
|
||||
const K27 = 'Öffnungszeiten live ist eingeschaltet'
|
||||
const mails = () => sink.sent
|
||||
const mailsMit = (betreff) => mails().filter((m) => (betreff instanceof RegExp ? betreff.test(m.subject) : m.subject === betreff))
|
||||
const tickets = () => [...aw.docs('workorders').values()]
|
||||
@@ -203,7 +203,7 @@ async function mitEinwurf(passt, dazwischen, fn) {
|
||||
}
|
||||
const liste = (collection) => (url, methode) => methode === 'GET' && new RegExp(`/collections/${collection}/documents(\\?|$)`).test(url)
|
||||
|
||||
/** Die volle Folge bis zum Abschalten: Tag 0 Abo-Ende erkannt (K26), Tag 30 abgeschaltet (K27). */
|
||||
/** Die volle Folge bis zum Abschalten: Tag 0 Abo-Ende erkannt (K29), Tag 30 abgeschaltet (K30). */
|
||||
async function bisAbgeschaltet() {
|
||||
await legeFunktion(1)
|
||||
abgelaufen()
|
||||
@@ -212,9 +212,9 @@ async function bisAbgeschaltet() {
|
||||
assert.equal(funktion().state, 'disabled')
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- Schritt 1: Abo-Ende erkennen, K26
|
||||
// ---------------------------------------------------------------- Schritt 1: Abo-Ende erkennen, K29
|
||||
|
||||
test('Abo endet: aboEndeAt gesetzt, K26 einmal', async () => {
|
||||
test('Abo endet: aboEndeAt gesetzt, K29 einmal', async () => {
|
||||
await legeFunktion(1)
|
||||
abgelaufen()
|
||||
|
||||
@@ -223,13 +223,13 @@ test('Abo endet: aboEndeAt gesetzt, K26 einmal', async () => {
|
||||
assert.equal(doc.state, 'active', 'die Funktion läuft bis zur Frist weiter')
|
||||
assert.equal(doc.enabled, true)
|
||||
assert.equal(ms(doc.aboEndeAt), JETZT.getTime(), 'aboEndeAt = Zeitpunkt der Erkennung')
|
||||
assert.equal(ms(doc.aboEndeMailAt), JETZT.getTime(), 'Marker der K26 erst nach dem Versand')
|
||||
assert.equal(ms(doc.aboEndeMailAt), JETZT.getTime(), 'Marker der K29 erst nach dem Versand')
|
||||
assert.equal(doc.unlockedBy, 'abo')
|
||||
|
||||
assert.equal(mails().length, 1)
|
||||
const mail = mails()[0]
|
||||
assert.deepEqual(mail.to.map((a) => a.address), ['silke@example.com'])
|
||||
assert.match(mail.subject, K26)
|
||||
assert.match(mail.subject, K29)
|
||||
assert.match(mail.text, /Silke Müller/)
|
||||
assert.match(mail.text, /Dein Wartungs-Abo ist beendet\./, 'Variante beendet')
|
||||
assert.match(mail.text, /Läuft weiter bis: 04\.11\.2026/, 'Frist = Tag der Erkennung + 30 Tage')
|
||||
@@ -255,7 +255,7 @@ test('Läuft das Abo, geschieht nichts: kein Schreiben, keine Mail', async () =>
|
||||
assert.equal(mails().length, 0)
|
||||
})
|
||||
|
||||
test('K26-Variante bei Wechsel zu Hosting und bei widerrufenem Wechsel', async () => {
|
||||
test('K29-Variante bei Wechsel zu Hosting und bei widerrufenem Wechsel', async () => {
|
||||
const varianten = [
|
||||
['Hosting-Abo', () => hosting('active'), /Dein Abo ist ein Hosting-Abo\./, /Du hast den Wechsel in das Wartungs-Abo widerrufen/],
|
||||
[
|
||||
@@ -274,14 +274,14 @@ test('K26-Variante bei Wechsel zu Hosting und bei widerrufenem Wechsel', async (
|
||||
await legeFunktion(1)
|
||||
abo()
|
||||
assert.deepEqual(await laufe(), ergebnis({ geprueft: 1, aboEnde: 1, mails: 1 }), name)
|
||||
assert.match(mails()[0].subject, K26, name)
|
||||
assert.match(mails()[0].subject, K29, name)
|
||||
assert.match(mails()[0].text, passt, name)
|
||||
assert.doesNotMatch(mails()[0].text, passtNicht, name)
|
||||
assert.equal(funktion().state, 'active', `${name}: die Frist läuft`)
|
||||
}
|
||||
})
|
||||
|
||||
test('K26 geht nur an die Adresse des Kundenkontos; ohne Adresse setzt der Lauf den Marker ohne Fehler', async () => {
|
||||
test('K29 geht nur an die Adresse des Kundenkontos; ohne Adresse setzt der Lauf den Marker ohne Fehler', async () => {
|
||||
await legeFunktion(1)
|
||||
aw.seed('customers', { $id: KUNDE, name: 'Silke Müller', email: '' })
|
||||
abgelaufen()
|
||||
@@ -291,7 +291,7 @@ test('K26 geht nur an die Adresse des Kundenkontos; ohne Adresse setzt der Lauf
|
||||
assert.deepEqual(await laufe(nach(1)), ergebnis({ geprueft: 1 }))
|
||||
})
|
||||
|
||||
test('K26 scheitert (Mail): aboEndeAt steht, der Marker fehlt, der nächste Lauf sendet nach (einmal)', async () => {
|
||||
test('K29 scheitert (Mail): aboEndeAt steht, der Marker fehlt, der nächste Lauf sendet nach (einmal)', async () => {
|
||||
await legeFunktion(1)
|
||||
abgelaufen()
|
||||
const roh = sink.sendMail
|
||||
@@ -305,16 +305,16 @@ test('K26 scheitert (Mail): aboEndeAt steht, der Marker fehlt, der nächste Lauf
|
||||
|
||||
sink.sendMail = roh
|
||||
assert.deepEqual(await laufe(nach(1)), ergebnis({ geprueft: 1, mails: 1 }))
|
||||
assert.equal(mailsMit(K26).length, 1)
|
||||
assert.equal(mailsMit(K29).length, 1)
|
||||
assert.equal(ms(funktion().aboEndeAt), JETZT.getTime(), 'aboEndeAt bleibt: die Frist wächst nicht')
|
||||
assert.match(mails()[0].text, /Läuft weiter bis: 04\.11\.2026/, 'dieselbe Frist wie beim ersten Versuch')
|
||||
assert.deepEqual(await laufe(nach(2)), ergebnis({ geprueft: 1 }))
|
||||
assert.equal(mails().length, 1)
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------- Schritt 2: nach 30 Tagen abschalten, K27, Ersatz-Ticket
|
||||
// ---------------------------------------------------------------- Schritt 2: nach 30 Tagen abschalten, K30, Ersatz-Ticket
|
||||
|
||||
test('Tag 29: noch aktiv; Tag 30: disabled abo_ende, K27, Ersatz-Ticket', async () => {
|
||||
test('Tag 29: noch aktiv; Tag 30: disabled abo_ende, K30, Ersatz-Ticket', async () => {
|
||||
await legeFunktion(1)
|
||||
abgelaufen()
|
||||
await laufe(JETZT)
|
||||
@@ -334,12 +334,12 @@ test('Tag 29: noch aktiv; Tag 30: disabled abo_ende, K27, Ersatz-Ticket', async
|
||||
assert.equal(doc.disabledReason, 'abo_ende')
|
||||
assert.equal(ms(doc.disabledAt), tag30.getTime())
|
||||
assert.equal(ms(doc.aboEndeAt), JETZT.getTime(), 'aboEndeAt bleibt als Spur stehen')
|
||||
assert.equal(ms(doc.aboEndeMailAt), tag30.getTime(), 'Marker gilt jetzt der K27')
|
||||
assert.equal(ms(doc.aboEndeMailAt), tag30.getTime(), 'Marker gilt jetzt der K30')
|
||||
assert.match(doc.adminNote, /System: Abgeschaltet: Wartungs-Abo beendet/)
|
||||
|
||||
// K27: Datum der Löschung = Abschalten + 30 Tage
|
||||
assert.equal(mailsMit(K27).length, 1)
|
||||
const mail = mailsMit(K27)[0]
|
||||
// K30: Datum der Löschung = Abschalten + 30 Tage
|
||||
assert.equal(mailsMit(K30).length, 1)
|
||||
const mail = mailsMit(K30)[0]
|
||||
assert.deepEqual(mail.to.map((a) => a.address), ['silke@example.com'])
|
||||
assert.match(mail.text, /Eingaben gespeichert bis: 04\.12\.2026/)
|
||||
assert.match(mail.text, /30 €/)
|
||||
@@ -370,7 +370,7 @@ test('Frist nach Berliner Kalendertagen, nicht nach 24-Stunden-Schritten (Ende d
|
||||
// 23.11., 00:01 Berliner Zeit: der Tag der Frist ist da, auch vor 23:30 (24 x 30 Stunden später wäre erst 22:30 am 23.11.)
|
||||
assert.deepEqual(await laufe(new Date('2026-11-22T23:01:00.000Z')), ergebnis({ geprueft: 1, abgeschaltet: 1, mails: 1 }))
|
||||
assert.equal(funktion().state, 'disabled')
|
||||
assert.match(mailsMit(K27)[0].text, /Eingaben gespeichert bis: 23\.12\.2026/)
|
||||
assert.match(mailsMit(K30)[0].text, /Eingaben gespeichert bis: 23\.12\.2026/)
|
||||
})
|
||||
|
||||
test('Tag 30 mit zurückgekehrtem Abo: nicht abgeschaltet, sondern die Rückkehr', async () => {
|
||||
@@ -381,10 +381,10 @@ test('Tag 30 mit zurückgekehrtem Abo: nicht abgeschaltet, sondern die Rückkehr
|
||||
assert.deepEqual(await laufe(nach(30)), ergebnis({ geprueft: 1, wiederEin: 1, mails: 1 }))
|
||||
assert.equal(funktion().state, 'active')
|
||||
assert.equal(ersatzTickets().length, 0)
|
||||
assert.equal(mailsMit(K27).length, 0)
|
||||
assert.equal(mailsMit(K30).length, 0)
|
||||
})
|
||||
|
||||
test('K27 scheitert (Mail): abgeschaltet bleibt, der Marker fehlt, der nächste Lauf sendet nach; das Ersatz-Ticket entsteht nur einmal', async () => {
|
||||
test('K30 scheitert (Mail): abgeschaltet bleibt, der Marker fehlt, der nächste Lauf sendet nach; das Ersatz-Ticket entsteht nur einmal', async () => {
|
||||
await legeFunktion(1)
|
||||
abgelaufen()
|
||||
await laufe(JETZT)
|
||||
@@ -394,18 +394,18 @@ test('K27 scheitert (Mail): abgeschaltet bleibt, der Marker fehlt, der nächste
|
||||
}
|
||||
assert.deepEqual(await laufe(nach(30)), ergebnis({ geprueft: 1, abgeschaltet: 1, fehler: 1 }))
|
||||
assert.equal(funktion().state, 'disabled')
|
||||
assert.ok(!funktion().aboEndeMailAt || ms(funktion().aboEndeMailAt) < ms(funktion().disabledAt), 'der Marker der K26 gilt nicht für die K27')
|
||||
assert.ok(!funktion().aboEndeMailAt || ms(funktion().aboEndeMailAt) < ms(funktion().disabledAt), 'der Marker der K29 gilt nicht für die K30')
|
||||
assert.equal(ersatzTickets().length, 1)
|
||||
|
||||
sink.sendMail = roh
|
||||
assert.deepEqual(await laufe(nach(31)), ergebnis({ geprueft: 1, mails: 1 }))
|
||||
assert.equal(mailsMit(K27).length, 1)
|
||||
assert.match(mailsMit(K27)[0].text, /Eingaben gespeichert bis: 04\.12\.2026/, 'Löschdatum von der echten Abschaltung, nicht von heute')
|
||||
assert.equal(mailsMit(K30).length, 1)
|
||||
assert.match(mailsMit(K30)[0].text, /Eingaben gespeichert bis: 04\.12\.2026/, 'Löschdatum von der echten Abschaltung, nicht von heute')
|
||||
assert.equal(ersatzTickets().length, 1)
|
||||
assert.deepEqual(await laufe(nach(32)), ergebnis({ geprueft: 1 }))
|
||||
})
|
||||
|
||||
test('Ersatz-Ticket scheitert: abgeschaltet bleibt, fehler zählt, K27 und Marker fehlen; der nächste Lauf legt das Ticket an und sendet K27 genau einmal', async () => {
|
||||
test('Ersatz-Ticket scheitert: abgeschaltet bleibt, fehler zählt, K30 und Marker fehlen; der nächste Lauf legt das Ticket an und sendet K30 genau einmal', async () => {
|
||||
await legeFunktion(1)
|
||||
abgelaufen()
|
||||
await laufe(JETZT)
|
||||
@@ -414,21 +414,21 @@ test('Ersatz-Ticket scheitert: abgeschaltet bleibt, fehler zählt, K27 und Marke
|
||||
assert.deepEqual(await laufe(nach(30)), ergebnis({ geprueft: 1, abgeschaltet: 1, fehler: 1 }))
|
||||
assert.equal(funktion().state, 'disabled')
|
||||
assert.equal(ersatzTickets().length, 0)
|
||||
assert.equal(mailsMit(K27).length, 0, 'K27 erst nach dem Ersatz-Ticket: so geht sie nie ein zweites Mal raus')
|
||||
assert.ok(!funktion().aboEndeMailAt || ms(funktion().aboEndeMailAt) < ms(funktion().disabledAt), 'kein K27-Marker')
|
||||
assert.equal(mailsMit(K30).length, 0, 'K30 erst nach dem Ersatz-Ticket: so geht sie nie ein zweites Mal raus')
|
||||
assert.ok(!funktion().aboEndeMailAt || ms(funktion().aboEndeMailAt) < ms(funktion().disabledAt), 'kein K30-Marker')
|
||||
|
||||
// der nächste Lauf wiederholt beides (Ticket idempotent über den Hash) und sendet K27 einmal
|
||||
// der nächste Lauf wiederholt beides (Ticket idempotent über den Hash) und sendet K30 einmal
|
||||
assert.deepEqual(await laufe(nach(31)), ergebnis({ geprueft: 1, mails: 1 }))
|
||||
assert.equal(ersatzTickets().length, 1)
|
||||
assert.equal(mailsMit(K27).length, 1)
|
||||
assert.match(mailsMit(K27)[0].text, /Eingaben gespeichert bis: 04\.12\.2026/, 'Löschdatum von der echten Abschaltung')
|
||||
assert.ok(ms(funktion().aboEndeMailAt) >= ms(funktion().disabledAt), 'Marker gilt jetzt der K27')
|
||||
assert.equal(mailsMit(K30).length, 1)
|
||||
assert.match(mailsMit(K30)[0].text, /Eingaben gespeichert bis: 04\.12\.2026/, 'Löschdatum von der echten Abschaltung')
|
||||
assert.ok(ms(funktion().aboEndeMailAt) >= ms(funktion().disabledAt), 'Marker gilt jetzt der K30')
|
||||
assert.deepEqual(await laufe(nach(32)), ergebnis({ geprueft: 1 }))
|
||||
assert.equal(ersatzTickets().length, 1)
|
||||
assert.equal(mailsMit(K27).length, 1)
|
||||
assert.equal(mailsMit(K30).length, 1)
|
||||
})
|
||||
|
||||
test('Ersatz-Ticket scheitert mehrere Läufe: kein K27, jeder Lauf zählt fehler; sobald es klappt, genau eine K27', async () => {
|
||||
test('Ersatz-Ticket scheitert mehrere Läufe: kein K30, jeder Lauf zählt fehler; sobald es klappt, genau eine K30', async () => {
|
||||
await legeFunktion(1)
|
||||
abgelaufen()
|
||||
await laufe(JETZT)
|
||||
@@ -437,18 +437,18 @@ test('Ersatz-Ticket scheitert mehrere Läufe: kein K27, jeder Lauf zählt fehler
|
||||
await laufe(nach(30))
|
||||
aw.failNext({ collection: 'workorders', method: 'POST', status: 500, message: 'Ticket kaputt' })
|
||||
assert.deepEqual(await laufe(nach(31)), ergebnis({ geprueft: 1, fehler: 1 }))
|
||||
assert.equal(mailsMit(K27).length, 0)
|
||||
assert.equal(mailsMit(K30).length, 0)
|
||||
assert.deepEqual(await laufe(nach(32)), ergebnis({ geprueft: 1, mails: 1 }))
|
||||
assert.equal(mailsMit(K27).length, 1)
|
||||
assert.equal(mailsMit(K30).length, 1)
|
||||
assert.equal(ersatzTickets().length, 1)
|
||||
})
|
||||
|
||||
test('Abschalten gelingt, die Folge scheitert (Marker der K26 steht noch, älter als disabledAt): der nächste Lauf holt K27 und das Ersatz-Ticket nach, einmal', async () => {
|
||||
// So steht das Dokument, wenn nach disable() nichts mehr geschrieben wurde: disabled/abo_ende, der Marker ist der der K26
|
||||
test('Abschalten gelingt, die Folge scheitert (Marker der K29 steht noch, älter als disabledAt): der nächste Lauf holt K30 und das Ersatz-Ticket nach, einmal', async () => {
|
||||
// So steht das Dokument, wenn nach disable() nichts mehr geschrieben wurde: disabled/abo_ende, der Marker ist der der K29
|
||||
await legeFunktion(1, { state: 'disabled', enabled: false, disabledReason: 'abo_ende', disabledAt: JETZT.toISOString(), aboEndeAt: ZUVOR, aboEndeMailAt: ZUVOR })
|
||||
abgelaufen()
|
||||
assert.deepEqual(await laufe(nach(1)), ergebnis({ geprueft: 1, mails: 1 }))
|
||||
assert.equal(mailsMit(K27).length, 1)
|
||||
assert.equal(mailsMit(K30).length, 1)
|
||||
assert.equal(ersatzTickets().length, 1)
|
||||
assert.ok(ms(funktion().aboEndeMailAt) >= ms(funktion().disabledAt))
|
||||
assert.deepEqual(await laufe(nach(2)), ergebnis({ geprueft: 1 }))
|
||||
@@ -456,7 +456,7 @@ test('Abschalten gelingt, die Folge scheitert (Marker der K26 steht noch, älter
|
||||
assert.equal(ersatzTickets().length, 1)
|
||||
})
|
||||
|
||||
test('Wanduhr nach dem Laufstart (disabledAt später als now): der K27-Marker liegt nie davor, K27 geht nicht täglich raus', async () => {
|
||||
test('Wanduhr nach dem Laufstart (disabledAt später als now): der K30-Marker liegt nie davor, K30 geht nicht täglich raus', async () => {
|
||||
await legeFunktion(1)
|
||||
abgelaufen()
|
||||
await laufe(JETZT)
|
||||
@@ -470,12 +470,12 @@ test('Wanduhr nach dem Laufstart (disabledAt später als now): der K27-Marker li
|
||||
}
|
||||
assert.ok(ms(funktion().aboEndeMailAt) >= ms(funktion().disabledAt), 'Marker nicht vor disabledAt')
|
||||
assert.deepEqual(await laufe(nach(31)), ergebnis({ geprueft: 1 }))
|
||||
assert.equal(mailsMit(K27).length, 1)
|
||||
assert.equal(mailsMit(K30).length, 1)
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------- Schritt 3: Rückkehr des Abos
|
||||
|
||||
test('Rückkehr an Tag 45: zurück auf active, Neubau, K24 wieder', async () => {
|
||||
test('Rückkehr an Tag 45: zurück auf active, Neubau, K27 wieder', async () => {
|
||||
await bisAbgeschaltet()
|
||||
mails().length = 0
|
||||
laeuft()
|
||||
@@ -492,9 +492,9 @@ test('Rückkehr an Tag 45: zurück auf active, Neubau, K24 wieder', async () =>
|
||||
|
||||
// Neubau nach dem Schreiben des Zustands und ausserhalb der Sperre
|
||||
assert.deepEqual(rebuilds, [{ projektId: 'p1', state: 'active', adminFrei: true }])
|
||||
// K24 mit dem Block wieder und dem Neubau-Text
|
||||
assert.equal(mailsMit(K24).length, 1)
|
||||
const mail = mailsMit(K24)[0]
|
||||
// K27 mit dem Block wieder und dem Neubau-Text
|
||||
assert.equal(mailsMit(K27).length, 1)
|
||||
const mail = mailsMit(K27)[0]
|
||||
assert.deepEqual(mail.to.map((a) => a.address), ['silke@example.com'])
|
||||
assert.match(mail.text, /Dein Wartungs-Abo läuft wieder/)
|
||||
assert.match(mail.text, /nach dem nächsten Veröffentlichen/)
|
||||
@@ -505,7 +505,7 @@ test('Rückkehr an Tag 45: zurück auf active, Neubau, K24 wieder', async () =>
|
||||
assert.equal(rebuilds.length, 1)
|
||||
})
|
||||
|
||||
test('Rückkehr aus disabled: ein nie freigeschaltetes Dokument (kein activatedAt) kommt auf setup zurück; einbau manuell sagt K24 "das Team baut ein"', async () => {
|
||||
test('Rückkehr aus disabled: ein nie freigeschaltetes Dokument (kein activatedAt) kommt auf setup zurück; einbau manuell sagt K27 "das Team baut ein"', async () => {
|
||||
await legeFunktion(1, { state: 'setup', enabled: false, activatedAt: null })
|
||||
abgelaufen()
|
||||
await laufe(JETZT)
|
||||
@@ -524,7 +524,7 @@ test('Rückkehr aus disabled: ein nie freigeschaltetes Dokument (kein activatedA
|
||||
assert.match(mails()[0].text, /baut unser Team/)
|
||||
})
|
||||
|
||||
test('Rückkehr vor dem Abschalten (Tag 10): aboEndeAt leer, Zustand bleibt, kein Neubau, K24 wieder ohne "Wie geht es weiter"', async () => {
|
||||
test('Rückkehr vor dem Abschalten (Tag 10): aboEndeAt leer, Zustand bleibt, kein Neubau, K27 wieder ohne "Wie geht es weiter"', async () => {
|
||||
for (const [name, over] of [
|
||||
['aktiv', {}],
|
||||
['in Einrichtung', { state: 'setup', enabled: false, activatedAt: null }],
|
||||
@@ -548,15 +548,15 @@ test('Rückkehr vor dem Abschalten (Tag 10): aboEndeAt leer, Zustand bleibt, kei
|
||||
assert.equal(doc.einbau, 'angestossen', name)
|
||||
assert.equal(rebuilds.length, 0, `${name}: die Leisten waren nie weg, kein Neubau`)
|
||||
|
||||
assert.equal(mailsMit(K24).length, 1, name)
|
||||
const text = mailsMit(K24)[0].text
|
||||
assert.equal(mailsMit(K27).length, 1, name)
|
||||
const text = mailsMit(K27)[0].text
|
||||
assert.match(text, /Dein Wartungs-Abo läuft wieder/, name)
|
||||
assert.doesNotMatch(text, /Wie geht es weiter/, `${name}: beide Blöcke aus, der Kasten entfällt`)
|
||||
assert.doesNotMatch(text, /nach dem nächsten Veröffentlichen|baut unser Team|Ohne gespeicherte Zeiten/, name)
|
||||
}
|
||||
})
|
||||
|
||||
test('Rückkehr ohne vorherige K26 (kein Marker): die Funktion wird bereinigt, der Kunde bekommt keine Mail über etwas, das er nie erfuhr', async () => {
|
||||
test('Rückkehr ohne vorherige K29 (kein Marker): die Funktion wird bereinigt, der Kunde bekommt keine Mail über etwas, das er nie erfuhr', async () => {
|
||||
await legeFunktion(1, { aboEndeAt: JETZT.toISOString(), behaltenSessionId: 'cs_test_offen' })
|
||||
laeuft()
|
||||
assert.deepEqual(await laufe(nach(3)), ergebnis({ geprueft: 1, wiederEin: 1 }))
|
||||
@@ -572,8 +572,8 @@ test('Rückkehr: das Abo kommt zwischen Vorauswahl und Sperre zurück ⇒ der fr
|
||||
const erg = await mitEinwurf(liste('subscriptions'), async () => laeuft(), () => laufe(nach(30)))
|
||||
assert.deepEqual(erg, ergebnis({ geprueft: 1, wiederEin: 1, mails: 1 }))
|
||||
assert.equal(funktion().state, 'active')
|
||||
assert.equal(mailsMit(K27).length, 0)
|
||||
assert.equal(mailsMit(K24).length, 1)
|
||||
assert.equal(mailsMit(K30).length, 0)
|
||||
assert.equal(mailsMit(K27).length, 1)
|
||||
assert.equal(ersatzTickets().length, 0)
|
||||
})
|
||||
|
||||
@@ -592,7 +592,7 @@ test('Löschung fällig, aber die Daten sind schon weg (Lauf nach dem Löschen u
|
||||
assert.deepEqual(await laufe(nach(60)), ergebnis({ geprueft: 1, geloescht: 1 }))
|
||||
assert.equal(funktion().state, 'off')
|
||||
assert.equal(rebuilds.length, 0)
|
||||
assert.equal(mailsMit(K24).length, 0)
|
||||
assert.equal(mailsMit(K27).length, 0)
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------- Schritt 4: Löschung
|
||||
@@ -615,22 +615,22 @@ test('Tag 60 nach dem Abo-Ende (30 Tage nach der Abschaltung): Daten gelöscht,
|
||||
assert.equal(doc.state, 'off')
|
||||
assert.equal(doc.enabled, false)
|
||||
assert.deepEqual(
|
||||
[doc.ticketId ?? '', doc.einbau ?? '', doc.k24At ?? null, doc.aboEndeAt ?? null, doc.behaltenSessionId ?? '', doc.activatedAt ?? null],
|
||||
[doc.ticketId ?? '', doc.einbau ?? '', doc.einschaltMailAt ?? null, doc.aboEndeAt ?? null, doc.behaltenSessionId ?? '', doc.activatedAt ?? null],
|
||||
['', '', null, null, '', null],
|
||||
'ticketId, einbau, k24At, aboEndeAt, behaltenSessionId, activatedAt geleert (sonst käme ein späterer Lauf zu früh auf active)'
|
||||
'ticketId, einbau, einschaltMailAt, aboEndeAt, behaltenSessionId, activatedAt geleert (sonst käme ein späterer Lauf zu früh auf active)'
|
||||
)
|
||||
assert.match(doc.adminNote, /Öffnungszeiten gelöscht, 30 Tage nach dem Abschalten/)
|
||||
assert.equal(await ladeZeiten(feature.$id), null, 'das Zeiten-Dokument (samt ersatzHash) ist gelöscht')
|
||||
assert.equal(aw.docs('oeffnungszeiten').size, 0)
|
||||
assert.equal(aw.docs('zeitenErinnerungen').has('m1'), false, 'Erinnerungsmerker der Funktion gelöscht')
|
||||
assert.equal(aw.docs('zeitenErinnerungen').has('m2'), true, 'fremde Merker bleiben')
|
||||
assert.equal(mails().length, 0, 'die K27 hat die Löschung angekündigt, es gibt keine weitere Mail')
|
||||
assert.equal(mails().length, 0, 'die K30 hat die Löschung angekündigt, es gibt keine weitere Mail')
|
||||
|
||||
// aus ist aus: der nächste Lauf liest das Dokument nicht mehr
|
||||
assert.deepEqual(await laufe(nach(61)), ergebnis())
|
||||
})
|
||||
|
||||
test('nach der Löschung: ticketId, einbau, k24At, ersatzHash leer; Neubestellung legt ein neues Ticket an', async () => {
|
||||
test('nach der Löschung: ticketId, einbau, einschaltMailAt, ersatzHash leer; Neubestellung legt ein neues Ticket an', async () => {
|
||||
await bisAbgeschaltet()
|
||||
await laufe(nach(60))
|
||||
assert.equal(funktion().state, 'off')
|
||||
@@ -639,7 +639,7 @@ test('nach der Löschung: ticketId, einbau, k24At, ersatzHash leer; Neubestellun
|
||||
mails().length = 0
|
||||
rebuilds.length = 0
|
||||
|
||||
// Das Abo läuft wieder, der Kunde schaltet neu ein (Gratisweg): neues Ticket, Neubau, K24
|
||||
// Das Abo läuft wieder, der Kunde schaltet neu ein (Gratisweg): neues Ticket, Neubau, K27
|
||||
laeuft()
|
||||
const { doc, changed } = await einschaltenZeiten({ customerId: KUNDE, projectId: 'p1', by: 'kunde', now: nach(61) })
|
||||
assert.equal(changed, true)
|
||||
@@ -647,8 +647,8 @@ test('nach der Löschung: ticketId, einbau, k24At, ersatzHash leer; Neubestellun
|
||||
assert.ok(doc.ticketId && doc.ticketId !== 't_1', 'ein neues Einbau-Ticket')
|
||||
assert.equal(tickets().length, ticketsVorher + 1)
|
||||
assert.equal(doc.einbau, 'angestossen')
|
||||
assert.ok(doc.k24At, 'K24 neu gesendet')
|
||||
assert.equal(mailsMit(K24).length, 1)
|
||||
assert.ok(doc.einschaltMailAt, 'K27 neu gesendet')
|
||||
assert.equal(mailsMit(K27).length, 1)
|
||||
assert.equal(rebuilds.length, 1)
|
||||
})
|
||||
|
||||
@@ -790,14 +790,14 @@ test('ein unlesbares Datum bei einem Dokument stoppt die anderen nicht (je Dokum
|
||||
const vorher = [stand(1), stand(2)]
|
||||
|
||||
const erg = await laufe(nach(30))
|
||||
assert.deepEqual(erg, ergebnis({ geprueft: 3, aboEnde: 1, mails: 1, fehler: 2 }), 'das dritte Dokument ist trotzdem dran: Abo-Ende erkannt, K26')
|
||||
assert.deepEqual(erg, ergebnis({ geprueft: 3, aboEnde: 1, mails: 1, fehler: 2 }), 'das dritte Dokument ist trotzdem dran: Abo-Ende erkannt, K29')
|
||||
assert.deepEqual([stand(1), stand(2)], vorher, 'die kaputten Dokumente bleiben, wie sie sind')
|
||||
assert.deepEqual(mails().map((m) => m.to[0].address), ['eva@example.com'])
|
||||
assert.equal(ersatzTickets().length, 0)
|
||||
assert.equal(aw.docs('oeffnungszeiten').size, 3, 'nichts gelöscht')
|
||||
})
|
||||
|
||||
test('Kunde nicht lesbar beim Versand: fehler, das nächste Dokument ist trotzdem dran, der nächste Lauf sendet die K26 nach', async () => {
|
||||
test('Kunde nicht lesbar beim Versand: fehler, das nächste Dokument ist trotzdem dran, der nächste Lauf sendet die K29 nach', async () => {
|
||||
await legeFunktion(1)
|
||||
await legeFunktion(2, {}, { kunde: 'c2' })
|
||||
aw.seed('customers', { $id: 'c2', name: 'Karl Weber', email: 'karl@example.com' })
|
||||
@@ -817,13 +817,13 @@ test('Kunde nicht lesbar beim Versand: fehler, das nächste Dokument ist trotzde
|
||||
test('Mails und Neubau gehen ohne gehaltene Sperre feature-admin raus', async () => {
|
||||
await legeFunktion(1)
|
||||
abgelaufen()
|
||||
await laufe(JETZT) // K26
|
||||
await laufe(nach(30)) // K27
|
||||
await laufe(JETZT) // K29
|
||||
await laufe(nach(30)) // K30
|
||||
laeuft()
|
||||
await laufe(nach(45)) // Neubau und K24
|
||||
await laufe(nach(45)) // Neubau und K27
|
||||
assert.deepEqual(
|
||||
sperrenBeimVersand.map((s) => [s.subject === K27 ? 'K27' : s.subject === K24 ? 'K24' : 'K26', s.admin]),
|
||||
[['K26', true], ['K27', true], ['K24', true]]
|
||||
sperrenBeimVersand.map((s) => [s.subject === K30 ? 'K30' : s.subject === K27 ? 'K27' : 'K29', s.admin]),
|
||||
[['K29', true], ['K30', true], ['K27', true]]
|
||||
)
|
||||
assert.equal(rebuilds[0].adminFrei, true)
|
||||
})
|
||||
@@ -840,7 +840,7 @@ test('Abo-Abfragen: die Vorauswahl liest je Kunde einmal, dazu das frische Lesen
|
||||
aw.resetListCalls()
|
||||
assert.deepEqual(await laufe(), ergebnis({ geprueft: 3, aboEnde: 2, mails: 2 }))
|
||||
assert.equal(abfragenAufAbos(), 2 + 2, 'zwei Kunden in der Vorauswahl; frisch nur für die zwei Dokumente von c1 (das von c2 braucht nichts)')
|
||||
assert.equal(mailsMit(K26).length, 2)
|
||||
assert.equal(mailsMit(K29).length, 2)
|
||||
assert.deepEqual(mails().map((m) => m.to[0].address), ['silke@example.com', 'silke@example.com'])
|
||||
assert.ok(funktion(1).aboEndeAt && funktion(2).aboEndeAt)
|
||||
assert.ok(!funktion(3).aboEndeAt)
|
||||
@@ -887,12 +887,12 @@ test('activate() ohne extra lässt aboEndeAt und behaltenSessionId stehen; admin
|
||||
assert.deepEqual([team.aboEndeAt ?? null, team.behaltenSessionId ?? ''], [null, ''])
|
||||
})
|
||||
|
||||
test('abgeschaltet mit abo_ende, aber ohne Marker (Lauf nach dem Abschalten unterbrochen): der nächste Lauf holt K27 und das Ersatz-Ticket nach', async () => {
|
||||
test('abgeschaltet mit abo_ende, aber ohne Marker (Lauf nach dem Abschalten unterbrochen): der nächste Lauf holt K30 und das Ersatz-Ticket nach', async () => {
|
||||
await legeFunktion(1, { state: 'disabled', enabled: false, disabledReason: 'abo_ende', disabledAt: JETZT.toISOString(), aboEndeAt: ZUVOR })
|
||||
abgelaufen()
|
||||
assert.deepEqual(await laufe(nach(1)), ergebnis({ geprueft: 1, mails: 1 }))
|
||||
assert.equal(mailsMit(K27).length, 1)
|
||||
assert.match(mailsMit(K27)[0].text, /Eingaben gespeichert bis: 04\.11\.2026/, 'Löschdatum = disabledAt + 30 Tage')
|
||||
assert.equal(mailsMit(K30).length, 1)
|
||||
assert.match(mailsMit(K30)[0].text, /Eingaben gespeichert bis: 04\.11\.2026/, 'Löschdatum = disabledAt + 30 Tage')
|
||||
assert.equal(ersatzTickets().length, 1)
|
||||
assert.deepEqual(await laufe(nach(2)), ergebnis({ geprueft: 1 }))
|
||||
assert.equal(mails().length, 1)
|
||||
|
||||
Reference in new issue
Block a user