This moves all the things and rules logic away from NymeaCore into their respective modules where it belongs. One major change is the removal of the removePolicy functionality. This was somewhat broken as it was only working for rules but not for all the other modules like scripts, experiences etc. After an attempt to create something that works with all modules it really seemed that this does not make a lot of sence after all, given that updating rules would in most cases leave something very broken behind and removing them was the only sane thing to do. On the other hand, experience plugins may not work well with such a policy eithre as they may require to do their own special thing. So in the end the removePolicy was dropped altogether. Apps should instead figure out themselves what removal of a thing may imply and inform the user about that beforehand.
2403 lines
98 KiB
C++
2403 lines
98 KiB
C++
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
*
|
|
* Copyright 2013 - 2020, nymea GmbH
|
|
* Contact: contact@nymea.io
|
|
*
|
|
* This file is part of nymea.
|
|
* This project including source code and documentation is protected by
|
|
* copyright law, and remains the property of nymea GmbH. All rights, including
|
|
* reproduction, publication, editing and translation, are reserved. The use of
|
|
* this project is subject to the terms of a license agreement to be concluded
|
|
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
|
* under https://nymea.io/license
|
|
*
|
|
* GNU General Public License Usage
|
|
* Alternatively, this project may be redistributed and/or modified under the
|
|
* terms of the GNU General Public License as published by the Free Software
|
|
* Foundation, GNU version 3. This project is distributed in the hope that it
|
|
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
|
* Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with
|
|
* this project. If not, see <https://www.gnu.org/licenses/>.
|
|
*
|
|
* For any further details and any questions please contact us under
|
|
* contact@nymea.io or see our FAQ/Licensing Information on
|
|
* https://nymea.io/license/faq
|
|
*
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
#include "nymeatestbase.h"
|
|
#include "nymeacore.h"
|
|
#include "nymeasettings.h"
|
|
|
|
#include "integrations/thingdiscoveryinfo.h"
|
|
#include "integrations/thingsetupinfo.h"
|
|
|
|
#include "servers/mocktcpserver.h"
|
|
#include "jsonrpc/integrationshandler.h"
|
|
#include "../plugins/mock/extern-plugininfo.h"
|
|
|
|
using namespace nymeaserver;
|
|
|
|
class TestIntegrations : public NymeaTestBase
|
|
{
|
|
Q_OBJECT
|
|
|
|
private:
|
|
ThingId m_mockThingAsyncId;
|
|
|
|
inline void verifyThingError(const QVariant &response, Thing::ThingError error = Thing::ThingErrorNoError) {
|
|
verifyError(response, "thingError", enumValueName(error));
|
|
}
|
|
|
|
private slots:
|
|
|
|
void initTestCase();
|
|
|
|
void getPlugins();
|
|
|
|
void getPluginConfig_data();
|
|
void getPluginConfig();
|
|
|
|
void setPluginConfig_data();
|
|
void setPluginConfig();
|
|
|
|
void getSupportedVendors();
|
|
|
|
void getThingClasses_data();
|
|
void getThingClasses();
|
|
|
|
void verifyInterfaces();
|
|
|
|
void addThing_data();
|
|
void addThing();
|
|
|
|
void thingAddedRemovedNotifications();
|
|
|
|
void thingChangedNotifications();
|
|
|
|
void getThings();
|
|
|
|
void getThing_data();
|
|
void getThing();
|
|
|
|
void storedThings();
|
|
|
|
void stateCache();
|
|
|
|
void discoverThings_data();
|
|
void discoverThings();
|
|
|
|
void addPushButtonThings_data();
|
|
void addPushButtonThings();
|
|
|
|
void addDisplayPinThings_data();
|
|
void addDisplayPinThings();
|
|
|
|
void parentChildThings();
|
|
|
|
void getActionTypes_data();
|
|
void getActionTypes();
|
|
|
|
void getEventTypes_data();
|
|
void getEventTypes();
|
|
|
|
void getStateTypes_data();
|
|
void getStateTypes();
|
|
|
|
void getStateValue_data();
|
|
void getStateValue();
|
|
|
|
void getStateValues_data();
|
|
void getStateValues();
|
|
|
|
void editThings_data();
|
|
void editThings();
|
|
|
|
void testThingSettings();
|
|
|
|
void reconfigureThings_data();
|
|
void reconfigureThings();
|
|
|
|
void reconfigureByDiscovery_data();
|
|
void reconfigureByDiscovery();
|
|
|
|
void reconfigureByDiscoveryAndPair();
|
|
void reconfigureAutoThing();
|
|
|
|
void testBrowsing_data();
|
|
void testBrowsing();
|
|
|
|
void testExecuteBrowserItem_data();
|
|
void testExecuteBrowserItem();
|
|
|
|
void testExecuteBrowserItemAction_data();
|
|
void testExecuteBrowserItemAction();
|
|
|
|
void executeAction_data();
|
|
void executeAction();
|
|
|
|
void triggerEvent();
|
|
void triggerStateChangeSignal();
|
|
|
|
void params();
|
|
|
|
void dynamicMinMax();
|
|
|
|
void asyncSetupEmitsSetupStatusUpdate();
|
|
|
|
void testTranslations();
|
|
|
|
// Keep those at last as they will remove things
|
|
void removeThing_data();
|
|
void removeThing();
|
|
|
|
void removeAutoThing();
|
|
|
|
void discoverThingsParenting();
|
|
};
|
|
|
|
void TestIntegrations::initTestCase()
|
|
{
|
|
NymeaTestBase::initTestCase();
|
|
QLoggingCategory::setFilterRules("*.debug=false\n"
|
|
"Tests.debug=true\n"
|
|
"Mock.debug=true\n"
|
|
"Translations.debug=true\n"
|
|
"PythonIntegrations.debug=true\n"
|
|
);
|
|
|
|
// Adding an async mock to be used in tests below
|
|
QVariantMap params;
|
|
params.insert("thingClassId", mockThingClassId);
|
|
params.insert("name", "Mocked Thing (Async)");
|
|
|
|
QVariantList thingParams;
|
|
|
|
QVariantMap asyncParam;
|
|
asyncParam.insert("paramTypeId", mockThingAsyncParamTypeId);
|
|
asyncParam.insert("value", true);
|
|
thingParams.append(asyncParam);
|
|
|
|
QVariantMap httpParam;
|
|
httpParam.insert("paramTypeId", mockThingHttpportParamTypeId);
|
|
httpParam.insert("value", 8765);
|
|
thingParams.append(httpParam);
|
|
|
|
params.insert("thingParams", thingParams);
|
|
|
|
QVariant response = injectAndWait("Integrations.AddThing", params);
|
|
|
|
m_mockThingAsyncId = ThingId(response.toMap().value("params").toMap().value("thingId").toString());
|
|
QVERIFY2(!m_mockThingAsyncId.isNull(), "Creating an async mock failed");
|
|
|
|
qCDebug(dcTests()) << "Created Async mock with ID" << m_mockThingAsyncId;
|
|
}
|
|
|
|
void TestIntegrations::getPlugins()
|
|
{
|
|
QVariant response = injectAndWait("Integrations.GetPlugins");
|
|
|
|
QVariantList plugins = response.toMap().value("params").toMap().value("plugins").toList();
|
|
|
|
QCOMPARE(plugins.count() > 0, true);
|
|
bool found = false;
|
|
foreach (const QVariant &listEntry, plugins) {
|
|
if (PluginId(listEntry.toMap().value("id").toString()) == mockPluginId) {
|
|
found = true;
|
|
}
|
|
}
|
|
QCOMPARE(found, true);
|
|
}
|
|
|
|
void TestIntegrations::getPluginConfig_data()
|
|
{
|
|
QTest::addColumn<PluginId>("pluginId");
|
|
QTest::addColumn<Thing::ThingError>("error");
|
|
|
|
QTest::newRow("valid plugin") << mockPluginId << Thing::ThingErrorNoError;
|
|
QTest::newRow("invalid plugin") << PluginId::createPluginId() << Thing::ThingErrorPluginNotFound;
|
|
}
|
|
|
|
void TestIntegrations::getPluginConfig()
|
|
{
|
|
QFETCH(PluginId, pluginId);
|
|
QFETCH(Thing::ThingError, error);
|
|
|
|
QVariantMap params;
|
|
params.insert("pluginId", pluginId);
|
|
QVariant response = injectAndWait("Integrations.GetPluginConfiguration", params);
|
|
verifyThingError(response, error);
|
|
}
|
|
|
|
void TestIntegrations::setPluginConfig_data()
|
|
{
|
|
QTest::addColumn<PluginId>("pluginId");
|
|
QTest::addColumn<QVariant>("value");
|
|
QTest::addColumn<Thing::ThingError>("error");
|
|
|
|
QTest::newRow("valid") << mockPluginId << QVariant(13) << Thing::ThingErrorNoError;
|
|
QTest::newRow("invalid plugin") << PluginId::createPluginId() << QVariant(13) << Thing::ThingErrorPluginNotFound;
|
|
QTest::newRow("too big") << mockPluginId << QVariant(130) << Thing::ThingErrorInvalidParameter;
|
|
QTest::newRow("too small") << mockPluginId << QVariant(-13) << Thing::ThingErrorInvalidParameter;
|
|
QTest::newRow("wrong type") << mockPluginId << QVariant("wrontType") << Thing::ThingErrorInvalidParameter;
|
|
}
|
|
|
|
void TestIntegrations::setPluginConfig()
|
|
{
|
|
QFETCH(PluginId, pluginId);
|
|
QFETCH(QVariant, value);
|
|
QFETCH(Thing::ThingError, error);
|
|
|
|
QVariantMap params;
|
|
params.insert("pluginId", pluginId);
|
|
|
|
QVariantList configuration;
|
|
QVariantMap configParam;
|
|
configParam.insert("paramTypeId", mockPluginConfigParamIntParamTypeId);
|
|
configParam.insert("value", value);
|
|
configuration.append(configParam);
|
|
params.insert("configuration", configuration);
|
|
QVariant response = injectAndWait("Integrations.SetPluginConfiguration", params);
|
|
verifyThingError(response, error);
|
|
|
|
if (error == Thing::ThingErrorNoError) {
|
|
params.clear();
|
|
params.insert("pluginId", pluginId);
|
|
response = injectAndWait("Integrations.GetPluginConfiguration", params);
|
|
verifyThingError(response);
|
|
qDebug() << value << response.toMap().value("params").toMap().value("configuration").toList().first();
|
|
QVERIFY2(ParamTypeId(response.toMap().value("params").toMap().value("configuration").toList().first().toMap().value("paramTypeId").toString()) == mockPluginConfigParamIntParamTypeId, "Value not set correctly");
|
|
QVERIFY2(response.toMap().value("params").toMap().value("configuration").toList().first().toMap().value("value") == value, "Value not set correctly");
|
|
}
|
|
}
|
|
|
|
void TestIntegrations::getSupportedVendors()
|
|
{
|
|
QVariant supportedVendors = injectAndWait("Integrations.GetVendors");
|
|
qDebug() << "response" << supportedVendors;
|
|
|
|
// Make sure there is exactly 1 Vendor with nymea's id
|
|
QVariantList vendorList = supportedVendors.toMap().value("params").toMap().value("vendors").toList();
|
|
QCOMPARE(vendorList.count() > 0, true);
|
|
bool found = false;
|
|
foreach (const QVariant &listEntry, vendorList) {
|
|
if (VendorId(listEntry.toMap().value("id").toString()) == nymeaVendorId) {
|
|
found = true;
|
|
}
|
|
}
|
|
QCOMPARE(found, true);
|
|
}
|
|
|
|
void TestIntegrations::getThingClasses_data()
|
|
{
|
|
QTest::addColumn<VendorId>("vendorId");
|
|
QTest::addColumn<QList<ThingClassId>>("thingClassIds");
|
|
QTest::addColumn<int>("resultCount");
|
|
|
|
QTest::newRow("vendor nymea") << nymeaVendorId << QList<ThingClassId>() << 16;
|
|
QTest::newRow("no filter") << VendorId() << QList<ThingClassId>() << 16;
|
|
QTest::newRow("invalid vendor") << VendorId("93e7d361-8025-4354-b17e-b68406c800bc") << QList<ThingClassId>() << 0;
|
|
QTest::newRow("mockThingClassId") << VendorId() << (QList<ThingClassId>() << mockThingClassId) << 1;
|
|
QTest::newRow("invalid thingClassId") << VendorId() << (QList<ThingClassId>() << ThingClassId("6c78ec28-09b6-476d-ac27-1d6966a45c57")) << 0;
|
|
}
|
|
|
|
void TestIntegrations::getThingClasses()
|
|
{
|
|
QFETCH(VendorId, vendorId);
|
|
QFETCH(QList<ThingClassId>, thingClassIds);
|
|
QFETCH(int, resultCount);
|
|
|
|
QVariantMap params;
|
|
if (!vendorId.isNull()) {
|
|
params.insert("vendorId", vendorId);
|
|
}
|
|
if (!thingClassIds.isEmpty()) {
|
|
QStringList thingClassIdStrings;
|
|
foreach (const ThingClassId &id, thingClassIds) {
|
|
thingClassIdStrings.append(id.toString());
|
|
}
|
|
params.insert("thingClassIds", thingClassIdStrings);
|
|
}
|
|
QVariant result = injectAndWait("Integrations.GetThingClasses", params);
|
|
|
|
QVariantList thingClasses = result.toMap().value("params").toMap().value("thingClasses").toList();
|
|
// Make sure there are the right amount of thing classes
|
|
QCOMPARE(thingClasses.count(), resultCount);
|
|
}
|
|
|
|
void TestIntegrations::verifyInterfaces()
|
|
{
|
|
QVariantMap params;
|
|
params.insert("vendorId", nymeaVendorId);
|
|
QVariant result = injectAndWait("Integrations.GetThingClasses", params);
|
|
QVariantList supportedThings = result.toMap().value("params").toMap().value("thingClasses").toList();
|
|
|
|
QVariantMap mock;
|
|
foreach (const QVariant &thingClass, supportedThings) {
|
|
if (thingClass.toMap().value("id").toUuid() == mockThingClassId) {
|
|
mock = thingClass.toMap();
|
|
}
|
|
}
|
|
QVERIFY(!mock.isEmpty());
|
|
|
|
QVariantList interfaces = mock.value("interfaces").toList();
|
|
QVariantList expectedInterfaces = {"system", "light", "power", "battery", "wirelessconnectable", "connectable", "update", "multibutton", "button"};
|
|
qCDebug(dcTests()) << interfaces;
|
|
qCDebug(dcTests()) << expectedInterfaces;
|
|
QCOMPARE(interfaces, expectedInterfaces);
|
|
}
|
|
|
|
void TestIntegrations::addThing_data()
|
|
{
|
|
QTest::addColumn<ThingClassId>("thingClassId");
|
|
QTest::addColumn<QVariantList>("thingParams");
|
|
QTest::addColumn<bool>("jsonValidation");
|
|
QTest::addColumn<Thing::ThingError>("thingError");
|
|
|
|
QVariantMap httpportParam;
|
|
httpportParam.insert("paramTypeId", mockThingHttpportParamTypeId.toString());
|
|
httpportParam.insert("value", m_mockThing1Port - 1);
|
|
QVariantMap asyncParam;
|
|
asyncParam.insert("paramTypeId", mockThingAsyncParamTypeId);
|
|
asyncParam.insert("value", true);
|
|
QVariantMap brokenParam;
|
|
brokenParam.insert("paramTypeId", mockThingBrokenParamTypeId);
|
|
brokenParam.insert("value", true);
|
|
|
|
QVariantList thingParams;
|
|
|
|
thingParams.clear(); thingParams << httpportParam;
|
|
QTest::newRow("User, JustAdd") << mockThingClassId << thingParams << true << Thing::ThingErrorNoError;
|
|
thingParams.clear(); thingParams << httpportParam << asyncParam;
|
|
QTest::newRow("User, JustAdd, Async") << mockThingClassId << thingParams << true << Thing::ThingErrorNoError;
|
|
QTest::newRow("Invalid ThingClassId") << ThingClassId::createThingClassId() << thingParams << true << Thing::ThingErrorThingClassNotFound;
|
|
thingParams.clear(); thingParams << httpportParam << brokenParam;
|
|
QTest::newRow("Setup failure") << mockThingClassId << thingParams << true << Thing::ThingErrorSetupFailed;
|
|
thingParams.clear(); thingParams << httpportParam << asyncParam << brokenParam;
|
|
QTest::newRow("Setup failure, Async") << mockThingClassId << thingParams << true << Thing::ThingErrorSetupFailed;
|
|
|
|
QVariantList invalidThingParams;
|
|
QTest::newRow("User, JustAdd, missing params") << mockThingClassId << invalidThingParams << true << Thing::ThingErrorMissingParameter;
|
|
|
|
QVariantMap fakeparam;
|
|
fakeparam.insert("paramTypeId", ParamTypeId::createParamTypeId());
|
|
invalidThingParams.append(fakeparam);
|
|
QTest::newRow("User, JustAdd, invalid param") << mockThingClassId << invalidThingParams << false << Thing::ThingErrorMissingParameter;
|
|
|
|
QVariantMap fakeparam2;
|
|
fakeparam2.insert("paramTypeId", mockThingHttpportParamTypeId.toString());
|
|
fakeparam2.insert("value", "blabla");
|
|
invalidThingParams.clear();
|
|
invalidThingParams.append(fakeparam2);
|
|
QTest::newRow("User, JustAdd, wrong param") << mockThingClassId << invalidThingParams << true << Thing::ThingErrorInvalidParameter;
|
|
|
|
thingParams.clear(); thingParams << httpportParam << fakeparam;
|
|
QTest::newRow("USer, JustAdd, additional invalid param") << mockThingClassId << thingParams << false << Thing::ThingErrorInvalidParameter;
|
|
|
|
thingParams.clear(); thingParams << httpportParam << fakeparam2;
|
|
QTest::newRow("USer, JustAdd, duplicate param") << mockThingClassId << thingParams << true << Thing::ThingErrorInvalidParameter;
|
|
|
|
}
|
|
|
|
void TestIntegrations::addThing()
|
|
{
|
|
QFETCH(ThingClassId, thingClassId);
|
|
QFETCH(QVariantList, thingParams);
|
|
QFETCH(bool, jsonValidation);
|
|
QFETCH(Thing::ThingError, thingError);
|
|
|
|
QVariantMap params;
|
|
params.insert("thingClassId", thingClassId);
|
|
params.insert("name", "Test Add Thing");
|
|
params.insert("thingParams", thingParams);
|
|
QVariant response = injectAndWait("Integrations.AddThing", params);
|
|
|
|
if (!jsonValidation) {
|
|
QCOMPARE(response.toMap().value("status").toString(), QString("error"));
|
|
return;
|
|
}
|
|
verifyThingError(response, thingError);
|
|
|
|
if (thingError == Thing::ThingErrorNoError) {
|
|
QUuid thingId(response.toMap().value("params").toMap().value("thingId").toString());
|
|
params.clear();
|
|
params.insert("thingId", thingId.toString());
|
|
response = injectAndWait("Integrations.RemoveThing", params);
|
|
verifyThingError(response);
|
|
}
|
|
}
|
|
|
|
void TestIntegrations::thingAddedRemovedNotifications()
|
|
{
|
|
enableNotifications({"Integrations"});
|
|
|
|
// Setup connection to mock client
|
|
QSignalSpy clientSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
|
|
|
|
// add thing and wait for notification
|
|
QVariantList thingParams;
|
|
QVariantMap httpportParam;
|
|
httpportParam.insert("paramTypeId", mockThingHttpportParamTypeId);
|
|
httpportParam.insert("value", 5678);
|
|
thingParams.append(httpportParam);
|
|
|
|
QVariantMap params; clientSpy.clear();
|
|
params.insert("thingClassId", mockThingClassId);
|
|
params.insert("name", "Mocked thing");
|
|
params.insert("thingParams", thingParams);
|
|
QVariant response = injectAndWait("Integrations.AddThing", params);
|
|
if (clientSpy.count() == 0) clientSpy.wait();
|
|
verifyThingError(response);
|
|
QVariantMap notificationThingMap = checkNotification(clientSpy, "Integrations.ThingAdded").toMap().value("params").toMap().value("thing").toMap();
|
|
|
|
ThingId thingId = ThingId(response.toMap().value("params").toMap().value("thingId").toString());
|
|
QVERIFY(!thingId.isNull());
|
|
|
|
// check the ThingAdded notification
|
|
QCOMPARE(notificationThingMap.value("thingClassId").toUuid(), QUuid(mockThingClassId));
|
|
QCOMPARE(notificationThingMap.value("id").toUuid(), QUuid(thingId));
|
|
foreach (const QVariant ¶m, notificationThingMap.value("params").toList()) {
|
|
if (param.toMap().value("name").toString() == "httpport") {
|
|
QCOMPARE(param.toMap().value("value").toInt(), httpportParam.value("value").toInt());
|
|
}
|
|
}
|
|
|
|
// now remove the thong and check the thing removed notification
|
|
params.clear(); response.clear(); clientSpy.clear();
|
|
params.insert("thingId", thingId);
|
|
response = injectAndWait("Integrations.RemoveThing", params);
|
|
if (clientSpy.count() == 0) clientSpy.wait();
|
|
verifyThingError(response);
|
|
checkNotification(clientSpy, "Integrations.ThingRemoved");
|
|
|
|
QCOMPARE(disableNotifications(), true);
|
|
}
|
|
|
|
void TestIntegrations::thingChangedNotifications()
|
|
{
|
|
enableNotifications({"Integrations"});
|
|
|
|
// Setup connection to mock client
|
|
QSignalSpy clientSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
|
|
|
|
// ADD
|
|
// add thing and wait for notification
|
|
QVariantList thingParams;
|
|
QVariantMap httpportParam;
|
|
httpportParam.insert("paramTypeId", mockThingHttpportParamTypeId);
|
|
httpportParam.insert("value", 23234);
|
|
thingParams.append(httpportParam);
|
|
|
|
clientSpy.clear();
|
|
QVariantMap params;
|
|
params.insert("thingClassId", mockThingClassId);
|
|
params.insert("name", "Mock");
|
|
params.insert("thingParams", thingParams);
|
|
QVariant response = injectAndWait("Integrations.AddThing", params);
|
|
ThingId thingId = ThingId(response.toMap().value("params").toMap().value("thingId").toString());
|
|
QVERIFY(!thingId.isNull());
|
|
if (clientSpy.count() == 0) clientSpy.wait();
|
|
verifyThingError(response);
|
|
QVariantMap notificationThingMap = checkNotification(clientSpy, "Integrations.ThingAdded").toMap().value("params").toMap().value("thing").toMap();
|
|
|
|
QCOMPARE(notificationThingMap.value("thingClassId").toUuid(), QUuid(mockThingClassId));
|
|
QCOMPARE(notificationThingMap.value("id").toUuid(), QUuid(thingId));
|
|
foreach (const QVariant ¶m, notificationThingMap.value("params").toList()) {
|
|
if (param.toMap().value("name").toString() == "httpport") {
|
|
QCOMPARE(param.toMap().value("value").toInt(), httpportParam.value("value").toInt());
|
|
}
|
|
}
|
|
|
|
// RECONFIGURE
|
|
// now reconfigure the thing and check the thing changed notification
|
|
QVariantList newThingParams;
|
|
QVariantMap newHttpportParam;
|
|
newHttpportParam.insert("paramTypeId", mockThingHttpportParamTypeId);
|
|
newHttpportParam.insert("value", 45473);
|
|
newThingParams.append(newHttpportParam);
|
|
|
|
params.clear(); response.clear(); clientSpy.clear();
|
|
params.insert("thingId", thingId);
|
|
params.insert("thingParams", newThingParams);
|
|
response = injectAndWait("Integrations.ReconfigureThing", params);
|
|
if (clientSpy.count() == 0) clientSpy.wait();
|
|
verifyThingError(response);
|
|
QVariantMap reconfigureThingNotificationMap = checkNotification(clientSpy, "Integrations.ThingChanged").toMap().value("params").toMap().value("thing").toMap();
|
|
QCOMPARE(reconfigureThingNotificationMap.value("thingClassId").toUuid(), QUuid(mockThingClassId));
|
|
QCOMPARE(reconfigureThingNotificationMap.value("id").toUuid(), QUuid(thingId));
|
|
foreach (const QVariant ¶m, reconfigureThingNotificationMap.value("params").toList()) {
|
|
if (param.toMap().value("name").toString() == "httpport") {
|
|
QCOMPARE(param.toMap().value("value").toInt(), newHttpportParam.value("value").toInt());
|
|
}
|
|
}
|
|
|
|
// EDIT thing name
|
|
QString thingName = "Test thing 1234";
|
|
params.clear(); response.clear(); clientSpy.clear();
|
|
params.insert("thingId", thingId);
|
|
params.insert("name", thingName);
|
|
response = injectAndWait("Integrations.EditThing", params);
|
|
if (clientSpy.count() == 0) clientSpy.wait();
|
|
verifyThingError(response);
|
|
QVariantMap editThingNotificationMap = checkNotification(clientSpy, "Integrations.ThingChanged").toMap().value("params").toMap().value("thing").toMap();
|
|
QCOMPARE(editThingNotificationMap.value("thingClassId").toUuid(), QUuid(mockThingClassId));
|
|
QCOMPARE(editThingNotificationMap.value("id").toUuid(), QUuid(thingId));
|
|
QCOMPARE(editThingNotificationMap.value("name").toString(), thingName);
|
|
|
|
// REMOVE
|
|
// now remove the thing and check the thing removed notification
|
|
params.clear(); response.clear(); clientSpy.clear();
|
|
params.insert("thingId", thingId);
|
|
response = injectAndWait("Integrations.RemoveThing", params);
|
|
if (clientSpy.count() == 0) clientSpy.wait();
|
|
verifyThingError(response);
|
|
checkNotification(clientSpy, "Integrations.ThingRemoved");
|
|
checkNotification(clientSpy, "Logging.LogDatabaseUpdated");
|
|
}
|
|
|
|
void TestIntegrations::getThings()
|
|
{
|
|
QVariant response = injectAndWait("Integrations.GetThings");
|
|
|
|
QVariantList things = response.toMap().value("params").toMap().value("things").toList();
|
|
QCOMPARE(things.count(), 3); // There should be: one auto created mock, one created in NymeaTestBase::initTestcase() and one created in TestIntegrations::initTestCase()
|
|
}
|
|
|
|
void TestIntegrations::getThing_data()
|
|
{
|
|
QTest::addColumn<ThingId>("thingId");
|
|
QTest::addColumn<Thing::ThingError>("expectedError");
|
|
|
|
QTest::newRow("valid thingId") << ThingId(m_mockThingId) << Thing::ThingErrorNoError;
|
|
QTest::newRow("invalid thingId") << ThingId::createThingId() << Thing::ThingErrorThingNotFound;
|
|
}
|
|
|
|
void TestIntegrations::getThing()
|
|
{
|
|
QFETCH(ThingId, thingId);
|
|
QFETCH(Thing::ThingError, expectedError);
|
|
|
|
QVariantMap params;
|
|
params.insert("thingId", thingId);
|
|
QVariant response = injectAndWait("Integrations.GetThings", params);
|
|
|
|
// qCDebug(dcTests()) << qUtf8Printable(QJsonDocument::fromVariant(response).toJson());
|
|
|
|
if (expectedError == Thing::ThingErrorNoError) {
|
|
QVariantList things = response.toMap().value("params").toMap().value("things").toList();
|
|
QCOMPARE(things.count(), 1);
|
|
}
|
|
}
|
|
|
|
void TestIntegrations::storedThings()
|
|
{
|
|
QVariantMap params;
|
|
params.insert("thingClassId", mockThingClassId);
|
|
params.insert("name", "Test stored thing");
|
|
QVariantList thingParams;
|
|
QVariantMap asyncParam;
|
|
asyncParam.insert("paramTypeId", mockThingAsyncParamTypeId);
|
|
asyncParam.insert("value", false);
|
|
thingParams.append(asyncParam);
|
|
QVariantMap brokenParam;
|
|
brokenParam.insert("paramTypeId", mockThingBrokenParamTypeId);
|
|
brokenParam.insert("value", false);
|
|
thingParams.append(brokenParam);
|
|
QVariantMap httpportParam;
|
|
httpportParam.insert("paramTypeId", mockThingHttpportParamTypeId);
|
|
httpportParam.insert("value", 8889);
|
|
thingParams.append(httpportParam);
|
|
params.insert("thingParams", thingParams);
|
|
|
|
QVariant response = injectAndWait("Integrations.AddThing", params);
|
|
verifyThingError(response);
|
|
ThingId addedThingId = ThingId(response.toMap().value("params").toMap().value("thingId").toString());
|
|
QVERIFY(!addedThingId.isNull());
|
|
|
|
clearLoggingDatabase();
|
|
|
|
// Restart the core instance to check if settings are loaded at startup
|
|
restartServer();
|
|
|
|
response = injectAndWait("Integrations.GetThings", QVariantMap());
|
|
|
|
bool found = false;
|
|
foreach (const QVariant &thing, response.toMap().value("params").toMap().value("things").toList()) {
|
|
if (ThingId(thing.toMap().value("id").toString()) == addedThingId) {
|
|
qCDebug(dcTests()) << "found added thing" << thing.toMap().value("params");
|
|
qCDebug(dcTests()) << "expected thingParams:" << thingParams;
|
|
verifyParams(thingParams, thing.toMap().value("params").toList());
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
QVERIFY2(found, "thing missing in config!");
|
|
|
|
// Wait for the DB to sync up and then verify that just restarting did not add state changes for this thing
|
|
waitForDBSync();
|
|
|
|
params.clear();
|
|
params.insert("thingIds", QVariantList() << addedThingId);
|
|
params.insert("loggingSources", QVariantList() << "LoggingSourceStates");
|
|
response = injectAndWait("Logging.GetLogEntries", params);
|
|
QVERIFY2(response.toMap().value("params").toMap().contains("logEntries"), "Huh? GetLogEntries failed!");
|
|
qCDebug(dcTests()) << "log response:" << response.toMap().value("params").toMap().value("logEntries");
|
|
QVERIFY2(response.toMap().value("params").toMap().value("logEntries").toList().isEmpty(), "There are state changed events after a core restart even though states did not change!");
|
|
|
|
params.clear();
|
|
params.insert("thingId", addedThingId);
|
|
response = injectAndWait("Integrations.RemoveThing", params);
|
|
verifyThingError(response);
|
|
}
|
|
|
|
void TestIntegrations::stateCache()
|
|
{
|
|
ThingClass mockThingClass = NymeaCore::instance()->thingManager()->findThingClass(mockThingClassId);
|
|
|
|
QVERIFY2(mockThingClass.getStateType(mockIntStateTypeId).cached(), "Mock int state is not cached (required to be true for this test)");
|
|
QVERIFY2(!mockThingClass.getStateType(mockBoolStateTypeId).cached(), "Mock bool state is cached (required to be false for this test)");
|
|
|
|
Thing* thing = NymeaCore::instance()->thingManager()->findConfiguredThings(mockThingClassId).first();
|
|
int port = thing->paramValue(mockThingHttpportParamTypeId).toInt();
|
|
QNetworkAccessManager nam;
|
|
QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*)));
|
|
|
|
|
|
// First set the state values to something that is *not* the default
|
|
int oldIntValue = mockThingClass.getStateType(mockIntStateTypeId).defaultValue().toInt();
|
|
int newIntValue = oldIntValue + 1;
|
|
bool oldBoolValue = mockThingClass.getStateType(mockBoolStateTypeId).defaultValue().toBool();
|
|
bool newBoolValue = !oldBoolValue;
|
|
|
|
QNetworkRequest request(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(port).arg(mockIntStateTypeId.toString()).arg(newIntValue)));
|
|
QNetworkReply *reply = nam.get(request);
|
|
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
|
spy.wait();
|
|
|
|
spy.clear();
|
|
request = QNetworkRequest(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(port).arg(mockBoolStateTypeId.toString()).arg(newBoolValue)));
|
|
reply = nam.get(request);
|
|
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
|
spy.wait();
|
|
|
|
// For completeness, verify through JSONRPC that they were actually yet.
|
|
QVariantMap params;
|
|
params.insert("thingId", thing->id());
|
|
|
|
params["stateTypeId"] = mockIntStateTypeId;
|
|
QVariant response = injectAndWait("Integrations.GetStateValue", params);
|
|
QCOMPARE(response.toMap().value("params").toMap().value("value").toInt(), newIntValue);
|
|
|
|
params["stateTypeId"] = mockBoolStateTypeId;
|
|
response = injectAndWait("Integrations.GetStateValue", params);
|
|
QCOMPARE(response.toMap().value("params").toMap().value("value").toBool(), newBoolValue);
|
|
|
|
// Restart the server
|
|
restartServer();
|
|
|
|
// And check if the cached int state has successfully been restored
|
|
params["stateTypeId"] = mockIntStateTypeId;
|
|
response = injectAndWait("Integrations.GetStateValue", params);
|
|
QCOMPARE(response.toMap().value("params").toMap().value("value").toInt(), newIntValue);
|
|
|
|
// and that the non-cached bool state is back to its default
|
|
params["stateTypeId"] = mockBoolStateTypeId;
|
|
response = injectAndWait("Integrations.GetStateValue", params);
|
|
QCOMPARE(response.toMap().value("params").toMap().value("value").toBool(), mockThingClass.getStateType(mockBoolStateTypeId).defaultValue().toBool());
|
|
|
|
// Reset back to default values
|
|
spy.clear();
|
|
request = QNetworkRequest(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(port).arg(mockBoolStateTypeId.toString()).arg(oldIntValue)));
|
|
reply = nam.get(request);
|
|
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
|
spy.wait();
|
|
spy.clear();
|
|
request = QNetworkRequest(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(port).arg(mockBoolStateTypeId.toString()).arg(oldBoolValue)));
|
|
reply = nam.get(request);
|
|
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
|
spy.wait();
|
|
}
|
|
|
|
void TestIntegrations::discoverThings_data()
|
|
{
|
|
QTest::addColumn<ThingClassId>("thingClassId");
|
|
QTest::addColumn<int>("resultCount");
|
|
QTest::addColumn<Thing::ThingError>("error");
|
|
QTest::addColumn<QVariantList>("discoveryParams");
|
|
|
|
QVariantList discoveryParams;
|
|
QVariantMap resultCountParam;
|
|
resultCountParam.insert("paramTypeId", mockDiscoveryResultCountParamTypeId);
|
|
resultCountParam.insert("value", 1);
|
|
discoveryParams.append(resultCountParam);
|
|
|
|
QTest::newRow("valid thingClassId") << mockThingClassId << 2 << Thing::ThingErrorNoError << QVariantList();
|
|
QTest::newRow("valid thingClassId with params") << mockThingClassId << 1 << Thing::ThingErrorNoError << discoveryParams;
|
|
QTest::newRow("invalid thingClassId") << ThingClassId::createThingClassId() << 0 << Thing::ThingErrorThingClassNotFound << QVariantList();
|
|
}
|
|
|
|
void TestIntegrations::discoverThings()
|
|
{
|
|
QFETCH(ThingClassId, thingClassId);
|
|
QFETCH(int, resultCount);
|
|
QFETCH(Thing::ThingError, error);
|
|
QFETCH(QVariantList, discoveryParams);
|
|
|
|
QVariantMap params;
|
|
params.insert("thingClassId", thingClassId);
|
|
params.insert("discoveryParams", discoveryParams);
|
|
QVariant response = injectAndWait("Integrations.DiscoverThings", params);
|
|
|
|
verifyThingError(response, error);
|
|
if (error == Thing::ThingErrorNoError) {
|
|
QCOMPARE(response.toMap().value("params").toMap().value("thingDescriptors").toList().count(), resultCount);
|
|
}
|
|
|
|
// If we found something, lets try to add it
|
|
if (error == Thing::ThingErrorNoError) {
|
|
ThingDescriptorId descriptorId = ThingDescriptorId(response.toMap().value("params").toMap().value("thingDescriptors").toList().first().toMap().value("id").toString());
|
|
|
|
params.clear();
|
|
params.insert("thingClassId", thingClassId);
|
|
params.insert("name", "Discoverd mock");
|
|
params.insert("thingDescriptorId", descriptorId.toString());
|
|
response = injectAndWait("Integrations.AddThing", params);
|
|
|
|
verifyThingError(response);
|
|
|
|
ThingId thingId(response.toMap().value("params").toMap().value("thingId").toString());
|
|
params.clear();
|
|
params.insert("thingId", thingId.toString());
|
|
response = injectAndWait("Integrations.RemoveThing", params);
|
|
verifyThingError(response);
|
|
}
|
|
}
|
|
|
|
void TestIntegrations::addPushButtonThings_data()
|
|
{
|
|
QTest::addColumn<ThingClassId>("thingClassId");
|
|
QTest::addColumn<Thing::ThingError>("error");
|
|
QTest::addColumn<bool>("waitForButtonPressed");
|
|
|
|
QTest::newRow("Valid: Add PushButton thing") << pushButtonMockThingClassId << Thing::ThingErrorNoError << true;
|
|
QTest::newRow("Invalid: Add PushButton thing (press to early)") << pushButtonMockThingClassId << Thing::ThingErrorAuthenticationFailure << false;
|
|
}
|
|
|
|
void TestIntegrations::addPushButtonThings()
|
|
{
|
|
QFETCH(ThingClassId, thingClassId);
|
|
QFETCH(Thing::ThingError, error);
|
|
QFETCH(bool, waitForButtonPressed);
|
|
|
|
// Discover things
|
|
QVariantList discoveryParams;
|
|
QVariantMap resultCountParam;
|
|
resultCountParam.insert("paramTypeId", pushButtonMockDiscoveryResultCountParamTypeId);
|
|
resultCountParam.insert("value", 1);
|
|
discoveryParams.append(resultCountParam);
|
|
|
|
QVariantMap params;
|
|
params.insert("thingClassId", thingClassId);
|
|
params.insert("discoveryParams", discoveryParams);
|
|
QVariant response = injectAndWait("Integrations.DiscoverThings", params);
|
|
|
|
verifyThingError(response, Thing::ThingErrorNoError);
|
|
QCOMPARE(response.toMap().value("params").toMap().value("thingDescriptors").toList().count(), 1);
|
|
|
|
|
|
// Pair thing
|
|
ThingDescriptorId descriptorId = ThingDescriptorId(response.toMap().value("params").toMap().value("thingDescriptors").toList().first().toMap().value("id").toString());
|
|
params.clear();
|
|
params.insert("thingClassId", thingClassId);
|
|
params.insert("name", "Pushbutton mock");
|
|
params.insert("thingDescriptorId", descriptorId.toString());
|
|
response = injectAndWait("Integrations.PairThing", params);
|
|
|
|
verifyThingError(response);
|
|
|
|
PairingTransactionId pairingTransactionId(response.toMap().value("params").toMap().value("pairingTransactionId").toString());
|
|
QString displayMessage = response.toMap().value("params").toMap().value("displayMessage").toString();
|
|
|
|
qDebug() << "displayMessage" << displayMessage;
|
|
|
|
if (waitForButtonPressed)
|
|
QTest::qWait(3500);
|
|
|
|
// Confirm pairing
|
|
params.clear();
|
|
params.insert("pairingTransactionId", pairingTransactionId.toString());
|
|
response = injectAndWait("Integrations.ConfirmPairing", params);
|
|
|
|
verifyThingError(response, error);
|
|
|
|
if (error == Thing::ThingErrorNoError) {
|
|
ThingId thingId(response.toMap().value("params").toMap().value("thingId").toString());
|
|
params.clear();
|
|
params.insert("thingId", thingId.toString());
|
|
response = injectAndWait("Integrations.RemoveThing", params);
|
|
verifyThingError(response);
|
|
}
|
|
}
|
|
|
|
void TestIntegrations::addDisplayPinThings_data()
|
|
{
|
|
QTest::addColumn<ThingClassId>("thingClassId");
|
|
QTest::addColumn<Thing::ThingError>("error");
|
|
QTest::addColumn<QString>("secret");
|
|
|
|
QTest::newRow("Valid: Add DisplayPin mock") << displayPinMockThingClassId << Thing::ThingErrorNoError << "243681";
|
|
QTest::newRow("Invalid: Add DisplayPin mock (wrong pin)") << displayPinMockThingClassId << Thing::ThingErrorAuthenticationFailure << "243682";
|
|
}
|
|
|
|
void TestIntegrations::addDisplayPinThings()
|
|
{
|
|
QFETCH(ThingClassId, thingClassId);
|
|
QFETCH(Thing::ThingError, error);
|
|
QFETCH(QString, secret);
|
|
|
|
// Discover things
|
|
QVariantList discoveryParams;
|
|
QVariantMap resultCountParam;
|
|
resultCountParam.insert("paramTypeId", displayPinMockDiscoveryResultCountParamTypeId);
|
|
resultCountParam.insert("value", 1);
|
|
discoveryParams.append(resultCountParam);
|
|
|
|
QVariantMap params;
|
|
params.insert("thingClassId", thingClassId);
|
|
params.insert("discoveryParams", discoveryParams);
|
|
QVariant response = injectAndWait("Integrations.DiscoverThings", params);
|
|
|
|
verifyThingError(response, Thing::ThingErrorNoError);
|
|
QCOMPARE(response.toMap().value("params").toMap().value("thingDescriptors").toList().count(), 1);
|
|
|
|
// Pair thing
|
|
ThingDescriptorId descriptorId = ThingDescriptorId(response.toMap().value("params").toMap().value("thingDescriptors").toList().first().toMap().value("id").toString());
|
|
params.clear();
|
|
params.insert("thingClassId", thingClassId);
|
|
params.insert("name", "Display pin mock");
|
|
params.insert("thingDescriptorId", descriptorId.toString());
|
|
response = injectAndWait("Integrations.PairThing", params);
|
|
|
|
verifyThingError(response);
|
|
|
|
PairingTransactionId pairingTransactionId(response.toMap().value("params").toMap().value("pairingTransactionId").toString());
|
|
QString displayMessage = response.toMap().value("params").toMap().value("displayMessage").toString();
|
|
|
|
qCDebug(dcTests()) << "displayMessage" << displayMessage;
|
|
|
|
params.clear();
|
|
params.insert("pairingTransactionId", pairingTransactionId.toString());
|
|
params.insert("secret", secret);
|
|
response = injectAndWait("Integrations.ConfirmPairing", params);
|
|
|
|
verifyThingError(response, error);
|
|
|
|
if (error == Thing::ThingErrorNoError) {
|
|
ThingId thingId(response.toMap().value("params").toMap().value("thingId").toString());
|
|
params.clear();
|
|
params.insert("thingId", thingId.toString());
|
|
response = injectAndWait("Integrations.RemoveThing", params);
|
|
verifyThingError(response);
|
|
}
|
|
}
|
|
|
|
void TestIntegrations::parentChildThings()
|
|
{
|
|
// add parent
|
|
QVariantMap params;
|
|
params.insert("thingClassId", parentMockThingClassId);
|
|
params.insert("name", "Parent");
|
|
|
|
QSignalSpy thingAddedSpy(NymeaCore::instance()->thingManager(), &ThingManager::thingAdded);
|
|
|
|
QVariant response = injectAndWait("Integrations.AddThing", params);
|
|
verifyThingError(response);
|
|
|
|
ThingId parentId = ThingId(response.toMap().value("params").toMap().value("thingId").toString());
|
|
QVERIFY(!parentId.isNull());
|
|
|
|
thingAddedSpy.wait();
|
|
QCOMPARE(thingAddedSpy.count(), 2);
|
|
|
|
// find child
|
|
response = injectAndWait("Integrations.GetThings");
|
|
|
|
QVariantList things = response.toMap().value("params").toMap().value("things").toList();
|
|
|
|
ThingId childId;
|
|
foreach (const QVariant thingVariant, things) {
|
|
QVariantMap thingMap = thingVariant.toMap();
|
|
|
|
if (thingMap.value("thingClassId").toUuid() == childMockThingClassId) {
|
|
if (thingMap.value("parentId").toUuid() == parentId) {
|
|
childId = ThingId(thingMap.value("id").toString());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
QVERIFY2(!childId.isNull(), QString("Could not find child:\nParent ID:%1\nResponse:%2")
|
|
.arg(parentId.toString())
|
|
.arg(qUtf8Printable(QJsonDocument::fromVariant(response).toJson()))
|
|
.toUtf8());
|
|
|
|
// Try to remove the child
|
|
params.clear();
|
|
params.insert("thingId", childId.toString());
|
|
response = injectAndWait("Integrations.RemoveThing", params);
|
|
verifyThingError(response, Thing::ThingErrorThingIsChild);
|
|
|
|
// check if the child is still there
|
|
response = injectAndWait("Integrations.GetThings");
|
|
things = response.toMap().value("params").toMap().value("things").toList();
|
|
bool found = false;
|
|
foreach (const QVariant thingVariant, things) {
|
|
QVariantMap thingMap = thingVariant.toMap();
|
|
if (thingMap.value("thingClassId").toUuid() == childMockThingClassId) {
|
|
if (thingMap.value("id").toUuid() == childId) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
QVERIFY2(found, "Could not find child.");
|
|
|
|
// remove the parent
|
|
params.clear();
|
|
params.insert("thingId", parentId.toString());
|
|
response = injectAndWait("Integrations.RemoveThing", params);
|
|
verifyThingError(response);
|
|
|
|
// check if the child is still there
|
|
response = injectAndWait("Integrations.GetThings");
|
|
things = response.toMap().value("params").toMap().value("things").toList();
|
|
found = false;
|
|
foreach (const QVariant thingVariant, things) {
|
|
QVariantMap thingMap = thingVariant.toMap();
|
|
if (thingMap.value("thingClassId").toString() == childMockThingClassId.toString()) {
|
|
if (thingMap.value("id") == childId.toString()) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
QVERIFY2(!found, "Could not find child.");
|
|
}
|
|
|
|
void TestIntegrations::getActionTypes_data()
|
|
{
|
|
QTest::addColumn<ThingClassId>("thingClassId");
|
|
QTest::addColumn<QList<ActionTypeId> >("actionTypeTestData");
|
|
|
|
QTest::newRow("valid thingClass") << mockThingClassId
|
|
<< (QList<ActionTypeId>() << mockIntWithLimitsActionTypeId << mockAsyncActionTypeId << mockAsyncFailingActionTypeId << mockFailingActionTypeId << mockWithoutParamsActionTypeId << mockPowerActionTypeId << mockWithoutParamsActionTypeId << mockBatteryLevelActionTypeId << mockSignalStrengthActionTypeId << mockUpdateStatusActionTypeId << mockPerformUpdateActionTypeId << mockPressButtonActionTypeId);
|
|
QTest::newRow("invalid thingClass") << ThingClassId("094f8024-5caa-48c1-ab6a-de486a92088f") << QList<ActionTypeId>();
|
|
}
|
|
|
|
void TestIntegrations::getActionTypes()
|
|
{
|
|
QFETCH(ThingClassId, thingClassId);
|
|
QFETCH(QList<ActionTypeId>, actionTypeTestData);
|
|
|
|
QVariantMap params;
|
|
params.insert("thingClassId", thingClassId);
|
|
QVariant response = injectAndWait("Integrations.GetActionTypes", params);
|
|
|
|
QVariantList actionTypes = response.toMap().value("params").toMap().value("actionTypes").toList();
|
|
QCOMPARE(actionTypes.count(), actionTypeTestData.count());
|
|
foreach (const ActionTypeId &testDataId, actionTypeTestData) {
|
|
bool found = false;
|
|
foreach (const QVariant &at, actionTypes) {
|
|
if (testDataId == at.toMap().value("id").toUuid()) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
QVERIFY(found);
|
|
}
|
|
}
|
|
|
|
void TestIntegrations::getEventTypes_data()
|
|
{
|
|
QTest::addColumn<ThingClassId>("thingClassId");
|
|
QTest::addColumn<int>("resultCount");
|
|
|
|
QTest::newRow("valid thingClass") << mockThingClassId << 3;
|
|
QTest::newRow("invalid thingClass") << ThingClassId("094f8024-5caa-48c1-ab6a-de486a92088f") << 0;
|
|
}
|
|
|
|
void TestIntegrations::getEventTypes()
|
|
{
|
|
QFETCH(ThingClassId, thingClassId);
|
|
QFETCH(int, resultCount);
|
|
|
|
QVariantMap params;
|
|
params.insert("thingClassId", thingClassId);
|
|
QVariant response = injectAndWait("Integrations.GetEventTypes", params);
|
|
|
|
qDebug() << response;
|
|
|
|
QVariantList eventTypes = response.toMap().value("params").toMap().value("eventTypes").toList();
|
|
QCOMPARE(eventTypes.count(), resultCount);
|
|
}
|
|
|
|
void TestIntegrations::getStateTypes_data()
|
|
{
|
|
QTest::addColumn<ThingClassId>("thingClassId");
|
|
QTest::addColumn<int>("resultCount");
|
|
|
|
QTest::newRow("valid thingClass") << mockThingClassId << 12;
|
|
QTest::newRow("invalid thingClass") << ThingClassId("094f8024-5caa-48c1-ab6a-de486a92088f") << 0;
|
|
}
|
|
|
|
void TestIntegrations::getStateTypes()
|
|
{
|
|
QFETCH(ThingClassId, thingClassId);
|
|
QFETCH(int, resultCount);
|
|
|
|
QVariantMap params;
|
|
params.insert("thingClassId", thingClassId);
|
|
QVariant response = injectAndWait("Integrations.GetStateTypes", params);
|
|
|
|
QVariantList stateTypes = response.toMap().value("params").toMap().value("stateTypes").toList();
|
|
QCOMPARE(stateTypes.count(), resultCount);
|
|
if (resultCount > 0) {
|
|
QCOMPARE(stateTypes.first().toMap().value("id").toUuid().toString(), mockIntStateTypeId.toString());
|
|
}
|
|
}
|
|
|
|
void TestIntegrations::getStateValue_data()
|
|
{
|
|
QTest::addColumn<ThingId>("thingId");
|
|
QTest::addColumn<StateTypeId>("stateTypeId");
|
|
QTest::addColumn<Thing::ThingError>("statusCode");
|
|
|
|
QTest::newRow("valid thingId") << ThingId(m_mockThingId) << mockIntStateTypeId << Thing::ThingErrorNoError;
|
|
QTest::newRow("invalid thingId") << ThingId("094f8024-5caa-48c1-ab6a-de486a92088f") << mockIntStateTypeId << Thing::ThingErrorThingNotFound;
|
|
QTest::newRow("invalid statetypeId") << ThingId(m_mockThingId) << StateTypeId("120514f1-343e-4621-9bff-dac616169df9") << Thing::ThingErrorStateTypeNotFound;
|
|
}
|
|
|
|
void TestIntegrations::getStateValue()
|
|
{
|
|
NymeaCore::instance()->thingManager()->findConfiguredThing(m_mockThingId)->setStateValue(mockIntStateTypeId, 10);
|
|
QFETCH(ThingId, thingId);
|
|
QFETCH(StateTypeId, stateTypeId);
|
|
QFETCH(Thing::ThingError, statusCode);
|
|
|
|
QVariantMap params;
|
|
params.insert("thingId", thingId);
|
|
params.insert("stateTypeId", stateTypeId);
|
|
QVariant response = injectAndWait("Integrations.GetStateValue", params);
|
|
|
|
QCOMPARE(response.toMap().value("params").toMap().value("thingError").toString(), enumValueName(statusCode));
|
|
if (statusCode == Thing::ThingErrorNoError) {
|
|
QVariant value = response.toMap().value("params").toMap().value("value");
|
|
QCOMPARE(value.toInt(), 10); // Mock has value 10 by default...
|
|
}
|
|
}
|
|
|
|
void TestIntegrations::getStateValues_data()
|
|
{
|
|
QTest::addColumn<ThingId>("thingId");
|
|
QTest::addColumn<Thing::ThingError>("statusCode");
|
|
|
|
QTest::newRow("valid thingId") << ThingId(m_mockThingId) << Thing::ThingErrorNoError;
|
|
QTest::newRow("invalid thingId") << ThingId("094f8024-5caa-48c1-ab6a-de486a92088f") << Thing::ThingErrorThingNotFound;
|
|
}
|
|
|
|
void TestIntegrations::getStateValues()
|
|
{
|
|
QFETCH(ThingId, thingId);
|
|
QFETCH(Thing::ThingError, statusCode);
|
|
|
|
QVariantMap params;
|
|
params.insert("thingId", thingId);
|
|
QVariant response = injectAndWait("Integrations.GetStateValues", params);
|
|
|
|
QCOMPARE(response.toMap().value("params").toMap().value("thingError").toString(), enumValueName(statusCode));
|
|
if (statusCode == Thing::ThingErrorNoError) {
|
|
QVariantList values = response.toMap().value("params").toMap().value("values").toList();
|
|
QCOMPARE(values.count(), 12); // Mock has 12 states...
|
|
}
|
|
}
|
|
|
|
void TestIntegrations::editThings_data()
|
|
{
|
|
QTest::addColumn<QString>("name");
|
|
|
|
QTest::newRow("change name") << "New name";
|
|
QTest::newRow("change name") << "Foo";
|
|
QTest::newRow("change name") << "Bar";
|
|
}
|
|
|
|
void TestIntegrations::editThings()
|
|
{
|
|
QFETCH(QString, name);
|
|
|
|
QString originalName = "Test thing";
|
|
|
|
// add thing
|
|
QVariantList thingParams;
|
|
QVariantMap httpportParam;
|
|
httpportParam.insert("paramTypeId", mockThingHttpportParamTypeId);
|
|
httpportParam.insert("value", 8889);
|
|
thingParams.append(httpportParam);
|
|
|
|
QVariantMap params;
|
|
params.insert("thingClassId", mockThingClassId);
|
|
params.insert("name", originalName);
|
|
params.insert("thingParams", thingParams);
|
|
QVariant response = injectAndWait("Integrations.AddThing", params);
|
|
verifyThingError(response);
|
|
ThingId thingId = ThingId(response.toMap().value("params").toMap().value("thingId").toString());
|
|
|
|
// edit thing
|
|
params.clear();
|
|
params.insert("thingId", thingId);
|
|
params.insert("name", name);
|
|
|
|
response = injectAndWait("Integrations.EditThing", params);
|
|
verifyThingError(response);
|
|
|
|
// verify changed
|
|
QString newName;
|
|
response = injectAndWait("Integrations.GetThings");
|
|
QVariantList things = response.toMap().value("params").toMap().value("things").toList();
|
|
|
|
foreach (const QVariant &thingVariant, things) {
|
|
QVariantMap thing = thingVariant.toMap();
|
|
if (ThingId(thing.value("id").toString()) == thingId) {
|
|
newName = thing.value("name").toString();
|
|
}
|
|
}
|
|
QCOMPARE(newName, name);
|
|
|
|
restartServer();
|
|
|
|
// check if the changed name is still there after loading
|
|
response = injectAndWait("Integrations.GetThings");
|
|
things = response.toMap().value("params").toMap().value("things").toList();
|
|
foreach (const QVariant &thingVariant, things) {
|
|
QVariantMap thing = thingVariant.toMap();
|
|
if (ThingId(thing.value("id").toString()) == thingId) {
|
|
newName = thing.value("name").toString();
|
|
break;
|
|
}
|
|
}
|
|
QCOMPARE(newName, name);
|
|
|
|
params.clear();
|
|
params.insert("thingId", thingId.toString());
|
|
response = injectAndWait("Integrations.RemoveThing", params);
|
|
verifyThingError(response);
|
|
}
|
|
|
|
void TestIntegrations::testThingSettings()
|
|
{
|
|
// add thing
|
|
QVariantList thingParams;
|
|
QVariantMap httpportParam;
|
|
httpportParam.insert("paramTypeId", mockThingHttpportParamTypeId);
|
|
httpportParam.insert("value", 8889);
|
|
thingParams.append(httpportParam);
|
|
|
|
QVariantMap params;
|
|
params.insert("thingClassId", mockThingClassId);
|
|
params.insert("name", "Mock");
|
|
params.insert("thingParams", thingParams);
|
|
QVariant response = injectAndWait("Integrations.AddThing", params);
|
|
verifyThingError(response);
|
|
ThingId thingId = ThingId(response.toMap().value("params").toMap().value("thingId").toString());
|
|
|
|
// check if default settings are loaded
|
|
params.clear();
|
|
params.insert("thingId", thingId);
|
|
response = injectAndWait("Integrations.GetThings", params);
|
|
QVariantList things = response.toMap().value("params").toMap().value("things").toList();
|
|
QVERIFY2(things.count() == 1, "Error creating thing");
|
|
|
|
QVariantMap thing = things.first().toMap();
|
|
QVERIFY2(ThingId(thing.value("id").toString()) == thingId, "thingId not matching");
|
|
|
|
QVariantList settings = thing.value("settings").toList();
|
|
QCOMPARE(settings.count(), 3);
|
|
|
|
QCOMPARE(settings.first().toMap().value("paramTypeId").toUuid(), QUuid(mockSettingsSetting1ParamTypeId));
|
|
QVERIFY2(settings.first().toMap().value("value").toInt() == 5, "Setting 1 default value not matching");
|
|
|
|
// change a setting
|
|
params.clear();
|
|
params.insert("thingId", thingId);
|
|
settings.clear();
|
|
QVariantMap setting;
|
|
setting.insert("paramTypeId", mockSettingsSetting1ParamTypeId);
|
|
setting.insert("value", 7);
|
|
settings.append(setting);
|
|
params.insert("settings", settings);
|
|
response = injectAndWait("Integrations.SetThingSettings", params);
|
|
|
|
// Check if the change happened
|
|
params.clear();
|
|
params.insert("thingId", thingId);
|
|
response = injectAndWait("Integrations.GetThings", params);
|
|
things = response.toMap().value("params").toMap().value("things").toList();
|
|
QVERIFY2(things.count() == 1, "Error creating thing");
|
|
|
|
thing = things.first().toMap();
|
|
QVERIFY2(ThingId(thing.value("id").toString()) == thingId, "thingId not matching");
|
|
|
|
settings = thing.value("settings").toList();
|
|
QCOMPARE(settings.count(), 3);
|
|
|
|
QCOMPARE(settings.first().toMap().value("paramTypeId").toUuid(), QUuid(mockSettingsSetting1ParamTypeId));
|
|
QVERIFY2(settings.first().toMap().value("value").toInt() == 7, "Setting 1 changed value not matching");
|
|
|
|
restartServer();
|
|
|
|
// Check if the change persisted
|
|
params.clear();
|
|
params.insert("thingId", thingId);
|
|
response = injectAndWait("Integrations.GetThings", params);
|
|
things = response.toMap().value("params").toMap().value("things").toList();
|
|
QVERIFY2(things.count() == 1, "Error creating thing");
|
|
|
|
thing = things.first().toMap();
|
|
QVERIFY2(ThingId(thing.value("id").toString()) == thingId, "thingId not matching");
|
|
|
|
settings = thing.value("settings").toList();
|
|
QCOMPARE(settings.count(), 3);
|
|
|
|
QCOMPARE(settings.first().toMap().value("paramTypeId").toUuid(), QUuid(mockSettingsSetting1ParamTypeId));
|
|
QVERIFY2(settings.first().toMap().value("value").toInt() == 7, "Setting 1 changed value not persisting restart");
|
|
|
|
}
|
|
|
|
void TestIntegrations::reconfigureThings_data()
|
|
{
|
|
QVariantList asyncChangeThingParams;
|
|
QVariantMap asyncParamDifferent;
|
|
asyncParamDifferent.insert("paramTypeId", mockThingAsyncParamTypeId);
|
|
asyncParamDifferent.insert("value", true);
|
|
asyncChangeThingParams.append(asyncParamDifferent);
|
|
|
|
QVariantList httpportChangeThingParams;
|
|
QVariantMap httpportParamDifferent;
|
|
httpportParamDifferent.insert("paramTypeId", mockThingHttpportParamTypeId);
|
|
httpportParamDifferent.insert("value", 8893); // if changing this, change also newPort in reconfigureThings()
|
|
httpportChangeThingParams.append(httpportParamDifferent);
|
|
|
|
QVariantList brokenChangedThingParams;
|
|
QVariantMap brokenParamDifferent;
|
|
brokenParamDifferent.insert("paramTypeId", mockThingBrokenParamTypeId);
|
|
brokenParamDifferent.insert("value", true);
|
|
brokenChangedThingParams.append(brokenParamDifferent);
|
|
|
|
QVariantList asyncAndPortChangeThingParams;
|
|
asyncAndPortChangeThingParams.append(asyncParamDifferent);
|
|
asyncAndPortChangeThingParams.append(httpportParamDifferent);
|
|
|
|
|
|
QVariantList changeAllWritableThingParams;
|
|
changeAllWritableThingParams.append(asyncParamDifferent);
|
|
changeAllWritableThingParams.append(httpportParamDifferent);
|
|
|
|
QTest::addColumn<bool>("broken");
|
|
QTest::addColumn<QVariantList>("newThingParams");
|
|
QTest::addColumn<Thing::ThingError>("thingError");
|
|
|
|
QTest::newRow("valid - change async param") << false << asyncChangeThingParams << Thing::ThingErrorParameterNotWritable;
|
|
QTest::newRow("valid - change httpport param") << false << httpportChangeThingParams << Thing::ThingErrorNoError;
|
|
QTest::newRow("invalid - change httpport and async param") << false << asyncAndPortChangeThingParams << Thing::ThingErrorParameterNotWritable;
|
|
QTest::newRow("invalid - change all params (except broken)") << false << changeAllWritableThingParams << Thing::ThingErrorParameterNotWritable;
|
|
}
|
|
|
|
void TestIntegrations::reconfigureThings()
|
|
{
|
|
QFETCH(bool, broken);
|
|
QFETCH(QVariantList, newThingParams);
|
|
QFETCH(Thing::ThingError, thingError);
|
|
|
|
// add thing
|
|
QVariantMap params;
|
|
params.insert("thingClassId", mockThingClassId);
|
|
params.insert("name", "Thing to edit");
|
|
QVariantList thingParams;
|
|
QVariantMap asyncParam;
|
|
asyncParam.insert("paramTypeId", mockThingAsyncParamTypeId);
|
|
asyncParam.insert("value", false);
|
|
thingParams.append(asyncParam);
|
|
QVariantMap brokenParam;
|
|
brokenParam.insert("paramTypeId", mockThingBrokenParamTypeId);
|
|
brokenParam.insert("value", broken);
|
|
thingParams.append(brokenParam);
|
|
QVariantMap httpportParam;
|
|
httpportParam.insert("paramTypeId", mockThingHttpportParamTypeId);
|
|
httpportParam.insert("value", 8892);
|
|
thingParams.append(httpportParam);
|
|
params.insert("thingParams", thingParams);
|
|
|
|
// add a mock
|
|
QVariant response = injectAndWait("Integrations.AddThing", params);
|
|
verifyThingError(response);
|
|
|
|
ThingId thingId = ThingId(response.toMap().value("params").toMap().value("thingId").toString());
|
|
QVERIFY(!thingId.isNull());
|
|
|
|
// now EDIT the added mock
|
|
response.clear();
|
|
QVariantMap editParams;
|
|
editParams.insert("thingId", thingId);
|
|
editParams.insert("thingParams", newThingParams);
|
|
response = injectAndWait("Integrations.ReconfigureThing", editParams);
|
|
verifyThingError(response, thingError);
|
|
|
|
// if the edit should have been successful
|
|
if (thingError == Thing::ThingErrorNoError) {
|
|
response = injectAndWait("Integrations.GetThings", QVariantMap());
|
|
|
|
bool found = false;
|
|
foreach (const QVariant &thing, response.toMap().value("params").toMap().value("things").toList()) {
|
|
if (ThingId(thing.toMap().value("id").toString()) == thingId) {
|
|
qDebug() << "found added thing" << thing.toMap().value("params");
|
|
qDebug() << "expected thingParams:" << newThingParams;
|
|
// check if the edit was ok
|
|
verifyParams(newThingParams, thing.toMap().value("params").toList(), false);
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
QVERIFY2(found, "Thing missing in config!");
|
|
|
|
// Restart the core instance to check if settings are loaded at startup
|
|
restartServer();
|
|
|
|
response = injectAndWait("Integrations.GetThings", QVariantMap());
|
|
|
|
found = false;
|
|
foreach (const QVariant &thing, response.toMap().value("params").toMap().value("things").toList()) {
|
|
if (ThingId(thing.toMap().value("id").toString()) == thingId) {
|
|
qDebug() << "found added thing" << thing.toMap().value("params");
|
|
qDebug() << "expected params:" << newThingParams;
|
|
// check if the edit was ok
|
|
verifyParams(newThingParams, thing.toMap().value("params").toList(), false);
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
QVERIFY2(found, "Thing missing in config!");
|
|
|
|
// delete it
|
|
params.clear();
|
|
params.insert("thingId", thingId);
|
|
response.clear();
|
|
response = injectAndWait("Integrations.RemoveThing", params);
|
|
verifyThingError(response);
|
|
return;
|
|
} else {
|
|
// The edit was not ok, check if the old params are still there
|
|
response = injectAndWait("Integrations.GetThings", QVariantMap());
|
|
|
|
bool found = false;
|
|
foreach (const QVariant &thing, response.toMap().value("params").toMap().value("things").toList()) {
|
|
if (ThingId(thing.toMap().value("id").toString()) == thingId) {
|
|
qDebug() << "found added thing" << thing.toMap().value("params");
|
|
qDebug() << "expected thingParams:" << newThingParams;
|
|
// check if the params are unchanged
|
|
verifyParams(thingParams, thing.toMap().value("params").toList());
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
QVERIFY2(found, "Thing missing in config!");
|
|
|
|
// Restart the core instance to check if settings are loaded at startup
|
|
restartServer();
|
|
|
|
response = injectAndWait("Integrations.GetThings", QVariantMap());
|
|
|
|
found = false;
|
|
foreach (const QVariant &thing, response.toMap().value("params").toMap().value("things").toList()) {
|
|
if (ThingId(thing.toMap().value("id").toString()) == thingId) {
|
|
qDebug() << "found added thing" << thing.toMap().value("params");
|
|
qDebug() << "expected thingParams:" << newThingParams;
|
|
// check if after the reboot the settings are unchanged
|
|
verifyParams(thingParams, thing.toMap().value("params").toList());
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
QVERIFY2(found, "Thing missing in config!");
|
|
}
|
|
|
|
// delete it
|
|
params.clear();
|
|
params.insert("thingId", thingId);
|
|
response = injectAndWait("Integrations.RemoveThing", params);
|
|
verifyThingError(response);
|
|
}
|
|
|
|
|
|
void TestIntegrations::reconfigureByDiscovery_data()
|
|
{
|
|
QTest::addColumn<ThingClassId>("thingClassId");
|
|
QTest::addColumn<int>("resultCount");
|
|
QTest::addColumn<Thing::ThingError>("error");
|
|
QTest::addColumn<QVariantList>("discoveryParams");
|
|
|
|
QVariantList discoveryParams;
|
|
QVariantMap resultCountParam;
|
|
resultCountParam.insert("paramTypeId", mockDiscoveryResultCountParamTypeId);
|
|
resultCountParam.insert("value", 2);
|
|
discoveryParams.append(resultCountParam);
|
|
|
|
QTest::newRow("discover 2 things with params") << mockThingClassId << 2 << Thing::ThingErrorNoError << discoveryParams;
|
|
}
|
|
|
|
void TestIntegrations::reconfigureByDiscovery()
|
|
{
|
|
QFETCH(ThingClassId, thingClassId);
|
|
QFETCH(int, resultCount);
|
|
QFETCH(Thing::ThingError, error);
|
|
QFETCH(QVariantList, discoveryParams);
|
|
|
|
qCDebug(dcTests()) << "Discovering...";
|
|
QVariantMap params;
|
|
params.insert("thingClassId", thingClassId);
|
|
params.insert("discoveryParams", discoveryParams);
|
|
QVariant response = injectAndWait("Integrations.DiscoverThings", params);
|
|
|
|
verifyThingError(response);
|
|
if (error == Thing::ThingErrorNoError) {
|
|
QCOMPARE(response.toMap().value("params").toMap().value("thingDescriptors").toList().count(), resultCount);
|
|
}
|
|
|
|
// add Discovered Thing 1 port 55555
|
|
QVariantList thingDescriptors = response.toMap().value("params").toMap().value("thingDescriptors").toList();
|
|
|
|
ThingDescriptorId descriptorId;
|
|
foreach (const QVariant &descriptor, thingDescriptors) {
|
|
// find the thing with port 55555
|
|
if (descriptor.toMap().value("description").toString() == "55555") {
|
|
descriptorId = ThingDescriptorId(descriptor.toMap().value("id").toString());
|
|
qDebug() << descriptorId.toString();
|
|
break;
|
|
}
|
|
}
|
|
|
|
QVERIFY(!descriptorId.isNull());
|
|
|
|
qCDebug(dcTests()) << "Adding...";
|
|
|
|
params.clear();
|
|
response.clear();
|
|
params.insert("thingClassId", thingClassId);
|
|
params.insert("name", "Discoverd mock");
|
|
params.insert("thingDescriptorId", descriptorId);
|
|
response = injectAndWait("Integrations.AddThing", params);
|
|
|
|
ThingId thingId(response.toMap().value("params").toMap().value("thingId").toString());
|
|
QVERIFY(!thingId.isNull());
|
|
|
|
// and now rediscover and find the existing thing in the discovery results
|
|
qCDebug(dcTests()) << "Re-Discovering...";
|
|
|
|
params.clear();
|
|
response.clear();
|
|
params.insert("thingClassId", thingClassId);
|
|
params.insert("discoveryParams", discoveryParams);
|
|
response = injectAndWait("Integrations.DiscoverThings", params);
|
|
|
|
verifyThingError(response, error);
|
|
if (error == Thing::ThingErrorNoError) {
|
|
QCOMPARE(response.toMap().value("params").toMap().value("thingDescriptors").toList().count(), resultCount);
|
|
}
|
|
|
|
thingDescriptors = response.toMap().value("params").toMap().value("thingDescriptors").toList();
|
|
|
|
// find the already added thing
|
|
descriptorId = ThingDescriptorId(); // reset it first
|
|
foreach (const QVariant &descriptor, thingDescriptors) {
|
|
if (descriptor.toMap().value("thingId").toUuid().toString() == thingId.toString()) {
|
|
descriptorId = ThingDescriptorId(descriptor.toMap().value("id").toString());
|
|
break;
|
|
}
|
|
}
|
|
QVERIFY2(!descriptorId.isNull(), QString("Tjhing %1 not found in discovery results: %2").arg(thingId.toString()).arg(qUtf8Printable(QJsonDocument::fromVariant(response).toJson())).toUtf8());
|
|
|
|
qCDebug(dcTests()) << "Reconfiguring...";
|
|
|
|
response.clear();
|
|
params.clear();
|
|
params.insert("thingDescriptorId", descriptorId);
|
|
// override port param
|
|
QVariantMap portParam;
|
|
portParam.insert("paramTypeId", mockThingHttpportParamTypeId);
|
|
portParam.insert("value", "55556");
|
|
params.insert("thingParams", QVariantList() << portParam);
|
|
response = injectAndWait("Integrations.ReconfigureThing", params);
|
|
verifyThingError(response, error);
|
|
|
|
response.clear();
|
|
response = injectAndWait("Integrations.GetThings", QVariantMap());
|
|
|
|
QVariantMap thingMap;
|
|
bool found = false;
|
|
foreach (const QVariant &thing, response.toMap().value("params").toMap().value("things").toList()) {
|
|
if (ThingId(thing.toMap().value("id").toString()) == thingId) {
|
|
qDebug() << "found added thing" << thing.toMap().value("params");
|
|
found = true;
|
|
thingMap = thing.toMap();
|
|
break;
|
|
}
|
|
}
|
|
|
|
QVERIFY2(found, "Thing missing in config!");
|
|
QCOMPARE(thingMap.value("id").toUuid(), QUuid(thingId));
|
|
if (thingMap.contains("setupComplete"))
|
|
QVERIFY2(thingMap.value("setupComplete").toBool(), "Setup not completed after edit");
|
|
|
|
// Note: this shows that by discovery a not editable param (name) can be changed!
|
|
foreach (QVariant param, thingMap.value("params").toList()) {
|
|
if (param.toMap().value("paramTypeId") == mockThingHttpportParamTypeId) {
|
|
QCOMPARE(param.toMap().value("value").toInt(), 55556);
|
|
}
|
|
}
|
|
|
|
// check if the daemons are running
|
|
QNetworkAccessManager nam;
|
|
QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*)));
|
|
|
|
// check if old daemon is still running (should not)
|
|
QNetworkRequest request(QUrl(QString("http://localhost:%1").arg(55555)));
|
|
QNetworkReply *reply = nam.get(request);
|
|
spy.wait();
|
|
QVERIFY2(reply->error(), "The old daemon is still running");
|
|
reply->deleteLater();
|
|
|
|
// check if the daemon is really running on the new port
|
|
request = QNetworkRequest(QUrl(QString("http://localhost:%1").arg(55556)));
|
|
reply = nam.get(request);
|
|
spy.wait();
|
|
QVERIFY2(reply->error() == QNetworkReply::NoError, "The new daemon is not running");
|
|
reply->deleteLater();
|
|
|
|
params.clear();
|
|
params.insert("thingId", thingId.toString());
|
|
response = injectAndWait("Integrations.RemoveThing", params);
|
|
verifyThingError(response);
|
|
}
|
|
|
|
void TestIntegrations::reconfigureByDiscoveryAndPair()
|
|
{
|
|
QVariantList discoveryParams;
|
|
QVariantMap resultCountParam;
|
|
resultCountParam.insert("paramTypeId", displayPinMockDiscoveryResultCountParamTypeId);
|
|
resultCountParam.insert("value", 1);
|
|
discoveryParams.append(resultCountParam);
|
|
|
|
qCDebug(dcTests()) << "Discovering things...";
|
|
|
|
QVariantMap params;
|
|
params.insert("thingClassId", displayPinMockThingClassId);
|
|
params.insert("discoveryParams", discoveryParams);
|
|
QVariant response = injectAndWait("Integrations.DiscoverThings", params);
|
|
|
|
verifyThingError(response);
|
|
QVariantList thingDescriptors = response.toMap().value("params").toMap().value("thingDescriptors").toList();
|
|
|
|
qCDebug(dcTests()) << "Discovery result:" << qUtf8Printable(QJsonDocument::fromVariant(thingDescriptors).toJson(QJsonDocument::Indented));
|
|
QCOMPARE(response.toMap().value("params").toMap().value("thingDescriptors").toList().count(), 1);
|
|
|
|
// add Discovered thing 1 port 55555
|
|
|
|
QVariant descriptor = thingDescriptors.first();
|
|
ThingDescriptorId descriptorId = ThingDescriptorId(descriptor.toMap().value("id").toString());
|
|
QVERIFY2(!descriptorId.isNull(), "ThingDescriptorId is Null");
|
|
|
|
qCDebug(dcTests()) << "Pairing descriptorId:" << descriptorId;
|
|
|
|
params.clear();
|
|
response.clear();
|
|
params.insert("thingClassId", displayPinMockThingClassId);
|
|
params.insert("name", "Discoverd mock");
|
|
params.insert("thingDescriptorId", descriptorId);
|
|
response = injectAndWait("Integrations.PairThing", params);
|
|
verifyThingError(response);
|
|
|
|
PairingTransactionId pairingTransactionId = PairingTransactionId(response.toMap().value("params").toMap().value("pairingTransactionId").toString());
|
|
qCDebug(dcTests()) << "PairThing result:" << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented));
|
|
|
|
qCDebug(dcTests()) << "Confirming pairing for transaction ID" << pairingTransactionId;
|
|
params.clear();
|
|
response.clear();
|
|
params.insert("pairingTransactionId", pairingTransactionId.toString());
|
|
params.insert("secret", "243681");
|
|
response = injectAndWait("Integrations.ConfirmPairing", params);
|
|
verifyThingError(response);
|
|
|
|
ThingId thingId(response.toMap().value("params").toMap().value("thingId").toString());
|
|
QVERIFY(!thingId.isNull());
|
|
|
|
qCDebug(dcTests()) << "Discovering again...";
|
|
|
|
// and now rediscover, and edit the first thing with the second
|
|
params.clear();
|
|
response.clear();
|
|
params.insert("thingClassId", displayPinMockThingClassId);
|
|
params.insert("discoveryParams", discoveryParams);
|
|
response = injectAndWait("Integrations.DiscoverThings", params);
|
|
|
|
thingDescriptors = response.toMap().value("params").toMap().value("thingDescriptors").toList();
|
|
qCDebug(dcTests()) << "Discovery result:" << qUtf8Printable(QJsonDocument::fromVariant(thingDescriptors).toJson(QJsonDocument::Indented));
|
|
|
|
verifyThingError(response, Thing::ThingErrorNoError);
|
|
QCOMPARE(thingDescriptors.count(), 1);
|
|
|
|
descriptor = thingDescriptors.first();
|
|
QVERIFY2(ThingId(descriptor.toMap().value("thingId").toString()) == thingId, "thingId not set in descriptor");
|
|
|
|
// get the descriptor again
|
|
descriptorId = ThingDescriptorId(descriptor.toMap().value("id").toString());
|
|
|
|
QVERIFY(!descriptorId.isNull());
|
|
|
|
qDebug() << "Reconfiguring thing by pairing again" << descriptorId;
|
|
|
|
params.clear();
|
|
response.clear();
|
|
params.insert("thingClassId", displayPinMockThingClassId);
|
|
params.insert("name", "Discoverd mock");
|
|
params.insert("thingDescriptorId", descriptorId);
|
|
response = injectAndWait("Integrations.PairThing", params);
|
|
verifyThingError(response);
|
|
|
|
pairingTransactionId = PairingTransactionId(response.toMap().value("params").toMap().value("pairingTransactionId").toString());
|
|
qCDebug(dcTests()) << "PairThing result:" << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented));
|
|
|
|
|
|
qCDebug(dcTests()) << "Confirming pairing for transaction ID" << pairingTransactionId;
|
|
params.clear();
|
|
response.clear();
|
|
params.insert("pairingTransactionId", pairingTransactionId.toString());
|
|
params.insert("secret", "243681");
|
|
response = injectAndWait("Integrations.ConfirmPairing", params);
|
|
verifyThingError(response);
|
|
|
|
thingId = ThingId(response.toMap().value("params").toMap().value("thingId").toString());
|
|
QVERIFY(!thingId.isNull());
|
|
|
|
}
|
|
|
|
void TestIntegrations::reconfigureAutoThing()
|
|
{
|
|
qCDebug(dcTests()) << "Reconfigure auto thing";
|
|
|
|
// Get the auto mock
|
|
QList<Thing*> things = NymeaCore::instance()->thingManager()->findConfiguredThings(autoMockThingClassId);
|
|
QVERIFY2(things.count() > 0, "There needs to be at least one auto-created Mock for this test");
|
|
|
|
// Get current auto mock infos
|
|
Thing *currentThing = things.first();
|
|
ThingId thingId = currentThing->id();
|
|
int currentPort = currentThing->paramValue(autoMockThingHttpportParamTypeId).toInt();
|
|
|
|
// Trigger reconfigure signal in mock
|
|
QNetworkAccessManager *nam = new QNetworkAccessManager(this);
|
|
QSignalSpy spy(nam, &QNetworkAccessManager::finished);
|
|
QNetworkReply *reply = nam->get(QNetworkRequest(QUrl(QString("http://localhost:%1/reconfigureautodevice").arg(currentPort))));
|
|
spy.wait();
|
|
QCOMPARE(spy.count(), 1);
|
|
reply->deleteLater();
|
|
|
|
Thing *thing = NymeaCore::instance()->thingManager()->findConfiguredThing(thingId);
|
|
QVERIFY(thing);
|
|
int newPort = thing->paramValue(autoMockThingHttpportParamTypeId).toInt();
|
|
// Note: reconfigure auto mock increases the http port by 1
|
|
QCOMPARE(newPort, currentPort + 1);
|
|
}
|
|
|
|
|
|
void TestIntegrations::removeThing_data()
|
|
{
|
|
QTest::addColumn<ThingId>("thingId");
|
|
QTest::addColumn<Thing::ThingError>("thingError");
|
|
|
|
QTest::newRow("Existing thing") << ThingId(m_mockThingId) << Thing::ThingErrorNoError;
|
|
QTest::newRow("Not existing thing") << ThingId::createThingId() << Thing::ThingErrorThingNotFound;
|
|
// QTest::newRow("Auto device") << m_mockThingAutoId << Thing::ThingErrorCreationMethodNotSupported;
|
|
}
|
|
|
|
void TestIntegrations::removeThing()
|
|
{
|
|
QFETCH(ThingId, thingId);
|
|
QFETCH(Thing::ThingError, thingError);
|
|
|
|
NymeaSettings settings(NymeaSettings::SettingsRoleThings);
|
|
settings.beginGroup("ThingConfig");
|
|
if (thingError == Thing::ThingErrorNoError) {
|
|
settings.beginGroup(m_mockThingId.toString());
|
|
// Make sure we have some config values for this device
|
|
QVERIFY(settings.allKeys().count() > 0);
|
|
}
|
|
|
|
QVariantMap params;
|
|
params.insert("thingId", thingId);
|
|
|
|
QVariant response = injectAndWait("Integrations.RemoveThing", params);
|
|
|
|
verifyThingError(response, thingError);
|
|
|
|
if (Thing::ThingErrorNoError) {
|
|
// Make sure the device is gone from settings too
|
|
QCOMPARE(settings.allKeys().count(), 0);
|
|
}
|
|
}
|
|
|
|
void TestIntegrations::removeAutoThing()
|
|
{
|
|
// Setup connection to mock client
|
|
QNetworkAccessManager *nam = new QNetworkAccessManager(this);
|
|
QSignalSpy spy(nam, SIGNAL(finished(QNetworkReply*)));
|
|
|
|
// First try to make a manually created device disappear. It must not go away
|
|
|
|
QList<Thing*> things = NymeaCore::instance()->thingManager()->findConfiguredThings(mockThingClassId);
|
|
int oldCount = things.count();
|
|
QVERIFY2(oldCount > 0, "There needs to be at least one configured Mock Device for this test");
|
|
Thing *thing = things.first();
|
|
|
|
// trigger disappear signal in mock device
|
|
int port = thing->paramValue(autoMockThingHttpportParamTypeId).toInt();
|
|
QNetworkRequest request(QUrl(QString("http://localhost:%1/disappear").arg(port)));
|
|
QNetworkReply *reply = nam->get(request);
|
|
spy.wait();
|
|
QCOMPARE(spy.count(), 1);
|
|
reply->deleteLater();
|
|
QVERIFY2(NymeaCore::instance()->thingManager()->findConfiguredThings(mockThingClassId).count() == oldCount, "Mocked thing has disappeared even though it shouldn't");
|
|
|
|
// Ok, now do the same with an autocreated one. It should go away
|
|
|
|
things = NymeaCore::instance()->thingManager()->findConfiguredThings(autoMockThingClassId);
|
|
oldCount = things.count();
|
|
QVERIFY2(oldCount > 0, "There needs to be at least one auto-created Mock Device for this test");
|
|
thing = things.first();
|
|
|
|
// trigger disappear signal in mock device
|
|
spy.clear();
|
|
port = thing->paramValue(autoMockThingHttpportParamTypeId).toInt();
|
|
request.setUrl(QUrl(QString("http://localhost:%1/disappear").arg(port)));
|
|
reply = nam->get(request);
|
|
|
|
spy.wait();
|
|
QCOMPARE(spy.count(), 1);
|
|
reply->deleteLater();
|
|
|
|
// Make sure one mock device has disappeared
|
|
QCOMPARE(NymeaCore::instance()->thingManager()->findConfiguredThings(autoMockThingClassId).count(), oldCount - 1);
|
|
}
|
|
|
|
void TestIntegrations::testBrowsing_data()
|
|
{
|
|
QTest::addColumn<ThingId>("thingId");
|
|
|
|
QTest::newRow("regular mock") << ThingId(m_mockThingId);
|
|
QTest::newRow("async mock") << ThingId(m_mockThingAsyncId);
|
|
}
|
|
|
|
void TestIntegrations::testBrowsing()
|
|
{
|
|
QFETCH(ThingId, thingId);
|
|
|
|
// Check if mockdevice is browsable
|
|
QVariant response = injectAndWait("Integrations.GetThingClasses");
|
|
|
|
QVariantMap mockThingClass;
|
|
foreach (const QVariant &thingClassVariant, response.toMap().value("params").toMap().value("thingClasses").toList()) {
|
|
if (ThingClassId(thingClassVariant.toMap().value("id").toString()) == mockThingClassId) {
|
|
mockThingClass = thingClassVariant.toMap();
|
|
}
|
|
}
|
|
|
|
QVERIFY2(ThingClassId(mockThingClass.value("id").toString()) == mockThingClassId, "Could not find mock device");
|
|
QCOMPARE(mockThingClass.value("browsable").toBool(), true);
|
|
|
|
|
|
// Browse it
|
|
QVariantMap params;
|
|
params.insert("thingId", thingId);
|
|
response = injectAndWait("Integrations.BrowseThing", params);
|
|
QCOMPARE(response.toMap().value("params").toMap().value("thingError").toString(), QString("ThingErrorNoError"));
|
|
QVariantList browserEntries = response.toMap().value("params").toMap().value("items").toList();
|
|
QVERIFY2(browserEntries.count() > 0, "BrowseThing did not return any items.");
|
|
|
|
// Browse item 001, it should be a folder with 2 items
|
|
params.insert("itemId", "001");
|
|
response = injectAndWait("Integrations.BrowseThing", params);
|
|
QCOMPARE(response.toMap().value("params").toMap().value("thingError").toString(), QString("ThingErrorNoError"));
|
|
browserEntries = response.toMap().value("params").toMap().value("items").toList();
|
|
QVERIFY2(browserEntries.count() == 2, "BrowseThing did not return 2 items as childs in folder with id 001.");
|
|
|
|
// Browse a non-existent item
|
|
params["itemId"] = "this-does-not-exist";
|
|
response = injectAndWait("Integrations.BrowseThing", params);
|
|
browserEntries = response.toMap().value("params").toMap().value("items").toList();
|
|
QCOMPARE(response.toMap().value("params").toMap().value("thingError").toString(), QString("ThingErrorItemNotFound"));
|
|
QCOMPARE(browserEntries.count(), 0);
|
|
|
|
|
|
}
|
|
|
|
void TestIntegrations::discoverThingsParenting()
|
|
{
|
|
// Try to discover a mock child device. We don't have a mockParent yet, so it should fail
|
|
ThingDiscoveryInfo *discoveryInfo = NymeaCore::instance()->thingManager()->discoverThings(childMockThingClassId, ParamList());
|
|
{
|
|
QSignalSpy spy(discoveryInfo, &ThingDiscoveryInfo::finished);
|
|
spy.wait();
|
|
}
|
|
QVERIFY(discoveryInfo->thingDescriptors().count() == 0);
|
|
|
|
|
|
// Now create a mock parent by discovering...
|
|
discoveryInfo = NymeaCore::instance()->thingManager()->discoverThings(parentMockThingClassId, ParamList());
|
|
{
|
|
QSignalSpy spy(discoveryInfo, &ThingDiscoveryInfo::finished);
|
|
spy.wait();
|
|
}
|
|
QVERIFY(discoveryInfo->thingDescriptors().count() == 1);
|
|
ThingDescriptorId descriptorId = discoveryInfo->thingDescriptors().first().id();
|
|
|
|
QSignalSpy addSpy(NymeaCore::instance()->thingManager(), &ThingManager::thingAdded);
|
|
ThingSetupInfo *setupInfo = NymeaCore::instance()->thingManager()->addConfiguredThing(descriptorId, ParamList(), "Mock Parent (Discovered)");
|
|
{
|
|
QSignalSpy spy(setupInfo, &ThingSetupInfo::finished);
|
|
spy.wait();
|
|
}
|
|
QCOMPARE(setupInfo->status(), Thing::ThingErrorNoError);
|
|
|
|
addSpy.wait();
|
|
QCOMPARE(addSpy.count(), 2); // Mock parent will also auto-create a child instantly
|
|
|
|
Thing *parentThing = addSpy.at(0).first().value<Thing*>();
|
|
qCDebug(dcTests()) << "Added parent:" << parentThing->name();
|
|
QVERIFY(parentThing->thingClassId() == parentMockThingClassId);
|
|
|
|
|
|
// Ok we have our parent device, let's discover for childs again
|
|
discoveryInfo = NymeaCore::instance()->thingManager()->discoverThings(childMockThingClassId, ParamList());
|
|
{
|
|
QSignalSpy spy(discoveryInfo, &ThingDiscoveryInfo::finished);
|
|
spy.wait();
|
|
}
|
|
QVERIFY(discoveryInfo->thingDescriptors().count() == 1);
|
|
descriptorId = discoveryInfo->thingDescriptors().first().id();
|
|
|
|
// Found one! Adding it...
|
|
addSpy.clear();
|
|
setupInfo = NymeaCore::instance()->thingManager()->addConfiguredThing(descriptorId, ParamList(), "Mock Child (Discovered)");
|
|
{
|
|
QSignalSpy spy(setupInfo, &ThingSetupInfo::finished);
|
|
spy.wait();
|
|
}
|
|
QCOMPARE(setupInfo->status(), Thing::ThingErrorNoError);
|
|
|
|
QCOMPARE(addSpy.count(), 1);
|
|
|
|
Thing *childThing = addSpy.at(0).first().value<Thing*>();
|
|
qCDebug(dcTests()) << "Added child:" << childThing->name();
|
|
QVERIFY(childThing->thingClassId() == childMockThingClassId);
|
|
|
|
// Now delete the parent and make sure the child will be deleted too
|
|
QSignalSpy removeSpy(NymeaCore::instance()->thingManager(), &ThingManager::thingRemoved);
|
|
Thing::ThingError ret = NymeaCore::instance()->thingManager()->removeConfiguredThing(parentThing->id());
|
|
QCOMPARE(ret, Thing::ThingErrorNoError);
|
|
QCOMPARE(removeSpy.count(), 3); // The parent, the auto-mock and the discovered mock
|
|
|
|
}
|
|
|
|
void TestIntegrations::testExecuteBrowserItem_data()
|
|
{
|
|
QTest::addColumn<ThingId>("thingId");
|
|
QTest::addColumn<QString>("itemId");
|
|
QTest::addColumn<QString>("thingError");
|
|
|
|
QTest::newRow("regular mock - good item") << ThingId(m_mockThingId) << "002" << "ThingErrorNoError";
|
|
QTest::newRow("regular mock - bad item") << ThingId(m_mockThingId) << "001" << "ThingErrorItemNotExecutable";
|
|
QTest::newRow("async mock - good item") << ThingId(m_mockThingAsyncId) << "002" << "ThingErrorNoError";
|
|
}
|
|
|
|
void TestIntegrations::testExecuteBrowserItem()
|
|
{
|
|
QFETCH(ThingId, thingId);
|
|
QFETCH(QString, itemId);
|
|
QFETCH(QString, thingError);
|
|
|
|
QVariantMap params;
|
|
params.insert("thingId", thingId);
|
|
params.insert("itemId", itemId);
|
|
QVariant response = injectAndWait("Integrations.ExecuteBrowserItem", params);
|
|
qCDebug(dcTests()) << "resp" << response;
|
|
|
|
QCOMPARE(response.toMap().value("params").toMap().value("thingError").toString(), thingError);
|
|
}
|
|
|
|
void TestIntegrations::testExecuteBrowserItemAction_data()
|
|
{
|
|
QTest::addColumn<ThingId>("thingId");
|
|
|
|
QTest::newRow("regular mock") << ThingId(m_mockThingId);
|
|
QTest::newRow("async mock") << ThingId(m_mockThingAsyncId);
|
|
}
|
|
|
|
void TestIntegrations::testExecuteBrowserItemAction()
|
|
{
|
|
QFETCH(ThingId, thingId);
|
|
|
|
QVariantMap getItemsParams;
|
|
getItemsParams.insert("thingId", thingId);
|
|
QVariant response = injectAndWait("Integrations.BrowseThing", getItemsParams);
|
|
QCOMPARE(response.toMap().value("status").toString(), QString("success"));
|
|
|
|
QVariantList browserEntries = response.toMap().value("params").toMap().value("items").toList();
|
|
QVERIFY(browserEntries.count() > 2);
|
|
|
|
QVariantMap item002; // Find the item we need for this test
|
|
foreach (const QVariant &item, browserEntries) {
|
|
if (item.toMap().value("id").toString() == "002") {
|
|
item002 = item.toMap();
|
|
break;
|
|
}
|
|
}
|
|
QVERIFY2(item002.value("id").toString() == QString("002"), "Item with context actions not found");
|
|
QVERIFY2(item002.value("actionTypeIds").toList().count() > 0, "Item doesn't have actionTypeIds");
|
|
QVERIFY2(ActionTypeId(item002.value("actionTypeIds").toList().first().toString()) == mockAddToFavoritesBrowserItemActionTypeId, "AddToFavorites action type id not found in item");
|
|
|
|
|
|
// Browse favorites
|
|
// ID is "favorites" in mock
|
|
// It should be ampty at this point
|
|
getItemsParams.insert("itemId", "favorites");
|
|
response = injectAndWait("Integrations.BrowseThing", getItemsParams);
|
|
QCOMPARE(response.toMap().value("status").toString(), QString("success"));
|
|
|
|
browserEntries = response.toMap().value("params").toMap().value("items").toList();
|
|
QVERIFY2(browserEntries.count() == 0, "Favorites should be empty at this point");
|
|
|
|
// Now add an item to the favorites
|
|
QVariantMap actionParams;
|
|
actionParams.insert("thingId", thingId);
|
|
actionParams.insert("itemId", "002");
|
|
actionParams.insert("actionTypeId", mockAddToFavoritesBrowserItemActionTypeId);
|
|
response = injectAndWait("Integrations.ExecuteBrowserItemAction", actionParams);
|
|
QCOMPARE(response.toMap().value("status").toString(), QString("success"));
|
|
QCOMPARE(response.toMap().value("params").toMap().value("thingError").toString(), QString("ThingErrorNoError"));
|
|
|
|
// Fetch the list again
|
|
response = injectAndWait("Integrations.BrowseThing", getItemsParams);
|
|
QCOMPARE(response.toMap().value("status").toString(), QString("success"));
|
|
|
|
browserEntries = response.toMap().value("params").toMap().value("items").toList();
|
|
QCOMPARE(browserEntries.count(), 1);
|
|
|
|
QString favoriteItemId = browserEntries.first().toMap().value("id").toString();
|
|
QVERIFY2(!favoriteItemId.isEmpty(), "ItemId is empty in favorites list");
|
|
|
|
// Now remove the again from favorites
|
|
actionParams.clear();
|
|
actionParams.insert("thingId", thingId);
|
|
actionParams.insert("itemId", favoriteItemId);
|
|
actionParams.insert("actionTypeId", mockRemoveFromFavoritesBrowserItemActionTypeId);
|
|
response = injectAndWait("Integrations.ExecuteBrowserItemAction", actionParams);
|
|
QCOMPARE(response.toMap().value("status").toString(), QString("success"));
|
|
QCOMPARE(response.toMap().value("params").toMap().value("thingError").toString(), QString("ThingErrorNoError"));
|
|
|
|
// Fetch the list again
|
|
response = injectAndWait("Integrations.BrowseThing", getItemsParams);
|
|
QCOMPARE(response.toMap().value("status").toString(), QString("success"));
|
|
|
|
browserEntries = response.toMap().value("params").toMap().value("items").toList();
|
|
QCOMPARE(browserEntries.count(), 0);
|
|
|
|
}
|
|
|
|
void TestIntegrations::executeAction_data()
|
|
{
|
|
QTest::addColumn<ThingId>("thingId");
|
|
QTest::addColumn<ActionTypeId>("actionTypeId");
|
|
QTest::addColumn<QVariantList>("actionParams");
|
|
QTest::addColumn<Thing::ThingError>("error");
|
|
|
|
QVariantList params;
|
|
QVariantMap param1;
|
|
param1.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId);
|
|
param1.insert("value", 5);
|
|
params.append(param1);
|
|
QVariantMap param2;
|
|
param2.insert("paramTypeId", mockWithParamsActionParam2ParamTypeId);
|
|
param2.insert("value", true);
|
|
params.append(param2);
|
|
|
|
QTest::newRow("valid action") << ThingId(m_mockThingId) << mockWithParamsActionTypeId << params << Thing::ThingErrorNoError;
|
|
QTest::newRow("invalid thingId") << ThingId::createThingId() << mockWithParamsActionTypeId << params << Thing::ThingErrorThingNotFound;
|
|
QTest::newRow("invalid actionTypeId") << ThingId(m_mockThingId) << ActionTypeId::createActionTypeId() << params << Thing::ThingErrorActionTypeNotFound;
|
|
QTest::newRow("missing params") << ThingId(m_mockThingId) << mockWithParamsActionTypeId << QVariantList() << Thing::ThingErrorMissingParameter;
|
|
QTest::newRow("async action") << ThingId(m_mockThingId) << mockAsyncActionTypeId << QVariantList() << Thing::ThingErrorNoError;
|
|
QTest::newRow("broken action") << ThingId(m_mockThingId) << mockFailingActionTypeId << QVariantList() << Thing::ThingErrorSetupFailed;
|
|
QTest::newRow("async broken action") << ThingId(m_mockThingId) << mockAsyncFailingActionTypeId << QVariantList() << Thing::ThingErrorSetupFailed;
|
|
}
|
|
|
|
void TestIntegrations::executeAction()
|
|
{
|
|
QFETCH(ThingId, thingId);
|
|
QFETCH(ActionTypeId, actionTypeId);
|
|
QFETCH(QVariantList, actionParams);
|
|
QFETCH(Thing::ThingError, error);
|
|
|
|
QVariantMap params;
|
|
params.insert("actionTypeId", actionTypeId);
|
|
params.insert("thingId", thingId);
|
|
params.insert("params", actionParams);
|
|
QVariant response = injectAndWait("Integrations.ExecuteAction", params);
|
|
qDebug() << "executeActionresponse" << response;
|
|
verifyError(response, "thingError", enumValueName(error));
|
|
|
|
// Fetch action execution history from mock device
|
|
QNetworkAccessManager nam;
|
|
QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*)));
|
|
|
|
QNetworkRequest request(QUrl(QString("http://localhost:%1/actionhistory").arg(m_mockThing1Port)));
|
|
QNetworkReply *reply = nam.get(request);
|
|
spy.wait();
|
|
QCOMPARE(spy.count(), 1);
|
|
reply->deleteLater();
|
|
QByteArray data = reply->readAll();
|
|
|
|
if (error == Thing::ThingErrorNoError) {
|
|
QVERIFY2(actionTypeId == ActionTypeId(data), QString("ActionTypeId mismatch. Got %1, Expected: %2")
|
|
.arg(ActionTypeId(data).toString()).arg(actionTypeId.toString()).toLatin1().data());
|
|
} else {
|
|
QVERIFY2(data.length() == 0, QString("Data is %1, should be empty.").arg(QString(data)).toLatin1().data());
|
|
}
|
|
|
|
// cleanup for the next run
|
|
spy.clear();
|
|
request.setUrl(QUrl(QString("http://localhost:%1/clearactionhistory").arg(m_mockThing1Port)));
|
|
reply = nam.get(request);
|
|
spy.wait();
|
|
QCOMPARE(spy.count(), 1);
|
|
reply->deleteLater();
|
|
|
|
spy.clear();
|
|
request.setUrl(QUrl(QString("http://localhost:%1/actionhistory").arg(m_mockThing1Port)));
|
|
reply = nam.get(request);
|
|
spy.wait();
|
|
QCOMPARE(spy.count(), 1);
|
|
reply->deleteLater();
|
|
data = reply->readAll();
|
|
qDebug() << "cleared data:" << data;
|
|
|
|
}
|
|
|
|
void TestIntegrations::triggerEvent()
|
|
{
|
|
enableNotifications({"Integrations"});
|
|
QList<Thing*> things = NymeaCore::instance()->thingManager()->findConfiguredThings(mockThingClassId);
|
|
QVERIFY2(things.count() > 0, "There needs to be at least one configured Mock Device for this test");
|
|
Thing *thing = things.first();
|
|
|
|
QSignalSpy spy(NymeaCore::instance()->thingManager(), &ThingManager::eventTriggered);
|
|
QSignalSpy notificationSpy(m_mockTcpServer, &MockTcpServer::outgoingData);
|
|
|
|
// Setup connection to mock client
|
|
QNetworkAccessManager nam;
|
|
|
|
// trigger event in mock device
|
|
int port = thing->paramValue(mockThingHttpportParamTypeId).toInt();
|
|
QNetworkRequest request(QUrl(QString("http://localhost:%1/generateevent?eventtypeid=%2").arg(port).arg(mockEvent1EventTypeId.toString())));
|
|
QNetworkReply *reply = nam.get(request);
|
|
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
|
|
|
// Lets wait for the notification
|
|
spy.wait();
|
|
QVERIFY(spy.count() > 0);
|
|
for (int i = 0; i < spy.count(); i++ ){
|
|
Event event = spy.at(i).at(0).value<Event>();
|
|
if (event.thingId() == thing->id()) {
|
|
// Make sure the event contains all the stuff we expect
|
|
QCOMPARE(event.eventTypeId(), mockEvent1EventTypeId);
|
|
}
|
|
}
|
|
|
|
// Check for the notification on JSON API
|
|
QVariantList notifications;
|
|
notifications = checkNotifications(notificationSpy, "Integrations.EventTriggered");
|
|
QVERIFY2(notifications.count() == 1, "Should get Integrations.EventTriggered notification");
|
|
QVariantMap notificationContent = notifications.first().toMap().value("params").toMap();
|
|
|
|
QCOMPARE(notificationContent.value("event").toMap().value("thingId").toUuid().toString(), thing->id().toString());
|
|
QCOMPARE(notificationContent.value("event").toMap().value("eventTypeId").toUuid().toString(), mockEvent1EventTypeId.toString());
|
|
}
|
|
|
|
void TestIntegrations::triggerStateChangeSignal()
|
|
{
|
|
enableNotifications({"Integrations"});
|
|
|
|
QList<Thing*> things = NymeaCore::instance()->thingManager()->findConfiguredThings(mockThingClassId);
|
|
QVERIFY2(things.count() > 0, "There needs to be at least one configured Mock for this test");
|
|
Thing *thing = things.first();
|
|
|
|
QSignalSpy spy(NymeaCore::instance()->thingManager(), &ThingManager::thingStateChanged);
|
|
QSignalSpy notificationSpy(m_mockTcpServer, &MockTcpServer::outgoingData);
|
|
|
|
// Setup connection to mock client
|
|
QNetworkAccessManager nam;
|
|
|
|
// trigger state changed event in mock device
|
|
int port = thing->paramValue(mockThingHttpportParamTypeId).toInt();
|
|
QNetworkRequest request(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(port).arg(mockIntStateTypeId.toString()).arg(37)));
|
|
QNetworkReply *reply = nam.get(request);
|
|
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
|
|
|
// Lets wait for the notification
|
|
spy.wait();
|
|
QVERIFY(spy.count() == 1);
|
|
Thing *t = spy.at(0).at(0).value<Thing*>();
|
|
QCOMPARE(t->id(), thing->id());
|
|
StateTypeId stId = spy.at(0).at(1).value<StateTypeId>();
|
|
QCOMPARE(stId, mockIntStateTypeId);
|
|
QVariant value = spy.at(0).at(2);
|
|
QCOMPARE(value.toInt(), 37);
|
|
|
|
// Check for the notification on JSON API
|
|
QVariantList notifications;
|
|
notifications = checkNotifications(notificationSpy, "Integrations.StateChanged");
|
|
QVERIFY2(notifications.count() == 1, "Should get Integrations.StateChanged notification");
|
|
QVariantMap notificationContent = notifications.first().toMap().value("params").toMap();
|
|
|
|
QCOMPARE(notificationContent.value("thingId").toUuid().toString(), thing->id().toString());
|
|
QCOMPARE(notificationContent.value("stateTypeId").toUuid().toString(), mockIntStateTypeId.toString());
|
|
QCOMPARE(notificationContent.value("value").toInt(), 37);
|
|
}
|
|
|
|
void TestIntegrations::params()
|
|
{
|
|
Event event;
|
|
ParamList params;
|
|
ParamTypeId id = ParamTypeId::createParamTypeId();
|
|
Param p(id, "foo bar");
|
|
params.append(p);
|
|
event.setParams(params);
|
|
|
|
QVERIFY(event.param(id).value().toString() == "foo bar");
|
|
QVERIFY(!event.param(ParamTypeId::createParamTypeId()).value().isValid());
|
|
}
|
|
|
|
void TestIntegrations::dynamicMinMax()
|
|
{
|
|
enableNotifications({"Integrations"});
|
|
|
|
QList<Thing*> things = NymeaCore::instance()->thingManager()->findConfiguredThings(mockThingClassId);
|
|
QVERIFY2(things.count() > 0, "There needs to be at least one configured Mock for this test");
|
|
Thing *thing = things.first();
|
|
|
|
QSignalSpy notificationSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
|
|
|
|
// Setup connection to mock client
|
|
QNetworkAccessManager nam;
|
|
|
|
// trigger state changed event in mock device
|
|
qCDebug(dcTests()) << "Changing state in mock thing to 11";
|
|
int port = thing->paramValue(mockThingHttpportParamTypeId).toInt();
|
|
QNetworkRequest request(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(port).arg(mockIntWithLimitsStateTypeId.toString()).arg(11)));
|
|
QNetworkReply *reply = nam.get(request);
|
|
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
|
|
|
// Check for the notification on JSON API
|
|
notificationSpy.wait();
|
|
QVariantList notifications;
|
|
notifications = checkNotifications(notificationSpy, "Integrations.StateChanged");
|
|
QVERIFY2(notifications.count() == 1, QString("Expected 1 Integrations.StateChanged notification. Received: %1").arg(notifications.count()).toUtf8());
|
|
QVariantMap notificationContent = notifications.first().toMap().value("params").toMap();
|
|
|
|
QCOMPARE(notificationContent.value("thingId").toUuid().toString(), thing->id().toString());
|
|
QCOMPARE(notificationContent.value("stateTypeId").toUuid().toString(), mockIntWithLimitsStateTypeId.toString());
|
|
QCOMPARE(notificationContent.value("value").toInt(), 11);
|
|
QCOMPARE(notificationContent.value("minValue").toInt(), 0);
|
|
QCOMPARE(notificationContent.value("maxValue").toInt(), 50);
|
|
|
|
// set the max to 8
|
|
qCDebug(dcTests()) << "Changing state max value to 8";
|
|
notificationSpy.clear();
|
|
thing->setStateMaxValue(mockIntWithLimitsStateTypeId, 8);
|
|
|
|
// Check for the notification on JSON API, state chould adapt to new max
|
|
notificationSpy.wait();
|
|
notifications = checkNotifications(notificationSpy, "Integrations.StateChanged");
|
|
QVERIFY2(notifications.count() == 1, "Should get Integrations.StateChanged notification");
|
|
notificationContent = notifications.first().toMap().value("params").toMap();
|
|
|
|
QCOMPARE(notificationContent.value("thingId").toUuid().toString(), thing->id().toString());
|
|
QCOMPARE(notificationContent.value("stateTypeId").toUuid().toString(), mockIntWithLimitsStateTypeId.toString());
|
|
QCOMPARE(notificationContent.value("value").toInt(), 8);
|
|
|
|
// Try to execute an action on the api that exceeds the max value
|
|
qCDebug(dcTests()) << "Executing action with invalid max value (40)";
|
|
QVariantMap actionParams;
|
|
actionParams.insert("thingId", thing->id());
|
|
actionParams.insert("actionTypeId", mockIntWithLimitsActionTypeId);
|
|
QVariantMap valueParam;
|
|
valueParam.insert("paramTypeId", mockIntWithLimitsActionIntWithLimitsParamTypeId);
|
|
// intentionally between thingClass max and dynamic max
|
|
valueParam.insert("value", 40);
|
|
actionParams.insert("params", QVariantList() << valueParam);
|
|
QVariant response = injectAndWait("Integrations.ExecuteAction", actionParams);
|
|
verifyThingError(response, Thing::ThingErrorInvalidParameter);
|
|
|
|
// Set the max to 100
|
|
qCDebug(dcTests()) << "Changing max state value to 100";
|
|
thing->setStateMaxValue(mockIntWithLimitsStateTypeId, 100);
|
|
|
|
// And try to execute the action again
|
|
// intentionally greater than thingClass max
|
|
qCDebug(dcTests()) << "Executing action with valid max (52)";
|
|
valueParam.insert("value", 52);
|
|
actionParams.insert("params", QVariantList() << valueParam);
|
|
response = injectAndWait("Integrations.ExecuteAction", actionParams);
|
|
verifyThingError(response, Thing::ThingErrorNoError);
|
|
|
|
}
|
|
|
|
void TestIntegrations::asyncSetupEmitsSetupStatusUpdate()
|
|
{
|
|
QVariantMap configuredDevices = injectAndWait("Integrations.GetThings").toMap();
|
|
foreach (const QVariant &deviceVariant, configuredDevices.value("params").toMap().value("things").toList()) {
|
|
QVariantMap device = deviceVariant.toMap();
|
|
qCDebug(dcTests()) << "configured thing" << device.value("setupStatus");
|
|
}
|
|
|
|
// Restart the core instance to check if settings are loaded at startup
|
|
restartServer();
|
|
enableNotifications({"Integrations"});
|
|
|
|
QSignalSpy notificationSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
|
|
|
|
configuredDevices = injectAndWait("Integrations.GetThings").toMap();
|
|
QList<QUuid> thingsWithSetupInProgress;
|
|
foreach (const QVariant &deviceVariant, configuredDevices.value("params").toMap().value("things").toList()) {
|
|
QVariantMap thing = deviceVariant.toMap();
|
|
qCDebug(dcTests()) << "Configured thing" << thing.value("name").toString() << "with setup status" << thing.value("setupStatus").toString();
|
|
if (thing.value("setupStatus").toString() == "ThingSetupStatusInProgress") {
|
|
thingsWithSetupInProgress << thing.value("id").toUuid();
|
|
}
|
|
}
|
|
QVERIFY2(thingsWithSetupInProgress.count() > 0, "This test requires at least one device that is still being set up at this point.");
|
|
|
|
QDateTime maxTime = QDateTime::currentDateTime().addSecs(10);
|
|
while (QDateTime::currentDateTime() < maxTime && thingsWithSetupInProgress.count() > 0) {
|
|
QList<QList<QVariant>> notifications = notificationSpy;
|
|
while (notifications.count() > 0) {
|
|
QByteArray notificationData = notifications.takeFirst().at(1).toByteArray();
|
|
QVariantMap notification = QJsonDocument::fromJson(notificationData).toVariant().toMap();
|
|
if (notification.value("notification").toString() == "Integrations.ThingChanged") {
|
|
QString setupStatus = notification.value("params").toMap().value("thing").toMap().value("setupStatus").toString();
|
|
if (setupStatus == "ThingSetupStatusComplete") {
|
|
qCDebug(dcTests()) << "Device setup completed for" << notification.value("params").toMap().value("thing").toMap().value("name").toString();
|
|
ThingId thingId = notification.value("params").toMap().value("thing").toMap().value("id").toUuid();
|
|
thingsWithSetupInProgress.removeAll(thingId);
|
|
}
|
|
}
|
|
}
|
|
notificationSpy.clear();
|
|
if (thingsWithSetupInProgress.count() > 0) {
|
|
notificationSpy.wait();
|
|
}
|
|
}
|
|
|
|
QVERIFY2(thingsWithSetupInProgress.isEmpty(), "Some things did not finish the setup!");
|
|
}
|
|
|
|
void TestIntegrations::testTranslations()
|
|
{
|
|
// switch language to de_AT
|
|
QVariantMap params;
|
|
params.insert("locale", "de_AT");
|
|
QVariantMap handShake = injectAndWait("JSONRPC.Hello", params).toMap();
|
|
QCOMPARE(handShake.value("params").toMap().value("locale").toString(), QString("de_AT"));
|
|
|
|
QVariantMap thingClasses = injectAndWait("Integrations.GetThingClasses").toMap();
|
|
bool found = false;
|
|
foreach (const QVariant &tcVariant, thingClasses.value("params").toMap().value("thingClasses").toList()) {
|
|
QVariantMap tcMap = tcVariant.toMap();
|
|
if (tcMap.value("id").toUuid() == autoMockThingClassId) {
|
|
found = true;
|
|
|
|
// Verify thingClass' displayName is translated
|
|
QCOMPARE(tcMap.value("displayName").toString(), QString("Mock \"Thing\" (automatisch erstellt)"));
|
|
|
|
// Verify paramTypes are translated
|
|
bool ptFound = false;
|
|
foreach (const QVariant &ptVariant, tcMap.value("paramTypes").toList()) {
|
|
QVariantMap ptMap = ptVariant.toMap();
|
|
if (ptMap.value("id").toUuid() == autoMockThingAsyncParamTypeId) {
|
|
ptFound = true;
|
|
QCOMPARE(ptMap.value("displayName").toString(), QString("asynchron"));
|
|
}
|
|
}
|
|
QVERIFY2(ptFound, "ParamType not found in mock thing class.");
|
|
|
|
|
|
// Verify settings are translated
|
|
bool sFound = false;
|
|
foreach (const QVariant &sVariant, tcMap.value("settingsTypes").toList()) {
|
|
QVariantMap sMap = sVariant.toMap();
|
|
if (sMap.value("id").toUuid() == autoMockSettingsMockSettingParamTypeId) {
|
|
sFound = true;
|
|
QCOMPARE(sMap.value("displayName").toString(), QString("Mock-Einstellung"));
|
|
}
|
|
}
|
|
QVERIFY2(sFound, "SettingsType not found in mock thing class.");
|
|
|
|
// Verify stateTypes are translated
|
|
bool stFound = false;
|
|
foreach (const QVariant &stVariant, tcMap.value("stateTypes").toList()) {
|
|
QVariantMap stMap = stVariant.toMap();
|
|
if (stMap.value("id").toUuid() == autoMockIntStateTypeId) {
|
|
stFound = true;
|
|
QCOMPARE(stMap.value("displayName").toString(), QString("Simulierter Integer Zustand"));
|
|
}
|
|
}
|
|
QVERIFY2(stFound, "StateType not found in mock thing class.");
|
|
|
|
// Verify eventTypes are translated
|
|
bool etFound = false;
|
|
foreach (const QVariant &etVariant, tcMap.value("eventTypes").toList()) {
|
|
QVariantMap etMap = etVariant.toMap();
|
|
if (etMap.value("id").toUuid() == autoMockEvent1EventTypeId) {
|
|
etFound = true;
|
|
QCOMPARE(etMap.value("displayName").toString(), QString("Mock Ereignis 1"));
|
|
}
|
|
}
|
|
QVERIFY2(etFound, "EventType not found in mock thing class.");
|
|
|
|
// Verify actionTypes are translated
|
|
bool atFound = false;
|
|
foreach (const QVariant &atVariant, tcMap.value("actionTypes").toList()) {
|
|
QVariantMap atMap = atVariant.toMap();
|
|
if (atMap.value("id").toUuid() == autoMockWithParamsActionTypeId) {
|
|
atFound = true;
|
|
QCOMPARE(atMap.value("displayName").toString(), QString("Mock Aktion 1 (mit Parameter)"));
|
|
}
|
|
}
|
|
QVERIFY2(atFound, "ActionType not found in mock thing class.");
|
|
}
|
|
}
|
|
QVERIFY2(found, "Mock thing class not found.");
|
|
|
|
}
|
|
|
|
#include "testintegrations.moc"
|
|
QTEST_MAIN(TestIntegrations)
|
|
|