Gedankenstriche durch Bindestriche ersetzt
Prüfung / Lint, Typen, Tests (push) Canceled after 0s
Prüfung / Images bauen (pdf) (push) Canceled after 0s
Prüfung / Images bauen (web) (push) Canceled after 0s
Prüfung / Images bauen (worker) (push) Canceled after 0s
Images / bauen (pdf, docker/pdf.Dockerfile) (push) Canceled after 0s
Images / bauen (web, docker/web.Dockerfile) (push) Canceled after 0s
Images / bauen (worker, docker/worker.Dockerfile) (push) Canceled after 0s
Prüfung / Lint, Typen, Tests (push) Canceled after 0s
Prüfung / Images bauen (pdf) (push) Canceled after 0s
Prüfung / Images bauen (web) (push) Canceled after 0s
Prüfung / Images bauen (worker) (push) Canceled after 0s
Images / bauen (pdf, docker/pdf.Dockerfile) (push) Canceled after 0s
Images / bauen (web, docker/web.Dockerfile) (push) Canceled after 0s
Images / bauen (worker, docker/worker.Dockerfile) (push) Canceled after 0s
375 Vorkommen in 210 Dateien. Betrifft Kommentare, Oberflaechentexte und die README; an keiner Stelle Bedeutung, nur Zeichensetzung.
This commit is contained in:
@@ -173,7 +173,7 @@ suite('Abteilungen', () => {
|
||||
expect(liste.map((a) => a.id)).toContain(offenId)
|
||||
expect(liste.map((a) => a.id)).not.toContain(geheimId)
|
||||
|
||||
// Und die Zahl im Diagramm stimmt mit der Liste überein — ein Zähler, der
|
||||
// Und die Zahl im Diagramm stimmt mit der Liste überein - ein Zähler, der
|
||||
// mehr zählt als die Liste zeigt, wäre selbst eine Auskunft.
|
||||
const verteilung = await caseTypesOfDepartment(niedrig, abteilungId)
|
||||
const summe = verteilung.reduce((s, stueck) => s + stueck.value, 0)
|
||||
|
||||
@@ -92,7 +92,7 @@ suite('Vorlage des Aktenblatts', () => {
|
||||
const imRaster = rows.flat().map((f) => f.key)
|
||||
|
||||
// Sie stehen unter der jeweiligen Unterschrift, nicht in einer eigenen
|
||||
// Zelle — bearbeitbar bleiben sie trotzdem.
|
||||
// Zelle - bearbeitbar bleiben sie trotzdem.
|
||||
expect(imRaster).not.toContain('personalarbeiter_datum')
|
||||
expect(imRaster).not.toContain('standortleiter_datum')
|
||||
expect(fields.map((f) => f.key)).toContain('personalarbeiter_datum')
|
||||
|
||||
@@ -9,7 +9,7 @@ import type { EffectivePermissions, Permission } from '@/lib/auth/permissions'
|
||||
* Ein Anhang hat keine eigene Freigabestufe, er erbt die seines Besitzers.
|
||||
* Diese Tests prüfen beide Seiten davon: dass die Stufe wirklich durchschlägt,
|
||||
* und dass eine Datei, die die Prüfkette nicht bestanden hat, gar nicht erst
|
||||
* ausgeliefert wird — auch nicht an einen Administrator.
|
||||
* ausgeliefert wird - auch nicht an einen Administrator.
|
||||
*/
|
||||
const url = process.env.DIRECT_DATABASE_URL ?? process.env.DATABASE_URL
|
||||
const suite = url ? describe : describe.skip
|
||||
@@ -130,7 +130,7 @@ suite('Sichtbarkeit von Anhängen', () => {
|
||||
})
|
||||
|
||||
it('verbirgt den Anhang eines Eintrags über der Stufe des Lesers', async () => {
|
||||
// Die Akte darf er sehen, den Eintrag nicht — und damit auch dessen Datei
|
||||
// Die Akte darf er sehen, den Eintrag nicht - und damit auch dessen Datei
|
||||
// nicht. Sonst verriete die Anhangsliste den Inhalt des Eintrags.
|
||||
expect(await sichtbar(perms(nutzer.stufe1, 1))).not.toContain(anhang.imEintrag)
|
||||
expect(await sichtbar(perms(nutzer.stufe4, 4))).toContain(anhang.imEintrag)
|
||||
|
||||
@@ -109,7 +109,7 @@ suite('Benachrichtigungen', () => {
|
||||
|
||||
// Stufe 4 bekommt die Meldung.
|
||||
expect(ziele).toContain(nutzer.stufe4)
|
||||
// Stufe 1 nicht — die Akte gibt es für ihn nicht.
|
||||
// Stufe 1 nicht - die Akte gibt es für ihn nicht.
|
||||
expect(ziele).not.toContain(nutzer.stufe1)
|
||||
// Wer abgeschaltet hat, bekommt nichts.
|
||||
expect(ziele).not.toContain(nutzer.aus)
|
||||
|
||||
@@ -9,7 +9,7 @@ import { caseText } from '@/lib/i18n/cases'
|
||||
/**
|
||||
* Die Zerlegung der Beteiligtenzeilen aus dem Formular „Neue Fallakte".
|
||||
*
|
||||
* Sie entscheidet, wer mit welcher Rolle in eine Akte kommt — und seit die
|
||||
* Sie entscheidet, wer mit welcher Rolle in eine Akte kommt - und seit die
|
||||
* Rolle über den Zugriff entscheidet (nur Arbeitsrollen öffnen die Akte, siehe
|
||||
* lib/cases/terms.ts), darf eine erfundene Rolle hier nicht durchrutschen.
|
||||
*/
|
||||
@@ -55,7 +55,7 @@ describe('Beteiligte beim Anlegen', () => {
|
||||
|
||||
describe('Zustände einer Fallakte', () => {
|
||||
it('hat für jeden Zustand einen Namen und eine Erklärung', () => {
|
||||
// Der Filter zeigte fünf von sieben Zuständen — ausgerechnet „Entwurf" und
|
||||
// Der Filter zeigte fünf von sieben Zuständen - ausgerechnet „Entwurf" und
|
||||
// „Versiegelt" fehlten, also die beiden häufigsten in der Liste. Diese
|
||||
// Prüfung fällt auf, wenn wieder einer vergessen wird.
|
||||
for (const status of CASE_STATUSES) {
|
||||
|
||||
@@ -10,7 +10,7 @@ import type { EffectivePermissions, Permission } from '@/lib/auth/permissions'
|
||||
/**
|
||||
* Der Chat erbt die Sichtbarkeit seines Ortes. Der heikelste Teil ist die
|
||||
* Vorschaukarte: ein Aktenzeichen im Text darf einem zu niedrig eingestuften
|
||||
* Leser nichts über die Akte verraten — auch nicht, ob es sie gibt.
|
||||
* Leser nichts über die Akte verraten - auch nicht, ob es sie gibt.
|
||||
*/
|
||||
const url = process.env.DIRECT_DATABASE_URL ?? process.env.DATABASE_URL
|
||||
const suite = url ? describe : describe.skip
|
||||
@@ -149,7 +149,7 @@ suite('Aktenchat', () => {
|
||||
'AfiS-1999-0001',
|
||||
])
|
||||
|
||||
// Sichtbar: mit Titel. Verborgen: ohne alles — und genauso wie eine Akte,
|
||||
// Sichtbar: mit Titel. Verborgen: ohne alles - und genauso wie eine Akte,
|
||||
// die es gar nicht gibt.
|
||||
expect(fuerStufe1.get(offenNummer)).toMatchObject({ visible: true, title: 'Offener Vorgang' })
|
||||
expect(fuerStufe1.get(geheimNummer)).toEqual({ caseNumber: geheimNummer, visible: false })
|
||||
@@ -181,7 +181,7 @@ suite('Aktenchat', () => {
|
||||
await sendMessage(perms(nutzer.stufe4, 4), {
|
||||
id,
|
||||
scope: { kind: 'case', caseNumber: offenNummer },
|
||||
// Der zweite Name gibt es nicht — er darf nicht in mentions landen.
|
||||
// Der zweite Name gibt es nicht - er darf nicht in mentions landen.
|
||||
body: `Bitte schauen @${nutzer.stufe1} und @niemand`,
|
||||
})
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { deliveryFileName } from '@/lib/files/delivery'
|
||||
* Die Typprüfung ist die Stelle, an der eine feindselige Datei auffallen muss.
|
||||
*
|
||||
* Geprüft wird deshalb nicht nur, dass die erlaubten Formate durchkommen,
|
||||
* sondern vor allem, dass die verbotenen es **nicht** tun — und zwar auch
|
||||
* sondern vor allem, dass die verbotenen es **nicht** tun - und zwar auch
|
||||
* dann, wenn sie sich als erlaubte ausgeben.
|
||||
*/
|
||||
|
||||
@@ -46,7 +46,7 @@ describe('Erkennung erlaubter Typen', () => {
|
||||
describe('Abwehr', () => {
|
||||
it('lehnt SVG ab, auch wenn es wie Text aussieht', () => {
|
||||
// Der eigentliche Grund für die ganze Kette: SVG führt im Browser Skripte
|
||||
// aus. Es ist gültiges UTF-8 und käme sonst als text/plain durch — das ist
|
||||
// aus. Es ist gültiges UTF-8 und käme sonst als text/plain durch - das ist
|
||||
// in Ordnung, denn als Text wird es nie gerendert, sondern ausgeliefert
|
||||
// mit Content-Disposition: attachment.
|
||||
const svg = text('<svg xmlns="http://www.w3.org/2000/svg"><script>alert(1)</script></svg>')
|
||||
@@ -129,7 +129,7 @@ describe('Ablagepfade', () => {
|
||||
|
||||
it('lässt keinen Pfad aus dem Dateiverzeichnis heraus', () => {
|
||||
process.env.FILES_DIR ??= '/tmp/afis-test-files'
|
||||
// Kommt aus der Datenbank, nicht aus einer Eingabe — aber ein
|
||||
// Kommt aus der Datenbank, nicht aus einer Eingabe - aber ein
|
||||
// Verzeichniswechsel wäre der teuerste Fehler an dieser Stelle.
|
||||
expect(() => absolutePath('../../etc/passwd')).toThrow()
|
||||
expect(() => absolutePath('store/../../../etc/passwd')).toThrow()
|
||||
|
||||
@@ -50,7 +50,7 @@ describe('Design-Tokens', () => {
|
||||
// Die Regel, aus der die Zahlen folgen: eine Beschriftung, die naeher am
|
||||
// Rahmen steht als der Inhalt des Rahmens an demselben Rahmen, wirkt
|
||||
// angeklebt. Also muss der Abstand nach aussen groesser sein als die
|
||||
// Polsterung nach innen. Geprueft wird die Regel, nicht der Wert — wer die
|
||||
// Polsterung nach innen. Geprueft wird die Regel, nicht der Wert - wer die
|
||||
// Polsterung aendert, faellt hier auf.
|
||||
const feld = css.slice(css.indexOf('.field {'), css.indexOf('.field:hover'))
|
||||
const polsterung = /padding:\s*([\d.]+)rem\s+([\d.]+)rem/.exec(feld)
|
||||
|
||||
@@ -147,7 +147,7 @@ suite('Sichtbarkeit von Dokumenten', () => {
|
||||
|
||||
// Das Dokument ist zu hoch eingestuft, obwohl die Akte offen liegt.
|
||||
expect(treffer).not.toContain(dok.hochImOffenenFall)
|
||||
// Das Dokument wäre lesbar, aber die Akte darüber nicht — die
|
||||
// Das Dokument wäre lesbar, aber die Akte darüber nicht - die
|
||||
// Dokumentenliste verriete sonst ihre Existenz.
|
||||
expect(treffer).not.toContain(dok.imGeheimenFall)
|
||||
expect(treffer).not.toContain(dok.anVerborgenerPerson)
|
||||
|
||||
@@ -9,7 +9,7 @@ import { splitBody } from '@/lib/chat/mentions'
|
||||
* Was passiert, wenn jemand versucht, über ein Textfeld Code einzuschleusen.
|
||||
*
|
||||
* Die Grundlage ist, dass React alles maskiert, was es als Text rendert, und
|
||||
* dass es in dieser Anwendung genau **ein** `dangerouslySetInnerHTML` gibt —
|
||||
* dass es in dieser Anwendung genau **ein** `dangerouslySetInnerHTML` gibt -
|
||||
* mit einer Konstanten aus demselben Modul. Diese Datei prüft die Ränder darum
|
||||
* herum: die Stellen, an denen Text nicht als Text ankommt, sondern als
|
||||
* Vorlage, als Stilangabe oder als Kopfzeile.
|
||||
@@ -50,7 +50,7 @@ describe('Vorlagen nehmen nur an, was sie kennen', () => {
|
||||
}
|
||||
})
|
||||
|
||||
it('lässt bösartigen Text als Beschriftung durch — als Text', () => {
|
||||
it('lässt bösartigen Text als Beschriftung durch - als Text', () => {
|
||||
// Das ist Absicht: eine Beschriftung darf jedes Zeichen enthalten, weil sie
|
||||
// als Text gerendert wird und nie als HTML. Verboten ist die Struktur,
|
||||
// nicht das Zeichen.
|
||||
@@ -68,7 +68,7 @@ describe('Platzhalter bleiben Text', () => {
|
||||
const text = fillPlaceholders('Hallo {{name}}', {
|
||||
name: '<script>alert(1)</script>',
|
||||
})
|
||||
// Unverändert übernommen — und React macht daraus beim Rendern Text.
|
||||
// Unverändert übernommen - und React macht daraus beim Rendern Text.
|
||||
expect(text).toBe('Hallo <script>alert(1)</script>')
|
||||
})
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ suite('Nachträgliche Änderungen am Verlauf', () => {
|
||||
const { AccessError } = await import('@/lib/cases/service')
|
||||
const id = await neuerEintrag('Alter Eintrag')
|
||||
|
||||
// Karenzzeit serverseitig auf null setzen — nicht über den Request.
|
||||
// Karenzzeit serverseitig auf null setzen - nicht über den Request.
|
||||
await db.appSetting.update({ where: { id: 1 }, data: { entryEditGraceMinutes: 0 } })
|
||||
|
||||
await expect(
|
||||
|
||||
@@ -10,7 +10,7 @@ import { invalidateUser, resolvePermissions } from '@/lib/auth/effective'
|
||||
*
|
||||
* Vier Wege führen dorthin: Herabstufung, Entzug der AfiS-Rolle, Rauswurf und
|
||||
* selbst gehen. Der Worker deckt die Discord-Seite ab (siehe
|
||||
* worker/tests/mitgliedschaft.test.ts) — hier steht die andere Hälfte: dass
|
||||
* worker/tests/mitgliedschaft.test.ts) - hier steht die andere Hälfte: dass
|
||||
* die Web-App den Entzug auch bemerkt.
|
||||
*
|
||||
* Der Kern ist, dass die **Zeile in der Datenbank** der Sitzungsnachweis ist
|
||||
|
||||
@@ -97,7 +97,7 @@ describe('Zeichen für den Druckabruf', () => {
|
||||
expect(claim.expiresAt).toBeGreaterThan(Date.now())
|
||||
|
||||
// Ein Zeichen mit vergangener Frist wird nicht angenommen, auch mit
|
||||
// gültiger Unterschrift nicht — die entsteht hier gar nicht erst.
|
||||
// gültiger Unterschrift nicht - die entsteht hier gar nicht erst.
|
||||
expect(verifyRenderToken(`${nutzlast}.zuwenig`)).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -8,7 +8,7 @@ import { detectType } from '@/lib/files/magic'
|
||||
* Die Neukodierung ist die wirksamste Massnahme der Kette: ein Bild, das durch
|
||||
* `sharp` gelaufen ist, besteht nur noch aus Pixeln.
|
||||
*
|
||||
* Diese Tests prüfen genau das — nicht, dass irgendetwas herauskommt, sondern
|
||||
* Diese Tests prüfen genau das - nicht, dass irgendetwas herauskommt, sondern
|
||||
* dass das, was nicht Inhalt ist, **weg** ist.
|
||||
*/
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import type { EffectivePermissions, Permission } from '@/lib/auth/permissions'
|
||||
* Rückverweise sind der häufigste Ort, an dem ein Aktensystem leckt.
|
||||
*
|
||||
* Eine Personalakte der Stufe 1, auf der „3 Fälle" steht, verrät die Existenz
|
||||
* von Ermittlungen, die der Leser nicht sehen darf — auch dann, wenn kein
|
||||
* von Ermittlungen, die der Leser nicht sehen darf - auch dann, wenn kein
|
||||
* Titel danebensteht. Deshalb prüft dieser Test nicht nur die Liste, sondern
|
||||
* ausdrücklich auch die **Zahl**. Siehe docs/05-akten-und-vorlagen.md.
|
||||
*/
|
||||
@@ -148,7 +148,7 @@ suite('Personalakten und Rückverweise', () => {
|
||||
const rollen = byRole.map((gruppe) => gruppe.role).sort()
|
||||
expect(rollen).toEqual(['SUBJECT', 'WITNESS'])
|
||||
// Drei Einträge: einer über die Personalakte im offenen Fall, zwei im
|
||||
// geheimen — über die Personalakte und über das Discord-Konto.
|
||||
// geheimen - über die Personalakte und über das Discord-Konto.
|
||||
expect(total).toBe(3)
|
||||
})
|
||||
|
||||
@@ -208,7 +208,7 @@ suite('Personalakten und Rückverweise', () => {
|
||||
role: 'INVESTIGATOR',
|
||||
})
|
||||
|
||||
// Danach schon — das ist der ausnahmsweise Zugang, wie bei der Fallakte.
|
||||
// Danach schon - das ist der ausnahmsweise Zugang, wie bei der Fallakte.
|
||||
expect(
|
||||
await db.personnelFile.findMany({
|
||||
where: { AND: [visiblePersonnelWhere(p), { id: hoheAkteId }] },
|
||||
|
||||
@@ -6,7 +6,7 @@ import { queryAuditLog, AUDIT_PAGE } from '@/lib/audit/query'
|
||||
|
||||
/**
|
||||
* Das Protokoll ist die Gegenprobe zum ganzen System: hier soll stehen, was
|
||||
* passiert ist, vollständig und unverändert. Geprüft wird deshalb zweierlei —
|
||||
* passiert ist, vollständig und unverändert. Geprüft wird deshalb zweierlei -
|
||||
* dass die Filter genau schneiden, und dass eine Zeile nicht verschwinden
|
||||
* kann, wenn ihr Ziel verschwindet.
|
||||
*/
|
||||
@@ -95,7 +95,7 @@ suite('Zugriffsprotokoll', () => {
|
||||
expect(zurAkte?.targetHref).toBe(`/faelle/${fallNummer}`)
|
||||
|
||||
// Ein Auftrag ist kein Datensatz, den das Protokoll auflösen kann. Die
|
||||
// Zeile bleibt trotzdem stehen — ein Protokoll mit Lücken wäre keins.
|
||||
// Zeile bleibt trotzdem stehen - ein Protokoll mit Lücken wäre keins.
|
||||
const zumAuftrag = rows.find((zeile) => zeile.targetType === 'ExportJob')
|
||||
expect(zumAuftrag).toBeDefined()
|
||||
expect(zumAuftrag?.targetLabel).toBeNull()
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { EffectivePermissions, Permission } from '@/lib/auth/permissions'
|
||||
|
||||
/**
|
||||
* Die Schreibgrenzen aus docs/03-auth-und-rechte.md, geprüft gegen die
|
||||
* Fachlogik selbst — nicht gegen die Oberfläche. Eine Server Action ist ein
|
||||
* Fachlogik selbst - nicht gegen die Oberfläche. Eine Server Action ist ein
|
||||
* Endpunkt: was hier durchgeht, geht auch durch, wenn jemand das Formular
|
||||
* umgeht.
|
||||
*/
|
||||
|
||||
@@ -6,7 +6,7 @@ import { stripRedacted } from '@/lib/redaction'
|
||||
/**
|
||||
* Die Schwärzung wird an zwei Stellen entfernt: in TypeScript beim Rendern und
|
||||
* in SQL, bevor der Suchvektor gefüllt wird. Laufen die beiden auseinander,
|
||||
* findet die Suche einen Begriff, den die Ansicht verbirgt — und der Treffer
|
||||
* findet die Suche einen Begriff, den die Ansicht verbirgt - und der Treffer
|
||||
* allein verrät ihn dann.
|
||||
*/
|
||||
const url = process.env.DIRECT_DATABASE_URL ?? process.env.DATABASE_URL
|
||||
|
||||
@@ -125,7 +125,7 @@ suite('Volltextsuche', () => {
|
||||
})
|
||||
|
||||
it('findet einen höher eingestuften Eintrag nicht, obwohl die Akte sichtbar ist', async () => {
|
||||
// Der klassische Leck: die Akte darf er sehen, den Eintrag nicht — und
|
||||
// Der klassische Leck: die Akte darf er sehen, den Eintrag nicht - und
|
||||
// ein Treffer würde verraten, dass darin etwas über "Nachtblende" steht.
|
||||
const fuerStufe1 = await searchCases(perms(nutzer.stufe1, 1), filter(wort.gestuft))
|
||||
expect(fuerStufe1.hits).toHaveLength(0)
|
||||
@@ -267,7 +267,7 @@ suite('Volltextsuche', () => {
|
||||
})
|
||||
|
||||
it('macht über einen Beteiligten keine zu hoch eingestufte Akte auffindbar', async () => {
|
||||
// Der Name eines Beteiligten steht in jeder Akte, die man lesen darf —
|
||||
// Der Name eines Beteiligten steht in jeder Akte, die man lesen darf -
|
||||
// aber die Akte selbst muss man lesen dürfen. Sonst wäre die Suche nach
|
||||
// einem Namen ein Weg, die Existenz einer Akte zu erraten.
|
||||
const name = `Verdeckte Person ${Date.now().toString(36)}`
|
||||
|
||||
@@ -12,7 +12,7 @@ import { join } from 'node:path'
|
||||
* 2. **`suppressHydrationWarning`.** Der Browser leert das Nonce-Attribut
|
||||
* nach dem Parsen, React vergleicht es beim Hydrieren trotzdem und meldet
|
||||
* einen Unterschied, den es nicht gibt. Die Meldung steht nur in der
|
||||
* Konsole des Entwicklungsservers — im Produktionsbau meldet React sie
|
||||
* Konsole des Entwicklungsservers - im Produktionsbau meldet React sie
|
||||
* nicht, ein e2e-Lauf kann sie also nicht auffangen. Deshalb hier auf der
|
||||
* Ebene der Quelle.
|
||||
* 3. **Der Schlüssel im Speicher**, derselbe wie im Umschalter. Weichen sie
|
||||
@@ -31,7 +31,7 @@ describe('Thema vor dem ersten Malen', () => {
|
||||
it('benutzt denselben Speicherschlüssel wie der Umschalter', () => {
|
||||
const toggle = readFileSync(join(process.cwd(), 'components/ThemeToggle.tsx'), 'utf8')
|
||||
|
||||
// Der Schlüssel wird importiert, nicht abgeschrieben — genau das prüft das
|
||||
// Der Schlüssel wird importiert, nicht abgeschrieben - genau das prüft das
|
||||
// hier: eine zweite, abgeschriebene Zeichenkette wäre der Fehler.
|
||||
expect(toggle).toMatch(/export const THEME_KEY = '([^']+)'/)
|
||||
expect(layout).toContain("import { THEME_KEY } from '@/components/ThemeToggle'")
|
||||
|
||||
@@ -8,7 +8,7 @@ const css = readFileSync(join(process.cwd(), 'app/globals.css'), 'utf8')
|
||||
* Das helle Thema ist verpflichtend, siehe docs/10-design-system.md. Diese
|
||||
* Tests halten die zwei Eigenschaften fest, die es leicht verliert: dass es
|
||||
* wirklich jede semantische Farbe tauscht, und dass eine Freigabestufe in
|
||||
* beiden Themen dieselbe Farbe behält — sonst lernt man sie zweimal.
|
||||
* beiden Themen dieselbe Farbe behält - sonst lernt man sie zweimal.
|
||||
*/
|
||||
describe('Helles Thema', () => {
|
||||
const semantisch = [
|
||||
@@ -43,7 +43,7 @@ describe('Helles Thema', () => {
|
||||
|
||||
it('behält die Klassifizierungsfarben als eigenes System', () => {
|
||||
// Die Farbtöne dürfen für hellen Grund nachgedunkelt werden, aber es muss
|
||||
// sie geben — sonst verschwindet der Stempel auf hellem Papier.
|
||||
// sie geben - sonst verschwindet der Stempel auf hellem Papier.
|
||||
const hell = css.slice(css.indexOf("[data-theme='light']"))
|
||||
for (const stufe of [0, 1, 2, 3, 4, 5]) {
|
||||
expect(hell).toContain(`--color-stamp-${stufe}`)
|
||||
|
||||
@@ -75,7 +75,7 @@ suite('Übersicht und Beobachtungsliste', () => {
|
||||
geheimId = geheim.id
|
||||
geheimNummer = geheim.caseNumber
|
||||
|
||||
// Der Stufe-1-Nutzer ist an beiden Akten beteiligt — an der geheimen aber
|
||||
// Der Stufe-1-Nutzer ist an beiden Akten beteiligt - an der geheimen aber
|
||||
// nicht über sein Konto, sondern gar nicht. Er darf sie nicht sehen.
|
||||
await db.caseParticipant.create({
|
||||
data: { caseId: offen.id, userId: nutzer.stufe1, role: 'INVESTIGATOR' },
|
||||
|
||||
@@ -5,7 +5,7 @@ import { uuidv7 } from '@/lib/uuidv7'
|
||||
* Auf der Sortierbarkeit dieser Kennungen beruhen zwei Dinge: die
|
||||
* Nachlieferung verpasster Nachrichten (`id > seit`) und die Reihenfolge im
|
||||
* Chat. Bricht sie, kommen Nachrichten durcheinander an oder gehen bei einem
|
||||
* Wiederverbinden verloren — beides fällt spät und unangenehm auf.
|
||||
* Wiederverbinden verloren - beides fällt spät und unangenehm auf.
|
||||
*/
|
||||
describe('UUIDv7', () => {
|
||||
it('hat die richtige Form, Version und Variante', () => {
|
||||
|
||||
@@ -8,7 +8,7 @@ import type { EffectivePermissions, Permission } from '@/lib/auth/permissions'
|
||||
|
||||
/**
|
||||
* Verknüpfungen zeigen in beide Richtungen. Eine Akte, die der Leser nicht
|
||||
* sehen darf, darf auch von der Gegenseite aus nicht auftauchen — sonst
|
||||
* sehen darf, darf auch von der Gegenseite aus nicht auftauchen - sonst
|
||||
* verrät die verknüpfte Akte ihre Existenz.
|
||||
*/
|
||||
loadEnvFromRepoRoot()
|
||||
|
||||
@@ -190,7 +190,7 @@ suite('Sichtbarkeit von Akten', () => {
|
||||
|
||||
it('öffnet die Akte nicht für Zeugen, Betroffene und Täter', async () => {
|
||||
// Die Beteiligtenliste beantwortet zwei Fragen: wer bearbeitet den Fall,
|
||||
// und um wen geht es darin. Nur die erste verschafft Zugang — sonst läse
|
||||
// und um wen geht es darin. Nur die erste verschafft Zugang - sonst läse
|
||||
// der Beschuldigte die Ermittlung gegen sich selbst mit.
|
||||
const vorlage = await db.documentTemplate.findFirstOrThrow({
|
||||
where: { key: { startsWith: marke } },
|
||||
|
||||
@@ -5,7 +5,7 @@ import { clearRecent, listRecent, noteOpened, RECENT_MAX } from '@/lib/recent/se
|
||||
import type { EffectivePermissions, Permission } from '@/lib/auth/permissions'
|
||||
|
||||
/**
|
||||
* Die Sprungliste ist eine Bequemlichkeit — und genau deshalb der Ort, an dem
|
||||
* Die Sprungliste ist eine Bequemlichkeit - und genau deshalb der Ort, an dem
|
||||
* eine Abkürzung besonders verlockend wäre: die Kennungen liegen ja schon da,
|
||||
* warum sie nochmal prüfen? Weil eine vorgemerkte Kennung keine Berechtigung
|
||||
* ist. Diese Datei prüft vor allem, dass die Liste nichts durchlässt.
|
||||
@@ -127,7 +127,7 @@ suite('Zuletzt geöffnet', () => {
|
||||
it('zeigt niemandem die Liste eines anderen', async () => {
|
||||
await noteOpened(nutzer.b, 'CASE', offenId)
|
||||
|
||||
// Nutzer a hat die Akte auch geöffnet — aber b bekommt seine eigene Zeile,
|
||||
// Nutzer a hat die Akte auch geöffnet - aber b bekommt seine eigene Zeile,
|
||||
// und ein Administrator bekommt seine eigene, nicht die von b.
|
||||
const admin = await listRecent(perms(`${marke}-admin`, 5, { isAdmin: true }))
|
||||
expect(admin).toHaveLength(0)
|
||||
|
||||
Reference in New Issue
Block a user