fix(networkmanager): free the unlock prompt state once it completes
parent
6e71c2fa45
commit
aa19d7e9e1
@ -0,0 +1,109 @@
|
||||
// SPDX-FileCopyrightText: 2026 Reion Wong <reionwong@gmail.com>
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#include "base/dbus.h"
|
||||
#include "base/event_loop.h"
|
||||
#include "base/timer.h"
|
||||
#include "networkmanager/secretreader.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
using namespace cutefish::base;
|
||||
using cutefish::networkmanager::SecretReader;
|
||||
|
||||
using Paths = std::vector<dbus::ObjectPath>;
|
||||
|
||||
static EventLoop* loop = nullptr;
|
||||
|
||||
static bool waitFor(const std::function<bool()>& condition, std::chrono::milliseconds timeout = 3000ms)
|
||||
{
|
||||
if (condition())
|
||||
return true;
|
||||
bool met = false;
|
||||
Timer poll;
|
||||
poll.onTimeout = [&] {
|
||||
if (condition()) {
|
||||
met = true;
|
||||
loop->quit();
|
||||
}
|
||||
};
|
||||
poll.start(2ms);
|
||||
Timer limit;
|
||||
limit.setSingleShot(true);
|
||||
limit.onTimeout = [&] { loop->quit(); };
|
||||
limit.start(timeout);
|
||||
loop->run();
|
||||
return met || condition();
|
||||
}
|
||||
|
||||
// A keyring whose only item is locked, and whose unlock prompt the user dismisses.
|
||||
class FakeSecretService
|
||||
{
|
||||
public:
|
||||
FakeSecretService()
|
||||
: m_bus(dbus::Connection::open(dbus::Connection::Bus::Session))
|
||||
, m_service(m_bus.get(), "/org/freedesktop/secrets", "org.freedesktop.Secret.Service")
|
||||
, m_prompt(m_bus.get(), "/prompt/1", "org.freedesktop.Secret.Prompt")
|
||||
{
|
||||
m_service.addMethod("OpenSession", [](const std::string&, const dbus::Variant&) {
|
||||
return std::tuple<dbus::Variant, dbus::ObjectPath>{dbus::Variant(std::string()),
|
||||
dbus::ObjectPath{"/session/1"}};
|
||||
});
|
||||
m_service.addMethod("SearchItems", [](const std::map<std::string, std::string>&) {
|
||||
return std::tuple<Paths, Paths>{Paths(), Paths{dbus::ObjectPath{"/item/1"}}};
|
||||
});
|
||||
m_service.addMethod("Unlock", [](const Paths&) {
|
||||
return std::tuple<Paths, dbus::ObjectPath>{Paths(), dbus::ObjectPath{"/prompt/1"}};
|
||||
});
|
||||
m_service.publish();
|
||||
|
||||
m_prompt.addMethod("Prompt", [this](const std::string&) {
|
||||
++prompts;
|
||||
m_bus->emitSignal("/prompt/1", "org.freedesktop.Secret.Prompt", "Completed", true, dbus::Variant(Paths()));
|
||||
});
|
||||
m_prompt.addSignal("Completed", "bv");
|
||||
m_prompt.publish();
|
||||
}
|
||||
|
||||
bool serve() { return m_bus->requestName("org.freedesktop.secrets"); }
|
||||
|
||||
int prompts = 0;
|
||||
|
||||
private:
|
||||
std::unique_ptr<dbus::Connection> m_bus;
|
||||
dbus::Object m_service;
|
||||
dbus::Object m_prompt;
|
||||
};
|
||||
|
||||
TEST(SecretReaderTest, DismissedPromptAnswersWithNothing)
|
||||
{
|
||||
FakeSecretService service;
|
||||
ASSERT_TRUE(service.serve());
|
||||
|
||||
std::optional<std::map<std::string, std::string>> loaded;
|
||||
{
|
||||
SecretReader reader;
|
||||
reader.load("uuid", "vpn", [&](const std::map<std::string, std::string>& secrets) { loaded = secrets; });
|
||||
ASSERT_TRUE(waitFor([&] { return loaded.has_value(); }));
|
||||
}
|
||||
|
||||
EXPECT_EQ(service.prompts, 1);
|
||||
EXPECT_TRUE(loaded->empty());
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
EventLoop eventLoop;
|
||||
loop = &eventLoop;
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
Loading…
Reference in New Issue