Skip to content

Commit 18db662

Browse files
committed
test: cover wallet passphrase clearing
1 parent f16e29e commit 18db662

3 files changed

Lines changed: 166 additions & 0 deletions

File tree

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// Copyright (c) 2026 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
import QtQuick 2.15
6+
import QtTest 1.2
7+
import "../../qml/components"
8+
9+
TestCase {
10+
name: "WalletPassphrasePopup"
11+
when: windowShown
12+
width: 500
13+
height: 320
14+
15+
property string submittedPassphrase: ""
16+
property int submittedCount: 0
17+
18+
Component {
19+
id: popupComponent
20+
21+
WalletPassphrasePopup {
22+
popupObjectName: "testPassphrasePopup"
23+
titleText: "Enter wallet password"
24+
descriptionText: "Test password prompt"
25+
passphraseFieldObjectName: "testPassphraseField"
26+
cancelButtonObjectName: "testPassphraseCancelButton"
27+
confirmButtonObjectName: "testPassphraseConfirmButton"
28+
}
29+
}
30+
31+
function findObjectByName(root, objectName) {
32+
if (!root) {
33+
return null
34+
}
35+
if (root.objectName === objectName) {
36+
return root
37+
}
38+
39+
if (root.contentItem) {
40+
const contentResult = findObjectByName(root.contentItem, objectName)
41+
if (contentResult) {
42+
return contentResult
43+
}
44+
}
45+
46+
const children = root.children || []
47+
for (let i = 0; i < children.length; ++i) {
48+
const childResult = findObjectByName(children[i], objectName)
49+
if (childResult) {
50+
return childResult
51+
}
52+
}
53+
54+
return null
55+
}
56+
57+
function createPopup() {
58+
submittedPassphrase = ""
59+
submittedCount = 0
60+
61+
const popup = createTemporaryObject(popupComponent, this)
62+
verify(popup !== null)
63+
popup.submitted.connect(function(passphrase) {
64+
submittedPassphrase = passphrase
65+
submittedCount += 1
66+
})
67+
popup.open()
68+
tryCompare(popup, "opened", true)
69+
return popup
70+
}
71+
72+
function test_submit_clears_field_after_emitting_passphrase() {
73+
const popup = createPopup()
74+
const field = findObjectByName(popup, "testPassphraseField")
75+
const button = findObjectByName(popup, "testPassphraseConfirmButton")
76+
verify(field !== null)
77+
verify(button !== null)
78+
79+
const passphrase = "correct horse battery staple"
80+
field.text = passphrase
81+
verify(button.enabled)
82+
83+
mouseClick(button, button.width / 2, button.height / 2)
84+
85+
compare(submittedCount, 1)
86+
compare(submittedPassphrase, passphrase)
87+
compare(field.text, "")
88+
}
89+
90+
function test_close_clears_field_without_submitting() {
91+
const popup = createPopup()
92+
const field = findObjectByName(popup, "testPassphraseField")
93+
verify(field !== null)
94+
95+
field.text = "unused passphrase"
96+
popup.close()
97+
tryCompare(popup, "opened", false)
98+
99+
compare(submittedCount, 0)
100+
compare(field.text, "")
101+
}
102+
103+
function test_reopen_clears_stale_field_text() {
104+
const popup = createPopup()
105+
const field = findObjectByName(popup, "testPassphraseField")
106+
verify(field !== null)
107+
108+
field.text = "stale passphrase"
109+
popup.close()
110+
tryCompare(popup, "opened", false)
111+
field.text = "stale passphrase"
112+
113+
popup.open()
114+
tryCompare(popup, "opened", true)
115+
116+
compare(field.text, "")
117+
}
118+
}

