/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2015 Simon Stürz * * * * This file is part of guh. * * * * Guh is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, version 2 of the License. * * * * Guh 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 guh. If not, see . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "guhtestbase.h" #include "nymeacore.h" #include "devicemanager.h" #include "mocktcpserver.h" #include "webserver.h" #include #include #include #include #include #include #include #include #include #include using namespace guhserver; class TestRestDeviceClasses: public GuhTestBase { Q_OBJECT private slots: void initTestCase(); void getSupportedDevices(); void invalidMethod(); void getActionTypes_data(); void getActionTypes(); void getStateTypes_data(); void getStateTypes(); void getEventTypes_data(); void getEventTypes(); void discoverDevices_data(); void discoverDevices(); }; #include "testrestdeviceclasses.moc" void TestRestDeviceClasses::initTestCase() { GuhTestBase::initTestCase(); foreach (const WebServerConfiguration &config, NymeaCore::instance()->configuration()->webServerConfigurations()) { if (config.port == 3333 && (config.address == QHostAddress("127.0.0.1") || config.address == QHostAddress("0.0.0.0"))) { qDebug() << "Already have a webserver listening on 127.0.0.1:3333"; return; } } qDebug() << "Creating new webserver instance on 127.0.0.1:3333"; WebServerConfiguration config; config.address = QHostAddress("127.0.0.1"); config.port = 3333; config.sslEnabled = true; NymeaCore::instance()->configuration()->setWebServerConfiguration(config); qApp->processEvents(); } void TestRestDeviceClasses::getSupportedDevices() { // Get all deviceclasses QUrl url("https://localhost:3333/api/v1/deviceclasses"); QVariant response = getAndWait(QNetworkRequest(url)); QVariantList deviceClassesList = response.toList(); QVERIFY2(deviceClassesList.count() > 0, "Not enought deviceclasses."); // Get each of thouse devices individualy foreach (const QVariant &deviceClass, deviceClassesList) { QVariantMap deviceClassMap = deviceClass.toMap(); QNetworkRequest request; request.setHeader(QNetworkRequest::ContentTypeHeader, "text/json"); request.setUrl(QUrl(QString("https://localhost:3333/api/v1/deviceclasses/%1").arg(deviceClassMap.value("id").toString()))); response = getAndWait(request); QVERIFY2(!response.isNull(), "Could not get device"); } // get with vendor filter QUrlQuery query; query.addQueryItem("vendorId", guhVendorId.toString()); url.setQuery(query); response = getAndWait(QNetworkRequest(url)); deviceClassesList = response.toList(); QVERIFY2(deviceClassesList.count() > 0, "Not enought deviceclasses."); // get with invalid vendor filter query.clear(); query.addQueryItem("vendorId", "uuid"); url.setQuery(query); response = getAndWait(QNetworkRequest(url), 400); QCOMPARE(JsonTypes::deviceErrorToString(DeviceManager::DeviceErrorVendorNotFound), response.toMap().value("error").toString()); } void TestRestDeviceClasses::invalidMethod() { QNetworkAccessManager nam; connect(&nam, &QNetworkAccessManager::sslErrors, [this, &nam](QNetworkReply *reply, const QList &) { reply->ignoreSslErrors(); }); QSignalSpy clientSpy(&nam, SIGNAL(finished(QNetworkReply*))); QNetworkRequest request; request.setUrl(QUrl("https://localhost:3333/api/v1/deviceclasses/")); QNetworkReply *reply = nam.post(request, QByteArray()); clientSpy.wait(); QVERIFY2(clientSpy.count() != 0, "expected at least 1 response from webserver"); bool ok = false; int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(&ok); QVERIFY2(ok, "Could not convert statuscode from response to int"); QCOMPARE(statusCode, 400); reply->deleteLater(); } void TestRestDeviceClasses::getActionTypes_data() { QTest::addColumn("deviceClassId"); QTest::addColumn("actionTypeId"); QTest::addColumn("expectedStatusCode"); QTest::addColumn("error"); QTest::newRow("all ActionTypes") << mockDeviceClassId.toString() << QString() << 200 << DeviceManager::DeviceErrorNoError; QTest::newRow("ActionType async") << mockDeviceClassId.toString() << mockActionIdAsync.toString() << 200 << DeviceManager::DeviceErrorNoError; QTest::newRow("ActionType no params") << mockDeviceClassId.toString() << mockActionIdNoParams.toString() << 200 << DeviceManager::DeviceErrorNoError; QTest::newRow("ActionType failing") << mockDeviceClassId.toString() << mockActionIdFailing.toString() << 200 << DeviceManager::DeviceErrorNoError; QTest::newRow("ActionType with params") << mockDeviceClassId.toString() << mockActionIdWithParams.toString() << 200 << DeviceManager::DeviceErrorNoError; QTest::newRow("invalid DeviceClassId") << DeviceClassId::createDeviceClassId().toString() << mockActionIdNoParams.toString() << 404 << DeviceManager::DeviceErrorDeviceClassNotFound; QTest::newRow("invalid ActionTypeId") << mockDeviceClassId.toString() << ActionTypeId::createActionTypeId().toString() << 404 << DeviceManager::DeviceErrorActionTypeNotFound; QTest::newRow("invalid ActionTypeId format") << mockDeviceClassId.toString() << "uuid" << 400 << DeviceManager::DeviceErrorActionTypeNotFound; QTest::newRow("invalid DeviceClassId format") << "uuid" << "uuid" << 400 << DeviceManager::DeviceErrorDeviceClassNotFound; } void TestRestDeviceClasses::getActionTypes() { QFETCH(QString, deviceClassId); QFETCH(QString, actionTypeId); QFETCH(int, expectedStatusCode); QFETCH(DeviceManager::DeviceError, error); QNetworkRequest request; if (!actionTypeId.isEmpty()) { request.setUrl(QUrl(QString("https://localhost:3333/api/v1/deviceclasses/%1/actiontypes/%2").arg(deviceClassId).arg(actionTypeId))); } else { // Get all actiontypes request.setUrl(QUrl(QString("https://localhost:3333/api/v1/deviceclasses/%1/actiontypes").arg(deviceClassId))); } QVariant response = getAndWait(request, expectedStatusCode); QVERIFY2(!response.isNull(), "Could not read get action type response"); if (expectedStatusCode != 200) QCOMPARE(JsonTypes::deviceErrorToString(error), response.toMap().value("error").toString()); } void TestRestDeviceClasses::getStateTypes_data() { QTest::addColumn("deviceClassId"); QTest::addColumn("stateTypeId"); QTest::addColumn("expectedStatusCode"); QTest::addColumn("error"); QTest::newRow("all ActionTypes") << mockDeviceClassId.toString() << QString() << 200 << DeviceManager::DeviceErrorNoError; QTest::newRow("StateType bool") << mockDeviceClassId.toString() << mockBoolStateId.toString() << 200 << DeviceManager::DeviceErrorNoError; QTest::newRow("StateType int") << mockDeviceClassId.toString() << mockIntStateId.toString() << 200 << DeviceManager::DeviceErrorNoError; QTest::newRow("invalid DeviceClassId") << DeviceClassId::createDeviceClassId().toString() << mockBoolStateId.toString() << 404 << DeviceManager::DeviceErrorDeviceClassNotFound; QTest::newRow("invalid StateTypeId") << mockDeviceClassId.toString() << StateTypeId::createStateTypeId().toString() << 404 << DeviceManager::DeviceErrorStateTypeNotFound; QTest::newRow("invalid StateTypeId format") << mockDeviceClassId.toString() << "uuid" << 400 << DeviceManager::DeviceErrorStateTypeNotFound; QTest::newRow("invalid DeviceClassId format") << "uuid" << "uuid" << 400 << DeviceManager::DeviceErrorDeviceClassNotFound; } void TestRestDeviceClasses::getStateTypes() { QFETCH(QString, deviceClassId); QFETCH(QString, stateTypeId); QFETCH(int, expectedStatusCode); QFETCH(DeviceManager::DeviceError, error); QNetworkRequest request; if (!stateTypeId.isEmpty()) { request.setUrl(QUrl(QString("https://localhost:3333/api/v1/deviceclasses/%1/statetypes/%2").arg(deviceClassId).arg(stateTypeId))); } else { // Get all actiontypes request.setUrl(QUrl(QString("https://localhost:3333/api/v1/deviceclasses/%1/statetypes").arg(deviceClassId))); } QVariant response = getAndWait(request, expectedStatusCode); QVERIFY2(!response.isNull(), "Could not read get action type response"); if (expectedStatusCode != 200) QCOMPARE(JsonTypes::deviceErrorToString(error), response.toMap().value("error").toString()); } void TestRestDeviceClasses::getEventTypes_data() { QTest::addColumn("deviceClassId"); QTest::addColumn("eventTypeId"); QTest::addColumn("expectedStatusCode"); QTest::addColumn("error"); QTest::newRow("all ActionTypes") << mockDeviceClassId.toString() << QString() << 200 << DeviceManager::DeviceErrorNoError; QTest::newRow("EventType 1") << mockDeviceClassId.toString() << mockEvent1Id.toString() << 200 << DeviceManager::DeviceErrorNoError; QTest::newRow("EventType 2") << mockDeviceClassId.toString() << mockEvent2Id.toString() << 200 << DeviceManager::DeviceErrorNoError; QTest::newRow("invalid DeviceClassId") << DeviceClassId::createDeviceClassId().toString() << mockEvent2Id.toString() << 404 << DeviceManager::DeviceErrorDeviceClassNotFound; QTest::newRow("invalid EventTypeId") << mockDeviceClassId.toString() << EventTypeId::createEventTypeId().toString() << 404 << DeviceManager::DeviceErrorEventTypeNotFound; QTest::newRow("invalid EventTypeId format") << mockDeviceClassId.toString() << "uuid" << 400 << DeviceManager::DeviceErrorEventTypeNotFound; QTest::newRow("invalid DeviceClassId format") << "uuid" << "uuid" << 400 << DeviceManager::DeviceErrorDeviceClassNotFound; } void TestRestDeviceClasses::getEventTypes() { QFETCH(QString, deviceClassId); QFETCH(QString, eventTypeId); QFETCH(int, expectedStatusCode); QFETCH(DeviceManager::DeviceError, error); QNetworkRequest request; if (!eventTypeId.isNull()) { request.setUrl(QUrl(QString("https://localhost:3333/api/v1/deviceclasses/%1/eventtypes/%2").arg(deviceClassId).arg(eventTypeId))); } else { // Get all actiontypes request.setUrl(QUrl(QString("https://localhost:3333/api/v1/deviceclasses/%1/eventtypes").arg(deviceClassId))); } QVariant response = getAndWait(request, expectedStatusCode); QVERIFY2(!response.isNull(), "Could not read get action type response"); if (expectedStatusCode != 200) QCOMPARE(JsonTypes::deviceErrorToString(error), response.toMap().value("error").toString()); } void TestRestDeviceClasses::discoverDevices_data() { QTest::addColumn("deviceClassId"); QTest::addColumn("resultCount"); QTest::addColumn("discoveryParams"); QTest::addColumn("expectedStatusCode"); QTest::addColumn("error"); QVariantMap resultCountParam; resultCountParam.insert("paramTypeId", resultCountParamTypeId); resultCountParam.insert("value", 1); QVariantMap invalidResultCountParam; invalidResultCountParam.insert("paramTypeId", resultCountParamTypeId); invalidResultCountParam.insert("value", 10); QVariantList discoveryParams; discoveryParams.append(resultCountParam); QVariantList invalidDiscoveryParams; invalidDiscoveryParams.append(invalidResultCountParam); QTest::newRow("valid deviceClassId without params") << mockDeviceClassId << 2 << QVariantList() << 200 << DeviceManager::DeviceErrorNoError; QTest::newRow("valid deviceClassId with params") << mockDeviceClassId << 1 << discoveryParams << 200 << DeviceManager::DeviceErrorNoError; QTest::newRow("invalid deviceClassId") << DeviceClassId::createDeviceClassId() << 0 << QVariantList() << 404 << DeviceManager::DeviceErrorDeviceClassNotFound; QTest::newRow("valid deviceClassId with invalid params") << mockDeviceClassId << 10 << invalidDiscoveryParams << 500 << DeviceManager::DeviceErrorInvalidParameter; } void TestRestDeviceClasses::discoverDevices() { QFETCH(DeviceClassId, deviceClassId); QFETCH(int, resultCount); QFETCH(QVariantList, discoveryParams); QFETCH(int, expectedStatusCode); QFETCH(DeviceManager::DeviceError, error); QVariantMap params; params.insert("deviceClassId", deviceClassId); params.insert("discoveryParams", discoveryParams); // DISCOVER QUrl url(QString("https://localhost:3333/api/v1/deviceclasses/%1/discover").arg(deviceClassId.toString())); if (!discoveryParams.isEmpty()) { QUrlQuery query; query.addQueryItem("params", QJsonDocument::fromVariant(discoveryParams).toJson(QJsonDocument::Compact)); url.setQuery(query); } QVariant response = getAndWait(QNetworkRequest(url), expectedStatusCode); QVERIFY2(!response.isNull(), "Could not read response"); if (expectedStatusCode != 200) { QCOMPARE(JsonTypes::deviceErrorToString(error), response.toMap().value("error").toString()); return; } // check response QVariantList foundDevices = response.toList(); QCOMPARE(foundDevices.count(), resultCount); // ADD the discovered device QNetworkRequest request(QUrl("https://localhost:3333/api/v1/devices")); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); DeviceDescriptorId descriptorId = DeviceDescriptorId(foundDevices.first().toMap().value("id").toString()); params.clear(); params.insert("deviceClassId", deviceClassId); params.insert("deviceDescriptorId", descriptorId.toString()); response = postAndWait(request, params, expectedStatusCode); QVERIFY2(!response.isNull(), "Could not read response"); DeviceId deviceId = DeviceId(response.toMap().value("id").toString()); QVERIFY2(!deviceId.isNull(), "got invalid device id"); // REMOVE added device request.setUrl(QUrl(QString("https://localhost:3333/api/v1/devices/%1").arg(deviceId.toString()))); response = deleteAndWait(request); QVERIFY2(!response.isNull(), "Could not delete device"); } QTEST_MAIN(TestRestDeviceClasses)