import { describe, expect, it } from "vitest"; import { entfernung, foerderplan, hoehenverlust, reibungsverlust, type Streckenpunkt, } from "@/lib/karte/wasserfoerderung"; /** * Wasserförderung über lange Wegstrecke. * * Physik mit klaren Formeln — und deshalb der Teil von Phase 6, der sich * vollständig ohne Karte, ohne Datenbank und ohne Browser prüfen lässt. * Geprüft wird nicht, ob eine Zahl herauskommt, sondern ob die Rechnung die * Tabellenwerte der Ausbildung trifft und ob sie zugibt, wenn eine Strecke * nicht zu fördern ist. */ /** Ein Punkt bei Musterhausen. */ function punkt(breitengrad: number, laengengrad: number, hoehe?: number): Streckenpunkt { return { breitengrad, laengengrad, hoehe: hoehe ?? null }; } describe("Entfernung", () => { it("rechnet eine kurze Strecke auf den Meter", () => { // Ein Zehntausendstel Grad Breite sind rund elf Meter. const meter = entfernung(punkt(48.5, 12.15), punkt(48.5001, 12.15)); expect(meter).toBeGreaterThanOrEqual(10); expect(meter).toBeLessThanOrEqual(12); }); it("rechnet eine Strecke über einen Kilometer", () => { // 0,01 Grad Breite sind rund 1,11 km. const meter = entfernung(punkt(48.5, 12.15), punkt(48.51, 12.15)); expect(meter).toBeGreaterThan(1100); expect(meter).toBeLessThan(1120); }); it("kennt keine Richtung — hin ist so weit wie zurück", () => { const hin = entfernung(punkt(48.5, 12.15), punkt(48.52, 12.19)); const zurueck = entfernung(punkt(48.52, 12.19), punkt(48.5, 12.15)); expect(hin).toBe(zurueck); }); it("ist null zwischen einem Punkt und sich selbst", () => { expect(entfernung(punkt(48.5, 12.15), punkt(48.5, 12.15))).toBe(0); }); }); describe("Druckverluste", () => { it("trifft den Tabellenwert für den B-Schlauch", () => { // 800 l/min im B-Schlauch: rund 1,5 bar je 100 Meter. const verlust = reibungsverlust(100, 800, 75); expect(verlust).toBeGreaterThanOrEqual(1.3); expect(verlust).toBeLessThanOrEqual(1.7); }); it("trifft den Tabellenwert für den A-Schlauch", () => { // Derselbe Durchfluss im A-Schlauch kostet einen Bruchteil. const verlust = reibungsverlust(100, 800, 110); expect(verlust).toBeGreaterThan(0); expect(verlust).toBeLessThan(0.4); }); it("steigt mit dem Quadrat der Fördermenge", () => { const einfach = reibungsverlust(100, 400, 75); const doppelt = reibungsverlust(100, 800, 75); // Doppelte Menge, vierfacher Verlust — das ist der Grund, warum man // lieber einen Schlauch mehr legt als schneller fördert. expect(doppelt / einfach).toBeGreaterThan(3.5); expect(doppelt / einfach).toBeLessThan(4.5); }); it("wächst gleichmäßig mit der Länge", () => { const hundert = reibungsverlust(100, 800, 75); const dreihundert = reibungsverlust(300, 800, 75); expect(dreihundert).toBeCloseTo(hundert * 3, 1); }); it("rechnet ohne Wasser keinen Verlust", () => { expect(reibungsverlust(500, 0, 75)).toBe(0); expect(reibungsverlust(0, 800, 75)).toBe(0); }); it("rechnet zehn Höhenmeter als ein bar", () => { expect(hoehenverlust(10)).toBe(1); expect(hoehenverlust(35)).toBe(3.5); }); it("gibt bergab den Druck zurück", () => { expect(hoehenverlust(-20)).toBe(-2); }); }); describe("Förderplan", () => { it("kommt bei kurzer Strecke mit einer Pumpe aus", () => { const plan = foerderplan({ punkte: [ { ...punkt(48.5, 12.15, 400), bezeichnung: "Weiher" }, { ...punkt(48.502, 12.15, 402), bezeichnung: "Einsatzstelle" }, ], durchmesser: 75, foerdermenge: 800, ausgangsdruck: 8, eingangsdruck: 1.5, }); expect(plan.machbar).toBe(true); expect(plan.pumpen).toBe(1); expect(plan.abschnitte).toHaveLength(1); expect(plan.enddruck).toBeLessThan(8); expect(plan.hinweise.join(" ")).toContain("Eine Pumpe genügt"); }); it("setzt eine Verstärkerpumpe, sobald der Druck nicht mehr reicht", () => { // Ein Kilometer eben — das schafft eine Pumpe im B-Schlauch bei 800 l/min // nicht: sie kommt rund 430 Meter weit. const plan = foerderplan({ punkte: [ { ...punkt(48.5, 12.15, 400), bezeichnung: "Entnahme" }, { ...punkt(48.503, 12.15, 400), bezeichnung: "Feldweg" }, { ...punkt(48.506, 12.15, 400), bezeichnung: "Kreuzung" }, { ...punkt(48.509, 12.15, 400), bezeichnung: "Einsatzstelle" }, ], durchmesser: 75, foerdermenge: 800, ausgangsdruck: 8, eingangsdruck: 1.5, }); expect(plan.machbar).toBe(true); expect(plan.pumpen).toBeGreaterThan(1); expect(plan.abschnitte.some((abschnitt) => abschnitt.pumpeNoetig)).toBe(true); expect(plan.hinweise.join(" ")).toMatch(/Verstärker/); }); it("braucht im A-Schlauch weniger Pumpen als im B-Schlauch", () => { const strecke = [ { ...punkt(48.5, 12.15, 400), bezeichnung: "Entnahme" }, { ...punkt(48.503, 12.15, 400), bezeichnung: "Mitte" }, { ...punkt(48.506, 12.15, 400), bezeichnung: "Einsatzstelle" }, ]; const b = foerderplan({ punkte: strecke, durchmesser: 75, foerdermenge: 800, ausgangsdruck: 8, eingangsdruck: 1.5, }); const a = foerderplan({ punkte: strecke, durchmesser: 110, foerdermenge: 800, ausgangsdruck: 8, eingangsdruck: 1.5, }); expect(a.pumpen).toBeLessThan(b.pumpen); }); it("rechnet die Steigung mit", () => { const eben = foerderplan({ punkte: [punkt(48.5, 12.15, 400), punkt(48.503, 12.15, 400)], durchmesser: 75, foerdermenge: 800, ausgangsdruck: 8, eingangsdruck: 1.5, }); const bergauf = foerderplan({ punkte: [punkt(48.5, 12.15, 400), punkt(48.503, 12.15, 460)], durchmesser: 75, foerdermenge: 800, ausgangsdruck: 8, eingangsdruck: 1.5, }); // 60 Höhenmeter kosten 6 bar — das ist mehr als die Reibung auf der // Strecke und dreht das Ergebnis. expect(bergauf.enddruck).toBeLessThan(eben.enddruck - 5); expect(bergauf.hoehenunterschied).toBe(60); }); it("gewinnt bergab Druck zurück", () => { const plan = foerderplan({ punkte: [punkt(48.5, 12.15, 460), punkt(48.503, 12.15, 400)], durchmesser: 75, foerdermenge: 800, ausgangsdruck: 8, eingangsdruck: 1.5, }); expect(plan.hoehenunterschied).toBe(-60); expect(plan.hinweise.join(" ")).toContain("fällt deutlich ab"); }); it("sagt es, wenn ein Abschnitt selbst mit voller Pumpe nicht zu schaffen ist", () => { // Über einen Kilometer am Stück im C-Schlauch: das geht nicht, und die // Ausgabe soll das sagen statt eine Zahl zu erfinden. const plan = foerderplan({ punkte: [ { ...punkt(48.5, 12.15, 400), bezeichnung: "Entnahme" }, { ...punkt(48.51, 12.15, 400), bezeichnung: "Einsatzstelle" }, ], durchmesser: 52, foerdermenge: 400, ausgangsdruck: 8, eingangsdruck: 1.5, }); expect(plan.machbar).toBe(false); expect(plan.hinweise.join(" ")).toContain("zu lang für eine Pumpe"); }); it("zählt Schlauchlängen auf, statt Meter zu nennen", () => { const plan = foerderplan({ punkte: [punkt(48.5, 12.15, 400), punkt(48.502, 12.15, 400)], durchmesser: 75, foerdermenge: 800, ausgangsdruck: 8, eingangsdruck: 1.5, }); // Rund 222 Meter — das sind zwölf Längen, nicht elf komma eins. expect(plan.laengen).toBe(Math.ceil(plan.gesamtlaenge / 20)); expect(Number.isInteger(plan.laengen)).toBe(true); }); it("verlangt mindestens zwei Punkte", () => { const plan = foerderplan({ punkte: [punkt(48.5, 12.15)], durchmesser: 75, foerdermenge: 800, ausgangsdruck: 8, eingangsdruck: 1.5, }); expect(plan.machbar).toBe(false); expect(plan.hinweise.join(" ")).toContain("Mindestens zwei Punkte"); }); it("rechnet ohne Höhenangaben mit ebenem Gelände", () => { const plan = foerderplan({ punkte: [punkt(48.5, 12.15), punkt(48.502, 12.15)], durchmesser: 75, foerdermenge: 800, ausgangsdruck: 8, eingangsdruck: 1.5, }); expect(plan.hoehenunterschied).toBe(0); expect(plan.machbar).toBe(true); }); it("sagt in jedem Fall dazu, dass es eine Planungshilfe ist", () => { const plan = foerderplan({ punkte: [punkt(48.5, 12.15), punkt(48.502, 12.15)], durchmesser: 75, foerdermenge: 800, ausgangsdruck: 8, eingangsdruck: 1.5, }); expect(plan.hinweise.at(-1)).toContain("Planungshilfe"); }); });