AB-Kern: Testhilfe und Vertragsprüfung der L0-Module
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8627ed1124
commit
68ffac24c4
2 files changed
+354
No files matched your search
@@ -0,0 +1,90 @@
|
||||
import { test, after } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { starteUmgebung, entwurfsDaten, sha256, KUNDE_ID, PROJEKT_ID, VON } from './helpers/auftraegeAb.js'
|
||||
|
||||
// Vertragsprüfung: Die L0-Module (Fundament) liefern, was der Strang L3 braucht (Übersicht V1 bis V4). Schlägt sie fehl,
|
||||
// weicht L0 vom Vertrag ab: nichts in fremden Dateien reparieren, sondern stoppen und melden.
|
||||
const umg = await starteUmgebung()
|
||||
after(() => umg.schliesse())
|
||||
const DIENSTE = '../server/services/auftraege'
|
||||
const store = await import(`${DIENSTE}/auftragStore.js`)
|
||||
const ablage = await import(`${DIENSTE}/ablage.js`)
|
||||
const { getSettings } = await import('../server/services/finance/settings.js')
|
||||
|
||||
const EXPORTE = {
|
||||
'konstanten.js': ['AUFTRAG_ARTEN', 'AUFTRAG_WEGE', 'ZAHLUNGSPLAN_ARTEN', 'KUNDENARTEN', 'VEREINBARTE_STATUS', 'STUNDENSATZ_STANDARD_CENTS', 'VIERTELSTUNDE', 'TOKEN_GUELTIG_TAGE', 'AGB_FASSUNG', 'LIMITS', 'SPERRE_NUMMER', 'sperreAuftrag', 'SCHALTER_AUS_MELDUNG'],
|
||||
'fehler.js': ['AuftragError', 'sendeFehler'],
|
||||
'schalter.js': ['auftraegeAktiv'],
|
||||
'auftragStore.js': ['getAuftrag', 'listAuftraege', 'createAuftrag', 'updateAuftrag', 'deleteAuftrag', 'findAuftragByTokenHash', 'findAuftragByNummer'],
|
||||
'projektRechnungenStore.js': ['getProjektRechnung', 'listProjektRechnungen', 'createProjektRechnung', 'updateProjektRechnung', 'umhaengen'],
|
||||
'auftragSicht.js': ['kundeSnapshot', 'auftragFuerTeam'],
|
||||
'ablage.js': ['speicherePdf', 'lesePdf', 'entfernePdf'], // entfernePdf: Nachtrag der Planrunde (Übersicht, Nachträge L0)
|
||||
'texte.js': ['TEXTE_FASSUNG'],
|
||||
}
|
||||
|
||||
test('L0-Module, Konfiguration und Einstellungen liefern die Namen der Übersicht', async () => {
|
||||
for (const [datei, namen] of Object.entries(EXPORTE)) {
|
||||
const modul = await import(`${DIENSTE}/${datei}`)
|
||||
for (const name of namen) assert.ok(name in modul, `${datei} exportiert ${name} nicht`)
|
||||
}
|
||||
for (const feld of ['mode', 'pilotEmails', 'uploadDir']) assert.ok(feld in umg.config.auftraege, `config.auftraege.${feld} fehlt`)
|
||||
assert.equal(umg.config.auftraege.uploadDir, umg.uploadDir)
|
||||
assert.equal(umg.config.collections.auftraege, 'auftraege')
|
||||
assert.equal(umg.config.collections.projektRechnungen, 'projektRechnungen')
|
||||
assert.equal((await getSettings()).steuernummer, '')
|
||||
// Sperren (V2): je Sperre ein zusammengesetzter Schlüssel, weil withLocks jedes Listenelement für sich sperrt
|
||||
const { SPERRE_NUMMER, sperreAuftrag } = await import(`${DIENSTE}/konstanten.js`)
|
||||
assert.deepEqual(SPERRE_NUMMER, ['auftraege-nummer'])
|
||||
assert.deepEqual(sperreAuftrag('a_x1'), ['auftraege-ab:a_x1'])
|
||||
})
|
||||
|
||||
test('createAuftrag legt einen Entwurf mit den Leerwerten aus V3 an, updateAuftrag ändert nur die genannten Felder', async () => {
|
||||
const a = await store.createAuftrag(entwurfsDaten())
|
||||
assert.deepEqual(
|
||||
[a.status, a.nummer, a.jahr, a.laufnummer, a.kunde, a.gesendetAm, a.tokenGueltigBis, a.bestaetigtAm, a.bestaetigtUeber, a.mailStatus, a.fruehbeginn, a.erstelltVon],
|
||||
['entwurf', '', null, null, null, null, null, null, '', '', false, VON],
|
||||
)
|
||||
assert.equal(typeof a.id, 'string')
|
||||
assert.ok(!Number.isNaN(new Date(a.erstelltAm).getTime()))
|
||||
assert.deepEqual(a.positionen, entwurfsDaten().positionen)
|
||||
assert.deepEqual(a.zahlungsplan, { art: 'anzahlung', prozent: 50, text: '' })
|
||||
assert.deepEqual(await store.getAuftrag(a.id), a)
|
||||
const b = await store.updateAuftrag(a.id, { titel: 'Anderer Titel' })
|
||||
assert.equal(b.titel, 'Anderer Titel')
|
||||
assert.deepEqual([b.positionen, b.zeitquelle, b.zahlungsplan, b.summeCents], [a.positionen, a.zeitquelle, a.zahlungsplan, a.summeCents])
|
||||
assert.deepEqual(await store.getAuftrag(a.id), b)
|
||||
assert.equal(await store.getAuftrag('a_gibtsnicht'), null)
|
||||
})
|
||||
|
||||
test('listAuftraege liefert alle Treffer, auch mehr als die 25 Dokumente, die Appwrite ohne limit liefert', async () => {
|
||||
for (let i = 0; i < 30; i += 1) await store.createAuftrag(entwurfsDaten({ kundenId: 'c_liste01' }))
|
||||
assert.equal((await store.listAuftraege({ kundenId: 'c_liste01' })).length, 30)
|
||||
})
|
||||
|
||||
test('ablage: speicherePdf legt <kundenId>/<id>.pdf an, überschreibt nie; lesePdf liest und lehnt fremde Pfade ab; entfernePdf räumt auf', async () => {
|
||||
const pdf = Buffer.from('%PDF-1.4 vertrag')
|
||||
assert.deepEqual(await ablage.speicherePdf('c_ablage', 'a_ablage', pdf), { pdfDatei: 'c_ablage/a_ablage.pdf', pdfSha256: sha256(pdf) })
|
||||
assert.deepEqual(await ablage.lesePdf('c_ablage/a_ablage.pdf'), pdf)
|
||||
await assert.rejects(async () => ablage.speicherePdf('c_ablage', 'a_ablage', Buffer.from('%PDF-1.4 anders')))
|
||||
await assert.rejects(async () => ablage.lesePdf('../etc/passwd'), (err) => err.status === 404)
|
||||
await ablage.entfernePdf('c_ablage/a_ablage.pdf')
|
||||
await assert.rejects(async () => ablage.lesePdf('c_ablage/a_ablage.pdf'), (err) => err.status === 404)
|
||||
assert.equal((await ablage.speicherePdf('c_ablage', 'a_ablage', pdf)).pdfDatei, 'c_ablage/a_ablage.pdf')
|
||||
})
|
||||
|
||||
test('umhaengen hängt alle Dokumente einer AB auf die neue um und zählt sie; ein zweiter Aufruf findet nichts mehr; list und update genügen für die Projektzuordnung', async () => {
|
||||
const { createProjektRechnung, getProjektRechnung, listProjektRechnungen, updateProjektRechnung, umhaengen } = await import(`${DIENSTE}/projektRechnungenStore.js`)
|
||||
const daten = (auftragId) => ({ kundenId: KUNDE_ID, projektId: PROJEKT_ID, auftragId, art: 'anzahlung', betragCents: 40000, arbeitsblattIds: [], rechnungsnummer: 'WEBKLAR-0001', aktiv: true, erstelltVon: VON, erstelltAm: new Date().toISOString() })
|
||||
await createProjektRechnung('in_Umhaengen1', daten('a_alt'))
|
||||
await createProjektRechnung('in_Umhaengen2', daten('a_anders'))
|
||||
assert.equal(await umhaengen('a_alt', 'a_neu'), 1)
|
||||
assert.equal((await getProjektRechnung('in_Umhaengen1')).auftragId, 'a_neu')
|
||||
assert.equal((await getProjektRechnung('in_Umhaengen2')).auftragId, 'a_anders')
|
||||
assert.equal(await umhaengen('a_alt', 'a_neu'), 0)
|
||||
// Projektzuordnung (Task 11): alle Dokumente einer AB, auch inaktive; die projektId lässt sich einzeln ändern, sonst bleibt alles
|
||||
await updateProjektRechnung('in_Umhaengen1', { aktiv: false })
|
||||
assert.deepEqual((await listProjektRechnungen({ auftragId: 'a_neu', nurAktiv: false })).map((r) => r.invoiceId), ['in_Umhaengen1'])
|
||||
assert.deepEqual(await listProjektRechnungen({ auftragId: 'a_neu' }), [])
|
||||
const geaendert = await updateProjektRechnung('in_Umhaengen1', { projektId: 'p_neu01' })
|
||||
assert.deepEqual([geaendert.projektId, geaendert.auftragId, geaendert.betragCents, geaendert.aktiv], ['p_neu01', 'a_neu', 40000, false])
|
||||
})
|
||||
@@ -0,0 +1,264 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { createHash } from 'node:crypto'
|
||||
import fs from 'node:fs'
|
||||
import os from 'node:os'
|
||||
import path from 'node:path'
|
||||
import { mock } from 'node:test'
|
||||
import { startAppwriteMock } from '../support/appwriteMock.js'
|
||||
import { useTestEnv } from '../support/testEnv.js'
|
||||
import { AUFTRAEGE_COLLECTIONS } from '../../server/services/auftraege/schema.js'
|
||||
|
||||
// Testhilfen des Strangs L3 (AB-Kern). Kein statischer Import von server/config.js: starteUmgebung() setzt zuerst die
|
||||
// Umgebung, erst danach darf config.js geladen werden.
|
||||
|
||||
const NAMEN = Object.keys(AUFTRAEGE_COLLECTIONS)
|
||||
const attribute = (name) => AUFTRAEGE_COLLECTIONS[name].attributes
|
||||
/** Attribut-Schlüssel je AB-Collection für startAppwriteMock({ schema }). */
|
||||
export const AUFTRAEGE_SCHEMA = Object.fromEntries(NAMEN.map((name) => [name, attribute(name).map((a) => a.key)]))
|
||||
/** Datumsfelder je AB-Collection für startAppwriteMock({ datetimes }): der Nachbau schreibt sie wie Appwrite ("+00:00"). */
|
||||
export const AUFTRAEGE_DATETIMES = Object.fromEntries(
|
||||
NAMEN.map((name) => [name, attribute(name).filter((a) => a.type === 'datetime').map((a) => a.key)]),
|
||||
)
|
||||
|
||||
export const KUNDE_ID = 'c_kunde01'
|
||||
export const PROJEKT_ID = 'p_projekt01'
|
||||
export const TICKET_ID = 't_ticket01'
|
||||
export const ZWEITES_TICKET = 't_zwei02'
|
||||
export const LEAD_KUNDE = 'c_lead01'
|
||||
export const FREMD = { kunde: 'c_fremd01', projekt: 'p_fremd01', ticket: 't_fremd01' }
|
||||
export const VON = 'kenso@webklar.com'
|
||||
|
||||
/** Feste Meldungen (wörtlich wie in der Tabelle „Meldungstexte“ des Plans). */
|
||||
export const MELDUNG = {
|
||||
auftragFehlt: 'Auftragsbestätigung nicht gefunden.',
|
||||
kundeFehlt: 'Kunde nicht gefunden.',
|
||||
positionen: 'Eine Auftragsbestätigung braucht 1 bis 20 Positionen.',
|
||||
nurEntwurfAendern: 'Nur ein Entwurf lässt sich ändern.',
|
||||
nurEntwurfLoeschen: 'Nur ein Entwurf lässt sich löschen.',
|
||||
nurEntwurfSenden: 'Nur ein Entwurf lässt sich senden.',
|
||||
keinFesterKunde: 'Auftragsbestätigungen nur für feste Kunden.',
|
||||
keineKundenart: 'Bitte zuerst die Kundenart angeben (Unternehmen oder Privatperson).',
|
||||
keineMail: 'Für den Versand fehlt die E-Mail-Adresse des Kunden.',
|
||||
adresse: 'Adresse des Kunden unvollständig (Straße, PLZ, Ort). Für den Versand bitte zuerst ergänzen.',
|
||||
pdf: 'Das PDF konnte nicht erzeugt werden.',
|
||||
pruefsumme: 'Die gespeicherte PDF-Datei stimmt nicht mit der Prüfsumme überein.',
|
||||
ersetzteUngueltig: 'Die ersetzte Auftragsbestätigung ist nicht mehr gültig. Bitte eine neue Fassung anlegen.',
|
||||
keineErinnerung: 'Eine Erinnerung gibt es nur für versendete Auftragsbestätigungen.',
|
||||
keineNeueFassung: 'Eine neue Fassung gibt es nur für versendete, bestätigte oder abgerechnete Aufträge.',
|
||||
schonEntwurf: 'Zu dieser Auftragsbestätigung gibt es schon einen Entwurf für eine neue Fassung.',
|
||||
keinStorno: 'Nur versendete oder bestätigte Auftragsbestätigungen lassen sich stornieren.',
|
||||
projektFehlt: 'Projekt: Bitte ein Projekt angeben.',
|
||||
projektUngueltig: 'Projekt: Ungültige Projekt-Kennung.',
|
||||
projektFremd: 'Projekt: Das Projekt gehört nicht zu diesem Kunden.',
|
||||
schonProjekt: 'Diese Auftragsbestätigung gehört schon zu einem Projekt.',
|
||||
keinProjekt: 'Einer stornierten, ersetzten oder widerrufenen Auftragsbestätigung lässt sich kein Projekt mehr zuordnen.',
|
||||
}
|
||||
|
||||
/** Kundendokument wie in Appwrite: Unternehmen mit vollständiger Anschrift und E-Mail. */
|
||||
export const kundeDoc = (patch = {}) => ({
|
||||
$id: KUNDE_ID,
|
||||
name: 'Anna Beispiel',
|
||||
companyName: 'Beispiel GmbH',
|
||||
email: 'anna@example.invalid',
|
||||
street: 'Hauptstraße 1',
|
||||
postalCode: '67722',
|
||||
city: 'Winnweiler',
|
||||
country: 'DE',
|
||||
code: 'K-1001',
|
||||
customerStatus: 'active',
|
||||
kundenart: 'unternehmen',
|
||||
...patch,
|
||||
})
|
||||
export const projektDoc = (patch = {}) => ({ $id: PROJEKT_ID, customerId: KUNDE_ID, projectName: 'Website Beispiel', ticketId: TICKET_ID, ...patch })
|
||||
export const ticketDoc = (patch = {}) => ({ $id: TICKET_ID, customerId: KUNDE_ID, woid: '100200', topic: 'Website Beispiel', ...patch })
|
||||
|
||||
/** Gültige POST-Eingabe: Festpreis, Summe 800,00 € (600 + 2 × 100), Anzahlung 50 % = 400,00 €. */
|
||||
export const festpreisEingabe = (patch = {}) => ({
|
||||
kundenId: KUNDE_ID,
|
||||
projektId: PROJEKT_ID,
|
||||
titel: '',
|
||||
art: 'festpreis',
|
||||
positionen: [
|
||||
{ beschreibung: 'Webdesign Startseite', menge: 1, einzelpreisCents: 60000 },
|
||||
{ beschreibung: 'Kontaktformular', menge: 2, einzelpreisCents: 10000 },
|
||||
],
|
||||
zahlungsplan: { art: 'anzahlung', prozent: 50, text: '' },
|
||||
zahlungszielTage: 14,
|
||||
auftragVom: '2026-10-05',
|
||||
auftragWeg: 'telefon',
|
||||
startAm: '',
|
||||
fertigBis: '',
|
||||
zeitquelle: [],
|
||||
...patch,
|
||||
})
|
||||
|
||||
/** Gültige POST-Eingabe: nach Aufwand zu 100 €/Std., Schätzung 10 Stunden. */
|
||||
export const aufwandEingabe = (patch = {}) => ({
|
||||
kundenId: KUNDE_ID,
|
||||
projektId: PROJEKT_ID,
|
||||
titel: '',
|
||||
art: 'aufwand',
|
||||
stundensatzCents: 10000,
|
||||
schaetzungMinuten: 600,
|
||||
zahlungsplan: { art: 'monatlich', prozent: null, text: '' },
|
||||
zahlungszielTage: 14,
|
||||
auftragVom: '2026-10-05',
|
||||
auftragWeg: 'email',
|
||||
startAm: '',
|
||||
fertigBis: '',
|
||||
zeitquelle: [],
|
||||
...patch,
|
||||
})
|
||||
|
||||
/** Genau die Felder, die legeEntwurfAn an createAuftrag übergibt (bereinigt, ohne kundenart). */
|
||||
export const entwurfsDaten = (patch = {}) => ({
|
||||
kundenId: KUNDE_ID,
|
||||
projektId: PROJEKT_ID,
|
||||
titel: 'Website Beispiel',
|
||||
zeitquelle: [TICKET_ID],
|
||||
art: 'festpreis',
|
||||
positionen: festpreisEingabe().positionen,
|
||||
stundensatzCents: null,
|
||||
schaetzungMinuten: null,
|
||||
summeCents: 80000,
|
||||
zahlungsplan: { art: 'anzahlung', prozent: 50, text: '' },
|
||||
zahlungszielTage: 14,
|
||||
auftragVom: '2026-10-05',
|
||||
auftragWeg: 'telefon',
|
||||
startAm: '',
|
||||
fertigBis: '',
|
||||
erstelltVon: VON,
|
||||
...patch,
|
||||
})
|
||||
|
||||
export const sha256 = (daten) => createHash('sha256').update(daten).digest('hex')
|
||||
export const warte = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
|
||||
/** Offene Zusage, die der Test später einlöst: { promise, freigeben }. */
|
||||
export function tor() {
|
||||
let freigeben
|
||||
const promise = new Promise((resolve) => {
|
||||
freigeben = resolve
|
||||
})
|
||||
return { promise, freigeben }
|
||||
}
|
||||
/** Wartet (höchstens eine Sekunde), bis bedingung() wahr ist. */
|
||||
export async function wartebis(bedingung) {
|
||||
for (let i = 0; i < 200; i += 1) {
|
||||
if (bedingung()) return
|
||||
await warte(5)
|
||||
}
|
||||
throw new Error('Bedingung nicht eingetreten')
|
||||
}
|
||||
/** Fängt eine Zusage ab, damit sie nie unbehandelt scheitert: { wert } oder { fehler }. */
|
||||
export const ergebnis = (promise) => promise.then((wert) => ({ wert }), (fehler) => ({ fehler }))
|
||||
|
||||
const pruefeFehler = (status, message, name) => (err) => {
|
||||
assert.equal(err.status, status, `${name} Status ${err.status}: ${err.message}`)
|
||||
if (message !== undefined) assert.equal(err.message, message, name)
|
||||
return true
|
||||
}
|
||||
/** Die Zusage scheitert mit diesem Status und (wenn angegeben) dieser Meldung. */
|
||||
export const abgelehnt = (promise, status, message, name = '') => assert.rejects(promise, pruefeFehler(status, message, name))
|
||||
/** Der Aufruf wirft sofort mit diesem Status und (wenn angegeben) dieser Meldung. */
|
||||
export const wirftFehler = (aufruf, status, message, name = '') => assert.throws(aufruf, pruefeFehler(status, message, name))
|
||||
|
||||
let tokenZaehler = 0
|
||||
/**
|
||||
* Attrappen für deps (kein pdfkit, kein SMTP); calls zeichnet jeden Aufruf auf. Optionen: pdf (feste Antwort, sonst
|
||||
* "%PDF-1.4 <nummer>"), pdfFehler, pdfWarten (Promise, auf die die PDF-Erzeugung wartet), mailFehler, mailWarten.
|
||||
*/
|
||||
export function baueDeps({ pdf = null, pdfFehler = null, pdfWarten = null, mailFehler = null, mailWarten = null } = {}) {
|
||||
const calls = { pdf: [], token: [], mail: [] }
|
||||
const deps = {
|
||||
async erzeugeAuftragPdf(auftrag, optionen) {
|
||||
calls.pdf.push({ auftrag: structuredClone(auftrag), optionen })
|
||||
if (pdfWarten) await pdfWarten
|
||||
if (pdfFehler) throw pdfFehler
|
||||
return pdf ?? Buffer.from(`%PDF-1.4 ${auftrag.nummer}`)
|
||||
},
|
||||
neuerToken(jetzt = new Date()) {
|
||||
tokenZaehler += 1
|
||||
const token = `t${tokenZaehler}`.padEnd(43, 'x')
|
||||
calls.token.push(token)
|
||||
return { token, hash: sha256(token), gueltigBis: new Date(jetzt.getTime() + 60 * 86_400_000) }
|
||||
},
|
||||
async sendeAuftragMail(auftrag, optionen) {
|
||||
calls.mail.push({ auftrag: structuredClone(auftrag), pdf: optionen.pdf, token: optionen.token, erinnerung: optionen.erinnerung ?? false })
|
||||
if (mailWarten) await mailWarten
|
||||
if (mailFehler) throw mailFehler
|
||||
},
|
||||
}
|
||||
return { deps, calls }
|
||||
}
|
||||
|
||||
const PRAEFIX = 'auftraege-l3-'
|
||||
function pruefeTestVerzeichnis(dir) {
|
||||
if (path.dirname(dir) !== os.tmpdir() || !path.basename(dir).startsWith(PRAEFIX)) throw new Error(`Kein Testverzeichnis des Strangs L3: ${dir}`)
|
||||
}
|
||||
export const legeUploadVerzeichnisAn = () => fs.mkdtempSync(path.join(os.tmpdir(), PRAEFIX))
|
||||
export function raeumeUploadVerzeichnisAuf(dir) {
|
||||
pruefeTestVerzeichnis(dir)
|
||||
fs.rmSync(dir, { recursive: true, force: true })
|
||||
}
|
||||
export function leereVerzeichnis(dir) {
|
||||
pruefeTestVerzeichnis(dir)
|
||||
for (const name of fs.readdirSync(dir)) fs.rmSync(path.join(dir, name), { recursive: true, force: true })
|
||||
}
|
||||
/** Alle Dateien unterhalb von dir, relativ und sortiert (leere Verzeichnisse zählen nicht). */
|
||||
export function dateienIn(dir) {
|
||||
const gefunden = []
|
||||
const durchsuche = (relativ) => {
|
||||
for (const eintrag of fs.readdirSync(path.join(dir, relativ), { withFileTypes: true })) {
|
||||
const pfad = path.join(relativ, eintrag.name)
|
||||
if (eintrag.isDirectory()) durchsuche(pfad)
|
||||
else gefunden.push(pfad)
|
||||
}
|
||||
}
|
||||
durchsuche('')
|
||||
return gefunden.sort()
|
||||
}
|
||||
|
||||
/**
|
||||
* Startet den Appwrite-Nachbau (mit dem Schema der AB-Collections, wie Appwrite mit 25 Treffern ohne limit), setzt die
|
||||
* Testumgebung (Schalter an, Upload-Verzeichnis im Temp-Ordner) und lädt erst danach config.js. Die Module des Strangs
|
||||
* importiert der Test selbst, danach.
|
||||
*/
|
||||
export async function starteUmgebung() {
|
||||
const uploadDir = legeUploadVerzeichnisAn()
|
||||
process.env.AUFTRAEGE_UPLOAD_DIR = uploadDir
|
||||
process.env.AUFTRAEGE_ENABLED = 'on'
|
||||
const aw = await startAppwriteMock({ schema: AUFTRAEGE_SCHEMA, datetimes: AUFTRAEGE_DATETIMES, defaultLimit: 25 })
|
||||
useTestEnv({ appwriteUrl: aw.url, stripeUrl: 'http://127.0.0.1:1' })
|
||||
const { config } = await import('../../server/config.js')
|
||||
const { deleteDocument } = await import('../../server/services/appwriteAdmin.js')
|
||||
const stamm = () => [
|
||||
[config.collections.customers, kundeDoc()],
|
||||
[config.collections.customers, kundeDoc({ $id: FREMD.kunde, name: 'Fritz Fremd', companyName: '', email: 'fritz@example.invalid', kundenart: '' })],
|
||||
[config.collections.customers, kundeDoc({ $id: LEAD_KUNDE, name: 'Lea Lead', customerStatus: 'lead', kundenart: '' })],
|
||||
[config.collections.websiteProjects, projektDoc()],
|
||||
[config.collections.websiteProjects, projektDoc({ $id: FREMD.projekt, customerId: FREMD.kunde, projectName: 'Fremde Website', ticketId: FREMD.ticket })],
|
||||
[config.collections.workorders, ticketDoc()],
|
||||
[config.collections.workorders, ticketDoc({ $id: ZWEITES_TICKET })],
|
||||
[config.collections.workorders, ticketDoc({ $id: FREMD.ticket, customerId: FREMD.kunde })],
|
||||
]
|
||||
return {
|
||||
aw,
|
||||
config,
|
||||
uploadDir,
|
||||
/** Vor jedem Test: Spione zurück, Schalter an, AB-Collections und Upload-Verzeichnis leer, Stammdaten neu. */
|
||||
async zuruecksetzen() {
|
||||
mock.restoreAll()
|
||||
config.auftraege.mode = 'on'
|
||||
config.auftraege.pilotEmails = []
|
||||
for (const collection of [config.collections.auftraege, config.collections.projektRechnungen]) {
|
||||
for (const id of aw.docs(collection).keys()) await deleteDocument(collection, id)
|
||||
}
|
||||
leereVerzeichnis(uploadDir)
|
||||
for (const [collection, doc] of stamm()) aw.seed(collection, doc)
|
||||
},
|
||||
async schliesse() {
|
||||
await aw.close()
|
||||
raeumeUploadVerzeichnisAuf(uploadDir)
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user