Skip to content

Commit 2bc3bd0

Browse files
nogeenharrieclaude
andauthored
test: add edge-case tests for gpgkeystate and passwordconfig (#1495)
gpgkeystate (17 → 23 tests): - multipleUidRecordsOnlyFirstNameUsed: only the first uid record sets name - pubRecordDoesNotSetHaveSecret: pub never sets have_secret, even with secret=true - secRecordSetsHaveSecretOnlyWhenSecretTrue: sec sets have_secret iff secret=true - createdDateParsedFromEpoch: pub created timestamp round-trips through epoch - expiryDateParsedFromEpoch: pub expiry timestamp round-trips through epoch - fprWithEmptyKeyIdIsNoop: fpr guard prevents update when key_id is empty passwordconfig (6 → 11 tests): - charsetCountIsCorrect: CHARSETS_COUNT == 4 - alphanumericContainsNoSpecialChars: every char passes isLetterOrNumber() - alphabeticalContainsNoDigits: every char passes isLetter() - customCharsDefaultMatchAllChars: CUSTOM initially equals ALLCHARS Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 0643ef4 commit 2bc3bd0

2 files changed

Lines changed: 120 additions & 0 deletions

File tree

tests/auto/gpgkeystate/tst_gpgkeystate.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ private Q_SLOTS:
2828
void parseGpgColonOutputWithGrpRecord();
2929
void parseGpgColonOutputUnknownRecordTypes();
3030
void parseGpgColonOutputAllPublicRecordTypes();
31+
void multipleUidRecordsOnlyFirstNameUsed();
32+
void pubRecordDoesNotSetHaveSecret();
33+
void secRecordSetsHaveSecretOnlyWhenSecretTrue();
34+
void createdDateParsedFromEpoch();
35+
void expiryDateParsedFromEpoch();
36+
void fprWithEmptyKeyIdIsNoop();
3137
};
3238

3339
void tst_gpgkeystate::parseMultiKeyPublic() {
@@ -384,5 +390,82 @@ void tst_gpgkeystate::parseGpgColonOutputAllPublicRecordTypes() {
384390
QVERIFY2(!result.first().name.isEmpty(), "UID name should be populated");
385391
}
386392

393+
void tst_gpgkeystate::multipleUidRecordsOnlyFirstNameUsed() {
394+
const QString input = QStringLiteral(
395+
"pub:u:4096:1:KEYID001:1774947438:::u::::\n"
396+
"uid:u::::1774947438::H1::First Name <first@test.org>::::\n"
397+
"uid:u::::1774947438::H2::Second Name <second@test.org>::::\n");
398+
QList<UserInfo> result = parseGpgColonOutput(input, false);
399+
QVERIFY2(result.size() == 1, "one key expected");
400+
QVERIFY2(result.first().name == QStringLiteral("First Name <first@test.org>"),
401+
"only the first uid record should set the name");
402+
}
403+
404+
void tst_gpgkeystate::pubRecordDoesNotSetHaveSecret() {
405+
const QString input =
406+
QStringLiteral("pub:u:4096:1:PUBKEY001:1774947438:::u::::\n"
407+
"uid:u::::1774947438::H::Alice <alice@test.org>::::\n");
408+
QList<UserInfo> result = parseGpgColonOutput(input, true);
409+
QVERIFY2(result.size() == 1, "one key expected");
410+
QVERIFY2(!result.first().have_secret,
411+
"pub record must not set have_secret even when secret=true");
412+
}
413+
414+
void tst_gpgkeystate::secRecordSetsHaveSecretOnlyWhenSecretTrue() {
415+
const QString secLine =
416+
QStringLiteral("sec:u:4096:1:SECKEY001:1774947438:::u::::\n"
417+
"uid:u::::1774947438::H::Bob <bob@test.org>::::\n");
418+
419+
QList<UserInfo> withSecret = parseGpgColonOutput(secLine, true);
420+
QVERIFY2(withSecret.size() == 1, "one key expected with secret=true");
421+
QVERIFY2(withSecret.first().have_secret,
422+
"sec record must set have_secret when secret=true");
423+
424+
QList<UserInfo> withoutSecret = parseGpgColonOutput(secLine, false);
425+
QVERIFY2(withoutSecret.size() == 1, "one key expected with secret=false");
426+
QVERIFY2(!withoutSecret.first().have_secret,
427+
"sec record must not set have_secret when secret=false");
428+
}
429+
430+
void tst_gpgkeystate::createdDateParsedFromEpoch() {
431+
const qint64 epoch = 1700000000;
432+
const QString input = QString("pub:u:4096:1:DATEKEY01:%1:::u::::\n"
433+
"uid:u::::%1::H::Date Test <dt@test.org>::::\n")
434+
.arg(epoch);
435+
QList<UserInfo> result = parseGpgColonOutput(input, false);
436+
QVERIFY2(result.size() == 1, "one key expected");
437+
QVERIFY2(result.first().created.isValid(),
438+
"created date must be valid when epoch is present");
439+
QVERIFY2(result.first().created.toSecsSinceEpoch() == epoch,
440+
"created date must round-trip through epoch seconds");
441+
}
442+
443+
void tst_gpgkeystate::expiryDateParsedFromEpoch() {
444+
const qint64 created = 1700000000;
445+
const qint64 expiry = 1800000000;
446+
const QString input =
447+
QString("pub:u:4096:1:EXPKEY001:%1:%2:u::::\n"
448+
"uid:u::::%1::H::Expiry Test <et@test.org>::::\n")
449+
.arg(created)
450+
.arg(expiry);
451+
QList<UserInfo> result = parseGpgColonOutput(input, false);
452+
QVERIFY2(result.size() == 1, "one key expected");
453+
QVERIFY2(result.first().expiry.isValid(),
454+
"expiry date must be valid when epoch is present");
455+
QVERIFY2(result.first().expiry.toSecsSinceEpoch() == expiry,
456+
"expiry date must round-trip through epoch seconds");
457+
}
458+
459+
void tst_gpgkeystate::fprWithEmptyKeyIdIsNoop() {
460+
UserInfo user;
461+
QStringList props;
462+
for (int i = 0; i < 10; ++i)
463+
props.append(QString());
464+
props[9] = QStringLiteral("SOMEFINGERPRINT");
465+
handleFprRecord(props, user);
466+
QVERIFY2(user.key_id.isEmpty(),
467+
"fpr record must not update key_id when key_id is empty");
468+
}
469+
387470
QTEST_MAIN(tst_gpgkeystate)
388471
#include "tst_gpgkeystate.moc"

tests/auto/passwordconfig/tst_passwordconfig.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ private Q_SLOTS:
1616
void passwordConfigurationCharacterSets();
1717
void passwordConfigurationLength();
1818
void passwordConfigurationCustomChars();
19+
void charsetCountIsCorrect();
20+
void alphanumericContainsNoSpecialChars();
21+
void alphabeticalContainsNoDigits();
22+
void customCharsDefaultMatchAllChars();
1923
};
2024

