Baustein 8: Lernzielkontrollen mit Freigabe, Abgabe und Korrektur
- Ausbilder gibt frei; vorher sieht der Azubi die Fragen nicht - Freigabe laesst sich zuruecknehmen, solange nicht abgegeben wurde - Antworten werden beim Tippen je Frage zwischengespeichert - Musterloesungen erst nach dem Absenden: sichtbareKontrolle() entfernt sie serverseitig, bevor die Daten in die Ausgabe gelangen - Korrektur mit Punkten und Anmerkung je Frage, Summe und Note laufen mit - Notenschluessel der IHK; solange eine Frage offen ist, bleibt das Ergebnis vorlaeufig und die Kontrolle gilt nicht als korrigiert - Ergebnis bei beiden sichtbar, Schnitt ueber alle Kontrollen - Abgegebene Kontrollen unter "Was auf dich wartet" und im Wochenfortschritt - scripts/pruefungen/musterloesung-dichtheit.mjs prueft die Sperre gegen den echten Wortlaut aus der Inhaltsdatei im rohen Seitenquelltext 36 neue Tests, davon 21 fuer die Sichtbarkeit der Musterloesung. Insgesamt 277. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
7c4a051347
commit
d226a6bae8
@@ -0,0 +1,178 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
darfBearbeiten,
|
||||
darfFragenSehen,
|
||||
gesamtpunkte,
|
||||
sichtbareKontrolle,
|
||||
zustandVon,
|
||||
type Bearbeitungsstand,
|
||||
} from './sicht';
|
||||
import type { Kontrolle } from '../inhalte/schema';
|
||||
import type { Betrachter } from '../auth/regeln';
|
||||
|
||||
const kontrolle: Kontrolle = {
|
||||
id: 'woche-03',
|
||||
woche: 3,
|
||||
titel: 'Lernzielkontrolle Woche 3',
|
||||
dauer_minuten: 45,
|
||||
hinweis: 'Offene Fragen.',
|
||||
fragen: [
|
||||
{ id: 'f1', frage: 'Was ist die Blockgröße bei /27?', punkte: 8, musterloesung: 'GEHEIM-EINS: 32.' },
|
||||
{ id: 'f2', frage: 'Wozu dient DHCP?', punkte: 5, musterloesung: 'GEHEIM-ZWEI: Adressvergabe.' },
|
||||
],
|
||||
};
|
||||
|
||||
const azubi: Betrachter = { id: 'azu-1', rolle: 'azubi', aktiv: 1 };
|
||||
const andererAzubi: Betrachter = { id: 'azu-2', rolle: 'azubi', aktiv: 1 };
|
||||
const ausbilder: Betrachter = { id: 'aus-1', rolle: 'ausbilder', aktiv: 1 };
|
||||
const fremderAusbilder: Betrachter = { id: 'aus-9', rolle: 'ausbilder', aktiv: 1 };
|
||||
const bezug = { azubiId: 'azu-1', ausbilderId: 'aus-1' };
|
||||
|
||||
function stand(teil: Partial<Bearbeitungsstand> = {}): Bearbeitungsstand {
|
||||
return { azubiId: 'azu-1', freigegebenAm: null, abgegebenAm: null, ...teil };
|
||||
}
|
||||
|
||||
/** Findet heraus, ob irgendwo im Ergebnis noch eine Musterlösung steckt. */
|
||||
function enthaeltGeheimnis(wert: unknown): boolean {
|
||||
return JSON.stringify(wert).includes('GEHEIM');
|
||||
}
|
||||
|
||||
describe('Musterlösung vor dem Absenden', () => {
|
||||
it('kommt beim gesperrten Zustand nicht mit', () => {
|
||||
const sicht = sichtbareKontrolle(kontrolle, azubi, stand(), bezug);
|
||||
expect(sicht.musterloesungSichtbar).toBe(false);
|
||||
expect(enthaeltGeheimnis(sicht)).toBe(false);
|
||||
});
|
||||
|
||||
it('kommt auch nach der Freigabe nicht mit, solange nicht abgegeben wurde', () => {
|
||||
const sicht = sichtbareKontrolle(kontrolle, azubi, stand({ freigegebenAm: '2026-09-21T07:00:00.000Z' }), bezug);
|
||||
expect(sicht.musterloesungSichtbar).toBe(false);
|
||||
expect(sicht.fragen.every((f) => f.musterloesung === null)).toBe(true);
|
||||
expect(enthaeltGeheimnis(sicht)).toBe(false);
|
||||
});
|
||||
|
||||
it('kommt nach dem Absenden mit', () => {
|
||||
const sicht = sichtbareKontrolle(
|
||||
kontrolle,
|
||||
azubi,
|
||||
stand({ freigegebenAm: '2026-09-21T07:00:00.000Z', abgegebenAm: '2026-09-21T08:00:00.000Z' }),
|
||||
bezug,
|
||||
);
|
||||
expect(sicht.musterloesungSichtbar).toBe(true);
|
||||
expect(sicht.fragen[0]!.musterloesung).toContain('GEHEIM-EINS');
|
||||
});
|
||||
|
||||
it('kommt für den zuständigen Ausbilder jederzeit mit', () => {
|
||||
const sicht = sichtbareKontrolle(kontrolle, ausbilder, stand(), bezug);
|
||||
expect(sicht.musterloesungSichtbar).toBe(true);
|
||||
expect(sicht.fragen[1]!.musterloesung).toContain('GEHEIM-ZWEI');
|
||||
});
|
||||
|
||||
it('kommt für einen fremden Ausbilder nie mit', () => {
|
||||
const sicht = sichtbareKontrolle(
|
||||
kontrolle,
|
||||
fremderAusbilder,
|
||||
stand({ abgegebenAm: '2026-09-21T08:00:00.000Z' }),
|
||||
bezug,
|
||||
);
|
||||
expect(sicht.musterloesungSichtbar).toBe(false);
|
||||
expect(enthaeltGeheimnis(sicht)).toBe(false);
|
||||
});
|
||||
|
||||
it('kommt für einen anderen Azubi nie mit', () => {
|
||||
const sicht = sichtbareKontrolle(
|
||||
kontrolle,
|
||||
andererAzubi,
|
||||
stand({ abgegebenAm: '2026-09-21T08:00:00.000Z' }),
|
||||
bezug,
|
||||
);
|
||||
expect(sicht.musterloesungSichtbar).toBe(false);
|
||||
expect(enthaeltGeheimnis(sicht)).toBe(false);
|
||||
});
|
||||
|
||||
it('kommt bei gesperrtem Konto nie mit', () => {
|
||||
const sicht = sichtbareKontrolle(
|
||||
kontrolle,
|
||||
{ ...azubi, aktiv: 0 },
|
||||
stand({ abgegebenAm: '2026-09-21T08:00:00.000Z' }),
|
||||
bezug,
|
||||
);
|
||||
expect(enthaeltGeheimnis(sicht)).toBe(false);
|
||||
});
|
||||
|
||||
it('kommt ohne Ausbildungsverhältnis für den Ausbilder nicht mit', () => {
|
||||
const sicht = sichtbareKontrolle(kontrolle, ausbilder, stand(), null);
|
||||
expect(enthaeltGeheimnis(sicht)).toBe(false);
|
||||
});
|
||||
|
||||
it('lässt Frage, Punkte und Titel immer durch – nur die Lösung fehlt', () => {
|
||||
const sicht = sichtbareKontrolle(kontrolle, azubi, stand(), bezug);
|
||||
expect(sicht.titel).toBe('Lernzielkontrolle Woche 3');
|
||||
expect(sicht.fragen[0]!.frage).toContain('Blockgröße');
|
||||
expect(sicht.fragen[0]!.punkte).toBe(8);
|
||||
expect(sicht.gesamtpunkte).toBe(13);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Fragen sehen', () => {
|
||||
it('zeigt dem Azubi vor der Freigabe nichts', () => {
|
||||
expect(darfFragenSehen(azubi, stand(), bezug)).toBe(false);
|
||||
});
|
||||
|
||||
it('zeigt sie ihm ab der Freigabe', () => {
|
||||
expect(darfFragenSehen(azubi, stand({ freigegebenAm: '2026-09-21T07:00:00.000Z' }), bezug)).toBe(true);
|
||||
});
|
||||
|
||||
it('zeigt sie dem zuständigen Ausbilder jederzeit – er muss sie vorher durchsehen', () => {
|
||||
expect(darfFragenSehen(ausbilder, stand(), bezug)).toBe(true);
|
||||
});
|
||||
|
||||
it('zeigt sie einem fremden Ausbilder nie', () => {
|
||||
expect(darfFragenSehen(fremderAusbilder, stand({ freigegebenAm: '2026-09-21T07:00:00.000Z' }), bezug)).toBe(false);
|
||||
});
|
||||
|
||||
it('zeigt sie einem anderen Azubi nie', () => {
|
||||
expect(darfFragenSehen(andererAzubi, stand({ freigegebenAm: '2026-09-21T07:00:00.000Z' }), bezug)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Bearbeiten', () => {
|
||||
it('geht nur zwischen Freigabe und Abgabe', () => {
|
||||
expect(darfBearbeiten(azubi, stand())).toBe(false);
|
||||
expect(darfBearbeiten(azubi, stand({ freigegebenAm: '2026-09-21T07:00:00.000Z' }))).toBe(true);
|
||||
expect(
|
||||
darfBearbeiten(azubi, stand({ freigegebenAm: '2026-09-21T07:00:00.000Z', abgegebenAm: '2026-09-21T08:00:00.000Z' })),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('geht für den Ausbilder nie – er korrigiert, er schreibt nicht mit', () => {
|
||||
expect(darfBearbeiten(ausbilder, stand({ freigegebenAm: '2026-09-21T07:00:00.000Z' }))).toBe(false);
|
||||
});
|
||||
|
||||
it('geht für einen anderen Azubi nie', () => {
|
||||
expect(darfBearbeiten(andererAzubi, stand({ freigegebenAm: '2026-09-21T07:00:00.000Z' }))).toBe(false);
|
||||
});
|
||||
|
||||
it('geht bei gesperrtem Konto nicht', () => {
|
||||
expect(darfBearbeiten({ ...azubi, aktiv: 0 }, stand({ freigegebenAm: '2026-09-21T07:00:00.000Z' }))).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Zustand', () => {
|
||||
it('benennt die vier Stufen', () => {
|
||||
expect(zustandVon(stand())).toBe('gesperrt');
|
||||
expect(zustandVon(stand({ freigegebenAm: 'x' }))).toBe('freigegeben');
|
||||
expect(zustandVon(stand({ freigegebenAm: 'x', abgegebenAm: 'y' }))).toBe('abgegeben');
|
||||
expect(zustandVon({ ...stand({ freigegebenAm: 'x', abgegebenAm: 'y' }), korrigiertAm: 'z' })).toBe('korrigiert');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Gesamtpunkte', () => {
|
||||
it('summiert die Punkte aller Fragen', () => {
|
||||
expect(gesamtpunkte(kontrolle)).toBe(13);
|
||||
});
|
||||
|
||||
it('kommt mit einer Kontrolle ohne Fragen zurecht', () => {
|
||||
expect(gesamtpunkte({ fragen: [] })).toBe(0);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user