Files
pw-generator/tests/GeneratorValidationTest.php

226 lines
8.5 KiB
PHP

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