622 lines
22 KiB
JavaScript
622 lines
22 KiB
JavaScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { randomUUID } from "node:crypto";
|
|
import { createApp } from "../server/index.js";
|
|
import { createCsvStorage } from "../server/services/csv-storage.js";
|
|
import { createBookingService } from "../server/services/booking-service.js";
|
|
import { createEmailService } from "../server/services/email-service.js";
|
|
import { packages } from "../shared/packages.js";
|
|
|
|
const input = {
|
|
packageId: packages[0].id,
|
|
name: 'Test, "Person"',
|
|
email: "person@example.test",
|
|
};
|
|
|
|
async function setup(t, options = {}) {
|
|
const directory = await mkdtemp(join(tmpdir(), "trihub-booking-"));
|
|
const file = join(directory, "bookings.csv");
|
|
const storage = createCsvStorage(file);
|
|
const sent = [];
|
|
const sendConfirmation =
|
|
options.sendConfirmation ??
|
|
(async (booking) => {
|
|
sent.push({ ...booking });
|
|
});
|
|
const dependencies = {
|
|
catalog: packages,
|
|
storage,
|
|
sendConfirmation,
|
|
...options,
|
|
};
|
|
const app = createApp(dependencies);
|
|
await new Promise((resolve) => app.listen(0, "127.0.0.1", resolve));
|
|
t.after(async () => {
|
|
app.closeAllConnections();
|
|
await new Promise((resolve) => app.close(resolve));
|
|
await rm(directory, { recursive: true, force: true });
|
|
});
|
|
const url = `http://127.0.0.1:${app.address().port}`;
|
|
async function post(body = input, key = randomUUID()) {
|
|
const response = await fetch(`${url}/api/bookings`, {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
"Idempotency-Key": key,
|
|
},
|
|
body: JSON.stringify(body),
|
|
});
|
|
return { status: response.status, body: await response.json() };
|
|
}
|
|
return { post, url, file, storage, sent, dependencies };
|
|
}
|
|
|
|
test("Buchung steht vor Versand in CSV; Paketname kommt vom Server", async (t) => {
|
|
const fixture = await setup(t);
|
|
const result = await fixture.post({
|
|
...input,
|
|
packageName: "Manipuliert",
|
|
price: 0,
|
|
});
|
|
assert.equal(result.status, 201);
|
|
assert.equal(result.body.emailStatus, "accepted");
|
|
assert.equal(result.body.packageName, packages[0].name);
|
|
assert.equal(result.body.bookingId, "TH-000001");
|
|
assert.ok(!Number.isNaN(Date.parse(result.body.createdAt)));
|
|
const records = await fixture.storage.readAll();
|
|
assert.equal(records.length, 1);
|
|
assert.equal(records[0].name, input.name);
|
|
assert.equal(records[0].emailStatus, "accepted");
|
|
assert.equal(fixture.sent.length, 1);
|
|
assert.equal(fixture.sent[0].emailStatus, "sending");
|
|
assert.ok(!("email" in result.body));
|
|
});
|
|
|
|
test("Pflichtfelder, Typen, Längen, E-Mail und unbekannte IDs werden abgelehnt", async (t) => {
|
|
const { post, storage, sent } = await setup(t);
|
|
for (const body of [
|
|
null,
|
|
[],
|
|
{},
|
|
{ ...input, name: " " },
|
|
{ ...input, name: 42 },
|
|
{ ...input, name: "a".repeat(121) },
|
|
{ ...input, name: "Test\nBcc: fremd" },
|
|
{ ...input, email: "keine-adresse" },
|
|
{ ...input, email: "a..b@example.test" },
|
|
{ ...input, email: ".person@example.test" },
|
|
{ ...input, email: "person.@example.test" },
|
|
{ ...input, email: "person@example..test" },
|
|
{ ...input, email: "person@-example.test" },
|
|
{ ...input, email: `person@${"a".repeat(64)}.test` },
|
|
{ ...input, email: "a\r\nb@example.test" },
|
|
{ ...input, email: false },
|
|
{ ...input, email: "a".repeat(255) },
|
|
{ ...input, packageId: "unknown" },
|
|
{ ...input, packageId: "../demo" },
|
|
{ ...input, packageId: 1 },
|
|
{ ...input, packageId: "a".repeat(81) },
|
|
]) {
|
|
const result = await post(body);
|
|
assert.equal(result.status, 400, JSON.stringify(body));
|
|
}
|
|
assert.equal((await post(input, "invalid")).status, 400);
|
|
assert.deepEqual(await storage.readAll(), []);
|
|
assert.equal(sent.length, 0);
|
|
});
|
|
|
|
test("Parallele Wiederholungen und Neustart erzeugen nur eine Buchung und Mail", async (t) => {
|
|
const { post, storage, sent, dependencies } = await setup(t);
|
|
const key = randomUUID();
|
|
const results = await Promise.all(
|
|
Array.from({ length: 8 }, () => post(input, key)),
|
|
);
|
|
assert.equal(new Set(results.map((r) => r.body.bookingId)).size, 1);
|
|
assert.equal(results.filter((r) => r.status === 201).length, 1);
|
|
assert.equal(sent.length, 1);
|
|
assert.equal((await storage.readAll()).length, 1);
|
|
// Neue Serviceinstanz liest ausschließlich den persistenten CSV-Zustand.
|
|
const restarted = createBookingService({ ...dependencies, packages: [] });
|
|
const replay = await restarted.book(input, key);
|
|
assert.equal(replay.status, 200);
|
|
assert.equal(replay.body.bookingId, results[0].body.bookingId);
|
|
assert.equal(sent.length, 1);
|
|
const conflict = await post({ ...input, name: "Andere Person" }, key);
|
|
assert.equal(conflict.status, 409);
|
|
});
|
|
|
|
test("Verschiedene gleichzeitige Buchungen behalten alle CSV-Zeilen", async (t) => {
|
|
const { post, storage } = await setup(t);
|
|
const results = await Promise.all(
|
|
Array.from({ length: 12 }, (_, i) =>
|
|
post({ ...input, name: `Person ${i}` }),
|
|
),
|
|
);
|
|
assert.ok(results.every((r) => r.status === 201));
|
|
const records = await storage.readAll();
|
|
assert.equal(records.length, 12);
|
|
assert.deepEqual(
|
|
records.map((record) => record.bookingId),
|
|
Array.from(
|
|
{ length: 12 },
|
|
(_, i) => `TH-${String(i + 1).padStart(6, "0")}`,
|
|
),
|
|
);
|
|
});
|
|
|
|
test("CSV schützt Formeln und erhält Kommas, Anführungszeichen, Apostrophe und Zeilenumbrüche", async (t) => {
|
|
const { storage, file } = await setup(t);
|
|
const values = [
|
|
'=HYPERLINK("evil")',
|
|
"+1",
|
|
"-1",
|
|
"@SUM(A1)",
|
|
"\t=1",
|
|
"\r=1",
|
|
"\n=1",
|
|
" =1",
|
|
"'Original",
|
|
'Test, "Name"\nzweite Zeile',
|
|
];
|
|
const rows = values.map((name) => ({ name, packageName: name }));
|
|
await storage.writeAll(rows);
|
|
assert.deepEqual(
|
|
(await storage.readAll()).map((r) => r.name),
|
|
values,
|
|
);
|
|
const text = await readFile(file, "utf8");
|
|
assert.ok(text.includes("'=HYPERLINK"));
|
|
assert.ok(text.includes("''Original"));
|
|
assert.ok(text.includes('"Test, ""Name""\nzweite Zeile"'));
|
|
await storage.writeAll(await storage.readAll());
|
|
assert.deepEqual(
|
|
(await storage.readAll()).map((r) => r.name),
|
|
values,
|
|
);
|
|
});
|
|
|
|
test("Fehlgeschlagenes erstes Schreiben verhindert Versand", async (t) => {
|
|
let sent = 0;
|
|
const { post } = await setup(t, {
|
|
storage: {
|
|
readAll: async () => [],
|
|
writeAll: async () => {
|
|
throw new Error("Disk full");
|
|
},
|
|
},
|
|
sendConfirmation: async () => {
|
|
sent++;
|
|
},
|
|
});
|
|
const result = await post();
|
|
assert.equal(result.status, 503);
|
|
assert.equal(result.body.error.code, "BOOKING_NOT_SAVED");
|
|
assert.equal(sent, 0);
|
|
});
|
|
|
|
test("Beschädigte CSV wird nicht überschrieben", async (t) => {
|
|
const { post, file, sent } = await setup(t);
|
|
await writeFile(file, "falscher,header\n1,2\n");
|
|
const result = await post();
|
|
assert.equal(result.status, 503);
|
|
assert.equal(result.body.error.code, "STORAGE_UNAVAILABLE");
|
|
assert.equal(await readFile(file, "utf8"), "falscher,header\n1,2\n");
|
|
assert.equal(sent.length, 0);
|
|
});
|
|
|
|
test("Mailfehler speichert Buchung; Wiederholung sendet nicht erneut", async (t) => {
|
|
let calls = 0;
|
|
const { post, storage } = await setup(t, {
|
|
sendConfirmation: async () => {
|
|
calls++;
|
|
throw new Error("SMTP rejected");
|
|
},
|
|
});
|
|
const key = randomUUID();
|
|
const first = await post(input, key);
|
|
assert.equal(first.status, 202);
|
|
assert.equal(first.body.saved, true);
|
|
assert.equal(first.body.emailStatus, "failed");
|
|
assert.equal((await storage.readAll())[0].emailStatus, "failed");
|
|
assert.equal((await post(input, key)).body.bookingId, first.body.bookingId);
|
|
assert.equal(calls, 1);
|
|
});
|
|
|
|
test("Ausstehender Versand kann nach Schreibfehler sicher fortgesetzt werden", async (t) => {
|
|
const { storage } = await setup(t);
|
|
let writes = 0;
|
|
let sends = 0;
|
|
const service = createBookingService({
|
|
packages: packages,
|
|
storage: {
|
|
readAll: () => storage.readAll(),
|
|
writeAll: async (rows) => {
|
|
if (++writes === 2) throw new Error("Disk full");
|
|
return storage.writeAll(rows);
|
|
},
|
|
},
|
|
sendConfirmation: async () => {
|
|
sends++;
|
|
},
|
|
});
|
|
const key = randomUUID();
|
|
const first = await service.book(input, key);
|
|
assert.equal(first.body.emailStatus, "pending");
|
|
assert.equal(sends, 0);
|
|
const retry = await service.book(input, key);
|
|
assert.equal(retry.body.emailStatus, "accepted");
|
|
assert.equal(retry.body.bookingId, first.body.bookingId);
|
|
assert.equal(sends, 1);
|
|
});
|
|
|
|
test("Status-Schreibfehler nach SMTP bleibt unklar und löst keinen Doppelversand aus", async (t) => {
|
|
const { storage } = await setup(t);
|
|
let writes = 0;
|
|
let sends = 0;
|
|
const service = createBookingService({
|
|
packages: packages,
|
|
storage: {
|
|
readAll: () => storage.readAll(),
|
|
writeAll: async (rows) => {
|
|
if (++writes === 3) throw new Error("Disk full");
|
|
return storage.writeAll(rows);
|
|
},
|
|
},
|
|
sendConfirmation: async () => {
|
|
sends++;
|
|
},
|
|
});
|
|
const key = randomUUID();
|
|
assert.equal((await service.book(input, key)).body.emailStatus, "unknown");
|
|
assert.equal((await storage.readAll())[0].emailStatus, "sending");
|
|
const restarted = createBookingService({
|
|
storage,
|
|
packages: packages,
|
|
sendConfirmation: async () => {
|
|
sends++;
|
|
},
|
|
});
|
|
assert.equal(
|
|
(await restarted.book(input, key)).body.emailStatus,
|
|
"unknown",
|
|
);
|
|
assert.equal(sends, 1);
|
|
});
|
|
|
|
test("SMTP-Nachricht enthält Buchungsnummer und Paket; externe Hosts sind standardmäßig gesperrt", async () => {
|
|
let message;
|
|
const send = createEmailService({}, () => ({
|
|
sendMail: async (mail) => {
|
|
message = mail;
|
|
return { accepted: [input.email] };
|
|
},
|
|
}));
|
|
await send({
|
|
...input,
|
|
bookingId: "TH-000001",
|
|
packageName: packages[0].name,
|
|
});
|
|
assert.ok(message.text.includes("TH-000001"));
|
|
assert.ok(message.text.includes(packages[0].name));
|
|
assert.equal(message.to.address, input.email);
|
|
assert.ok(message.text.includes("Zahlungsvorgang wird übersprungen"));
|
|
assert.ok(message.text.includes("keine Abbuchung"));
|
|
assert.throws(
|
|
() => createEmailService({ SMTP_HOST: "smtp.example.org" }),
|
|
/gesperrt/,
|
|
);
|
|
const rejected = createEmailService({}, () => ({
|
|
sendMail: async () => ({ accepted: [] }),
|
|
}));
|
|
await assert.rejects(() => rejected(input));
|
|
});
|
|
|
|
test("SMTP-Verbindungsabbruch unterscheidet sich von expliziter Ablehnung", async () => {
|
|
for (const [code, expected] of [
|
|
["ETIMEDOUT", true],
|
|
["EAUTH", false],
|
|
]) {
|
|
const send = createEmailService({}, () => ({
|
|
sendMail: async () => {
|
|
throw Object.assign(new Error("SMTP"), { code });
|
|
},
|
|
}));
|
|
await assert.rejects(
|
|
() => send(input),
|
|
(error) => error.deliveryUnknown === expected,
|
|
);
|
|
}
|
|
});
|
|
|
|
test("HTTP-Vertrag: JSON, Größenlimit, Methoden und private Dateien", async (t) => {
|
|
const { url } = await setup(t);
|
|
const response = await fetch(`${url}/api/bookings`);
|
|
assert.equal(response.status, 405);
|
|
for (const path of [
|
|
"/src/server/data/bookings.csv",
|
|
"/.env",
|
|
"/data/bookings.csv",
|
|
]) {
|
|
assert.equal((await fetch(`${url}${path}`)).status, 404);
|
|
}
|
|
assert.equal(
|
|
(await fetch(`${url}/api/bookings`, { method: "POST", body: "{}" }))
|
|
.status,
|
|
415,
|
|
);
|
|
assert.equal(
|
|
(
|
|
await fetch(`${url}/api/bookings`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: "{",
|
|
})
|
|
).status,
|
|
400,
|
|
);
|
|
assert.equal(
|
|
(
|
|
await fetch(`${url}/api/bookings`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ name: "a".repeat(9000) }),
|
|
})
|
|
).status,
|
|
413,
|
|
);
|
|
const catalog = await (await fetch(`${url}/api/angebote`)).json();
|
|
assert.deepEqual(
|
|
catalog.packages,
|
|
packages.map(({ id, name, summary }) => ({ id, name, summary })),
|
|
);
|
|
assert.equal((await fetch(`${url}/api/packages`)).status, 200);
|
|
});
|
|
|
|
test("Paketvertrag lehnt fehlende, numerische und doppelte IDs ab", () => {
|
|
for (const catalog of [
|
|
[null],
|
|
[{ ...packages[0], id: 123 }],
|
|
[{ ...packages[0], id: undefined }],
|
|
[packages[0], packages[0]],
|
|
]) {
|
|
assert.throws(
|
|
() => createApp({ catalog, sendConfirmation: async () => {} }),
|
|
/Paketdaten erfüllen/,
|
|
);
|
|
}
|
|
});
|
|
|
|
test("Fortlaufende Nummern bleiben nach Neustart erhalten und Wiederholungen verbrauchen keine Nummer", async (t) => {
|
|
const { storage, post } = await setup(t);
|
|
const key = randomUUID();
|
|
assert.equal((await post(input, key)).body.bookingId, "TH-000001");
|
|
assert.equal((await post(input, key)).body.bookingId, "TH-000001");
|
|
const restarted = createBookingService({
|
|
storage,
|
|
packages: packages,
|
|
sendConfirmation: async () => {},
|
|
});
|
|
assert.equal(
|
|
(await restarted.book(input, randomUUID())).body.bookingId,
|
|
"TH-000002",
|
|
);
|
|
assert.equal((await storage.readAll()).length, 2);
|
|
});
|
|
|
|
test("Bestehende UUID-Buchungen bleiben unverändert und weiterhin abrufbar", async (t) => {
|
|
const { storage, post } = await setup(t);
|
|
const key = randomUUID();
|
|
await post(input, key);
|
|
const records = await storage.readAll();
|
|
const legacyId = randomUUID();
|
|
records[0].bookingId = legacyId;
|
|
await storage.writeAll(records);
|
|
assert.equal((await post(input, key)).body.bookingId, legacyId);
|
|
assert.equal((await post()).body.bookingId, "TH-000001");
|
|
assert.equal((await storage.readAll())[0].bookingId, legacyId);
|
|
});
|
|
|
|
test("Nummernvergabe verwendet den höchsten CSV-Wert und wächst über sechs Stellen hinaus", async (t) => {
|
|
const { storage, post } = await setup(t);
|
|
await post();
|
|
await post();
|
|
const records = await storage.readAll();
|
|
records[0].bookingId = "TH-999999";
|
|
await storage.writeAll(records);
|
|
assert.equal((await post()).body.bookingId, "TH-1000000");
|
|
});
|
|
|
|
test("Detail-API und Vorschau liefern die gewählte Variante mit centgenauen Preisen", async (t) => {
|
|
const { url, storage, sent } = await setup(t, { catalog: packages });
|
|
const details = await (
|
|
await fetch(`${url}/api/packages/ernaehrung-premium`)
|
|
).json();
|
|
assert.equal(details.variants.length, 2);
|
|
assert.ok(details.includedFeatures.length > 0);
|
|
assert.equal((await fetch(`${url}/api/packages/unbekannt`)).status, 404);
|
|
const response = await fetch(`${url}/api/bookings/preview`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({
|
|
packageId: details.id,
|
|
variantId: "ernaehrung-premium-52",
|
|
price: 1,
|
|
}),
|
|
});
|
|
assert.equal(response.status, 200);
|
|
const preview = await response.json();
|
|
assert.equal(preview.durationWeeks, 52);
|
|
assert.equal(preview.grossPrice, 1399);
|
|
assert.equal(preview.netPrice, 1175.63);
|
|
assert.equal(preview.taxAmount, 223.37);
|
|
assert.equal(preview.taxRate, 19);
|
|
assert.equal(preview.currency, "EUR");
|
|
assert.deepEqual(preview.includedFeatures, details.includedFeatures);
|
|
assert.deepEqual(await storage.readAll(), []);
|
|
assert.equal(sent.length, 0);
|
|
});
|
|
|
|
test("Bestelldaten bleiben nach Katalogänderung und Neustart unverändert", async (t) => {
|
|
const fixture = await setup(t, { catalog: packages });
|
|
const key = randomUUID();
|
|
const data = {
|
|
...input,
|
|
packageId: "ernaehrung-premium",
|
|
variantId: "ernaehrung-premium-52",
|
|
price: 1,
|
|
includedFeatures: ["Manipuliert"],
|
|
};
|
|
const result = await fixture.post(data, key);
|
|
assert.equal(result.status, 201);
|
|
const order = result.body.bookedPackage;
|
|
assert.equal(order.grossPrice, 1399);
|
|
assert.equal(order.durationWeeks, 52);
|
|
assert.equal(order.variantId, data.variantId);
|
|
assert.ok(!order.includedFeatures.includes("Manipuliert"));
|
|
assert.deepEqual((await fixture.storage.readAll())[0].bookedPackage, order);
|
|
assert.deepEqual(fixture.sent[0].bookedPackage, order);
|
|
const restarted = createBookingService({
|
|
storage: fixture.storage,
|
|
packages: [],
|
|
details: [],
|
|
sendConfirmation: async () => assert.fail("Doppelversand"),
|
|
});
|
|
assert.deepEqual(
|
|
(await restarted.book(data, key)).body.bookedPackage,
|
|
order,
|
|
);
|
|
assert.equal(
|
|
(
|
|
await fixture.post(
|
|
{ ...data, variantId: "ernaehrung-premium-24" },
|
|
key,
|
|
)
|
|
).status,
|
|
409,
|
|
);
|
|
});
|
|
|
|
test("Unbekannte und paketfremde Varianten werden vor dem Speichern abgelehnt", async (t) => {
|
|
const { post, storage, sent, url } = await setup(t, { catalog: packages });
|
|
for (const data of [
|
|
{ packageId: "ernaehrung-premium", variantId: "unbekannt" },
|
|
{ packageId: "ernaehrung-starter", variantId: "ernaehrung-premium-52" },
|
|
{ packageId: "ernaehrung-premium", variantId: 52 },
|
|
]) {
|
|
assert.equal((await post({ ...input, ...data })).status, 400);
|
|
assert.equal(
|
|
(
|
|
await fetch(`${url}/api/bookings/preview`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(data),
|
|
})
|
|
).status,
|
|
400,
|
|
);
|
|
}
|
|
assert.deepEqual(await storage.readAll(), []);
|
|
assert.equal(sent.length, 0);
|
|
});
|
|
|
|
test("Bestehende CSV ohne Bestelldaten bleibt lesbar und wird verlustfrei erweitert", async (t) => {
|
|
const { post, file, storage } = await setup(t);
|
|
const key = randomUUID();
|
|
await post(input, key);
|
|
const [record] = await storage.readAll();
|
|
await writeFile(
|
|
file,
|
|
`bookingId,createdAt,packageId,packageName,name,email,emailStatus,idempotencyKey,requestHash\nTH-000001,${record.createdAt},ernaehrung-starter,Altes Paket,Alte Person,person@example.test,accepted,alter-key,alter-hash\n`,
|
|
);
|
|
assert.equal((await post()).status, 201);
|
|
const records = await storage.readAll();
|
|
assert.equal(records.length, 2);
|
|
assert.equal(records[0].packageName, "Altes Paket");
|
|
assert.equal(records[0].bookedPackage, null);
|
|
assert.equal(records[1].bookedPackage.packageId, "ernaehrung-starter");
|
|
});
|
|
|
|
test("Bestätigungsmail enthält gespeicherte Leistungen, Variante, Preise und maskiertes HTML", async (t) => {
|
|
const { post } = await setup(t, { catalog: packages });
|
|
const result = await post({
|
|
...input,
|
|
packageId: "ernaehrung-premium",
|
|
variantId: "ernaehrung-premium-52",
|
|
});
|
|
let message;
|
|
const send = createEmailService({}, () => ({
|
|
sendMail: async (mail) => {
|
|
message = mail;
|
|
return { accepted: [input.email] };
|
|
},
|
|
}));
|
|
await send({ ...input, name: "<img src=x>", ...result.body });
|
|
assert.match(message.subject, /Bestellbestätigung TH-/);
|
|
for (const text of [
|
|
"52-Wochen-Variante",
|
|
"52 Wochen",
|
|
"1.399,00",
|
|
"1.175,63",
|
|
"223,37",
|
|
"19 %",
|
|
"einmalig",
|
|
"Bestelldatum:",
|
|
...result.body.bookedPackage.includedFeatures,
|
|
]) {
|
|
assert.ok(message.text.includes(text), text);
|
|
}
|
|
assert.ok(message.html.includes("<img src=x>"));
|
|
assert.ok(!message.html.includes("<img src=x>"));
|
|
});
|
|
|
|
test("Swagger-Kundendaten und Zustimmung werden validiert und dauerhaft gespeichert", async (t) => {
|
|
const { post, storage, sent } = await setup(t, { catalog: packages });
|
|
const customer = {
|
|
firstName: "Thomas",
|
|
lastName: "Müller",
|
|
email: "thomas@example.test",
|
|
phone: "+49 170 1234567",
|
|
ageGroup: "50-59",
|
|
notes: "Vorbereitung auf die Mitteldistanz.",
|
|
};
|
|
const request = {
|
|
packageId: "ernaehrung-standard",
|
|
customer,
|
|
acceptedTerms: true,
|
|
};
|
|
for (const body of [
|
|
{ ...request, acceptedTerms: false },
|
|
{ ...request, acceptedTerms: "true" },
|
|
{ ...request, customer: [] },
|
|
{ ...request, customer: { ...customer, firstName: "" } },
|
|
{ ...request, customer: { ...customer, email: "ungültig" } },
|
|
{ ...request, customer: { ...customer, phone: 123 } },
|
|
])
|
|
assert.equal((await post(body)).status, 400);
|
|
const key = randomUUID();
|
|
const result = await post(request, key);
|
|
assert.equal(result.status, 201);
|
|
assert.equal(result.body.customerEmail, customer.email);
|
|
const [record] = await storage.readAll();
|
|
assert.equal(record.name, "Thomas Müller");
|
|
assert.deepEqual(record.customer, customer);
|
|
assert.equal(record.acceptedTerms, true);
|
|
assert.deepEqual(sent[0].customer, customer);
|
|
assert.equal((await post(request, key)).status, 200);
|
|
assert.equal(
|
|
(
|
|
await post(
|
|
{
|
|
...request,
|
|
customer: { ...customer, notes: "Anderer Auftrag" },
|
|
},
|
|
key,
|
|
)
|
|
).status,
|
|
409,
|
|
);
|
|
});
|