feat(cert-manager): Zusammenführen mit Fullchain und Nur Kette
- Kettenbau mit Aussteller- und Signaturprüfung (checkIssued plus verify), Wurzel nur auf Wunsch - build-Route für Fullchain und Nur Kette (PEM), eigene JSON-Grenze 512 KiB mit 413 und Code tooLarge - Reiter Zusammenführen mit Kettenansicht, Hinweis bei fehlendem Zwischenzertifikat Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
import { PassThrough } from 'node:stream';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import {
|
||||
CERT_BUILD_JSON_LIMIT,
|
||||
CERT_BUILD_ROUTE,
|
||||
certBuildBodyErrors,
|
||||
certBuildJsonBody,
|
||||
} from './cert-json-body';
|
||||
import {
|
||||
CERT_BASENAME_MAX,
|
||||
CERT_PASSWORD_MAX,
|
||||
CERT_PEM_MAX,
|
||||
CERT_POOL_MAX,
|
||||
} from './dto/cert-build.dto';
|
||||
|
||||
/** PEM-aehnlicher Text: eine Zeile je 64 Zeichen, genau `chars` Zeichen lang. */
|
||||
function pemLike(chars: number): string {
|
||||
let out = '';
|
||||
while (out.length < chars) out += `${'A'.repeat(64)}\n`;
|
||||
return out.slice(0, chars);
|
||||
}
|
||||
|
||||
interface FakeReq extends PassThrough {
|
||||
headers: Record<string, string>;
|
||||
method: string;
|
||||
body?: unknown;
|
||||
}
|
||||
|
||||
function fakeRequest(raw: string): FakeReq {
|
||||
const req = new PassThrough() as FakeReq;
|
||||
req.headers = {
|
||||
'content-type': 'application/json',
|
||||
'content-length': String(Buffer.byteLength(raw)),
|
||||
};
|
||||
req.method = 'POST';
|
||||
req.end(raw);
|
||||
return req;
|
||||
}
|
||||
|
||||
function runParser(raw: string): Promise<{ req: FakeReq; error: unknown }> {
|
||||
const req = fakeRequest(raw);
|
||||
return new Promise((resolve) => {
|
||||
certBuildJsonBody(req as never, {} as never, (error?: unknown) => resolve({ req, error }));
|
||||
});
|
||||
}
|
||||
|
||||
function fakeResponse() {
|
||||
const res = {
|
||||
statusCode: 0,
|
||||
payload: undefined as unknown,
|
||||
status(code: number) {
|
||||
res.statusCode = code;
|
||||
return res;
|
||||
},
|
||||
json(body: unknown) {
|
||||
res.payload = body;
|
||||
return res;
|
||||
},
|
||||
};
|
||||
return res;
|
||||
}
|
||||
|
||||
describe('cert-json-body (D-26)', () => {
|
||||
it('traegt den Namen certBuildJsonBody, nie jsonParser oder urlencodedParser', () => {
|
||||
expect(certBuildJsonBody.name).toBe('certBuildJsonBody');
|
||||
expect(CERT_BUILD_ROUTE).toBe('/modules/cert-manager/build');
|
||||
expect(CERT_BUILD_JSON_LIMIT).toBe(512 * 1024);
|
||||
});
|
||||
|
||||
it('die groesste Anfrage innerhalb der DTO-Grenzen bleibt unter dem Grenzwert und wird gelesen', async () => {
|
||||
const body = {
|
||||
content: 'fullchain',
|
||||
certPem: pemLike(CERT_PEM_MAX),
|
||||
poolPems: Array.from({ length: CERT_POOL_MAX }, () => pemLike(CERT_PEM_MAX)),
|
||||
keyPem: pemLike(CERT_PEM_MAX),
|
||||
csrPem: pemLike(CERT_PEM_MAX),
|
||||
password: 'p'.repeat(CERT_PASSWORD_MAX),
|
||||
baseName: 'b'.repeat(CERT_BASENAME_MAX),
|
||||
};
|
||||
const raw = JSON.stringify(body);
|
||||
expect(Buffer.byteLength(raw)).toBeLessThan(CERT_BUILD_JSON_LIMIT);
|
||||
const { req, error } = await runParser(raw);
|
||||
expect(error).toBeUndefined();
|
||||
expect((req.body as typeof body).poolPems).toHaveLength(CERT_POOL_MAX);
|
||||
});
|
||||
|
||||
it('eine Anfrage ueber 512 KiB wird mit 413 und Code tooLarge beantwortet', async () => {
|
||||
const { error } = await runParser(JSON.stringify({ content: 'x'.repeat(600 * 1024) }));
|
||||
const res = fakeResponse();
|
||||
const next = vi.fn();
|
||||
certBuildBodyErrors(error, {} as never, res as never, next);
|
||||
expect(res.statusCode).toBe(413);
|
||||
expect(res.payload).toMatchObject({ code: 'tooLarge' });
|
||||
expect(typeof (res.payload as { message: string }).message).toBe('string');
|
||||
expect(next).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('fehlerhaftes JSON ergibt 400 mit Code invalidInput', async () => {
|
||||
const { error } = await runParser('{"content": ');
|
||||
const res = fakeResponse();
|
||||
const next = vi.fn();
|
||||
certBuildBodyErrors(error, {} as never, res as never, next);
|
||||
expect(res.statusCode).toBe(400);
|
||||
expect(res.payload).toMatchObject({ code: 'invalidInput' });
|
||||
expect(next).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('jeden anderen Fehler reicht die Funktion unveraendert weiter', () => {
|
||||
const other = new Error('boom');
|
||||
const res = fakeResponse();
|
||||
const next = vi.fn();
|
||||
certBuildBodyErrors(other, {} as never, res as never, next);
|
||||
expect(next).toHaveBeenCalledWith(other);
|
||||
expect(res.statusCode).toBe(0);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user