1 #include <QLoggingCategory>
2 #include <QSignalSpy>
3 #include <QTest>
4 #include <cstdint>
5 #include <functional>
6
7 #include "pushnotificationstestutils.h"
8 #include "pushnotifications.h"
9
10 Q_LOGGING_CATEGORY(lcFakeWebSocketServer, "nextcloud.test.fakewebserver", QtInfoMsg)
11
FakeWebSocketServer(quint16 port,QObject * parent)12 FakeWebSocketServer::FakeWebSocketServer(quint16 port, QObject *parent)
13 : QObject(parent)
14 , _webSocketServer(new QWebSocketServer(QStringLiteral("Fake Server"), QWebSocketServer::NonSecureMode, this))
15 {
16 if (!_webSocketServer->listen(QHostAddress::Any, port)) {
17 Q_UNREACHABLE();
18 }
19 connect(_webSocketServer, &QWebSocketServer::newConnection, this, &FakeWebSocketServer::onNewConnection);
20 connect(_webSocketServer, &QWebSocketServer::closed, this, &FakeWebSocketServer::closed);
21 qCInfo(lcFakeWebSocketServer) << "Open fake websocket server on port:" << port;
22 _processTextMessageSpy = std::make_unique<QSignalSpy>(this, &FakeWebSocketServer::processTextMessage);
23 }
24
~FakeWebSocketServer()25 FakeWebSocketServer::~FakeWebSocketServer()
26 {
27 close();
28 }
29
authenticateAccount(const OCC::AccountPtr account,std::function<void (OCC::PushNotifications * pushNotifications)> beforeAuthentication,std::function<void (void)> afterAuthentication)30 QWebSocket *FakeWebSocketServer::authenticateAccount(const OCC::AccountPtr account, std::function<void(OCC::PushNotifications *pushNotifications)> beforeAuthentication, std::function<void(void)> afterAuthentication)
31 {
32 const auto pushNotifications = account->pushNotifications();
33 Q_ASSERT(pushNotifications);
34 QSignalSpy readySpy(pushNotifications, &OCC::PushNotifications::ready);
35
36 beforeAuthentication(pushNotifications);
37
38 // Wait for authentication
39 if (!waitForTextMessages()) {
40 return nullptr;
41 }
42
43 // Right authentication data should be sent
44 if (textMessagesCount() != 2) {
45 return nullptr;
46 }
47
48 const auto socket = socketForTextMessage(0);
49 const auto userSent = textMessage(0);
50 const auto passwordSent = textMessage(1);
51
52 if (userSent != account->credentials()->user() || passwordSent != account->credentials()->password()) {
53 return nullptr;
54 }
55
56 // Sent authenticated
57 socket->sendTextMessage("authenticated");
58
59 // Wait for ready signal
60 readySpy.wait();
61 if (readySpy.count() != 1 || !account->pushNotifications()->isReady()) {
62 return nullptr;
63 }
64
65 afterAuthentication();
66
67 return socket;
68 }
69
close()70 void FakeWebSocketServer::close()
71 {
72 if (_webSocketServer->isListening()) {
73 qCInfo(lcFakeWebSocketServer) << "Close fake websocket server";
74
75 _webSocketServer->close();
76 qDeleteAll(_clients.begin(), _clients.end());
77 }
78 }
79
processTextMessageInternal(const QString & message)80 void FakeWebSocketServer::processTextMessageInternal(const QString &message)
81 {
82 auto client = qobject_cast<QWebSocket *>(sender());
83 emit processTextMessage(client, message);
84 }
85
onNewConnection()86 void FakeWebSocketServer::onNewConnection()
87 {
88 qCInfo(lcFakeWebSocketServer) << "New connection on fake websocket server";
89
90 auto socket = _webSocketServer->nextPendingConnection();
91
92 connect(socket, &QWebSocket::textMessageReceived, this, &FakeWebSocketServer::processTextMessageInternal);
93 connect(socket, &QWebSocket::disconnected, this, &FakeWebSocketServer::socketDisconnected);
94
95 _clients << socket;
96 }
97
socketDisconnected()98 void FakeWebSocketServer::socketDisconnected()
99 {
100 qCInfo(lcFakeWebSocketServer) << "Socket disconnected";
101
102 auto client = qobject_cast<QWebSocket *>(sender());
103
104 if (client) {
105 _clients.removeAll(client);
106 client->deleteLater();
107 }
108 }
109
waitForTextMessages() const110 bool FakeWebSocketServer::waitForTextMessages() const
111 {
112 return _processTextMessageSpy->wait();
113 }
114
textMessagesCount() const115 uint32_t FakeWebSocketServer::textMessagesCount() const
116 {
117 return _processTextMessageSpy->count();
118 }
119
textMessage(int messageNumber) const120 QString FakeWebSocketServer::textMessage(int messageNumber) const
121 {
122 Q_ASSERT(0 <= messageNumber && messageNumber < _processTextMessageSpy->count());
123 return _processTextMessageSpy->at(messageNumber).at(1).toString();
124 }
125
socketForTextMessage(int messageNumber) const126 QWebSocket *FakeWebSocketServer::socketForTextMessage(int messageNumber) const
127 {
128 Q_ASSERT(0 <= messageNumber && messageNumber < _processTextMessageSpy->count());
129 return _processTextMessageSpy->at(messageNumber).at(0).value<QWebSocket *>();
130 }
131
clearTextMessages()132 void FakeWebSocketServer::clearTextMessages()
133 {
134 _processTextMessageSpy->clear();
135 }
136
createAccount(const QString & username,const QString & password)137 OCC::AccountPtr FakeWebSocketServer::createAccount(const QString &username, const QString &password)
138 {
139 auto account = OCC::Account::create();
140
141 QStringList typeList;
142 typeList.append("files");
143 typeList.append("activities");
144 typeList.append("notifications");
145
146 QString websocketUrl("ws://localhost:12345");
147
148 QVariantMap endpointsMap;
149 endpointsMap["websocket"] = websocketUrl;
150
151 QVariantMap notifyPushMap;
152 notifyPushMap["type"] = typeList;
153 notifyPushMap["endpoints"] = endpointsMap;
154
155 QVariantMap capabilitiesMap;
156 capabilitiesMap["notify_push"] = notifyPushMap;
157
158 account->setCapabilities(capabilitiesMap);
159
160 auto credentials = new CredentialsStub(username, password);
161 account->setCredentials(credentials);
162
163 return account;
164 }
165
CredentialsStub(const QString & user,const QString & password)166 CredentialsStub::CredentialsStub(const QString &user, const QString &password)
167 : _user(user)
168 , _password(password)
169 {
170 }
171
authType() const172 QString CredentialsStub::authType() const
173 {
174 return "";
175 }
176
user() const177 QString CredentialsStub::user() const
178 {
179 return _user;
180 }
181
password() const182 QString CredentialsStub::password() const
183 {
184 return _password;
185 }
186
createQNAM() const187 QNetworkAccessManager *CredentialsStub::createQNAM() const
188 {
189 return nullptr;
190 }
191
ready() const192 bool CredentialsStub::ready() const
193 {
194 return false;
195 }
196
fetchFromKeychain()197 void CredentialsStub::fetchFromKeychain() { }
198
askFromUser()199 void CredentialsStub::askFromUser() { }
200
stillValid(QNetworkReply *)201 bool CredentialsStub::stillValid(QNetworkReply * /*reply*/)
202 {
203 return false;
204 }
205
persist()206 void CredentialsStub::persist() { }
207
invalidateToken()208 void CredentialsStub::invalidateToken() { }
209
forgetSensitiveData()210 void CredentialsStub::forgetSensitiveData() { }
211