fix(domains): null in den Einstellungen ergibt 400 statt 500 (WR-04)
@IsOptional liess null durch; der Dienst scheiterte dann an trim()/map() oder an einer NOT-NULL-Spalte. Felder duerfen fehlen, aber nicht null sein (Kontext- nummern bleiben ausdruecklich null-faehig). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
import 'reflect-metadata';
|
||||
import { plainToInstance } from 'class-transformer';
|
||||
import { validate } from 'class-validator';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { SaveDomainsSettingsDto } from './domains-settings.dto';
|
||||
|
||||
async function errorsOf(plain: Record<string, unknown>) {
|
||||
const errors = await validate(plainToInstance(SaveDomainsSettingsDto, plain));
|
||||
return errors.map((e) => e.property).sort();
|
||||
}
|
||||
|
||||
describe('SaveDomainsSettingsDto — null ist keine fehlende Angabe', () => {
|
||||
it('ein leerer Rumpf und nur gesetzte Felder sind gueltig', async () => {
|
||||
expect(await errorsOf({})).toEqual([]);
|
||||
expect(
|
||||
await errorsOf({
|
||||
environment: 'DEMO',
|
||||
demoUser: 'u',
|
||||
demoPassword: '',
|
||||
defaultNameServers: ['ns1.example.com', 'ns2.example.com'],
|
||||
}),
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it.each([
|
||||
'environment',
|
||||
'confirmLive',
|
||||
'demoUser',
|
||||
'demoPassword',
|
||||
'liveUser',
|
||||
'livePassword',
|
||||
'defaultNameServers',
|
||||
])('%s: null -> Validierungsfehler (400) statt TypeError im Dienst', async (field) => {
|
||||
expect(await errorsOf({ [field]: null })).toEqual([field]);
|
||||
});
|
||||
|
||||
it('Kontextnummern duerfen ausdruecklich null sein (Zugang loeschen)', async () => {
|
||||
expect(await errorsOf({ demoContext: null, liveContext: null })).toEqual([]);
|
||||
expect(await errorsOf({ demoContext: 'x' })).toEqual(['demoContext']);
|
||||
});
|
||||
});
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
IsBoolean,
|
||||
IsIn,
|
||||
IsInt,
|
||||
IsOptional,
|
||||
IsString,
|
||||
Max,
|
||||
MaxLength,
|
||||
@@ -12,6 +11,17 @@ import {
|
||||
ValidateIf,
|
||||
} from 'class-validator';
|
||||
|
||||
/**
|
||||
* Teilspeichern: ein Feld darf FEHLEN (`undefined`), aber nicht `null` sein —
|
||||
* `@IsOptional()` liesse `null` durch und der Dienst scheiterte dann mit einem
|
||||
* 500 statt einer 400 (`null.trim()`, `null.map()`, NOT-NULL-Spalte).
|
||||
*/
|
||||
const isPresent = (_object: unknown, value: unknown): boolean => value !== undefined;
|
||||
|
||||
/** Kontextnummern duerfen zusaetzlich ausdruecklich `null` sein (= Zugang loeschen). */
|
||||
const isPresentOrNull = (_object: unknown, value: unknown): boolean =>
|
||||
value !== null && value !== undefined;
|
||||
|
||||
/**
|
||||
* Einstellungen des Moduls Domains (quick-261008-dts). Jedes Feld ist
|
||||
* optional — gespeichert wird nur, was mitgeschickt wird (jede Karte der
|
||||
@@ -19,48 +29,48 @@ import {
|
||||
* Passwort laesst das gespeicherte unveraendert.
|
||||
*/
|
||||
export class SaveDomainsSettingsDto {
|
||||
@IsOptional()
|
||||
@ValidateIf(isPresent)
|
||||
@IsIn(['DEMO', 'LIVE'])
|
||||
environment?: 'DEMO' | 'LIVE';
|
||||
|
||||
/** Ausdrueckliche Bestaetigung fuer den Wechsel auf das Live-System. */
|
||||
@IsOptional()
|
||||
@ValidateIf(isPresent)
|
||||
@IsBoolean()
|
||||
confirmLive?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
@ValidateIf(isPresent)
|
||||
@IsString()
|
||||
@MaxLength(100)
|
||||
demoUser?: string;
|
||||
|
||||
@IsOptional()
|
||||
@ValidateIf(isPresent)
|
||||
@IsString()
|
||||
@MaxLength(200)
|
||||
demoPassword?: string;
|
||||
|
||||
@ValidateIf((_o, value) => value !== null && value !== undefined)
|
||||
@ValidateIf(isPresentOrNull)
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
@Max(2147483647)
|
||||
demoContext?: number | null;
|
||||
|
||||
@IsOptional()
|
||||
@ValidateIf(isPresent)
|
||||
@IsString()
|
||||
@MaxLength(100)
|
||||
liveUser?: string;
|
||||
|
||||
@IsOptional()
|
||||
@ValidateIf(isPresent)
|
||||
@IsString()
|
||||
@MaxLength(200)
|
||||
livePassword?: string;
|
||||
|
||||
@ValidateIf((_o, value) => value !== null && value !== undefined)
|
||||
@ValidateIf(isPresentOrNull)
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
@Max(2147483647)
|
||||
liveContext?: number | null;
|
||||
|
||||
@IsOptional()
|
||||
@ValidateIf(isPresent)
|
||||
@IsArray()
|
||||
@ArrayMaxSize(6)
|
||||
@IsString({ each: true })
|
||||
|
||||
Reference in New Issue
Block a user