initialization

This commit is contained in:
Reindl David (IT-PTR-CEN2-SL10) committed 2026-10-07 01:16:57 +02:00
commit 9ff09693ed
92 files changed
+46209

No files matched your search

+225
View File
@@ -0,0 +1,225 @@
<?php
declare(strict_types=1);
use App\Generators\HighEntropyGenerator;
use App\Generators\PasswordGeneratorInterface;
use App\Generators\PhraseAbbreviatorGenerator;
use App\Generators\PwgenGenerator;
use App\Managers\PasswordGeneratorManager;
use PHPUnit\Framework\TestCase;
final class GeneratorValidationTest extends TestCase
{
public function testDefaultGenerationWorksAcrossSupportedModes(): void
{
$manager = new PasswordGeneratorManager();
foreach ([
'high-entropy',
'xkcdpass',
'diceware',
'pwgen',
'passkey',
'phrase-abbreviator',
] as $type) {
$defaults = $manager->getDefaultOptions($type);
$result = $manager->generate($type, 1, $defaults);
$this->assertArrayHasKey('passwords', $result);
$this->assertNotEmpty($result['passwords']);
$this->assertNotSame('', trim((string) $result['passwords'][0]));
}
}
public function testHighEntropyRejectsAllFalseCharacterSets(): void
{
$this->expectException(\RuntimeException::class);
(new HighEntropyGenerator())->generate(1, [
'length' => 16,
'include_lowercase' => false,
'include_uppercase' => false,
'include_numbers' => false,
'include_symbols' => false,
]);
}
public function testPhraseAbbreviatorUsesFallbackSentenceWhenMissing(): void
{
$generator = new PhraseAbbreviatorGenerator();
$validated = $generator->validateOptions(['sentence' => '']);
$this->assertNotSame('', $validated['sentence']);
$this->assertSame('The quick brown fox jumps over the lazy dog.', $validated['sentence']);
}
public function testGeneratorDefinitionsMatchFrontendMetadataContract(): void
{
$manager = new PasswordGeneratorManager();
$definitions = $manager->getGeneratorDefinitions();
$this->assertNotEmpty($definitions);
$this->assertArrayHasKey('high-entropy', $definitions);
$highEntropy = $definitions['high-entropy'];
$this->assertSame('high-entropy_title', $highEntropy['nameKey']);
$this->assertSame('high-entropy_description', $highEntropy['descriptionKey']);
$this->assertNotEmpty($highEntropy['options']);
$this->assertSame('high-entropy_option_length', $highEntropy['options'][0]['labelKey']);
}
public function testGeneratedPasswordsExposeSecurityAssessmentAndEntropyDetails(): void
{
$manager = new PasswordGeneratorManager();
$result = $manager->generate('diceware', 1, [
'words' => 6,
'show_dice_rolls' => true,
]);
$this->assertArrayHasKey('passwords', $result);
$this->assertArrayHasKey('security_assessment', $result);
$this->assertNotSame('', trim((string) $result['security_assessment']));
$this->assertArrayHasKey('raw_technical_details', $result);
$this->assertArrayHasKey('entropy_bits', $result['raw_technical_details']);
$this->assertGreaterThan(0, $result['raw_technical_details']['entropy_bits']);
}
public function testPwgenGeneratorDoesNotExposeRemovedTypePresetOption(): void
{
$generator = new PwgenGenerator();
$options = $generator->getOptionDefinitions();
$this->assertArrayNotHasKey('type', $options);
$this->assertSame('custom', $generator->validateOptions([])['type'] ?? 'custom');
}
public function testXkcdEntropyReflectsWordLengthFiltering(): void
{
$manager = new PasswordGeneratorManager();
$unfiltered = $manager->generate('xkcdpass', 1, [
'words' => 4,
'wordlist' => 'en',
'separator' => '-',
'max_word_length' => 0,
'space_handling' => 'allow',
'add_number' => false,
]);
$shortFiltered = $manager->generate('xkcdpass', 1, [
'words' => 4,
'wordlist' => 'en',
'separator' => '-',
'max_word_length' => 4,
'space_handling' => 'allow',
'add_number' => false,
]);
$this->assertGreaterThan($shortFiltered['entropy_bits'], $unfiltered['entropy_bits']);
}
public function testXkcdResponseIncludesEntropyBreakdownDetails(): void
{
$manager = new PasswordGeneratorManager();
$result = $manager->generate('xkcdpass', 1, [
'words' => 4,
'wordlist' => 'en',
'separator' => '-_',
'max_word_length' => 6,
'space_handling' => 'skip',
'add_number' => true,
'number_position' => 'random',
]);
$this->assertArrayHasKey('raw_technical_details', $result);
$this->assertArrayHasKey('entropy_breakdown', $result['raw_technical_details']);
$breakdown = $result['raw_technical_details']['entropy_breakdown'];
$this->assertSame('xkcd-candidate-space-v1', $breakdown['entropy_model']);
$this->assertArrayHasKey('effective_wordlist_candidates', $breakdown);
$this->assertGreaterThan(0, $breakdown['effective_wordlist_candidates']);
$this->assertArrayHasKey('separator_entropy_bits', $breakdown);
$this->assertArrayHasKey('number_entropy_bits', $breakdown);
}
public function testGeneratorsExposeEntropyModelConfidenceInBreakdown(): void
{
$manager = new PasswordGeneratorManager();
$cases = [
'diceware' => ['words' => 6],
'passkey' => ['syllables' => 6],
'pwgen' => ['length' => 12, 'separator' => '-_', 'include_numbers' => true, 'include_capitals' => true],
];
foreach ($cases as $type => $options) {
$result = $manager->generate($type, 1, $options);
$this->assertArrayHasKey('raw_technical_details', $result);
$this->assertArrayHasKey('entropy_breakdown', $result['raw_technical_details']);
$this->assertArrayHasKey('entropy_model', $result['raw_technical_details']['entropy_breakdown']);
$this->assertArrayHasKey('model_confidence', $result['raw_technical_details']['entropy_breakdown']);
$this->assertContains(
$result['raw_technical_details']['entropy_breakdown']['model_confidence'],
['high', 'medium', 'low']
);
}
}
public function testPwgenAndXkcdCurrentOptionsProduceExpectedBehavior(): void
{
$manager = new PasswordGeneratorManager();
$pwgen16 = (string) $manager->generate('pwgen', 1, ['length' => 16])['passwords'][0];
$pwgen12 = (string) $manager->generate('pwgen', 1, ['length' => 12])['passwords'][0];
$pwgen8 = (string) $manager->generate('pwgen', 1, ['length' => 8])['passwords'][0];
$this->assertSame(16, strlen($pwgen16));
$this->assertSame(12, strlen($pwgen12));
$this->assertSame(8, strlen($pwgen8));
$xkcdGerman = (string) $manager->generate('xkcdpass', 1, [
'wordlist' => 'de',
'words' => 4,
'separator' => '-',
'add_number' => true,
'number_position' => 'end',
])['passwords'][0];
$xkcdEnglish = (string) $manager->generate('xkcdpass', 1, [
'wordlist' => 'en',
'words' => 4,
'separator' => '-',
'add_number' => true,
'number_position' => 'end',
])['passwords'][0];
$xkcdSlaengikon = (string) $manager->generate('xkcdpass', 1, [
'wordlist' => 'ch',
'words' => 4,
'separator' => '_',
'add_number' => true,
'number_position' => 'end',
])['passwords'][0];
$this->assertSame(1, preg_match('/\d/', $xkcdGerman));
$this->assertSame(1, preg_match('/\d/', $xkcdEnglish));
$this->assertSame(1, preg_match('/\d/', $xkcdSlaengikon));
}
public function testDicewareUsesLargeEffectiveCandidateSpace(): void
{
$manager = new PasswordGeneratorManager();
$result = $manager->generate('diceware', 1, [
'words' => 6,
'show_dice_rolls' => true,
]);
$this->assertArrayHasKey('raw_technical_details', $result);
$this->assertArrayHasKey('entropy_breakdown', $result['raw_technical_details']);
$this->assertArrayHasKey('wordlist_size', $result['raw_technical_details']['entropy_breakdown']);
$this->assertGreaterThanOrEqual(7000, $result['raw_technical_details']['entropy_breakdown']['wordlist_size']);
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
use App\Api\OpenApiGenerator;
use PHPUnit\Framework\TestCase;
final class OpenApiGeneratorTest extends TestCase
{
public function testGenerateIncludesPublicApiRoutes(): void
{
$spec = OpenApiGenerator::generate('http://localhost:8080');
$this->assertSame('3.0.3', $spec['openapi']);
$this->assertArrayHasKey('/api/v1/passwords', $spec['paths']);
$this->assertArrayHasKey('/api/v1/passwords/generate', $spec['paths']);
$this->assertArrayHasKey('/api/v1/health', $spec['paths']);
$this->assertArrayHasKey('/api/v1/locales/{lang}', $spec['paths']);
$this->assertSame('http://localhost:8080', $spec['servers'][0]['url']);
$this->assertSame('boolean', $spec['paths']['/api/v1/passwords']['get']['responses']['200']['content']['application/json']['schema']['properties']['success']['type']);
$this->assertArrayHasKey('types', $spec['paths']['/api/v1/passwords']['get']['responses']['200']['content']['application/json']['schema']['properties']['data']['properties']);
$this->assertSame('#/components/schemas/PasswordGenerationSuccess', $spec['paths']['/api/v1/passwords/generate']['post']['responses']['200']['content']['application/json']['schema']['$ref']);
$passwordResponseData = $spec['components']['schemas']['PasswordGenerationSuccess']['properties']['data']['properties'];
$this->assertArrayHasKey('security_assessment', $passwordResponseData);
$this->assertArrayHasKey('raw_technical_details', $passwordResponseData);
$this->assertArrayHasKey('entropy_breakdown', $passwordResponseData['raw_technical_details']['properties']);
$localeEnum = $spec['paths']['/api/v1/locales/{lang}']['get']['parameters'][0]['schema']['enum'];
$this->assertContains('ja', $localeEnum);
$this->assertContains('de_CH', $localeEnum);
$this->assertNotContains('js', $localeEnum);
$this->assertNotContains('ch', $localeEnum);
}
}
+23
View File
@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
use App\Utils\SecurityConfig;
use PHPUnit\Framework\TestCase;
final class ProductionSecurityConfigTest extends TestCase
{
public function testDevelopmentDefaultsKeepLocalhostCorsAndDebugLogging(): void
{
$this->assertSame('http://localhost:8080', SecurityConfig::resolveCorsOrigin('development'));
$this->assertSame(100, SecurityConfig::resolveRateLimit('development', 100));
$this->assertSame(100, SecurityConfig::resolveLogLevel('debug'));
}
public function testProductionDefaultsRejectWildcardOriginsAndUseRestrictedLogging(): void
{
$this->assertSame('', SecurityConfig::resolveCorsOrigin('production'));
$this->assertSame(120, SecurityConfig::resolveRateLimit('production', 120));
$this->assertSame(300, SecurityConfig::resolveLogLevel('warning'));
}
}