Build and Push Multi-Platform Images / build-and-push (push) Successful in 55s
Die tägliche E-Mail-Zusammenfassung zeigte Nutzern Bereiche, für die sie
keine Berechtigung haben. Zwei unabhängige Ursachen:
- Das Backend prüfte weiterhin die Altgruppe PM_Belege. Die Umbenennung
zu PM_Buchhaltung (ca1d371) war nur im Frontend angekommen, weshalb
der Digest "Manuell bearbeiten" und "In Agrarmonitor" anbot, während
die Oberfläche beide Bereiche sperrte.
- Der Cron-Versand wertet die Gruppen aus user_settings aus. Diese Spalte
wurde nur beim Aufruf der Benutzereinstellungen gefüllt; entzogene
Berechtigungen erreichten den Digest daher unter Umständen nie.
Behoben durch Angleichen des Gruppen-Mappings und den neuen
UserIdentitySyncService, der E-Mail, Benutzername und Gruppen bei jedem
authentifizierten Request aus dem Token spiegelt – ohne den Request zu
blockieren und ohne DB-Zugriff, solange sich das Token nicht ändert. Die
doppelte Identitätspflege im UserSettingsService entfällt.
WebDAV wird nicht eingesetzt und ist entfernt; Export-Ziele bieten nur
noch FTP. Damit verschwindet das ESM-Paket webdav, an dem zwei
Jest-Suites bereits beim Parsen scheiterten.
Veraltete Tests instand gesetzt: email.controller und settings.controller
mockten weniger Abhängigkeiten, als die Klassen inzwischen haben;
postprocessing.service.spec beschrieb noch das alte Regelmodell mit
Einzelfeldern statt FilterJson und ist gegen die heutige Filter-Engine
neu geschrieben (AND/OR, verschachtelte Gruppen, Fehlerprotokollierung).
Enthält außerdem den Arbeitsstand der E-Rechnungs-Mandantenzuordnung, da
sich beide Änderungen dieselben Dateien teilen (SettingsPage, package.json).
98 Tests in 12 Suites grün, Backend- und Frontend-Build sauber.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
167 lines
6.6 KiB
TypeScript
167 lines
6.6 KiB
TypeScript
import { Test, TestingModule } from '@nestjs/testing';
|
|
import { getRepositoryToken } from '@nestjs/typeorm';
|
|
import { SettingsController } from './settings.controller';
|
|
import { DocumentType } from '../database/entities/document-type.entity';
|
|
import { Postprocessing } from '../database/entities/postprocessing.entity';
|
|
import { PostprocessingAction } from '../database/entities/postprocessing-action.entity';
|
|
import { UserClient } from '../database/entities/user-client.entity';
|
|
import { Client } from '../database/entities/client.entity';
|
|
import { Setting } from '../database/entities/setting.entity';
|
|
import { PostprocessingLog } from '../database/entities/postprocessing-log.entity';
|
|
import { ExportTarget } from '../database/entities/export-target.entity';
|
|
import { ClientIdentifier } from '../database/entities/client-identifier.entity';
|
|
import { DocumentField } from '../database/entities/document-field.entity';
|
|
import { CorrespondentSetting } from '../database/entities/correspondent-setting.entity';
|
|
import { InboxPostprocessingAction } from '../database/entities/inbox-postprocessing-action.entity';
|
|
import { PaperlessService } from '../paperless/paperless.service';
|
|
import { ExportService } from '../postprocessing/export.service';
|
|
|
|
const makeRepo = (data: any[] = []) => ({
|
|
find: jest.fn().mockResolvedValue(data),
|
|
findOneByOrFail: jest.fn().mockResolvedValue(data[0] ?? {}),
|
|
create: jest.fn().mockImplementation((d) => d),
|
|
save: jest.fn().mockImplementation((d) => Promise.resolve({ Id: 99, ...d })),
|
|
update: jest.fn().mockResolvedValue({ affected: 1 }),
|
|
delete: jest.fn().mockResolvedValue({ affected: 1 }),
|
|
});
|
|
|
|
describe('SettingsController', () => {
|
|
let controller: SettingsController;
|
|
let docTypeRepo: ReturnType<typeof makeRepo>;
|
|
let ppRepo: ReturnType<typeof makeRepo>;
|
|
let ppActionRepo: ReturnType<typeof makeRepo>;
|
|
let userClientRepo: ReturnType<typeof makeRepo>;
|
|
let paperlessService: { getDocumentTypes: jest.Mock };
|
|
|
|
beforeEach(async () => {
|
|
paperlessService = { getDocumentTypes: jest.fn().mockResolvedValue([]) };
|
|
docTypeRepo = makeRepo([{ Id: 1, DocumentTypeId: 10, TitelTemplate: 'T' }]);
|
|
ppRepo = makeRepo([{ Id: 1, Name: 'Rule1', Order: 1, IsActive: true }]);
|
|
ppActionRepo = makeRepo([]);
|
|
userClientRepo = makeRepo([{ Id: 1, UserId: 'u1', ClientId: 2 }]);
|
|
|
|
const module: TestingModule = await Test.createTestingModule({
|
|
controllers: [SettingsController],
|
|
providers: [
|
|
{ provide: getRepositoryToken(DocumentType), useValue: docTypeRepo },
|
|
{ provide: getRepositoryToken(Postprocessing), useValue: ppRepo },
|
|
{
|
|
provide: getRepositoryToken(PostprocessingAction),
|
|
useValue: ppActionRepo,
|
|
},
|
|
{ provide: getRepositoryToken(UserClient), useValue: userClientRepo },
|
|
{ provide: getRepositoryToken(Client), useValue: makeRepo() },
|
|
{
|
|
provide: getRepositoryToken(Setting),
|
|
useValue: makeRepo([{ ID: 1, Typ: 1, Wert: 'v' }]),
|
|
},
|
|
{
|
|
provide: getRepositoryToken(PostprocessingLog),
|
|
useValue: makeRepo(),
|
|
},
|
|
{ provide: getRepositoryToken(ExportTarget), useValue: makeRepo() },
|
|
{ provide: getRepositoryToken(ClientIdentifier), useValue: makeRepo() },
|
|
{ provide: getRepositoryToken(DocumentField), useValue: makeRepo() },
|
|
{
|
|
provide: getRepositoryToken(CorrespondentSetting),
|
|
useValue: makeRepo(),
|
|
},
|
|
{
|
|
provide: getRepositoryToken(InboxPostprocessingAction),
|
|
useValue: makeRepo(),
|
|
},
|
|
{ provide: PaperlessService, useValue: paperlessService },
|
|
{ provide: ExportService, useValue: { testConnection: jest.fn() } },
|
|
],
|
|
}).compile();
|
|
|
|
controller = module.get<SettingsController>(SettingsController);
|
|
});
|
|
|
|
it('should be defined', () => {
|
|
expect(controller).toBeDefined();
|
|
});
|
|
|
|
// Document Types
|
|
it('getDocumentTypes returns list', async () => {
|
|
const result = await controller.getDocumentTypes();
|
|
expect(result).toHaveLength(1);
|
|
expect(docTypeRepo.find).toHaveBeenCalledWith({ order: { Id: 'ASC' } });
|
|
});
|
|
|
|
it('getDocumentTypes legt in Paperless neu angelegte Typen an', async () => {
|
|
paperlessService.getDocumentTypes.mockResolvedValue([
|
|
{ id: 10, name: 'Bekannt' },
|
|
{ id: 11, name: 'Neu' },
|
|
]);
|
|
|
|
await controller.getDocumentTypes();
|
|
|
|
expect(docTypeRepo.create).toHaveBeenCalledTimes(1);
|
|
expect(docTypeRepo.create).toHaveBeenCalledWith(
|
|
expect.objectContaining({ DocumentTypeId: 11 }),
|
|
);
|
|
expect(docTypeRepo.save).toHaveBeenCalled();
|
|
});
|
|
|
|
it('getDocumentTypes liefert die Liste auch bei Paperless-Ausfall', async () => {
|
|
paperlessService.getDocumentTypes.mockRejectedValue(new Error('down'));
|
|
|
|
const result = await controller.getDocumentTypes();
|
|
|
|
expect(result).toHaveLength(1);
|
|
expect(docTypeRepo.create).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('updateDocumentType calls update + findOneByOrFail', async () => {
|
|
await controller.updateDocumentType('1', { TitelTemplate: 'New' });
|
|
expect(docTypeRepo.update).toHaveBeenCalledWith(1, {
|
|
TitelTemplate: 'New',
|
|
});
|
|
expect(docTypeRepo.findOneByOrFail).toHaveBeenCalledWith({ Id: 1 });
|
|
});
|
|
|
|
// Postprocessing
|
|
it('getPostprocessingRules returns ordered list', async () => {
|
|
const result = await controller.getPostprocessingRules();
|
|
expect(result).toHaveLength(1);
|
|
expect(ppRepo.find).toHaveBeenCalledWith({ order: { Order: 'ASC' } });
|
|
});
|
|
|
|
it('createPostprocessingRule creates and saves', async () => {
|
|
const result = await controller.createPostprocessingRule({
|
|
Name: 'New',
|
|
} as any);
|
|
expect(ppRepo.create).toHaveBeenCalledWith({ Name: 'New' });
|
|
expect(ppRepo.save).toHaveBeenCalled();
|
|
expect(result).toHaveProperty('Id', 99);
|
|
});
|
|
|
|
it('deletePostprocessingRule deletes', async () => {
|
|
const result = await controller.deletePostprocessingRule('5');
|
|
expect(ppRepo.delete).toHaveBeenCalledWith(5);
|
|
expect(result).toEqual({ deleted: true });
|
|
});
|
|
|
|
// User Clients
|
|
it('getUserClients returns list', async () => {
|
|
const result = await controller.getUserClients();
|
|
expect(result).toHaveLength(1);
|
|
});
|
|
|
|
it('createUserClient creates', async () => {
|
|
const result = await controller.createUserClient({
|
|
UserId: 'u2',
|
|
ClientId: 3,
|
|
} as any);
|
|
expect(userClientRepo.create).toHaveBeenCalled();
|
|
expect(result).toHaveProperty('Id', 99);
|
|
});
|
|
|
|
it('deleteUserClient deletes', async () => {
|
|
const result = await controller.deleteUserClient('1');
|
|
expect(userClientRepo.delete).toHaveBeenCalledWith(1);
|
|
expect(result).toEqual({ deleted: true });
|
|
});
|
|
});
|