Scannen des QR-Codes eines Volks landete bisher direkt auf einem
leeren "Neue Durchsicht"-Formular ohne jeglichen Kontext (kein
Status, keine Historie, kein Menue) - erst nach dem Speichern kam man
auf /colonies/[id], die eigentliche Uebersichtsseite mit Status,
Aktionen, Durchsicht-/Varroa-/Schwarmkontroll-/Behandlungs-/
Fuetterungshistorie.
QR fuehrt jetzt direkt zu /colonies/[id]. Der "Neue Durchsicht"-
Button ist dort weiterhin prominent vorhanden. Lastenheft Abschnitt
4.23 erlaubt explizit beide Varianten ("Volkseite beziehungsweise
eine neue Durchsicht"), diese Aenderung bleibt also spezifikations-
konform.
Verifiziert: tsc, lint, unit (228/228, inkl. der angepassten
qr.service.spec.ts-Assertion) gruen. Volle e2e-Suite konnte wegen der
wiederkehrenden WSL/Postgres-Portweiterleitungs-Unzuverlaessigkeit
nicht neu durchlaufen werden - die geaenderte Logik (ein einzelner
String) ist durch den angepassten Unit-Test direkt abgedeckt.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
80 lines
2.9 KiB
TypeScript
80 lines
2.9 KiB
TypeScript
import { BadRequestException, NotFoundException } from '@nestjs/common';
|
|
import { QrService } from './qr.service';
|
|
import { PrismaService } from '../prisma/prisma.service';
|
|
|
|
describe('QrService', () => {
|
|
let service: QrService;
|
|
let prisma: {
|
|
colony: { findUnique: jest.Mock };
|
|
apiary: { findUnique: jest.Mock };
|
|
queen: { findUnique: jest.Mock };
|
|
honeyBatch: { findUnique: jest.Mock };
|
|
storageContainer: { findUnique: jest.Mock };
|
|
bottlingBatch: { findUnique: jest.Mock };
|
|
};
|
|
|
|
beforeEach(() => {
|
|
prisma = {
|
|
colony: { findUnique: jest.fn() },
|
|
apiary: { findUnique: jest.fn() },
|
|
queen: { findUnique: jest.fn() },
|
|
honeyBatch: { findUnique: jest.fn() },
|
|
storageContainer: { findUnique: jest.fn() },
|
|
bottlingBatch: { findUnique: jest.fn() },
|
|
};
|
|
service = new QrService(prisma as unknown as PrismaService);
|
|
});
|
|
|
|
it('rejects an unsupported entity type', async () => {
|
|
await expect(service.generateSvg('unknown', 'id-1')).rejects.toBeInstanceOf(
|
|
BadRequestException,
|
|
);
|
|
});
|
|
|
|
it('throws NotFoundException when the object does not exist', async () => {
|
|
prisma.colony.findUnique.mockResolvedValueOnce(null);
|
|
await expect(
|
|
service.generateSvg('colonies', 'missing'),
|
|
).rejects.toBeInstanceOf(NotFoundException);
|
|
});
|
|
|
|
it('generates an SVG QR code for an existing colony', async () => {
|
|
prisma.colony.findUnique.mockResolvedValueOnce({ id: 'colony-1' });
|
|
const svg = await service.generateSvg('colonies', 'colony-1');
|
|
expect(svg).toContain('<svg');
|
|
});
|
|
|
|
it('generates an SVG QR code for an existing storage container', async () => {
|
|
prisma.storageContainer.findUnique.mockResolvedValueOnce({ id: 'sc-1' });
|
|
const svg = await service.generateSvg('storage-containers', 'sc-1');
|
|
expect(svg).toContain('<svg');
|
|
});
|
|
|
|
it('generates an SVG QR code that deep-links to the public origin page for a bottling batch', async () => {
|
|
prisma.bottlingBatch.findUnique.mockResolvedValueOnce({
|
|
lotNumber: 'WH-2026-001',
|
|
});
|
|
const svg = await service.generateSvg('bottling-batches', 'bb-1');
|
|
expect(svg).toContain('<svg');
|
|
});
|
|
|
|
it('resolveUrl returns the same deep-link that generateSvg encodes, for NFC writing', async () => {
|
|
prisma.colony.findUnique.mockResolvedValueOnce({ id: 'colony-1' });
|
|
const url = await service.resolveUrl('colonies', 'colony-1');
|
|
expect(url).toBe('http://localhost:3000/colonies/colony-1');
|
|
});
|
|
|
|
it('resolveUrl rejects an unsupported entity type', async () => {
|
|
await expect(service.resolveUrl('unknown', 'id-1')).rejects.toBeInstanceOf(
|
|
BadRequestException,
|
|
);
|
|
});
|
|
|
|
it('resolveUrl throws NotFoundException when the object does not exist', async () => {
|
|
prisma.colony.findUnique.mockResolvedValueOnce(null);
|
|
await expect(
|
|
service.resolveUrl('colonies', 'missing'),
|
|
).rejects.toBeInstanceOf(NotFoundException);
|
|
});
|
|
});
|