test/test_walletqmlcontroller.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ private Q_SLOTS:
160160
void selectWalletBeforeInitializationSetsLoadError();
161161
void initializedControllerPropagatesCreateErrors();
162162
void initializedControllerForwardsMigrationPassphrase();
163+
void initializedControllerForwardsUtf8CreatePassphrase();
163164
void initializedControllerRequestsPassphraseBeforeEncryptedMigration();
164165
void initializedControllerMigratesUnencryptedWalletWithoutPassphrase();
165166
};
@@ -361,6 +362,36 @@ void WalletQmlControllerTests::initializedControllerForwardsMigrationPassphrase(
361362
QVERIFY(!controller.walletMigrationInProgress());
362363
}
363364

365+
void WalletQmlControllerTests::initializedControllerForwardsUtf8CreatePassphrase()
366+
{
367+
using ::testing::StrictMock;
368+
369+
StrictMock<MockNode> node;
370+
FakeWalletLoader loader;
371+
ExpectControllerInitialization(node, loader);
372+
373+
WalletQmlController controller(node);
374+
controller.initialize();
375+
376+
const QString passphrase{QString::fromUtf8("pässwörd-₿")};
377+
const std::string expected_passphrase{passphrase.toUtf8().toStdString()};
378+
379+
bool saw_expected_passphrase{false};
380+
loader.create_wallet_fn = [&](const std::string&,
381+
const SecureString& passphrase,
382+
uint64_t,
383+
std::vector<bilingual_str>&) {
384+
saw_expected_passphrase = (std::string{passphrase.begin(), passphrase.end()} == expected_passphrase);
385+
return util::Result<std::unique_ptr<interfaces::Wallet>>{
386+
util::Error{Untranslated("Wallet creation failed.")}};
387+
};
388+
389+
QVERIFY(!controller.createSingleSigWallet("test_wallet", passphrase));
390+
QVERIFY(saw_expected_passphrase);
391+
QCOMPARE(loader.create_wallet_calls, 1);
392+
QCOMPARE(controller.walletCreateError(), QString{"Wallet creation failed."});
393+
}
394+
364395
void WalletQmlControllerTests::initializedControllerRequestsPassphraseBeforeEncryptedMigration()
365396
{
366397
using ::testing::StrictMock;

test/test_walletqmlmodel.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ private Q_SLOTS:
213213
void transactionChangedEmitsBalanceChanged();
214214
void prepareTransactionOnLockedWalletRequiresPassword();
215215
void prepareTransactionWithPrivateKeysDisabledDoesNotRequirePassword();
216+
void prepareTransactionWithPassphraseForwardsUtf8Bytes();
216217
void prepareTransactionWithPassphraseRelocksWhenRecipientsInvalid();
217218
void prepareTransactionWithPassphraseRelocksWhenCustomFeeInvalid();
218219
void sendTransactionCommitsPreparedTransactionWithoutUnlockingAgain();
@@ -721,6 +722,22 @@ void WalletQmlModelTests::prepareTransactionWithPrivateKeysDisabledDoesNotRequir
721722
QCOMPARE(wallet->lock_calls, 0);
722723
}
723724

725+
void WalletQmlModelTests::prepareTransactionWithPassphraseForwardsUtf8Bytes()
726+
{
727+
FakePasswordWallet* wallet{nullptr};
728+
auto model = MakeWalletModel(wallet);
729+
SetPasswordRecipient(*model, 1'000);
730+
731+
const QString passphrase{QString::fromUtf8("pässwörd-₿")};
732+
const std::string expected_passphrase{passphrase.toUtf8().toStdString()};
733+
734+
QVERIFY(model->prepareTransactionWithPassphrase(passphrase));
735+
QCOMPARE(wallet->unlock_calls, 1);
736+
QCOMPARE(wallet->unlock_passphrases.size(), size_t{1});
737+
QCOMPARE(wallet->unlock_passphrases.front(), expected_passphrase);
738+
QCOMPARE(wallet->lock_calls, 1);
739+
}
740+
724741
void WalletQmlModelTests::prepareTransactionWithPassphraseRelocksWhenRecipientsInvalid()
725742
{
726743
FakePasswordWallet* wallet{nullptr};

0 commit comments

Comments
 (0)