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']); } }