feat(domains): Kunden, Kontakte aus AutoDNS mit Zuordnung und Domainliste
- Kunden mit Markierung eigene Firma, Kontakte live aus AutoDNS (seitenweise, 60 s Zwischenspeicher), Zuordnung je System - Kontakt anlegen (Person/Organisation) und einem oder mehreren Kunden zuordnen, nur mit Verwalten - Domainliste mit Kunde (ueber den Inhaber-Kontakt), Inhaber, Ablaufdatum, Status; Filter und Gruppierung nach Kunde - Mandantenschutz-Inventar um die beiden neuen Zugriffspaare ergaenzt Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,31 @@ import { DomainsController } from './domains.controller';
|
||||
const proto = DomainsController.prototype as any;
|
||||
const req = (tenantId?: string) => ({ tenantId }) as any;
|
||||
|
||||
const MANAGE_HANDLERS = [
|
||||
'getSettings',
|
||||
'saveSettings',
|
||||
'testConnection',
|
||||
'createCustomer',
|
||||
'updateCustomer',
|
||||
'deleteCustomer',
|
||||
'createContact',
|
||||
'assignContacts',
|
||||
];
|
||||
const USE_HANDLERS = ['getStatus', 'listCustomers', 'listContacts', 'listDomains'];
|
||||
|
||||
function makeDirectory() {
|
||||
return {
|
||||
listCustomers: vi.fn(async (..._a: unknown[]) => []),
|
||||
createCustomer: vi.fn(async (..._a: unknown[]) => ({})),
|
||||
updateCustomer: vi.fn(async (..._a: unknown[]) => ({})),
|
||||
deleteCustomer: vi.fn(async (..._a: unknown[]) => ({})),
|
||||
listContacts: vi.fn(async (..._a: unknown[]) => ({})),
|
||||
createContact: vi.fn(async (..._a: unknown[]) => ({})),
|
||||
assignContacts: vi.fn(async (..._a: unknown[]) => ({})),
|
||||
listDomains: vi.fn(async (..._a: unknown[]) => ({})),
|
||||
};
|
||||
}
|
||||
|
||||
function makeSettings() {
|
||||
return {
|
||||
getStatus: vi.fn(async (..._a: unknown[]) => ({})),
|
||||
@@ -23,16 +48,18 @@ describe('DomainsController — Metadaten', () => {
|
||||
expect(Reflect.getMetadata(MODULE_SLUG_KEY, DomainsController)).toBe('domains');
|
||||
});
|
||||
|
||||
it('Einstellungen und Verbindungstest verlangen Verwalten, ohne Rollen-Decorator', () => {
|
||||
for (const name of ['getSettings', 'saveSettings', 'testConnection']) {
|
||||
it('Einstellungen, Verbindungstest, Kunden-/Kontakt-Schreibwege verlangen Verwalten, ohne Rollen-Decorator', () => {
|
||||
for (const name of MANAGE_HANDLERS) {
|
||||
expect(Reflect.getMetadata(MODULE_MANAGE_KEY, proto[name]), name).toBe(true);
|
||||
expect(Reflect.getMetadata(ROLES_KEY, proto[name]), name).toBeUndefined();
|
||||
}
|
||||
});
|
||||
|
||||
it('getStatus steht auf Benutzen-Ebene', () => {
|
||||
expect(Reflect.getMetadata(MODULE_MANAGE_KEY, proto.getStatus)).toBeUndefined();
|
||||
expect(Reflect.getMetadata(ROLES_KEY, proto.getStatus)).toBeUndefined();
|
||||
it('Lesewege stehen auf Benutzen-Ebene', () => {
|
||||
for (const name of USE_HANDLERS) {
|
||||
expect(Reflect.getMetadata(MODULE_MANAGE_KEY, proto[name]), name).toBeUndefined();
|
||||
expect(Reflect.getMetadata(ROLES_KEY, proto[name]), name).toBeUndefined();
|
||||
}
|
||||
});
|
||||
|
||||
it('Pfade und Methoden', () => {
|
||||
@@ -45,6 +72,14 @@ describe('DomainsController — Metadaten', () => {
|
||||
expect(route('getSettings')).toEqual([0, 'settings']);
|
||||
expect(route('saveSettings')).toEqual([2, 'settings']);
|
||||
expect(route('testConnection')).toEqual([1, 'connection-test']);
|
||||
expect(route('listCustomers')).toEqual([0, 'customers']);
|
||||
expect(route('createCustomer')).toEqual([1, 'customers']);
|
||||
expect(route('listContacts')).toEqual([0, 'contacts']);
|
||||
expect(route('createContact')).toEqual([1, 'contacts']);
|
||||
expect(route('assignContacts')).toEqual([1, 'contacts/assign']);
|
||||
expect(route('listDomains')).toEqual([0, 'domains']);
|
||||
expect(route('updateCustomer')).toEqual([2, 'customers/:id']);
|
||||
expect(route('deleteCustomer')).toEqual([3, 'customers/:id']);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -55,7 +90,7 @@ describe('DomainsController — Routen-Reihenfolge (statisch vor :id)', () => {
|
||||
);
|
||||
const isIdRoute = (n: string) => String(Reflect.getMetadata('path', proto[n])).includes(':id');
|
||||
const firstId = names.findIndex(isIdRoute);
|
||||
if (firstId === -1) return;
|
||||
expect(firstId, 'es gibt :id-Routen').toBeGreaterThan(-1);
|
||||
names.slice(firstId).forEach((n) => {
|
||||
expect(isIdRoute(n), `${n} steht nach einer :id-Route, ist aber statisch`).toBe(true);
|
||||
});
|
||||
@@ -65,7 +100,8 @@ describe('DomainsController — Routen-Reihenfolge (statisch vor :id)', () => {
|
||||
describe('DomainsController — Verhalten', () => {
|
||||
it('reicht req.tenantId weiter', async () => {
|
||||
const settings = makeSettings();
|
||||
const c = new DomainsController(settings as any);
|
||||
const directory = makeDirectory();
|
||||
const c = new DomainsController(settings as any, directory as any);
|
||||
await c.getStatus(req('t1'));
|
||||
await c.getSettings(req('t1'));
|
||||
await c.saveSettings(req('t1'), { demoUser: 'x' } as any);
|
||||
@@ -76,8 +112,42 @@ describe('DomainsController — Verhalten', () => {
|
||||
expect(settings.testConnection).toHaveBeenCalledWith('t1', 'DEMO');
|
||||
});
|
||||
|
||||
it('Kunden-, Kontakt- und Domainwege reichen req.tenantId weiter; refresh nur bei 1/true', async () => {
|
||||
const settings = makeSettings();
|
||||
const directory = makeDirectory();
|
||||
const c = new DomainsController(settings as any, directory as any);
|
||||
const id = '11111111-1111-4111-8111-111111111111';
|
||||
await c.listCustomers(req('t1'));
|
||||
await c.createCustomer(req('t1'), { name: 'A' } as any);
|
||||
await c.updateCustomer(req('t1'), id, { name: 'B' } as any);
|
||||
await c.deleteCustomer(req('t1'), id);
|
||||
await c.listContacts(req('t1'), '1');
|
||||
await c.listContacts(req('t1'), 'true');
|
||||
await c.listContacts(req('t1'), undefined);
|
||||
await c.listContacts(req('t1'), '0');
|
||||
await c.createContact(req('t1'), { type: 'PERSON' } as any);
|
||||
await c.assignContacts(req('t1'), { contactIds: [1], customerId: null });
|
||||
await c.listDomains(req('t1'), 'true');
|
||||
expect(directory.listCustomers).toHaveBeenCalledWith('t1');
|
||||
expect(directory.createCustomer).toHaveBeenCalledWith('t1', { name: 'A' });
|
||||
expect(directory.updateCustomer).toHaveBeenCalledWith('t1', id, { name: 'B' });
|
||||
expect(directory.deleteCustomer).toHaveBeenCalledWith('t1', id);
|
||||
expect(directory.listContacts.mock.calls.map((x) => x[1])).toEqual([
|
||||
{ refresh: true },
|
||||
{ refresh: true },
|
||||
{ refresh: false },
|
||||
{ refresh: false },
|
||||
]);
|
||||
expect(directory.createContact).toHaveBeenCalledWith('t1', { type: 'PERSON' });
|
||||
expect(directory.assignContacts).toHaveBeenCalledWith('t1', {
|
||||
contactIds: [1],
|
||||
customerId: null,
|
||||
});
|
||||
expect(directory.listDomains).toHaveBeenCalledWith('t1', { refresh: true });
|
||||
});
|
||||
|
||||
it('ohne Mandantenkontext 403', async () => {
|
||||
const c = new DomainsController(makeSettings() as any);
|
||||
const c = new DomainsController(makeSettings() as any, makeDirectory() as any);
|
||||
await expect(c.getStatus(req(undefined))).rejects.toBeInstanceOf(ForbiddenException);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user