2125
void tst_passwordconfig::initTestCase() {
@@ -75,5 +79,38 @@ void tst_passwordconfig::passwordConfigurationCustomChars() {
7579
QCOMPARE(config.Characters[PasswordConfiguration::CUSTOM], custom);
7680
}
7781

82+
void tst_passwordconfig::charsetCountIsCorrect() {
83+
QCOMPARE(static_cast<int>(PasswordConfiguration::CHARSETS_COUNT), 4);
84+
}
85+
86+
void tst_passwordconfig::alphanumericContainsNoSpecialChars() {
87+
PasswordConfiguration config;
88+
const QString &chars = config.Characters[PasswordConfiguration::ALPHANUMERIC];
89+
for (QChar c : chars) {
90+
QVERIFY2(c.isLetterOrNumber(),
91+
qPrintable(QString("ALPHANUMERIC contains non-alphanumeric char: "
92+
"'%1'")
93+
.arg(c)));
94+
}
95+
}
96+
97+
void tst_passwordconfig::alphabeticalContainsNoDigits() {
98+
PasswordConfiguration config;
99+
const QString &chars = config.Characters[PasswordConfiguration::ALPHABETICAL];
100+
for (QChar c : chars) {
101+
QVERIFY2(c.isLetter(),
102+
qPrintable(QString("ALPHABETICAL contains non-letter char: "
103+
"'%1'")
104+
.arg(c)));
105+
}
106+
}
107+
108+
void tst_passwordconfig::customCharsDefaultMatchAllChars() {
109+
PasswordConfiguration config;
110+
QVERIFY2(config.Characters[PasswordConfiguration::CUSTOM] ==
111+
config.Characters[PasswordConfiguration::ALLCHARS],
112+
"CUSTOM character set must default to ALLCHARS");
113+
}
114+
78115
QTEST_MAIN(tst_passwordconfig)
79116
#include "tst_passwordconfig.moc"

0 commit comments

Comments
 (0)