/** * Rauchtest gegen das **Store-Paket** (AppX/MSIX). * * ## Warum es diesen Test neben `gepackt.spec.ts` gibt * * `gepackt.spec.ts` misst den NSIS-Bau unter `release/win-unpacked/`. Das * Store-Paket ist ein anderes Erzeugnis mit einer anderen Umgebung: eigene * Paketidentität, eigener AppContainer, eigene Registrierungssicht. Ein Test, * der den einen Bau misst, sagt über den anderen nichts. * * ## Was hier geprüft wird – und warum gerade das * * Der Punkt, an dem sich die beiden Wege am ehesten unterscheiden, ist der * **Ablageort der Nutzerdaten**. Windows kann den Anwendungsdatenordner eines * Paketes umlenken, üblicherweise nach `%LOCALAPPDATA%\Packages\…`. Genau * davon ging dieses Projekt lange aus: `docs/datenschutz.md` 4.6 und 7.4 * sagten es dem Leser zu, und `docs/stand.md` führte es als „am gepackten * Store-Paket zu messen“. * * **Gemessen am 28.08.2026: Es wird nicht umgelenkt.** Das Paket lief unter * seiner echten Store-Identität, und `lernstand.db-wal` und `-shm` – die * Begleitdateien, die SQLite nur führt, solange die Datenbank offen ist – * lagen unter `%APPDATA%\Waffensachkunde Lernsoftware\`. Im paketeigenen * Bereich lag ausschließlich, was Windows selbst dort anlegt. * * Dieser Test hält dieses Ergebnis fest. Er behauptet nichts über Windows, * sondern misst, was diese Anwendung in dieser Verpackung tut – und schlägt * an, wenn eine künftige Windows- oder electron-builder-Fassung es ändert. * Dann sind 4.6 und 7.4 der Datenschutzerklärung nachzuführen, nicht der * Test. * * ## Eine Falle für den, der das nachprüfen will * * Eine Suche nach `lernstand.db` über das Benutzerprofil findet einen Treffer * unter `%LOCALAPPDATA%\Packages\Claude_…\LocalCache\Roaming\` und legt den * Schluss nahe, es werde eben doch umgelenkt. Wird es nicht: Am 28.08.2026 * mit `fsutil file queryfileid` gegengeprüft – dieser Pfad und * `%APPDATA%\Waffensachkunde Lernsoftware\` tragen **dieselbe NTFS-Datei-ID** * und denselben SHA-256. Es ist eine Datei, gesehen durch die Umlenkung eines * **fremden** Paketbehälters, nicht durch die dieser Anwendung. Wer das * nachmisst, prüfe die Datei-ID, nicht den Pfad. * * ## Warum er sich überspringt statt fehlzuschlagen * * Er braucht ein registriertes Paket, und das entsteht nicht beim Bauen, * sondern durch einen bewussten Schritt am Rechner (`npm run dist:store`, * auspacken, `Add-AppxPackage -Register`). Ein harter Abbruch machte jeden * gewöhnlichen Lauf rot, ohne dass etwas falsch wäre. Übersprungen **mit * Grund** ist die ehrliche Form: Der Gate-Bericht sagt dann, was er nicht * geprüft hat. */ import { execFileSync } from 'node:child_process'; import { existsSync } from 'node:fs'; import { join } from 'node:path'; import { expect, test } from './konsolenwache'; /** Die Paketfamilie aus dem Partner Center – Produktverwaltung, Product Identity. */ const PAKETFAMILIE = 'O-W.Waffensachkunde-Lernsoftware-OW_c9rr4ryw66n3y'; /* Zugriff über den Index und nicht über die Punktschreibweise: `process.env` ist eine Indexsignatur, und `noPropertyAccessFromIndexSignature` besteht zu Recht darauf — bei `process.env.APPDATA` fiele ein Tippfehler erst zur Laufzeit auf, und zwar als leerer Pfad statt als Fehler. */ /** Der Ordner, den die Anwendung ohne Umlenkung benutzt. */ const KLASSISCH = join(process.env['APPDATA'] ?? '', 'Waffensachkunde Lernsoftware'); /** Der paketeigene Bereich, in den Windows umlenken **könnte**. */ const PAKETABLAGE = join(process.env['LOCALAPPDATA'] ?? '', 'Packages', PAKETFAMILIE); /** Ist das Paket auf diesem Rechner registriert? `null`, wenn nicht messbar. */ function paketRegistriert(): boolean | null { if (process.platform !== 'win32') { return null; } try { const ausgabe = execFileSync( 'powershell', [ '-NoProfile', '-Command', `(Get-AppxPackage -Name 'O-W.Waffensachkunde-Lernsoftware-OW').PackageFullName`, ], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }, ).trim(); return ausgabe.length > 0; } catch { /* Kein PowerShell, keine Berechtigung, kein Windows – nicht messbar. */ return null; } } test.describe('Store-Paket – Ablageort der Nutzerdaten', () => { test.beforeAll(() => { const registriert = paketRegistriert(); test.skip( registriert !== true, 'Kein registriertes Store-Paket auf diesem Rechner. Erzeugen mit ' + '`npm run dist:store`, auspacken (die Namen im AppX sind ' + 'prozentkodiert!) und `Add-AppxPackage -Register …\\AppxManifest.xml`.', ); }); test('legt den Lernstand am klassischen Ort ab, nicht in der Paketablage', () => { /* Die eine Aussage, auf die sich die Datenschutzerklärung stützt. Geprüft wird die Datenbank selbst und nicht ein Ordner: Ein Ordner entsteht auch leer, eine `lernstand.db` nur, wenn die Anwendung dort wirklich geschrieben hat. */ expect( existsSync(join(KLASSISCH, 'lernstand.db')), `Unter ${KLASSISCH} liegt keine lernstand.db. Entweder hat die ` + 'Anwendung dort noch nie geschrieben, oder der Ablageort hat sich ' + 'geändert — dann sind docs/datenschutz.md 4.6 und 7.4 nachzuführen.', ).toBe(true); }); test('legt keine zweite Datenbank in der Paketablage an', () => { /* Die Gegenrichtung, und der eigentliche Wächter: Sobald Windows oder electron-builder die Umlenkung doch einschalten, entsteht hier eine zweite lernstand.db — und dann stimmt die Zusage in der Datenschutzerklärung nicht mehr, dass beide Fassungen sich denselben Lernstand teilen. */ const umgelenkt = [ join(PAKETABLAGE, 'LocalCache', 'Roaming', 'Waffensachkunde Lernsoftware', 'lernstand.db'), join(PAKETABLAGE, 'RoamingState', 'Waffensachkunde Lernsoftware', 'lernstand.db'), join(PAKETABLAGE, 'LocalState', 'Waffensachkunde Lernsoftware', 'lernstand.db'), ]; const gefunden = umgelenkt.filter((pfad) => existsSync(pfad)); expect( gefunden, 'In der Paketablage liegt ein Lernstand. Windows lenkt den ' + 'Anwendungsdatenordner jetzt also doch um. Das ist kein Fehler dieser ' + 'Anwendung, aber docs/datenschutz.md 4.6 und 7.4 behaupten dann das ' + 'Gegenteil und sind vor der nächsten Veröffentlichung nachzuführen.', ).toEqual([]); }); });