lsa-planer
LSA-Planer Professional – Planungssoftware für Lichtsignalanlagen nach RiLSA 2015 und § 45 StVO. EUPL-1.2.
/ tests services karteAbruf.test.ts
| 1 | import { afterEach, describe, expect, it, vi } from 'vitest'; |
| 2 | import { holeKartenbild } from '../../electron/karte'; |
| 3 | |
| 4 | /** |
| 5 | * Abruf beim Kartendienst. |
| 6 | * |
| 7 | * Geprueft wird das Verhalten in genau den Faellen, die im Betrieb wirklich |
| 8 | * auftreten und die sich von Hand kaum herstellen lassen: eine Fehlermeldung |
| 9 | * des Dienstes mit Statuscode 200, eine unter Last abgebrochene Verbindung und |
| 10 | * eine Adresse, die nicht freigegeben ist. |
| 11 | * |
| 12 | * `fetch` wird dazu ersetzt. Ein Test, der wirklich ins Netz greift, waere von |
| 13 | * der Auslastung fremder Server abhaengig und damit wertlos. |
| 14 | */ |
| 15 | |
| 16 | const echtesFetch = globalThis.fetch; |
| 17 | |
| 18 | afterEach(() => { |
| 19 | globalThis.fetch = echtesFetch; |
| 20 | vi.restoreAllMocks(); |
| 21 | }); |
| 22 | |
| 23 | function bildAntwort(): Response { |
| 24 | return new Response(new Uint8Array([0xff, 0xd8, 0xff, 0xdb]), { |
| 25 | status: 200, |
| 26 | headers: { 'content-type': 'image/jpeg' }, |
| 27 | }); |
| 28 | } |
| 29 | |
| 30 | const NRW = 'https://www.wms.nrw.de/geobasis/wms_nw_dop?SERVICE=WMS&REQUEST=GetMap'; |
| 31 | |
| 32 | describe('holeKartenbild', () => { |
| 33 | it('greift bei einer nicht freigegebenen Adresse gar nicht erst zu', async () => { |
| 34 | const gerufen = vi.fn(); |
| 35 | globalThis.fetch = gerufen; |
| 36 | |
| 37 | const antwort = await holeKartenbild({ url: 'https://beispiel.example/wms?REQUEST=GetMap' }); |
| 38 | |
| 39 | expect(antwort.ok).toBe(false); |
| 40 | expect(antwort.fehler).toContain('nicht freigegeben'); |
| 41 | expect(gerufen).not.toHaveBeenCalled(); |
| 42 | }); |
| 43 | |
| 44 | it('gibt die Bilddaten als Daten-URL zurueck', async () => { |
| 45 | globalThis.fetch = vi.fn(async () => bildAntwort()); |
| 46 | |
| 47 | const antwort = await holeKartenbild({ url: NRW }); |
| 48 | |
| 49 | expect(antwort.ok).toBe(true); |
| 50 | expect(antwort.datenUrl?.startsWith('data:image/jpeg;base64,')).toBe(true); |
| 51 | expect(antwort.fehler).toBeNull(); |
| 52 | }); |
| 53 | |
| 54 | it('erkennt eine Dienstmeldung, die als XML mit Statuscode 200 kommt', async () => { |
| 55 | // Genau so meldet ein WMS einen unbekannten Ebenennamen. Ohne die Pruefung |
| 56 | // des Inhaltstyps landete die Meldung als "Bild" im Projekt. |
| 57 | globalThis.fetch = vi.fn( |
| 58 | async () => |
| 59 | new Response('<ServiceExceptionReport>LayerNotDefined</ServiceExceptionReport>', { |
| 60 | status: 200, |
| 61 | headers: { 'content-type': 'text/xml' }, |
| 62 | }), |
| 63 | ); |
| 64 | |
| 65 | const antwort = await holeKartenbild({ url: NRW }); |
| 66 | |
| 67 | expect(antwort.ok).toBe(false); |
| 68 | expect(antwort.fehler).toContain('kein Bild'); |
| 69 | expect(antwort.fehler).toContain('LayerNotDefined'); |
| 70 | }); |
| 71 | |
| 72 | it('wiederholt einmal, wenn die Verbindung abbricht', async () => { |
| 73 | let aufrufe = 0; |
| 74 | globalThis.fetch = vi.fn(async () => { |
| 75 | aufrufe += 1; |
| 76 | if (aufrufe === 1) throw new TypeError('terminated'); |
| 77 | return bildAntwort(); |
| 78 | }); |
| 79 | |
| 80 | const antwort = await holeKartenbild({ url: NRW }); |
| 81 | |
| 82 | expect(aufrufe).toBe(2); |
| 83 | expect(antwort.ok).toBe(true); |
| 84 | }); |
| 85 | |
| 86 | it('wiederholt hoechstens einmal und meldet dann verstaendlich', async () => { |
| 87 | let aufrufe = 0; |
| 88 | globalThis.fetch = vi.fn(async () => { |
| 89 | aufrufe += 1; |
| 90 | throw new TypeError('terminated'); |
| 91 | }); |
| 92 | |
| 93 | const antwort = await holeKartenbild({ url: NRW }); |
| 94 | |
| 95 | expect(aufrufe).toBe(2); |
| 96 | expect(antwort.ok).toBe(false); |
| 97 | expect(antwort.fehler).toContain('abgebrochen'); |
| 98 | // Die rohe Meldung der Netzschicht darf nicht durchschlagen. |
| 99 | expect(antwort.fehler).not.toContain('terminated'); |
| 100 | }); |
| 101 | |
| 102 | it('wiederholt nach Ablauf der Wartezeit nicht', async () => { |
| 103 | let aufrufe = 0; |
| 104 | globalThis.fetch = vi.fn(async () => { |
| 105 | aufrufe += 1; |
| 106 | const fehler = new Error('The operation was aborted.'); |
| 107 | fehler.name = 'AbortError'; |
| 108 | throw fehler; |
| 109 | }); |
| 110 | |
| 111 | const antwort = await holeKartenbild({ url: NRW }); |
| 112 | |
| 113 | expect(aufrufe).toBe(1); |
| 114 | expect(antwort.ok).toBe(false); |
| 115 | expect(antwort.fehler).toContain('nicht rechtzeitig'); |
| 116 | }); |
| 117 | |
| 118 | it('meldet eine fehlende Namensaufloesung als Verbindungsproblem', async () => { |
| 119 | globalThis.fetch = vi.fn(async () => { |
| 120 | const fehler = new TypeError('fetch failed'); |
| 121 | (fehler as { cause?: unknown }).cause = { code: 'ENOTFOUND' }; |
| 122 | throw fehler; |
| 123 | }); |
| 124 | |
| 125 | const antwort = await holeKartenbild({ url: NRW }); |
| 126 | |
| 127 | expect(antwort.ok).toBe(false); |
| 128 | expect(antwort.fehler).toContain('nicht erreichbar'); |
| 129 | }); |
| 130 | |
| 131 | it('reicht einen Statuscode des Dienstes weiter', async () => { |
| 132 | globalThis.fetch = vi.fn( |
| 133 | async () => new Response('gesperrt', { status: 403, statusText: 'Forbidden' }), |
| 134 | ); |
| 135 | |
| 136 | const antwort = await holeKartenbild({ url: NRW }); |
| 137 | |
| 138 | expect(antwort.ok).toBe(false); |
| 139 | expect(antwort.fehler).toContain('403'); |
| 140 | }); |
| 141 | }); |