diff --git a/libnymea-core/jsonrpc/devicehandler.cpp b/libnymea-core/jsonrpc/devicehandler.cpp index 9bdee4bd..21b8ad28 100644 --- a/libnymea-core/jsonrpc/devicehandler.cpp +++ b/libnymea-core/jsonrpc/devicehandler.cpp @@ -298,6 +298,12 @@ DeviceHandler::DeviceHandler(QObject *parent) : params.insert("device", JsonTypes::deviceRef()); setParams("DeviceChanged", params); + params.clear(); returns.clear(); + setDescription("PluginConfigurationChanged", "Emitted whenever a plugin's configuration is changed."); + params.insert("pluginId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); + params.insert("configuration", QVariantList() << JsonTypes::paramRef()); + setParams("PluginConfigurationChanged", params); + connect(NymeaCore::instance(), &NymeaCore::pluginConfigChanged, this, &DeviceHandler::pluginConfigChanged); connect(NymeaCore::instance(), &NymeaCore::deviceStateChanged, this, &DeviceHandler::deviceStateChanged); connect(NymeaCore::instance(), &NymeaCore::deviceRemoved, this, &DeviceHandler::deviceRemovedNotification); @@ -637,11 +643,11 @@ void DeviceHandler::pluginConfigChanged(const PluginId &id, const ParamList &con { QVariantMap params; params.insert("pluginId", id); - QVariantMap configMap; + QVariantList configList; foreach (const Param ¶m, config) { - configMap.insert(param.paramTypeId().toString(), param.value()); + configList << JsonTypes::packParam(param); } - params.insert("configuration", configMap); + params.insert("configuration", configList); emit PluginConfigurationChanged(params); } diff --git a/libnymea-core/jsonrpc/jsonrpcserver.cpp b/libnymea-core/jsonrpc/jsonrpcserver.cpp index a670638f..9625153b 100644 --- a/libnymea-core/jsonrpc/jsonrpcserver.cpp +++ b/libnymea-core/jsonrpc/jsonrpcserver.cpp @@ -248,6 +248,11 @@ JsonReply *JsonRPCServer::Hello(const QVariantMap ¶ms) qCDebug(dcJsonRpc()) << "Client" << clientId << "initiated handshake." << m_clientLocales.value(clientId); + // If we waited for the handshake, here it is. Remove the timer... + if (m_newConnectionWaitTimers.contains(clientId)) { + delete m_newConnectionWaitTimers.take(clientId); + } + return createReply(createWelcomeMessage(interface, clientId)); } @@ -285,6 +290,7 @@ JsonReply* JsonRPCServer::Version(const QVariantMap ¶ms) const JsonReply* JsonRPCServer::SetNotificationStatus(const QVariantMap ¶ms) { QUuid clientId = this->property("clientId").toUuid(); + Q_ASSERT_X(m_clientNotifications.contains(clientId), "JsonRPCServer", "SetNotificationStatus for an unknown client called"); m_clientNotifications[clientId] = params.value("enabled").toBool(); QVariantMap returns; returns.insert("enabled", m_clientNotifications[clientId]); @@ -585,12 +591,16 @@ void JsonRPCServer::processJsonPacket(TransportInterface *interface, const QUuid if (NymeaCore::instance()->userManager()->initRequired()) { if (!(targetNamespace == "JSONRPC" && authExemptMethodsNoUser.contains(method)) && (token.isEmpty() || !NymeaCore::instance()->userManager()->verifyToken(token))) { sendUnauthorizedResponse(interface, clientId, commandId, "Initial setup required. Call CreateUser first."); + qCWarning(dcJsonRpc()) << "Initial setup required but client does not call the setup. Dropping connection."; + interface->terminateClientConnection(clientId); return; } } else { // ok, we have a user. if there isn't a valid token, let's fail unless this is a Authenticate, Introspect Hello call if (!(targetNamespace == "JSONRPC" && authExemptMethodsWithUser.contains(method)) && (token.isEmpty() || !NymeaCore::instance()->userManager()->verifyToken(token))) { sendUnauthorizedResponse(interface, clientId, commandId, "Forbidden: Invalid token."); + qCWarning(dcJsonRpc()) << "Client did not not present a valid token. Dropping connection."; + interface->terminateClientConnection(clientId); return; } } @@ -622,7 +632,16 @@ void JsonRPCServer::processJsonPacket(TransportInterface *interface, const QUuid qCDebug(dcJsonRpc()) << "Invoking method" << targetNamespace << method.toLatin1().data(); - if (targetNamespace != "JSONRPC" || method != "Hello") { + if (!(targetNamespace == "JSONRPC" && method == "Hello")) { + // This is not the handshake message. If we've waited for it, consider this a protocol violation and drop connection + if (m_newConnectionWaitTimers.contains(clientId)) { + sendErrorResponse(interface, clientId, commandId, "Handshake required. Call JSONRPC.Hello first."); + qCWarning(dcJsonRpc()) << "Connection requires a handshake but client did not initiate handshake. Dropping connection"; + interface->terminateClientConnection(clientId); + return; + } + + // Unless this is the Hello message, which allows setting the locale explicity, attach the locale // for this connection // If the client did request a locale in the Hello message, use that locale @@ -767,7 +786,17 @@ void JsonRPCServer::clientConnected(const QUuid &clientId) // Initialize the connection locale to the settings default m_clientLocales.insert(clientId, NymeaCore::instance()->configuration()->locale()); - interface->sendData(clientId, QJsonDocument::fromVariant(createWelcomeMessage(interface, clientId)).toJson(QJsonDocument::Compact)); + QTimer *timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, [this, timer, clientId, interface](){ + // Client did not initiate handshake within timeout. Drop connection... + m_clientTransports.value(clientId)->disconnect(); + timer->deleteLater(); + m_newConnectionWaitTimers.remove(clientId); + qCDebug(dcJsonRpc()) << "Client" << clientId << "did not initiate the handshake within the required timeout. Dropping connection."; + interface->terminateClientConnection(clientId); + }); + m_newConnectionWaitTimers.insert(clientId, timer); + timer->start(10000); } void JsonRPCServer::clientDisconnected(const QUuid &clientId) @@ -780,6 +809,9 @@ void JsonRPCServer::clientDisconnected(const QUuid &clientId) if (m_pushButtonTransactions.values().contains(clientId)) { NymeaCore::instance()->userManager()->cancelPushButtonAuth(m_pushButtonTransactions.key(clientId)); } + if (m_newConnectionWaitTimers.contains(clientId)) { + delete m_newConnectionWaitTimers.take(clientId); + } } } diff --git a/libnymea-core/jsonrpc/jsonrpcserver.h b/libnymea-core/jsonrpc/jsonrpcserver.h index e85a6da6..0730b3c0 100644 --- a/libnymea-core/jsonrpc/jsonrpcserver.h +++ b/libnymea-core/jsonrpc/jsonrpcserver.h @@ -107,6 +107,7 @@ private: QHash m_clientNotifications; QHash m_clientLocales; QHash m_pushButtonTransactions; + QHash m_newConnectionWaitTimers; QHash m_pairingRequests; diff --git a/libnymea-core/jsonrpc/jsontypes.cpp b/libnymea-core/jsonrpc/jsontypes.cpp index 4b6e66ad..f8b87dc6 100644 --- a/libnymea-core/jsonrpc/jsontypes.cpp +++ b/libnymea-core/jsonrpc/jsontypes.cpp @@ -1372,7 +1372,6 @@ Rule JsonTypes::unpackRule(const QVariantMap &ruleMap) QList eventDescriptors; if (ruleMap.contains("eventDescriptors")) { QVariantList eventDescriptorVariantList = ruleMap.value("eventDescriptors").toList(); - qCDebug(dcJsonRpc) << "unpacking eventDescriptors:" << eventDescriptorVariantList; foreach (const QVariant &eventDescriptorVariant, eventDescriptorVariantList) { eventDescriptors.append(JsonTypes::unpackEventDescriptor(eventDescriptorVariant.toMap())); } diff --git a/libnymea-core/servers/mocktcpserver.cpp b/libnymea-core/servers/mocktcpserver.cpp index 5650034f..68f48901 100644 --- a/libnymea-core/servers/mocktcpserver.cpp +++ b/libnymea-core/servers/mocktcpserver.cpp @@ -33,6 +33,14 @@ MockTcpServer::MockTcpServer(QObject *parent): TransportInterface(ServerConfiguration(), parent) { s_allServers.append(this); + + connect(this, &TransportInterface::clientConnected, this, [this](const QUuid &clientId){ + m_connectedClients.append(clientId); + }); + + connect(this, &TransportInterface::clientDisconnected, this, [this](const QUuid &clientId){ + m_connectedClients.removeAll(clientId); + }); } MockTcpServer::~MockTcpServer() @@ -65,6 +73,7 @@ QList MockTcpServer::servers() void MockTcpServer::injectData(const QUuid &clientId, const QByteArray &data) { + Q_ASSERT_X(m_connectedClients.contains(clientId), "MockTcpServer", "Cannot inject data. Client is not connected"); emit dataAvailable(clientId, data); } diff --git a/libnymea-core/servers/mocktcpserver.h b/libnymea-core/servers/mocktcpserver.h index 47040a87..9253d719 100644 --- a/libnymea-core/servers/mocktcpserver.h +++ b/libnymea-core/servers/mocktcpserver.h @@ -57,6 +57,8 @@ public slots: private: static QList s_allServers; + + QList m_connectedClients; }; #endif // TCPSERVER_H diff --git a/nymea.pro b/nymea.pro index 60ecd7e9..d64e57d0 100644 --- a/nymea.pro +++ b/nymea.pro @@ -15,11 +15,10 @@ doc.commands += cd $$top_srcdir/doc; ./generate-interfaces-qdoc.py; doc.commands += cd $$top_srcdir/doc; ./generate-api-qdoc.py; doc.commands += cd $$top_srcdir/doc; qdoc --highlighting config.qdocconf; cp -r images/* html/images/; \ cp -r favicons/* html/; cp -r $$top_srcdir/doc/html $$top_builddir/ +QMAKE_EXTRA_TARGETS += doc licensecheck.commands = $$top_srcdir/tests/auto/checklicenseheaders.sh $$top_srcdir - -test.depends = licensecheck -test.commands = LD_LIBRARY_PATH=$$top_builddir/libnymea-core:$$top_builddir/libnymea make check +QMAKE_EXTRA_TARGETS += licensecheck # Translations: # make lupdate to update .ts files @@ -42,7 +41,11 @@ translations.path = /usr/share/nymea/translations translations.depends = lrelease INSTALLS += translations -QMAKE_EXTRA_TARGETS += licensecheck doc test lupdate lrelease +QMAKE_EXTRA_TARGETS += lupdate lrelease + +test.depends = licensecheck lrelease +test.commands = LD_LIBRARY_PATH=$$top_builddir/libnymea-core:$$top_builddir/libnymea make check +QMAKE_EXTRA_TARGETS += test # Show doc files in project tree OTHER_FILES += doc/*.qdoc* \ diff --git a/tests/auto/jsonrpc/testjsonrpc.cpp b/tests/auto/jsonrpc/testjsonrpc.cpp index 07c5e4cf..3c4c6f93 100644 --- a/tests/auto/jsonrpc/testjsonrpc.cpp +++ b/tests/auto/jsonrpc/testjsonrpc.cpp @@ -133,25 +133,22 @@ void TestJSONRPC::initTestCase() { NymeaTestBase::initTestCase(); QLoggingCategory::setFilterRules("*.debug=false\n" -// "JsonRpc*.debug=true\n" +// "JsonRpcTraffic.debug=true\n" + "JsonRpc.debug=true\n" "Translations.debug=true\n" "Tests.debug=true"); } void TestJSONRPC::testHandshake() { - // first test if the handshake message is auto-sent upon connecting - QSignalSpy spy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray))); - QUuid newClientId = QUuid::createUuid(); m_mockTcpServer->clientConnected(newClientId); - QVERIFY2(spy.count() > 0, "Did not get the handshake message upon connect."); - QVERIFY2(spy.first().first() == newClientId, "Handshake message addressed at the wrong client."); + qApp->processEvents(); - QJsonDocument jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); - QVariantMap handShake = jsonDoc.toVariant().toMap(); + // Check the Hello reply + QVariantMap handShake = injectAndWait("JSONRPC.Hello", QVariantMap(), newClientId).toMap(); QString nymeaVersionString(NYMEA_VERSION_STRING); - QVERIFY2(handShake.value("version").toString() == nymeaVersionString, "Handshake version doesn't match nymea version."); + QVERIFY2(handShake.value("params").toMap().value("version").toString() == nymeaVersionString, "Handshake version doesn't match nymea version."); // Check whether pushButtonAuth is disabled QCOMPARE(handShake.value("pushButtonAuthAvailable").toBool(), false); @@ -164,11 +161,12 @@ void TestJSONRPC::testHandshake() handShake = injectAndWait("JSONRPC.Hello").toMap(); QCOMPARE(handShake.value("params").toMap().value("version").toString(), nymeaVersionString); - m_mockTcpServer->clientDisconnected(newClientId); - - // Check whether pushButtonAuth is now + // Check whether pushButtonAuth is now enabled QCOMPARE(handShake.value("params").toMap().value("pushButtonAuthAvailable").toBool(), true); + emit m_mockTcpServer->clientDisconnected(newClientId); + + // And now check if it is sent again when calling JSONRPC.Hello handShake = injectAndWait("JSONRPC.Hello").toMap(); QCOMPARE(handShake.value("params").toMap().value("version").toString(), nymeaVersionString); @@ -222,8 +220,11 @@ void TestJSONRPC::testInitialSetup() QSignalSpy spy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray))); QVERIFY(spy.isValid()); + QSignalSpy connectedSpy(m_mockTcpServer, &MockTcpServer::clientConnected); + QSignalSpy disconnectedSpy(m_mockTcpServer, &MockTcpServer::clientDisconnected); // Introspect call should work in any case + qCDebug(dcTests()) << "Calling Introspect, expecting success"; m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Introspect\"}\n"); if (spy.count() == 0) { spy.wait(); @@ -231,12 +232,13 @@ void TestJSONRPC::testInitialSetup() QVERIFY(spy.count() == 1); QJsonDocument jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); QVariantMap response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling introspect on uninitialized instance:" << response.value("status").toString() << response.value("error").toString(); + qCDebug(dcTests()) << "Result:" << response.value("status").toString() << response.value("error").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("success")); // Hello call should work in any case too spy.clear(); + qCDebug(dcTests()) << "Calling Hello, expecting success"; m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Hello\"}\n"); if (spy.count() == 0) { spy.wait(); @@ -244,12 +246,14 @@ void TestJSONRPC::testInitialSetup() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling Hello on uninitialized instance:" << response.value("status").toString() << response.value("error").toString(); + qCDebug(dcTests()) << "Result:" << response.value("status").toString() << response.value("error").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("success")); QCOMPARE(response.value("params").toMap().value("initialSetupRequired").toBool(), true); // Any other call should fail with "unauthorized" even if we use a previously valid token spy.clear(); + disconnectedSpy.clear(); + qCDebug(dcTests()) << "Calling Version, expecting failure (unauthenticated)"; m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"token\": \"" + m_apiToken + "\", \"method\": \"JSONRPC.Version\"}\n"); if (spy.count() == 0) { spy.wait(); @@ -257,13 +261,31 @@ void TestJSONRPC::testInitialSetup() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling Version on uninitialized instance:" << response.value("status").toString() << response.value("error").toString(); + qCDebug(dcTests()) << "Result:" << response.value("status").toString() << response.value("error").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("unauthorized")); + // Connection should terminate + if (disconnectedSpy.count() == 0) disconnectedSpy.wait(); + QCOMPARE(disconnectedSpy.count(), 1); + qCDebug(dcTests()) << "Mock client disconnected"; + connectedSpy.clear(); + emit m_mockTcpServer->clientConnected(m_clientId); + if (connectedSpy.count() == 0) connectedSpy.wait(); + QCOMPARE(connectedSpy.count(), 1); + qCDebug(dcTests()) << "Mock client connected"; + + spy.clear(); + m_mockTcpServer->injectData(m_clientId, "{\"id\": 0, \"method\": \"JSONRPC.Hello\"}"); + if (spy.count() == 0) { + spy.wait(); + } + QVERIFY(spy.count() == 1); + // Except CreateUser // But it should still fail when giving a an invalid username spy.clear(); + qCDebug(dcTests()) << "Calling CreateUser, expecting failure (bad username)"; m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.CreateUser\", \"params\": {\"username\": \"dummy\", \"password\": \"DummyPW1!\"}}\n"); if (spy.count() == 0) { spy.wait(); @@ -271,12 +293,13 @@ void TestJSONRPC::testInitialSetup() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling CreateUser on uninitialized instance with invalid user:" << response.value("status").toString() << response.value("params").toMap().value("error").toString(); + qCDebug(dcTests()) << "Calling CreateUser on uninitialized instance with invalid user:" << response.value("status").toString() << response.value("params").toMap().value("error").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("success")); QCOMPARE(NymeaCore::instance()->userManager()->users().count(), 0); // or when giving a bad password spy.clear(); + qCDebug(dcTests()) << "Calling CreateUser, expecting failure (bad password)"; m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.CreateUser\", \"params\": {\"username\": \"dummy@guh.io\", \"password\": \"weak\"}}\n"); if (spy.count() == 0) { spy.wait(); @@ -284,12 +307,13 @@ void TestJSONRPC::testInitialSetup() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling CreateUser on uninitialized instance with weak password:" << response.value("status").toString() << response.value("params").toMap().value("error").toString(); + qCDebug(dcTests()) << "Calling CreateUser on uninitialized instance with weak password:" << response.value("status").toString() << response.value("params").toMap().value("error").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("success")); QCOMPARE(NymeaCore::instance()->userManager()->users().count(), 0); // Now lets play by the rules (with an uppercase email) spy.clear(); + qCDebug(dcTests()) << "Calling CreateUser, expecting success"; m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.CreateUser\", \"params\": {\"username\": \"Dummy@guh.io\", \"password\": \"DummyPW1!\"}}\n"); if (spy.count() == 0) { spy.wait(); @@ -297,12 +321,13 @@ void TestJSONRPC::testInitialSetup() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling CreateUser on uninitialized instance:" << response.value("status").toString() << response.value("error").toString(); + qCDebug(dcTests) << "Calling CreateUser on uninitialized instance:" << response.value("status").toString() << response.value("error").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("success")); QCOMPARE(NymeaCore::instance()->userManager()->users().count(), 1); // Now that we have a user, initialSetup should be false in the Hello call spy.clear(); + qCDebug(dcTests()) << "Calling Hello, expecting success"; m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Hello\"}\n"); if (spy.count() == 0) { spy.wait(); @@ -310,12 +335,14 @@ void TestJSONRPC::testInitialSetup() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling Hello on initialized instance:" << response.value("status").toString() << response.value("error").toString(); + qCDebug(dcTests) << "Calling Hello on initialized instance:" << response.value("status").toString() << response.value("error").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("success")); QCOMPARE(response.value("params").toMap().value("initialSetupRequired").toBool(), false); // Calls should still fail, given we didn't get a new token yet spy.clear(); + disconnectedSpy.clear(); + qCDebug(dcTests()) << "Calling Version, expecting failure (bad token)"; m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"token\": \"" + m_apiToken + "\", \"method\": \"JSONRPC.Version\"}\n"); if (spy.count() == 0) { spy.wait(); @@ -323,11 +350,30 @@ void TestJSONRPC::testInitialSetup() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling Version with old token:" << response.value("status").toString() << response.value("error").toString(); + qCDebug(dcTests) << "Calling Version with old token:" << response.value("status").toString() << response.value("error").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("unauthorized")); + // Connection should terminate + if (disconnectedSpy.count() == 0) disconnectedSpy.wait(); + QCOMPARE(disconnectedSpy.count(), 1); + qCDebug(dcTests()) << "Mock client disconnected"; + connectedSpy.clear(); + emit m_mockTcpServer->clientConnected(m_clientId); + if (connectedSpy.count() == 0) connectedSpy.wait(); + QCOMPARE(connectedSpy.count(), 1); + qCDebug(dcTests()) << "Mock client connected"; + + spy.clear(); + m_mockTcpServer->injectData(m_clientId, "{\"id\": 0, \"method\": \"JSONRPC.Hello\"}"); + if (spy.count() == 0) { + spy.wait(); + } + QVERIFY(spy.count() == 1); + + // Now lets authenticate with a wrong user spy.clear(); + qCDebug(dcTests()) << "Calling Authenticate, expecting failure (bad user)"; m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Authenticate\", \"params\": {\"username\": \"Dummy@wrong.domain\", \"password\": \"DummyPW1!\", \"deviceName\": \"testcase\"}}\n"); if (spy.count() == 0) { spy.wait(); @@ -335,7 +381,7 @@ void TestJSONRPC::testInitialSetup() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling Authenticate with wrong user:" << response.value("params").toMap().value("success").toString() << response.value("params").toMap().value("token").toString(); + qCDebug(dcTests()) << "Calling Authenticate with wrong user:" << response.value("params").toMap().value("success").toString() << response.value("params").toMap().value("token").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("success")); QCOMPARE(response.value("params").toMap().value("success").toBool(), false); QVERIFY(response.value("params").toMap().value("token").toByteArray().isEmpty()); @@ -343,6 +389,7 @@ void TestJSONRPC::testInitialSetup() // Now lets authenticate with a wrong password spy.clear(); + qCDebug(dcTests()) << "Calling Authenticate, expecting failure (bad password)"; m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Authenticate\", \"params\": {\"username\": \"Dummy@guh.io\", \"password\": \"wrongpw\", \"deviceName\": \"testcase\"}}\n"); if (spy.count() == 0) { spy.wait(); @@ -350,7 +397,7 @@ void TestJSONRPC::testInitialSetup() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling Authenticate with wrong password:" << response.value("params").toMap().value("success").toString() << response.value("params").toMap().value("token").toString(); + qCDebug(dcTests()) << "Calling Authenticate with wrong password:" << response.value("params").toMap().value("success").toString() << response.value("params").toMap().value("token").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("success")); QCOMPARE(response.value("params").toMap().value("success").toBool(), false); QVERIFY(response.value("params").toMap().value("token").toByteArray().isEmpty()); @@ -358,6 +405,7 @@ void TestJSONRPC::testInitialSetup() // Now lets authenticate for real (but intentionally use a lowercase email here, should still work) spy.clear(); + qCDebug(dcTests()) << "Calling Authenticate, expecting success"; m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Authenticate\", \"params\": {\"username\": \"dummy@guh.io\", \"password\": \"DummyPW1!\", \"deviceName\": \"testcase\"}}\n"); if (spy.count() == 0) { spy.wait(); @@ -365,7 +413,7 @@ void TestJSONRPC::testInitialSetup() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling Authenticate with valid credentials:" << response.value("params").toMap().value("success").toString() << response.value("params").toMap().value("token").toString(); + qCDebug(dcTests()) << "Calling Authenticate with valid credentials:" << response.value("params").toMap().value("success").toString() << response.value("params").toMap().value("token").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("success")); QCOMPARE(response.value("params").toMap().value("success").toBool(), true); m_apiToken = response.value("params").toMap().value("token").toByteArray(); @@ -373,6 +421,7 @@ void TestJSONRPC::testInitialSetup() // Now do a Version call with the valid token and it should work spy.clear(); + qCDebug(dcTests()) << "Calling Version, expecting success"; m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"token\": \"" + m_apiToken + "\", \"method\": \"JSONRPC.Version\"}\n"); if (spy.count() == 0) { spy.wait(); @@ -380,15 +429,19 @@ void TestJSONRPC::testInitialSetup() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling Version with valid token:" << response.value("status").toString() << response.value("error").toString(); + qCDebug(dcTests()) << "Calling Version with valid token:" << response.value("status").toString() << response.value("error").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("success")); } void TestJSONRPC::testRevokeToken() { - QSignalSpy spy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray))); + QSignalSpy spy(m_mockTcpServer, &MockTcpServer::outgoingData); QVERIFY(spy.isValid()); + QSignalSpy disconnectedSpy(m_mockTcpServer, &MockTcpServer::clientDisconnected); + QVERIFY(disconnectedSpy.isValid()); + QSignalSpy connectedSpy(m_mockTcpServer, &MockTcpServer::clientConnected); + QVERIFY(connectedSpy.isValid()); // Now get all the tokens spy.clear(); @@ -399,7 +452,7 @@ void TestJSONRPC::testRevokeToken() QVERIFY(spy.count() == 1); QJsonDocument jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); QVariantMap response = jsonDoc.toVariant().toMap(); - qWarning() << "Getting existing Tokens" << response.value("status").toString() << response; + qCDebug(dcTests()) << "Getting existing Tokens" << response.value("status").toString() << response; QCOMPARE(response.value("status").toString(), QStringLiteral("success")); QVariantList tokenList = response.value("params").toMap().value("tokenInfoList").toList(); QCOMPARE(tokenList.count(), 1); @@ -414,7 +467,7 @@ void TestJSONRPC::testRevokeToken() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling Authenticate with valid credentials:" << response.value("params").toMap().value("success").toString() << response.value("params").toMap().value("token").toString(); + qCDebug(dcTests()) << "Calling Authenticate with valid credentials:" << response.value("params").toMap().value("success").toString() << response.value("params").toMap().value("token").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("success")); QCOMPARE(response.value("params").toMap().value("success").toBool(), true); QByteArray newToken = response.value("params").toMap().value("token").toByteArray(); @@ -429,7 +482,7 @@ void TestJSONRPC::testRevokeToken() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling Version with valid token:" << response.value("status").toString() << response.value("error").toString(); + qCDebug(dcTests()) << "Calling Version with valid token:" << response.value("status").toString() << response.value("error").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("success")); // Now get all the tokens using the old token @@ -441,7 +494,7 @@ void TestJSONRPC::testRevokeToken() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling Tokens" << response.value("status").toString(); + qCDebug(dcTests()) << "Calling Tokens" << response.value("status").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("success")); tokenList = response.value("params").toMap().value("tokenInfoList").toList(); QCOMPARE(tokenList.count(), 2); @@ -464,11 +517,12 @@ void TestJSONRPC::testRevokeToken() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling RemoveToken" << response.value("status").toString() << response; + qCDebug(dcTests()) << "Calling RemoveToken" << response.value("status").toString() << response; QCOMPARE(response.value("status").toString(), QStringLiteral("success")); // Do a call with the now removed token, it should be forbidden spy.clear(); + disconnectedSpy.clear(); m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"token\": \"" + newToken + "\", \"method\": \"JSONRPC.Version\"}\n"); if (spy.count() == 0) { spy.wait(); @@ -476,8 +530,19 @@ void TestJSONRPC::testRevokeToken() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling Version with valid token:" << response.value("status").toString() << response.value("error").toString(); + qCDebug(dcTests()) << "Calling Version with valid token:" << response.value("status").toString() << response.value("error").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("unauthorized")); + + // And connection should drop + if (disconnectedSpy.count() == 0) disconnectedSpy.wait(); + QCOMPARE(disconnectedSpy.count(), 1); + + // Connect again to not impact subsequent tests... + connectedSpy.clear(); + emit m_mockTcpServer->clientConnected(m_clientId); + if (connectedSpy.count() == 0) connectedSpy.wait(); + QCOMPARE(connectedSpy.count(), 1); + injectAndWait("JSONRPC.Hello"); } void TestJSONRPC::testBasicCall_data() @@ -492,9 +557,9 @@ void TestJSONRPC::testBasicCall_data() QTest::newRow("missing id") << QByteArray("{\"method\":\"JSONRPC.Introspect\"}\n") << false << false; QTest::newRow("missing method") << QByteArray("{\"id\":42}\n") << true << false; QTest::newRow("borked") << QByteArray("{\"id\":42, \"method\":\"JSO}\n") << false << false; - QTest::newRow("invalid function") << QByteArray("{\"id\":42, \"method\":\"JSONRPC.Foobar\"}\n") << true << false; - QTest::newRow("invalid namespace") << QByteArray("{\"id\":42, \"method\":\"FOO.Introspect\"}\n") << true << false; - QTest::newRow("missing dot") << QByteArray("{\"id\":42, \"method\":\"JSONRPCIntrospect\"}\n") << true << false; + QTest::newRow("invalid function") << QByteArray("{\"id\":42, \"method\":\"JSONRPC.Foobar\", \"token\": \"" + m_apiToken + "\"}\n") << true << false; + QTest::newRow("invalid namespace") << QByteArray("{\"id\":42, \"method\":\"FOO.Introspect\", \"token\": \"" + m_apiToken + "\"}\n") << true << false; + QTest::newRow("missing dot") << QByteArray("{\"id\":42, \"method\":\"JSONRPCIntrospect\", \"token\": \"" + m_apiToken + "\"}\n") << true << false; QTest::newRow("invalid params") << QByteArray("{\"id\":42, \"method\":\"JSONRPC.Introspect\", \"params\":{\"törööö\":\"chooo-chooo\"}}\n") << true << false; } @@ -594,7 +659,7 @@ void TestJSONRPC::deviceAddedRemovedNotifications() params.insert("name", "Mock device"); params.insert("deviceParams", deviceParams); QVariant response = injectAndWait("Devices.AddConfiguredDevice", params); - clientSpy.wait(2000); + if (clientSpy.count() == 0) clientSpy.wait(); verifyDeviceError(response); QVariantMap notificationDeviceMap = checkNotification(clientSpy, "Devices.DeviceAdded").toMap().value("params").toMap().value("device").toMap(); @@ -614,7 +679,7 @@ void TestJSONRPC::deviceAddedRemovedNotifications() params.clear(); response.clear(); clientSpy.clear(); params.insert("deviceId", deviceId); response = injectAndWait("Devices.RemoveConfiguredDevice", params); - clientSpy.wait(2000); + if (clientSpy.count() == 0) clientSpy.wait(); verifyDeviceError(response); checkNotification(clientSpy, "Devices.DeviceRemoved"); @@ -659,7 +724,7 @@ void TestJSONRPC::ruleAddedRemovedNotifications() params.insert("stateEvaluator", stateEvaluator); QVariant response = injectAndWait("Rules.AddRule", params); - clientSpy.wait(2000); + if (clientSpy.count() == 0) clientSpy.wait(); QVariantMap notificationRuleMap = checkNotification(clientSpy, "Rules.RuleAdded").toMap().value("params").toMap().value("rule").toMap(); verifyRuleError(response); @@ -678,7 +743,7 @@ void TestJSONRPC::ruleAddedRemovedNotifications() params.clear(); response.clear(); clientSpy.clear(); params.insert("ruleId", ruleId); response = injectAndWait("Rules.RemoveRule", params); - clientSpy.wait(2000); + if (clientSpy.count() == 0) clientSpy.wait(); checkNotification(clientSpy, "Devices.DeviceRemoved"); verifyRuleError(response); @@ -719,7 +784,7 @@ void TestJSONRPC::ruleActiveChangedNotifications() QSignalSpy clientSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray))); response = injectAndWait("Rules.AddRule", params); - clientSpy.wait(); + if (clientSpy.count() == 0) clientSpy.wait(); QVariant notificationVariant = checkNotification(clientSpy, "Rules.RuleAdded"); verifyRuleError(response); @@ -743,7 +808,7 @@ void TestJSONRPC::ruleActiveChangedNotifications() QNetworkReply *reply = nam.get(request); connect(reply, SIGNAL(finished()), reply, SLOT(deleteLater())); - spy.wait(); + if (spy.count() == 0) spy.wait(); notificationVariant = checkNotification(clientSpy, "Rules.RuleActiveChanged"); verifyRuleError(response); @@ -756,12 +821,12 @@ void TestJSONRPC::ruleActiveChangedNotifications() qDebug() << "setting mock int state to 42"; QNetworkRequest request2(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockIntStateId.toString()).arg(42))); QNetworkReply *reply2 = nam.get(request2); - spy.wait(); + if (spy.count() == 0) spy.wait(); QCOMPARE(spy.count(), 1); connect(reply2, SIGNAL(finished()), reply2, SLOT(deleteLater())); - clientSpy.wait(); + if (clientSpy.count() == 0) clientSpy.wait(); notificationVariant = checkNotification(clientSpy, "Rules.RuleActiveChanged"); verifyRuleError(response); @@ -773,7 +838,7 @@ void TestJSONRPC::ruleActiveChangedNotifications() params.insert("ruleId", ruleId); response = injectAndWait("Rules.RemoveRule", params); - clientSpy.wait(); + if (clientSpy.count() == 0) clientSpy.wait(); notificationVariant = checkNotification(clientSpy, "Rules.RuleRemoved"); checkNotification(clientSpy, "Logging.LogDatabaseUpdated"); verifyRuleError(response); @@ -807,7 +872,7 @@ void TestJSONRPC::deviceChangedNotifications() response = injectAndWait("Devices.AddConfiguredDevice", params); DeviceId deviceId = DeviceId(response.toMap().value("params").toMap().value("deviceId").toString()); QVERIFY(!deviceId.isNull()); - clientSpy.wait(); + if (clientSpy.count() == 0) clientSpy.wait(); verifyDeviceError(response); QVariantMap notificationDeviceMap = checkNotification(clientSpy, "Devices.DeviceAdded").toMap().value("params").toMap().value("device").toMap(); @@ -831,7 +896,7 @@ void TestJSONRPC::deviceChangedNotifications() params.insert("deviceId", deviceId); params.insert("deviceParams", newDeviceParams); response = injectAndWait("Devices.ReconfigureDevice", params); - clientSpy.wait(2000); + if (clientSpy.count() == 0) clientSpy.wait(); verifyDeviceError(response); QVariantMap reconfigureDeviceNotificationMap = checkNotification(clientSpy, "Devices.DeviceChanged").toMap().value("params").toMap().value("device").toMap(); QCOMPARE(reconfigureDeviceNotificationMap.value("deviceClassId").toString(), mockDeviceClassId.toString()); @@ -848,7 +913,7 @@ void TestJSONRPC::deviceChangedNotifications() params.insert("deviceId", deviceId); params.insert("name", deviceName); response = injectAndWait("Devices.EditDevice", params); - clientSpy.wait(2000); + if (clientSpy.count() == 0) clientSpy.wait(); verifyDeviceError(response); QVariantMap editDeviceNotificationMap = checkNotification(clientSpy, "Devices.DeviceChanged").toMap().value("params").toMap().value("device").toMap(); QCOMPARE(editDeviceNotificationMap.value("deviceClassId").toString(), mockDeviceClassId.toString()); @@ -860,7 +925,7 @@ void TestJSONRPC::deviceChangedNotifications() params.clear(); response.clear(); clientSpy.clear(); params.insert("deviceId", deviceId); response = injectAndWait("Devices.RemoveConfiguredDevice", params); - clientSpy.wait(); + if (clientSpy.count() == 0) clientSpy.wait(); verifyDeviceError(response); checkNotification(clientSpy, "Devices.DeviceRemoved"); checkNotification(clientSpy, "Logging.LogDatabaseUpdated"); @@ -882,7 +947,7 @@ void TestJSONRPC::stateChangeEmitsNotifications() QNetworkReply *reply = nam.get(request); connect(reply, SIGNAL(finished()), reply, SLOT(deleteLater())); QSignalSpy replySpy(reply, SIGNAL(finished())); - replySpy.wait(); + if (replySpy.count() == 0) replySpy.wait(); // Make sure the notification contains all the stuff we expect QVariantList stateChangedVariants = checkNotifications(clientSpy, "Devices.StateChanged"); @@ -1001,9 +1066,7 @@ void TestJSONRPC::testPushButtonAuth() pushButtonAgent.sendButtonPressed(); - if (clientSpy.count() == 0) { - clientSpy.wait(); - } + if (clientSpy.count() == 0) clientSpy.wait(); QVariantMap rsp = checkNotification(clientSpy, "JSONRPC.PushButtonAuthFinished").toMap(); QCOMPARE(rsp.value("params").toMap().value("transactionId").toInt(), transactionId); @@ -1023,6 +1086,9 @@ void TestJSONRPC::testPushButtonAuthInterrupt() // Create a new clientId for mallory and connect it to the server QUuid malloryId = QUuid::createUuid(); m_mockTcpServer->clientConnected(malloryId); + QSignalSpy responseSpy(m_mockTcpServer, &MockTcpServer::outgoingData); + m_mockTcpServer->injectData(malloryId, "{\"id\": 0, \"method\": \"JSONRPC.Hello\"}"); + if (responseSpy.count() == 0) responseSpy.wait(); // Snoop in on everything the TCP server sends to its clients. QSignalSpy clientSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray))); @@ -1120,11 +1186,13 @@ void TestJSONRPC::testPushButtonAuthConnectionDrop() pushButtonAgent.init(); // Snoop in on everything the TCP server sends to its clients. - QSignalSpy clientSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray))); + QSignalSpy clientSpy(m_mockTcpServer, &MockTcpServer::outgoingData); // Create a new clientId for alice and connect it to the server QUuid aliceId = QUuid::createUuid(); m_mockTcpServer->clientConnected(aliceId); + m_mockTcpServer->injectData(aliceId, "{\"id\": 0, \"method\": \"JSONRPC.Hello\"}"); + if (clientSpy.count() == 0) clientSpy.wait(); // request push button auth for client 1 (alice) and check for OK reply QVariantMap params; @@ -1139,6 +1207,9 @@ void TestJSONRPC::testPushButtonAuthConnectionDrop() // Create a new clientId for bob and connect it to the server QUuid bobId = QUuid::createUuid(); m_mockTcpServer->clientConnected(bobId); + clientSpy.clear(); + m_mockTcpServer->injectData(bobId, "{\"id\": 0, \"method\": \"JSONRPC.Hello\"}"); + if (clientSpy.count() == 0) clientSpy.wait(); // request push button auth for client 2 (bob) and check for OK reply params.clear(); @@ -1175,7 +1246,7 @@ void TestJSONRPC::testInitialSetupWithPushButtonAuth() NymeaCore::instance()->userManager()->removeUser(""); QVERIFY(NymeaCore::instance()->userManager()->initRequired()); - QSignalSpy spy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray))); + QSignalSpy spy(m_mockTcpServer, &MockTcpServer::outgoingData); QVERIFY(spy.isValid()); PushButtonAgent pushButtonAgent; @@ -1183,6 +1254,7 @@ void TestJSONRPC::testInitialSetupWithPushButtonAuth() // Hello call should work in any case, telling us initial setup is required spy.clear(); + qCDebug(dcTests()) << "Calling Hello on uninitialized instance"; m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Hello\"}\n"); if (spy.count() == 0) { spy.wait(); @@ -1190,13 +1262,16 @@ void TestJSONRPC::testInitialSetupWithPushButtonAuth() QVERIFY(spy.count() == 1); QJsonDocument jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); QVariant response = jsonDoc.toVariant(); - qWarning() << "Calling Hello on uninitialized instance:" << response.toMap().value("status").toString() << response.toMap().value("error").toString(); + qCDebug(dcTests()) << "Result:" << response.toMap().value("status").toString() << response.toMap().value("error").toString(); QCOMPARE(response.toMap().value("status").toString(), QStringLiteral("success")); QCOMPARE(response.toMap().value("params").toMap().value("initialSetupRequired").toBool(), true); // request push button auth for alice and check for OK reply QUuid aliceId = QUuid::createUuid(); m_mockTcpServer->clientConnected(aliceId); + spy.clear(); + m_mockTcpServer->injectData(aliceId, "{\"id\": 0, \"method\": \"JSONRPC.Hello\"}"); + if (spy.count() == 0) spy.wait(); QVariantMap params; params.insert("deviceName", "alice"); @@ -1223,6 +1298,7 @@ void TestJSONRPC::testInitialSetupWithPushButtonAuth() // initialSetupRequired should be false in Hello call now spy.clear(); + qCDebug(dcTests()) << "Calling Hello on uninitialized instance"; m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Hello\"}\n"); if (spy.count() == 0) { spy.wait(); @@ -1230,13 +1306,15 @@ void TestJSONRPC::testInitialSetupWithPushButtonAuth() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant(); - qWarning() << "Calling Hello on uninitialized instance:" << response.toMap().value("status").toString() << response.toMap().value("error").toString(); + qCDebug(dcTests()) << "Result:" << response.toMap().value("status").toString() << response.toMap().value("error").toString(); QCOMPARE(response.toMap().value("status").toString(), QStringLiteral("success")); QCOMPARE(response.toMap().value("params").toMap().value("initialSetupRequired").toBool(), false); // CreateUser without a token should fail now even though there are 0 users in the DB spy.clear(); + QSignalSpy disconnectedSpy(m_mockTcpServer, &MockTcpServer::clientDisconnected); + qCDebug(dcTests()) << "Calling CreateUser on uninitialized instance"; m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.CreateUser\", \"params\": {\"username\": \"Dummy@guh.io\", \"password\": \"DummyPW1!\"}}\n"); if (spy.count() == 0) { spy.wait(); @@ -1244,10 +1322,19 @@ void TestJSONRPC::testInitialSetupWithPushButtonAuth() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant(); - qWarning() << "Calling CreateUser on uninitialized instance:" << response.toMap().value("status").toString() << response.toMap().value("error").toString(); + qCDebug(dcTests()) << "Result:" << response.toMap().value("status").toString() << response.toMap().value("error").toString(); QCOMPARE(response.toMap().value("status").toString(), QStringLiteral("unauthorized")); QCOMPARE(NymeaCore::instance()->userManager()->users().count(), 0); + // Connection should drop + if (disconnectedSpy.isEmpty()) disconnectedSpy.wait(); + QCOMPARE(disconnectedSpy.count(), 1); + + // Reconnect to not impact subsequent tests + m_mockTcpServer->clientConnected(m_clientId); + spy.clear(); + m_mockTcpServer->injectData(m_clientId, "{\"id\": 0, \"method\": \"JSONRPC.Hello\"}"); + if (spy.isEmpty()) spy.wait(); } void TestJSONRPC::testDataFragmentation_data() @@ -1302,12 +1389,10 @@ void TestJSONRPC::testGarbageData() for (int i = 0; i < 1024; i++) { data.append("a"); } - for (int i = 0; i < 11; i ++) { + for (int i = 0; i < 11 && spy.count() == 0; i ++) { m_mockTcpServer->injectData(m_clientId, data); } - QCOMPARE(spy.count(), 1); - } #include "testjsonrpc.moc" diff --git a/tests/auto/nymeatestbase.cpp b/tests/auto/nymeatestbase.cpp index 8064b733..1fbdf188 100644 --- a/tests/auto/nymeatestbase.cpp +++ b/tests/auto/nymeatestbase.cpp @@ -143,9 +143,11 @@ void NymeaTestBase::initTestCase() m_clientId = QUuid::createUuid(); m_mockTcpServer->clientConnected(m_clientId); + QVariant response = injectAndWait("JSONRPC.Hello"); + createMockDevice(); - QVariant response = injectAndWait("Devices.GetConfiguredDevices", {}); + response = injectAndWait("Devices.GetConfiguredDevices", {}); foreach (const QVariant &device, response.toMap().value("params").toMap().value("devices").toList()) { if (device.toMap().value("deviceClassId").toUuid() == mockDeviceAutoClassId) { m_mockDeviceAutoId = DeviceId(device.toMap().value("id").toString()); @@ -442,6 +444,8 @@ void NymeaTestBase::restartServer() coreSpy.wait(); m_mockTcpServer = MockTcpServer::servers().first(); m_mockTcpServer->clientConnected(m_clientId); + + injectAndWait("JSONRPC.Hello"); } void NymeaTestBase::clearLoggingDatabase() diff --git a/tests/auto/websocketserver/testwebsocketserver.cpp b/tests/auto/websocketserver/testwebsocketserver.cpp index 8b773185..3fd3403c 100644 --- a/tests/auto/websocketserver/testwebsocketserver.cpp +++ b/tests/auto/websocketserver/testwebsocketserver.cpp @@ -81,8 +81,14 @@ void TestWebSocketServer::testHandshake() { QWebSocket *socket = new QWebSocket("nymea tests", QWebSocketProtocol::Version13); connect(socket, &QWebSocket::sslErrors, this, &TestWebSocketServer::sslErrors); - QSignalSpy spy(socket, SIGNAL(textMessageReceived(QString))); + + QSignalSpy connectedSpy(socket, &QWebSocket::connected); socket->open(QUrl(QStringLiteral("wss://localhost:4444"))); + connectedSpy.wait(); + + QSignalSpy spy(socket, SIGNAL(textMessageReceived(QString))); + socket->sendTextMessage("{\"id\":0, \"method\": \"JSONRPC.Hello\"}"); + spy.wait(); QVERIFY2(spy.count() > 0, "Did not get the handshake message upon connect."); QJsonDocument jsonDoc = QJsonDocument::fromJson(spy.first().first().toByteArray()); @@ -90,8 +96,8 @@ void TestWebSocketServer::testHandshake() QString nymeaVersionString(NYMEA_VERSION_STRING); QString jsonProtocolVersionString(JSON_PROTOCOL_VERSION); - QCOMPARE(handShake.value("version").toString(), nymeaVersionString); - QCOMPARE(handShake.value("protocol version").toString(), jsonProtocolVersionString); + QCOMPARE(handShake.value("params").toMap().value("version").toString(), nymeaVersionString); + QCOMPARE(handShake.value("params").toMap().value("protocol version").toString(), jsonProtocolVersionString); socket->close(); socket->deleteLater(); @@ -173,6 +179,10 @@ QVariant TestWebSocketServer::injectSocketAndWait(const QString &method, const Q } QSignalSpy spy(socket, SIGNAL(textMessageReceived(QString))); + socket->sendTextMessage("{\"id\":0, \"method\": \"JSONRPC.Hello\"}"); + spy.wait(); + + spy.clear(); socket->sendTextMessage(QString(jsonDoc.toJson(QJsonDocument::Compact))); spy.wait(); @@ -214,6 +224,11 @@ QVariant TestWebSocketServer::injectSocketData(const QByteArray &data) } QSignalSpy spy(socket, SIGNAL(textMessageReceived(QString))); + + socket->sendTextMessage("{\"id\":0, \"method\": \"JSONRPC.Hello\"}"); + spy.wait(); + + spy.clear(); socket->sendTextMessage(QString(data)); spy.wait();