mirror of https://github.com/nymea/nymea.git
380 lines
15 KiB
C++
380 lines
15 KiB
C++
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* *
|
|
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
|
|
* Copyright (C) 2014 Michael Zanetti <michael_zanetti@gmx.net> *
|
|
* *
|
|
* This file is part of nymea. *
|
|
* *
|
|
* nymea 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. *
|
|
* *
|
|
**
|
|
* 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 nymea. If not, see <http://www.gnu.org/licenses/>. *
|
|
* *
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
#include "nymeatestbase.h"
|
|
#include "nymeacore.h"
|
|
#include "servers/mocktcpserver.h"
|
|
|
|
using namespace nymeaserver;
|
|
|
|
class TestRestLogging : public NymeaTestBase
|
|
{
|
|
Q_OBJECT
|
|
|
|
private:
|
|
|
|
private slots:
|
|
void initTestCase();
|
|
|
|
void initLogs();
|
|
|
|
void invalidFilter_data();
|
|
void invalidFilter();
|
|
|
|
void invalidFilterJson();
|
|
|
|
void eventLogs();
|
|
|
|
void actionLog();
|
|
|
|
// this has to be the last test
|
|
void removeDevice();
|
|
};
|
|
|
|
void TestRestLogging::initTestCase()
|
|
{
|
|
NymeaTestBase::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;
|
|
config.restServerEnabled = true;
|
|
NymeaCore::instance()->configuration()->setWebServerConfiguration(config);
|
|
qApp->processEvents();
|
|
}
|
|
|
|
void TestRestLogging::initLogs()
|
|
{
|
|
QNetworkRequest request(QUrl("https://localhost:3333/api/v1/logs"));
|
|
QVariant response = getAndWait(request);
|
|
|
|
QVariantList logEntries = response.toList();
|
|
qDebug() << "Got" << logEntries.count() << "logs";
|
|
QVERIFY(logEntries.count() > 0);
|
|
|
|
clearLoggingDatabase();
|
|
|
|
response = getAndWait(request);
|
|
logEntries = response.toList();
|
|
qDebug() << "Got" << logEntries.count() << "logs";
|
|
QVERIFY(logEntries.count() == 0);
|
|
|
|
}
|
|
|
|
void TestRestLogging::invalidFilter_data()
|
|
{
|
|
QVariantMap invalidSourcesFilter;
|
|
invalidSourcesFilter.insert("loggingSources", QVariantList() << "bla");
|
|
|
|
QVariantMap invalidFilterValue;
|
|
invalidFilterValue.insert("loggingSource", QVariantList() << "bla");
|
|
|
|
QVariantMap invalidTypeIds;
|
|
invalidTypeIds.insert("typeId", QVariantList() << "bla" << "blub");
|
|
|
|
QVariantMap invalidEventTypes;
|
|
invalidEventTypes.insert("eventTypes", QVariantList() << JsonTypes::loggingEventTypeToString(Logging::LoggingEventTypeTrigger) << "blub");
|
|
|
|
QTest::addColumn<QVariantMap>("filter");
|
|
|
|
QTest::newRow("Invalid source") << invalidSourcesFilter;
|
|
QTest::newRow("Invalid filter value") << invalidFilterValue;
|
|
QTest::newRow("Invalid typeIds") << invalidTypeIds;
|
|
QTest::newRow("Invalid eventTypes") << invalidEventTypes;
|
|
}
|
|
|
|
void TestRestLogging::invalidFilter()
|
|
{
|
|
QFETCH(QVariantMap, filter);
|
|
|
|
QUrl url("https://localhost:3333/api/v1/logs");
|
|
QUrlQuery query;
|
|
query.addQueryItem("filter", QJsonDocument::fromVariant(filter).toJson(QJsonDocument::Compact));
|
|
url.setQuery(query);
|
|
|
|
// there should be 2 logs, one for shutdown, one for startup (from server restart)
|
|
QVariant response = getAndWait(QNetworkRequest(url));
|
|
QVERIFY(!response.isNull());
|
|
|
|
// TODO: validate filter for REST api
|
|
}
|
|
|
|
void TestRestLogging::invalidFilterJson()
|
|
{
|
|
QUrl url("https://localhost:3333/api/v1/logs");
|
|
QUrlQuery query;
|
|
query.addQueryItem("filter", "blabla:!!");
|
|
url.setQuery(query);
|
|
|
|
getAndWait(QNetworkRequest(url), 400);
|
|
}
|
|
|
|
void TestRestLogging::eventLogs()
|
|
{
|
|
QList<Device*> devices = NymeaCore::instance()->deviceManager()->findConfiguredDevices(mockDeviceClassId);
|
|
QVERIFY2(devices.count() > 0, "There needs to be at least one configured Mock Device for this test");
|
|
Device *device = devices.first();
|
|
|
|
enableNotifications({"Logging"});
|
|
|
|
// Setup connection to mock client
|
|
QNetworkAccessManager nam;
|
|
QSignalSpy clientSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
|
|
|
|
// trigger event in mock device
|
|
int port = device->paramValue(mockDeviceHttpportParamTypeId).toInt();
|
|
QNetworkRequest request(QUrl(QString("http://localhost:%1/generateevent?eventtypeid=%2").arg(port).arg(mockEvent1EventTypeId.toString())));
|
|
QNetworkReply *reply = nam.get(request);
|
|
clientSpy.wait();
|
|
|
|
// Lets wait for the notification
|
|
QVariantList loggEntryAddedVariants = checkNotifications(clientSpy, "Logging.LogEntryAdded");
|
|
QVERIFY2(!loggEntryAddedVariants.isEmpty(), "Did not get Logging.LogEntryAdded notification.");
|
|
qDebug() << "got" << loggEntryAddedVariants.count() << "Logging.LogEntryAdded notifications";
|
|
reply->deleteLater();
|
|
|
|
|
|
bool found = false;
|
|
qDebug() << "got" << loggEntryAddedVariants.count() << "Logging.LogEntryAdded";
|
|
foreach (const QVariant &loggEntryAddedVariant, loggEntryAddedVariants) {
|
|
QVariantMap logEntry = loggEntryAddedVariant.toMap().value("params").toMap().value("logEntry").toMap();
|
|
if (logEntry.value("deviceId").toString() == device->id().toString()) {
|
|
found = true;
|
|
// Make sure the notification contains all the stuff we expect
|
|
QCOMPARE(logEntry.value("typeId").toString(), mockEvent1EventTypeId.toString());
|
|
QCOMPARE(logEntry.value("eventType").toString(), JsonTypes::loggingEventTypeToString(Logging::LoggingEventTypeTrigger));
|
|
QCOMPARE(logEntry.value("source").toString(), JsonTypes::loggingSourceToString(Logging::LoggingSourceEvents));
|
|
QCOMPARE(logEntry.value("loggingLevel").toString(), JsonTypes::loggingLevelToString(Logging::LoggingLevelInfo));
|
|
break;
|
|
}
|
|
}
|
|
if (!found)
|
|
qDebug() << QJsonDocument::fromVariant(loggEntryAddedVariants).toJson();
|
|
|
|
QVERIFY2(found, "Could not find the corresponding Logging.LogEntryAdded notification");
|
|
|
|
// get this logentry with filter
|
|
QVariantMap params;
|
|
params.insert("deviceIds", QVariantList() << device->id());
|
|
params.insert("loggingSources", QVariantList() << JsonTypes::loggingSourceToString(Logging::LoggingSourceEvents));
|
|
params.insert("eventTypes", QVariantList() << JsonTypes::loggingEventTypeToString(Logging::LoggingEventTypeTrigger));
|
|
params.insert("typeIds", QVariantList() << mockEvent1EventTypeId);
|
|
|
|
QVariant response = injectAndWait("Logging.GetLogEntries", params);
|
|
verifyLoggingError(response);
|
|
|
|
QVariantList logEntries = response.toMap().value("params").toMap().value("logEntries").toList();
|
|
QVERIFY(logEntries.count() == 1);
|
|
|
|
// disable notifications
|
|
QCOMPARE(disableNotifications(), true);
|
|
}
|
|
|
|
void TestRestLogging::actionLog()
|
|
{
|
|
QVariantList actionParams;
|
|
QVariantMap param1;
|
|
param1.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId);
|
|
param1.insert("value", 7);
|
|
actionParams.append(param1);
|
|
QVariantMap param2;
|
|
param2.insert("paramTypeId", mockWithParamsActionParam2ParamTypeId);
|
|
param2.insert("value", true);
|
|
actionParams.append(param2);
|
|
|
|
QVariantMap params;
|
|
params.insert("actionTypeId", mockWithParamsActionTypeId);
|
|
params.insert("deviceId", m_mockDeviceId);
|
|
params.insert("params", actionParams);
|
|
|
|
// enable notifications
|
|
enableNotifications({"Logging"});
|
|
|
|
QSignalSpy clientSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
|
|
|
|
// EXECUTE with params
|
|
QVariant response = injectAndWait("Actions.ExecuteAction", params);
|
|
verifyDeviceError(response);
|
|
|
|
// Lets wait 3for the notification
|
|
clientSpy.wait(200);
|
|
QVariant notification = checkNotification(clientSpy, "Logging.LogEntryAdded");
|
|
QVERIFY(!notification.isNull());
|
|
|
|
QVariantMap logEntry = notification.toMap().value("params").toMap().value("logEntry").toMap();
|
|
|
|
// Make sure the notification contains all the stuff we expect
|
|
QCOMPARE(logEntry.value("typeId").toString(), mockWithParamsActionTypeId.toString());
|
|
QCOMPARE(logEntry.value("deviceId").toString(), m_mockDeviceId.toString());
|
|
QCOMPARE(logEntry.value("eventType").toString(), JsonTypes::loggingEventTypeToString(Logging::LoggingEventTypeTrigger));
|
|
QCOMPARE(logEntry.value("source").toString(), JsonTypes::loggingSourceToString(Logging::LoggingSourceActions));
|
|
QCOMPARE(logEntry.value("loggingLevel").toString(), JsonTypes::loggingLevelToString(Logging::LoggingLevelInfo));
|
|
|
|
// EXECUTE without params
|
|
params.clear(); clientSpy.clear();
|
|
params.insert("actionTypeId", mockWithoutParamsActionTypeId);
|
|
params.insert("deviceId", m_mockDeviceId);
|
|
response = injectAndWait("Actions.ExecuteAction", params);
|
|
verifyDeviceError(response);
|
|
|
|
clientSpy.wait(200);
|
|
notification = checkNotification(clientSpy, "Logging.LogEntryAdded");
|
|
QVERIFY(!notification.isNull());
|
|
|
|
// get this logentry with filter
|
|
params.clear();
|
|
params.insert("deviceIds", QVariantList() << m_mockDeviceId);
|
|
params.insert("loggingSources", QVariantList() << JsonTypes::loggingSourceToString(Logging::LoggingSourceActions));
|
|
params.insert("eventTypes", QVariantList() << JsonTypes::loggingEventTypeToString(Logging::LoggingEventTypeTrigger));
|
|
|
|
// FIXME: filter for values currently disabled
|
|
//params.insert("values", QVariantList() << "7, true");
|
|
|
|
QUrl url("https://localhost:3333/api/v1/logs");
|
|
QUrlQuery query;
|
|
query.addQueryItem("filter", QJsonDocument::fromVariant(params).toJson(QJsonDocument::Compact));
|
|
url.setQuery(query);
|
|
|
|
response = getAndWait(QNetworkRequest(url));
|
|
QVariantList logEntries = response.toList();
|
|
QVERIFY(!logEntries.isEmpty());
|
|
|
|
// EXECUTE broken action
|
|
params.clear(); clientSpy.clear();
|
|
params.insert("actionTypeId", mockFailingActionTypeId);
|
|
params.insert("deviceId", m_mockDeviceId);
|
|
response = injectAndWait("Actions.ExecuteAction", params);
|
|
verifyDeviceError(response, Device::DeviceErrorSetupFailed);
|
|
|
|
clientSpy.wait(200);
|
|
notification = checkNotification(clientSpy, "Logging.LogEntryAdded");
|
|
QVERIFY(!notification.isNull());
|
|
|
|
logEntry = notification.toMap().value("params").toMap().value("logEntry").toMap();
|
|
|
|
// Make sure the notification contains all the stuff we expect
|
|
QCOMPARE(logEntry.value("typeId").toString(), mockFailingActionTypeId.toString());
|
|
QCOMPARE(logEntry.value("deviceId").toString(), m_mockDeviceId.toString());
|
|
QCOMPARE(logEntry.value("eventType").toString(), JsonTypes::loggingEventTypeToString(Logging::LoggingEventTypeTrigger));
|
|
QCOMPARE(logEntry.value("source").toString(), JsonTypes::loggingSourceToString(Logging::LoggingSourceActions));
|
|
QCOMPARE(logEntry.value("loggingLevel").toString(), JsonTypes::loggingLevelToString(Logging::LoggingLevelAlert));
|
|
QCOMPARE(logEntry.value("errorCode").toString(), JsonTypes::deviceErrorToString(Device::DeviceErrorSetupFailed));
|
|
|
|
// get this logentry with filter
|
|
params.clear();
|
|
params.insert("deviceIds", QVariantList() << m_mockDeviceId);
|
|
params.insert("loggingSources", QVariantList() << JsonTypes::loggingSourceToString(Logging::LoggingSourceActions));
|
|
params.insert("eventTypes", QVariantList() << JsonTypes::loggingEventTypeToString(Logging::LoggingEventTypeTrigger));
|
|
|
|
// FIXME: filter for values currently disabled
|
|
//params.insert("values", QVariantList() << "7, true");
|
|
|
|
query.clear();
|
|
query.addQueryItem("filter", QJsonDocument::fromVariant(params).toJson(QJsonDocument::Compact));
|
|
url.setQuery(query);
|
|
|
|
response = getAndWait(QNetworkRequest(url));
|
|
logEntries = response.toList();
|
|
QVERIFY(!logEntries.isEmpty());
|
|
|
|
// check different filters
|
|
params.clear();
|
|
params.insert("deviceIds", QVariantList() << m_mockDeviceId);
|
|
params.insert("loggingSources", QVariantList() << JsonTypes::loggingSourceToString(Logging::LoggingSourceActions));
|
|
params.insert("eventTypes", QVariantList() << JsonTypes::loggingEventTypeToString(Logging::LoggingEventTypeTrigger));
|
|
params.insert("typeIds", QVariantList() << mockWithoutParamsActionTypeId);
|
|
|
|
query.clear();
|
|
query.addQueryItem("filter", QJsonDocument::fromVariant(params).toJson(QJsonDocument::Compact));
|
|
url.setQuery(query);
|
|
|
|
response = getAndWait(QNetworkRequest(url));
|
|
logEntries = response.toList();
|
|
QVERIFY(!logEntries.isEmpty());
|
|
|
|
params.clear();
|
|
params.insert("deviceIds", QVariantList() << m_mockDeviceId);
|
|
params.insert("loggingSources", QVariantList() << JsonTypes::loggingSourceToString(Logging::LoggingSourceActions));
|
|
params.insert("eventTypes", QVariantList() << JsonTypes::loggingEventTypeToString(Logging::LoggingEventTypeTrigger));
|
|
params.insert("typeIds", QVariantList() << mockWithoutParamsActionTypeId << mockWithParamsActionTypeId << mockFailingActionTypeId);
|
|
|
|
query.clear();
|
|
query.addQueryItem("filter", QJsonDocument::fromVariant(params).toJson(QJsonDocument::Compact));
|
|
url.setQuery(query);
|
|
|
|
response = getAndWait(QNetworkRequest(url));
|
|
logEntries = response.toList();
|
|
QVERIFY(!logEntries.isEmpty());
|
|
|
|
// disable notifications
|
|
QCOMPARE(disableNotifications(), true);
|
|
}
|
|
|
|
void TestRestLogging::removeDevice()
|
|
{
|
|
// enable notifications
|
|
enableNotifications({"Logging"});
|
|
|
|
QSignalSpy clientSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
|
|
|
|
QNetworkRequest deleteRequest(QUrl(QString("https://localhost:3333/api/v1/devices/%1").arg(m_mockDeviceId.toString())));
|
|
QVariant response = deleteAndWait(deleteRequest);
|
|
QVERIFY2(!response.isNull(), "Could not delete device");
|
|
|
|
clientSpy.wait(200);
|
|
QVariant notification = checkNotification(clientSpy, "Logging.LogDatabaseUpdated");
|
|
QVERIFY(!notification.isNull());
|
|
|
|
// get this logentry with filter
|
|
QUrl url("https://localhost:3333/api/v1/logs");
|
|
response = getAndWait(QNetworkRequest(url));
|
|
QVariantList logEntries = response.toList();
|
|
QVERIFY2(!logEntries.count() != 0, "No log entries left");
|
|
|
|
// verify that the logs from this device where removed from the db
|
|
QVariantMap params;
|
|
params.insert("deviceIds", QVariantList() << m_mockDeviceId);
|
|
QUrlQuery query;
|
|
query.addQueryItem("filter", QJsonDocument::fromVariant(params).toJson(QJsonDocument::Compact));
|
|
url.setQuery(query);
|
|
|
|
response = getAndWait(QNetworkRequest(url));
|
|
logEntries = response.toList();
|
|
|
|
QCOMPARE(logEntries.count(), 0);
|
|
|
|
// disable notifications
|
|
QCOMPARE(disableNotifications(), true);
|
|
}
|
|
|
|
#include "testrestlogging.moc"
|
|
QTEST_MAIN(TestRestLogging)
|
|
|