/*
 * Copyright (C) 2014-2026 CZ.NIC
 *
 * This program 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, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * In addition, as a special exception, the copyright holders give
 * permission to link the code of portions of this program with the
 * OpenSSL library under certain conditions as described in each
 * individual source file, and distribute linked combinations including
 * the two.
 */

#include <QByteArray>
#include <QCoreApplication>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QObject>
#include <QSignalSpy>
#include <QString>
#include <QtTest/QtTest>

#include "src/datovka_shared/compat/compiler.h" /* macroStdMove */
#include "src/datovka_shared/io/draft_db.h"
#include "src/datovka_shared/json/draft_interface.h"
#include "src/datovka_shared/json/basic.h"
#include "src/datovka_shared/log/log.h"
#include "src/global.h"
#include "tests/test_draft_db.h"

static DraftDb *draftDbPtr = Q_NULLPTR;

class TestDraftDb : public QObject {
	Q_OBJECT

public:
	TestDraftDb(void);

private slots:
	void initTestCase(void);

	void cleanupTestCase(void);

	void dmDraftAttachment(void);

	//void dmDraftEnvelope(void); /* Isds::Envelope should be checked elsewhere. */

	void dmDraft(void);

	void dmDraftAttachmentJsonConversion(void);

	void dmDraftJsonConversion(void);

	void testDraftDbInsert(void);

	void testDraftDbUpdate(void);

	void testDraftDbDelete(void);

	/* Missing single file. */
	void testsReadWhenMissingFiles1(void);

	/* Missing entire file directory. */
	void testsReadWhenMissingFiles2(void);

private:
	void _init(void);

	void _cleanup(void);

	/*
	 * An application object must exist before any QObject.
	 * See https://doc.qt.io/qt-6/threads-qobject.html ,
	 * also see QTEST_GUILESS_MAIN().
	 */
	int m_argc;
	char **m_argv;
	QCoreApplication m_app;

	const QString m_connectionPrefix; /*!< SQL connection prefix. */
	const QString m_dbLocationPath; /*!< Directory path. */
	QDir m_dbDir;  /*!< Directory containing testing databases. */
	const QString m_dbName; /*!< Database file name. */
	const QString m_dbPath;
	const QString m_dbAssociatedDirPath;

	const QString m_attachPath;
};

#define DRAFTS "drafts"

TestDraftDb::TestDraftDb(void)
    : QObject(Q_NULLPTR),
    m_argc(0), m_argv(NULL), m_app(m_argc, m_argv),
    m_connectionPrefix(QLatin1String("DRAFTDB")),
    m_dbLocationPath(QDir::currentPath() + QDir::separator() + QLatin1String("_db")),
    m_dbDir(m_dbLocationPath),
    m_dbName(QLatin1String(DRAFTS ".db")),
    m_dbPath(m_dbLocationPath + QDir::separator() + m_dbName),
    m_dbAssociatedDirPath(m_dbLocationPath + QDir::separator() + QLatin1String(DRAFTS)),
    m_attachPath("data/DDZ_3401761.zfo")
{
}

void TestDraftDb::initTestCase(void)
{
	/*
	 * https://stackoverflow.com/questions/22044737/when-where-and-why-use-namespace-when-registering-custom-types-for-qt
	 */
	qRegisterMetaType<DraftEntryList>("DraftEntryList");
	qRegisterMetaType<Json::Int64StringList>("Json::Int64StringList");
	qRegisterMetaType<DraftCounts>("DraftCounts");

	QVERIFY(GlobInstcs::logPtr == Q_NULLPTR);
	GlobInstcs::logPtr = new (::std::nothrow) LogDevice;
	QVERIFY(GlobInstcs::logPtr != Q_NULLPTR);

	/* Pointer must be null before initialisation. */
	QVERIFY(draftDbPtr == Q_NULLPTR);

	QVERIFY(m_dbDir.removeRecursively());
	QVERIFY(!m_dbDir.exists());

	QVERIFY(m_dbDir.mkpath("."));
}

void TestDraftDb::cleanupTestCase(void)
{
	QVERIFY(draftDbPtr == Q_NULLPTR);

	delete GlobInstcs::logPtr; GlobInstcs::logPtr = Q_NULLPTR;

	QVERIFY(m_dbDir.removeRecursively());
	QVERIFY(!m_dbDir.exists());
}

void TestDraftDb::dmDraftAttachment(void)
{
	Json::DmDraft::Attachment a1, a2;
	const QString name("name");
	const QByteArray data("data");
	const QString path("path");

	/* Must be null. */
	QVERIFY(a1.name().isNull());
	QVERIFY(a1.data().isNull());
	QVERIFY(a1.path().isNull());
	QVERIFY(a2.name().isNull());
	QVERIFY(a2.data().isNull());
	QVERIFY(a2.path().isNull());

	QVERIFY(a1.isNull());
	QVERIFY(a2.isNull());
	QVERIFY(a1 == a2);

	/* Check whether values are not cross-linked. */
	a1.setName(name);
	a1.setData(data);
	a1.setPath(path);

	QVERIFY(a1.name() == name);
	QVERIFY(a1.data() == data);
	QVERIFY(a1.path() == path);

	QVERIFY(!a1.isNull());
	QVERIFY(a2.isNull());
	QVERIFY(a1 != a2);

	/* Check value copying. */
	a2 = a1;

	QVERIFY(a2.name() == name);
	QVERIFY(a2.data() == data);
	QVERIFY(a2.path() == path);

	QVERIFY(!a1.isNull());
	QVERIFY(!a2.isNull());
	QVERIFY(a1 == a2);

	/* Clear value. */
	a1 = Json::DmDraft::Attachment();

	QVERIFY(a1.name().isNull());
	QVERIFY(a1.data().isNull());
	QVERIFY(a1.path().isNull());
	QVERIFY(a2.name() == name);
	QVERIFY(a2.data() == data);
	QVERIFY(a2.path() == path);

	QVERIFY(a1.isNull());
	QVERIFY(!a2.isNull());
	QVERIFY(a1 != a2);

#ifdef Q_COMPILER_RVALUE_REFS
	/* Move. */
	a1 = ::std::move(a2);

	QVERIFY(a1.name() == name);
	QVERIFY(a1.data() == data);
	QVERIFY(a1.path() == path);
	QVERIFY(a2.name().isNull());
	QVERIFY(a2.data().isNull());
	QVERIFY(a2.path().isNull());

	QVERIFY(!a1.isNull());
	QVERIFY(a2.isNull());
	QVERIFY(a1 != a2);
#endif /* Q_COMPILER_RVALUE_REFS */
}

void TestDraftDb::dmDraft(void)
{
	Json::DmDraft d1, d2;
	Isds::Envelope dmEnvelope;
	dmEnvelope.setDmAnnotation("dmAnnotation");
	const QStringList dmRecipients({"recip1", "recip2"});
	Json::DmDraft::AttachmentList dmAttachments;
	{
		Json::DmDraft::Attachment a;
		a.setName("name");
		a.setData("data");
		a.setPath("path");
		dmAttachments.append(a);
	}
	const QString shortMessageText("shortMessageText");
	const qint64 shortMessageTextFmt = 1;
	const bool cntrlDowmloadMessage = true;
	const bool cntrlUploadMessageRm = true;

	/* Must be null. */
	QVERIFY(d1.dmEnvelope().isNull());
	QVERIFY(d1.dmRecipients().isEmpty());
	QVERIFY(d1.dmAttachments().isEmpty());
	QVERIFY(d1.shortMessageText().isNull());
	QVERIFY(d1.shortMessageTextFmt() == -1);
	QVERIFY(d1.cntrlDowmloadMessage() == false);
	QVERIFY(d1.cntrlUploadMessageRm() == false);

	QVERIFY(d1.isNull());
	QVERIFY(d2.isNull());
	QVERIFY(d1 == d2);

	/* Check whether values are not cross-linked. */
	d1.setDmEnvelope(dmEnvelope);
	d1.setDmRecipients(dmRecipients);
	d1.setDmAttachments(dmAttachments);
	d1.setShortMessageText(shortMessageText);
	d1.setShortMessageTextFmt(shortMessageTextFmt);
	d1.setCntrlDowmloadMessage(cntrlDowmloadMessage);
	d1.setCntrlUploadMessageRm(cntrlUploadMessageRm);

	QVERIFY(d1.dmEnvelope() == dmEnvelope);
	QVERIFY(d1.dmRecipients() == dmRecipients);
	QVERIFY(d1.dmAttachments() == dmAttachments);
	QVERIFY(d1.shortMessageText() == shortMessageText);
	QVERIFY(d1.shortMessageTextFmt() == shortMessageTextFmt);
	QVERIFY(d1.cntrlDowmloadMessage() == cntrlDowmloadMessage);
	QVERIFY(d1.cntrlUploadMessageRm() == cntrlUploadMessageRm);

	QVERIFY(!d1.isNull());
	QVERIFY(d2.isNull());
	QVERIFY(d1 != d2);

	/* Check value copying. */
	d2 = d1;

	QVERIFY(d2.dmEnvelope() == dmEnvelope);
	QVERIFY(d2.dmRecipients() == dmRecipients);
	QVERIFY(d2.dmAttachments() == dmAttachments);
	QVERIFY(d2.shortMessageText() == shortMessageText);
	QVERIFY(d2.shortMessageTextFmt() == shortMessageTextFmt);
	QVERIFY(d2.cntrlDowmloadMessage() == cntrlDowmloadMessage);
	QVERIFY(d2.cntrlUploadMessageRm() == cntrlUploadMessageRm);

	QVERIFY(!d1.isNull());
	QVERIFY(!d2.isNull());
	QVERIFY(d1 == d2);

	/* Clear value. */
	d1 = Json::DmDraft();

	QVERIFY(d1.dmEnvelope().isNull());
	QVERIFY(d1.dmRecipients().isEmpty());
	QVERIFY(d1.dmAttachments().isEmpty());
	QVERIFY(d1.shortMessageText().isNull());
	QVERIFY(d1.shortMessageTextFmt() == -1);
	QVERIFY(d1.cntrlDowmloadMessage() == false);
	QVERIFY(d1.cntrlUploadMessageRm() == false);
	QVERIFY(d2.dmEnvelope() == dmEnvelope);
	QVERIFY(d2.dmRecipients() == dmRecipients);
	QVERIFY(d2.dmAttachments() == dmAttachments);
	QVERIFY(d2.shortMessageText() == shortMessageText);
	QVERIFY(d2.shortMessageTextFmt() == shortMessageTextFmt);
	QVERIFY(d2.cntrlDowmloadMessage() == cntrlDowmloadMessage);
	QVERIFY(d2.cntrlUploadMessageRm() == cntrlUploadMessageRm);

	QVERIFY(d1.isNull());
	QVERIFY(!d2.isNull());
	QVERIFY(d1 != d2);

#ifdef Q_COMPILER_RVALUE_REFS
	/* Move. */
	d1 = ::std::move(d2);

	QVERIFY(d1.dmEnvelope() == dmEnvelope);
	QVERIFY(d1.dmRecipients() == dmRecipients);
	QVERIFY(d1.dmAttachments() == dmAttachments);
	QVERIFY(d1.shortMessageText() == shortMessageText);
	QVERIFY(d1.shortMessageTextFmt() == shortMessageTextFmt);
	QVERIFY(d1.cntrlDowmloadMessage() == cntrlDowmloadMessage);
	QVERIFY(d1.cntrlUploadMessageRm() == cntrlUploadMessageRm);
	QVERIFY(d2.dmEnvelope().isNull());
	QVERIFY(d2.dmRecipients().isEmpty());
	QVERIFY(d2.dmAttachments().isEmpty());
	QVERIFY(d2.shortMessageText().isNull());
	QVERIFY(d2.shortMessageTextFmt() == -1);
	QVERIFY(d2.cntrlDowmloadMessage() == false);
	QVERIFY(d2.cntrlUploadMessageRm() == false);

	QVERIFY(!d1.isNull());
	QVERIFY(d2.isNull());
	QVERIFY(d1 != d2);
#endif /* Q_COMPILER_RVALUE_REFS */
}

void TestDraftDb::dmDraftAttachmentJsonConversion(void)
{
	Json::DmDraft::Attachment a1, a2;
	const QString name("name");
	const QByteArray data("data");
	const QString path("path");
	const bool createdFromText = true;

	bool iOk = false;
	QJsonValue jsonVal;

	QVERIFY(a1.isNull());
	QVERIFY(a2.isNull());
	QVERIFY(a1 == a2);

	a1.setName(name);
	a1.setData(data);
	a1.setPath(path);
	a1.setCreatedFromText(createdFromText);

	QVERIFY(!a1.isNull());
	QVERIFY(a2.isNull());
	QVERIFY(a1 != a2);

	/* Null value converts onto null object. */
	iOk = false;
	a2 = Json::DmDraft::Attachment::fromJsonVal(QJsonValue(), &iOk);
	QVERIFY(iOk == true);
	QVERIFY(a2.isNull());

	/* Null object converts onto null value. */
	iOk = false;
	iOk = a2.toJsonVal(jsonVal);
	QVERIFY(iOk == true);
	QVERIFY(jsonVal.isNull());

	iOk = false;
	iOk = a1.toJsonVal(jsonVal);
	QVERIFY(iOk == true);
	QVERIFY(!jsonVal.isNull());

	iOk = false;
	a2 = Json::DmDraft::Attachment::fromJsonVal(jsonVal, &iOk);
	QVERIFY(iOk == true);
	QVERIFY(!a2.isNull());

	QVERIFY(a2.name() == name);
	QVERIFY(a2.data() == data);
	QVERIFY(a2.path() == path);
	QVERIFY(a2.createdFromText() == createdFromText);

	QVERIFY(!a1.isNull());
	QVERIFY(!a2.isNull());
	QVERIFY(a1 == a2);

	/* Name. */
	a1.setName(QString());
	a1.setData(data);
	a1.setPath(path);
	a1.setCreatedFromText(createdFromText);

	iOk = false;
	iOk = a1.toJsonVal(jsonVal);
	QVERIFY(iOk == true);
	QVERIFY(!jsonVal.isNull());

	iOk = false;
	a2 = Json::DmDraft::Attachment::fromJsonVal(jsonVal, &iOk);
	QVERIFY(iOk == true);
	QVERIFY(!a2.isNull());

	QVERIFY(a2.name().isNull());
	QVERIFY(a2.data() == data);
	QVERIFY(a2.path() == path);
	QVERIFY(a2.createdFromText() == createdFromText);

	a1.setName("");
	a1.setData(data);
	a1.setPath(path);
	a1.setCreatedFromText(createdFromText);

	iOk = false;
	iOk = a1.toJsonVal(jsonVal);
	QVERIFY(iOk == true);
	QVERIFY(!jsonVal.isNull());

	iOk = false;
	a2 = Json::DmDraft::Attachment::fromJsonVal(jsonVal, &iOk);
	QVERIFY(iOk == true);
	QVERIFY(!a2.isNull());

	QVERIFY(!a2.name().isNull());
	QVERIFY(a2.name().isEmpty());
	QVERIFY(a2.data() == data);
	QVERIFY(a2.path() == path);
	QVERIFY(a2.createdFromText() == createdFromText);

	/* Data. */
	a1.setName(name);
	a1.setData(QByteArray());
	a1.setPath(path);
	a1.setCreatedFromText(createdFromText);

	iOk = false;
	iOk = a1.toJsonVal(jsonVal);
	QVERIFY(iOk == true);
	QVERIFY(!jsonVal.isNull());

	iOk = false;
	a2 = Json::DmDraft::Attachment::fromJsonVal(jsonVal, &iOk);
	QVERIFY(iOk == true);
	QVERIFY(!a2.isNull());

	QVERIFY(a2.name() == name);
	QVERIFY(a2.data().isNull());
	QVERIFY(a2.path() == path);
	QVERIFY(a2.createdFromText() == createdFromText);

	a1.setName(name);
	a1.setData("");
	a1.setPath(path);
	a1.setCreatedFromText(createdFromText);

	iOk = false;
	iOk = a1.toJsonVal(jsonVal);
	QVERIFY(iOk == true);
	QVERIFY(!jsonVal.isNull());

	iOk = false;
	a2 = Json::DmDraft::Attachment::fromJsonVal(jsonVal, &iOk);
	QVERIFY(iOk == true);
	QVERIFY(!a2.isNull());

	QVERIFY(a2.name() == name);
	//QVERIFY(a2.data().isNull());
	QVERIFY(a2.data().isEmpty());
	QVERIFY(a2.path() == path);
	QVERIFY(a2.createdFromText() == createdFromText);

	/* Path. */
	a1.setName(name);
	a1.setData(data);
	a1.setPath(QString());
	a1.setCreatedFromText(createdFromText);

	iOk = false;
	iOk = a1.toJsonVal(jsonVal);
	QVERIFY(iOk == true);
	QVERIFY(!jsonVal.isNull());

	iOk = false;
	a2 = Json::DmDraft::Attachment::fromJsonVal(jsonVal, &iOk);
	QVERIFY(iOk == true);
	QVERIFY(!a2.isNull());

	QVERIFY(a2.name() == name);
	QVERIFY(a2.data() == data);
	QVERIFY(a2.path().isNull());
	QVERIFY(a2.createdFromText() == createdFromText);

	a1.setName(name);
	a1.setData(data);
	a1.setPath("");
	a1.setCreatedFromText(createdFromText);

	iOk = false;
	iOk = a1.toJsonVal(jsonVal);
	QVERIFY(iOk == true);
	QVERIFY(!jsonVal.isNull());

	iOk = false;
	a2 = Json::DmDraft::Attachment::fromJsonVal(jsonVal, &iOk);
	QVERIFY(iOk == true);
	QVERIFY(!a2.isNull());

	QVERIFY(a2.name() == name);
	QVERIFY(a2.data() == data);
	QVERIFY(!a2.path().isNull());
	QVERIFY(a2.path().isEmpty());
	QVERIFY(a2.createdFromText() == createdFromText);

	/* Created from text. */
	a1.setName(name);
	a1.setData(data);
	a1.setPath(path);
	a1.setCreatedFromText(false);

	iOk = false;
	iOk = a1.toJsonVal(jsonVal);
	QVERIFY(iOk == true);
	QVERIFY(!jsonVal.isNull());

	iOk = false;
	a2 = Json::DmDraft::Attachment::fromJsonVal(jsonVal, &iOk);
	QVERIFY(iOk == true);
	QVERIFY(!a2.isNull());

	QVERIFY(a2.name() == name);
	QVERIFY(a2.data() == data);
	QVERIFY(a2.path() == path);
	QVERIFY(a2.createdFromText() == false);

	/* == */
	a1.setName(name);
	a1.setData(data);
	a1.setPath(path);
	a1.setCreatedFromText(createdFromText);

	iOk = false;
	iOk = a1.toJsonVal(jsonVal);
	QVERIFY(iOk == true);
	QVERIFY(!jsonVal.isNull());

	iOk = false;
	a2 = Json::DmDraft::Attachment::fromJsonVal(jsonVal, &iOk);
	QVERIFY(iOk == true);
	QVERIFY(!a2.isNull());

	QVERIFY(a2.name() == name);
	QVERIFY(a2.data() == data);
	QVERIFY(a2.path() == path);
	QVERIFY(a2.createdFromText() == createdFromText);
}

void TestDraftDb::dmDraftJsonConversion(void)
{
	Json::DmDraft d1, d2;
	Isds::Envelope dmEnvelope;
	dmEnvelope.setDmAnnotation("dmAnnotation");
	const QStringList dmRecipients({"recip1", "recip2"});
	Json::DmDraft::AttachmentList dmAttachments;
	{
		Json::DmDraft::Attachment a;
		a.setName("name");
		a.setData("data");
		a.setPath("path");
		a.setCreatedFromText(true);
		dmAttachments.append(a);
	}
	const QString shortMessageText("shortMessageText");
	const qint64 shortMessageTextFmt = 1;
	const bool cntrlDowmloadMessage = true;
	const bool cntrlUploadMessageRm = true;

	bool iOk = false;
	QJsonValue jsonVal;

	QVERIFY(d1.isNull());
	QVERIFY(d2.isNull());
	QVERIFY(d1 == d2);

	d1.setDmEnvelope(dmEnvelope);
	d1.setDmRecipients(dmRecipients);
	d1.setDmAttachments(dmAttachments);
	d1.setShortMessageText(shortMessageText);
	d1.setShortMessageTextFmt(shortMessageTextFmt);
	d1.setCntrlDowmloadMessage(cntrlDowmloadMessage);
	d1.setCntrlUploadMessageRm(cntrlUploadMessageRm);

	QVERIFY(!d1.isNull());
	QVERIFY(d2.isNull());
	QVERIFY(d1 != d2);

	/* Null value converts onto null object. */
	iOk = false;
	d2 = Json::DmDraft::fromJsonVal(QJsonValue(), &iOk);
	QVERIFY(iOk == true);
	QVERIFY(d2.isNull());

	/* Null object converts onto null value. */
	iOk = false;
	iOk = d2.toJsonVal(jsonVal);
	QVERIFY(iOk == true);
	QVERIFY(jsonVal.isNull());

	iOk = false;
	iOk = d1.toJsonVal(jsonVal);
	QVERIFY(iOk == true);
	QVERIFY(!jsonVal.isNull());

	iOk = false;
	d2 = Json::DmDraft::fromJsonVal(jsonVal, &iOk);
	QVERIFY(iOk == true);
	QVERIFY(!d2.isNull());

	QVERIFY(d2.dmEnvelope() == dmEnvelope);
	QVERIFY(d2.dmRecipients() == dmRecipients);
	QVERIFY(d2.dmAttachments() == dmAttachments);
	QVERIFY(d2.shortMessageText() == shortMessageText);
	QVERIFY(d2.shortMessageTextFmt() == shortMessageTextFmt);
	QVERIFY(d2.cntrlDowmloadMessage() == cntrlDowmloadMessage);
	QVERIFY(d2.cntrlUploadMessageRm() == cntrlUploadMessageRm);

	QVERIFY(!d1.isNull());
	QVERIFY(!d2.isNull());
	QVERIFY(d1 == d2);

	/* Envelope. */
	d1.setDmEnvelope(Isds::Envelope());
	d1.setDmRecipients(dmRecipients);
	d1.setDmAttachments(dmAttachments);
	d1.setShortMessageText(shortMessageText);
	d1.setShortMessageTextFmt(shortMessageTextFmt);
	d1.setCntrlDowmloadMessage(cntrlDowmloadMessage);
	d1.setCntrlUploadMessageRm(cntrlUploadMessageRm);

	iOk = false;
	iOk = d1.toJsonVal(jsonVal);
	QVERIFY(iOk == true);
	QVERIFY(!jsonVal.isNull());

	iOk = false;
	d2 = Json::DmDraft::fromJsonVal(jsonVal, &iOk);
	QVERIFY(iOk == true);
	QVERIFY(!d2.isNull());

	//QVERIFY(d2.dmEnvelope().isNull()); /* TODO - Null value should be equal to non-null with defaults. */
	QVERIFY(d2.dmRecipients() == dmRecipients);
	QVERIFY(d2.dmAttachments() == dmAttachments);
	QVERIFY(d2.shortMessageText() == shortMessageText);
	QVERIFY(d2.shortMessageTextFmt() == shortMessageTextFmt);
	QVERIFY(d2.cntrlDowmloadMessage() == cntrlDowmloadMessage);
	QVERIFY(d2.cntrlUploadMessageRm() == cntrlUploadMessageRm);

	QVERIFY(!d1.isNull());
	QVERIFY(!d2.isNull());
	//QVERIFY(d1 == d2);

	/* Recipients. */
	d1.setDmEnvelope(dmEnvelope);
	d1.setDmRecipients(QStringList());
	d1.setDmAttachments(dmAttachments);
	d1.setShortMessageText(shortMessageText);
	d1.setShortMessageTextFmt(shortMessageTextFmt);
	d1.setCntrlDowmloadMessage(cntrlDowmloadMessage);
	d1.setCntrlUploadMessageRm(cntrlUploadMessageRm);

	iOk = false;
	iOk = d1.toJsonVal(jsonVal);
	QVERIFY(iOk == true);
	QVERIFY(!jsonVal.isNull());

	iOk = false;
	d2 = Json::DmDraft::fromJsonVal(jsonVal, &iOk);
	QVERIFY(iOk == true);
	QVERIFY(!d2.isNull());

	QVERIFY(d2.dmEnvelope() == dmEnvelope);
	QVERIFY(d2.dmRecipients().isEmpty());
	QVERIFY(d2.dmAttachments() == dmAttachments);
	QVERIFY(d2.shortMessageText() == shortMessageText);
	QVERIFY(d2.shortMessageTextFmt() == shortMessageTextFmt);
	QVERIFY(d2.cntrlDowmloadMessage() == cntrlDowmloadMessage);
	QVERIFY(d2.cntrlUploadMessageRm() == cntrlUploadMessageRm);

	/* Attachments. */
	d1.setDmEnvelope(dmEnvelope);
	d1.setDmRecipients(dmRecipients);
	d1.setDmAttachments(Json::DmDraft::AttachmentList());
	d1.setShortMessageText(shortMessageText);
	d1.setShortMessageTextFmt(shortMessageTextFmt);
	d1.setCntrlDowmloadMessage(cntrlDowmloadMessage);
	d1.setCntrlUploadMessageRm(cntrlUploadMessageRm);

	iOk = false;
	iOk = d1.toJsonVal(jsonVal);
	QVERIFY(iOk == true);
	QVERIFY(!jsonVal.isNull());

	iOk = false;
	d2 = Json::DmDraft::fromJsonVal(jsonVal, &iOk);
	QVERIFY(iOk == true);
	QVERIFY(!d2.isNull());

	QVERIFY(d2.dmEnvelope() == dmEnvelope);
	QVERIFY(d2.dmRecipients() == dmRecipients);
	QVERIFY(d2.dmAttachments().isEmpty());
	QVERIFY(d2.shortMessageText() == shortMessageText);
	QVERIFY(d2.shortMessageTextFmt() == shortMessageTextFmt);
	QVERIFY(d2.cntrlDowmloadMessage() == cntrlDowmloadMessage);
	QVERIFY(d2.cntrlUploadMessageRm() == cntrlUploadMessageRm);

	/* Short message text. */
	d1.setDmEnvelope(dmEnvelope);
	d1.setDmRecipients(dmRecipients);
	d1.setDmAttachments(dmAttachments);
	d1.setShortMessageText(QString());
	d1.setShortMessageTextFmt(shortMessageTextFmt);
	d1.setCntrlDowmloadMessage(cntrlDowmloadMessage);
	d1.setCntrlUploadMessageRm(cntrlUploadMessageRm);

	iOk = false;
	iOk = d1.toJsonVal(jsonVal);
	QVERIFY(iOk == true);
	QVERIFY(!jsonVal.isNull());

	iOk = false;
	d2 = Json::DmDraft::fromJsonVal(jsonVal, &iOk);
	QVERIFY(iOk == true);
	QVERIFY(!d2.isNull());

	QVERIFY(d2.dmEnvelope() == dmEnvelope);
	QVERIFY(d2.dmRecipients() == dmRecipients);
	QVERIFY(d2.dmAttachments() == dmAttachments);
	QVERIFY(d2.shortMessageText().isNull());
	QVERIFY(d2.shortMessageTextFmt() == shortMessageTextFmt);
	QVERIFY(d2.cntrlDowmloadMessage() == cntrlDowmloadMessage);
	QVERIFY(d2.cntrlUploadMessageRm() == cntrlUploadMessageRm);

	d1.setDmEnvelope(dmEnvelope);
	d1.setDmRecipients(dmRecipients);
	d1.setDmAttachments(dmAttachments);
	d1.setShortMessageText("");
	d1.setShortMessageTextFmt(shortMessageTextFmt);
	d1.setCntrlDowmloadMessage(cntrlDowmloadMessage);
	d1.setCntrlUploadMessageRm(cntrlUploadMessageRm);

	iOk = false;
	iOk = d1.toJsonVal(jsonVal);
	QVERIFY(iOk == true);
	QVERIFY(!jsonVal.isNull());

	iOk = false;
	d2 = Json::DmDraft::fromJsonVal(jsonVal, &iOk);
	QVERIFY(iOk == true);
	QVERIFY(!d2.isNull());

	QVERIFY(d2.dmEnvelope() == dmEnvelope);
	QVERIFY(d2.dmRecipients() == dmRecipients);
	QVERIFY(d2.dmAttachments() == dmAttachments);
	//QVERIFY(!d2.shortMessageText().isNull());
	QVERIFY(d2.shortMessageText().isEmpty());
	QVERIFY(d2.shortMessageTextFmt() == shortMessageTextFmt);
	QVERIFY(d2.cntrlDowmloadMessage() == cntrlDowmloadMessage);
	QVERIFY(d2.cntrlUploadMessageRm() == cntrlUploadMessageRm);

	/* Short message text format. */
	d1.setDmEnvelope(dmEnvelope);
	d1.setDmRecipients(dmRecipients);
	d1.setDmAttachments(dmAttachments);
	d1.setShortMessageText(shortMessageText);
	d1.setShortMessageTextFmt(-1);
	d1.setCntrlDowmloadMessage(cntrlDowmloadMessage);
	d1.setCntrlUploadMessageRm(cntrlUploadMessageRm);

	iOk = false;
	iOk = d1.toJsonVal(jsonVal);
	QVERIFY(iOk == true);
	QVERIFY(!jsonVal.isNull());

	iOk = false;
	d2 = Json::DmDraft::fromJsonVal(jsonVal, &iOk);
	QVERIFY(iOk == true);
	QVERIFY(!d2.isNull());

	QVERIFY(d2.dmEnvelope() == dmEnvelope);
	QVERIFY(d2.dmRecipients() == dmRecipients);
	QVERIFY(d2.dmAttachments() == dmAttachments);
	QVERIFY(d2.shortMessageText() == shortMessageText);
	QVERIFY(d2.shortMessageTextFmt() == -1);
	QVERIFY(d2.cntrlDowmloadMessage() == cntrlDowmloadMessage);
	QVERIFY(d2.cntrlUploadMessageRm() == cntrlUploadMessageRm);

	/* cntrlDowmloadMessage */
	d1.setDmEnvelope(dmEnvelope);
	d1.setDmRecipients(dmRecipients);
	d1.setDmAttachments(dmAttachments);
	d1.setShortMessageText(shortMessageText);
	d1.setShortMessageTextFmt(shortMessageTextFmt);
	d1.setCntrlDowmloadMessage(false);
	d1.setCntrlUploadMessageRm(cntrlUploadMessageRm);

	iOk = false;
	iOk = d1.toJsonVal(jsonVal);
	QVERIFY(iOk == true);
	QVERIFY(!jsonVal.isNull());

	iOk = false;
	d2 = Json::DmDraft::fromJsonVal(jsonVal, &iOk);
	QVERIFY(iOk == true);
	QVERIFY(!d2.isNull());

	QVERIFY(d2.dmEnvelope() == dmEnvelope);
	QVERIFY(d2.dmRecipients() == dmRecipients);
	QVERIFY(d2.dmAttachments() == dmAttachments);
	QVERIFY(d2.shortMessageText() == shortMessageText);
	QVERIFY(d2.shortMessageTextFmt() == shortMessageTextFmt);
	QVERIFY(d2.cntrlDowmloadMessage() == false);
	QVERIFY(d2.cntrlUploadMessageRm() == cntrlUploadMessageRm);

	/* cntrlUploadMessageRm */
	d1.setDmEnvelope(dmEnvelope);
	d1.setDmRecipients(dmRecipients);
	d1.setDmAttachments(dmAttachments);
	d1.setShortMessageText(shortMessageText);
	d1.setShortMessageTextFmt(shortMessageTextFmt);
	d1.setCntrlDowmloadMessage(cntrlDowmloadMessage);
	d1.setCntrlUploadMessageRm(false);

	iOk = false;
	iOk = d1.toJsonVal(jsonVal);
	QVERIFY(iOk == true);
	QVERIFY(!jsonVal.isNull());

	iOk = false;
	d2 = Json::DmDraft::fromJsonVal(jsonVal, &iOk);
	QVERIFY(iOk == true);
	QVERIFY(!d2.isNull());

	QVERIFY(d2.dmEnvelope() == dmEnvelope);
	QVERIFY(d2.dmRecipients() == dmRecipients);
	QVERIFY(d2.dmAttachments() == dmAttachments);
	QVERIFY(d2.shortMessageText() == shortMessageText);
	QVERIFY(d2.shortMessageTextFmt() == shortMessageTextFmt);
	QVERIFY(d2.cntrlDowmloadMessage() == cntrlDowmloadMessage);
	QVERIFY(d2.cntrlUploadMessageRm() == false);

	/* == */
	d1.setDmEnvelope(dmEnvelope);
	d1.setDmRecipients(dmRecipients);
	d1.setDmAttachments(dmAttachments);
	d1.setShortMessageText(shortMessageText);
	d1.setShortMessageTextFmt(shortMessageTextFmt);
	d1.setCntrlDowmloadMessage(cntrlDowmloadMessage);
	d1.setCntrlUploadMessageRm(cntrlUploadMessageRm);

	iOk = false;
	iOk = d1.toJsonVal(jsonVal);
	QVERIFY(iOk == true);
	QVERIFY(!jsonVal.isNull());

	iOk = false;
	d2 = Json::DmDraft::fromJsonVal(jsonVal, &iOk);
	QVERIFY(iOk == true);
	QVERIFY(!d2.isNull());

	QVERIFY(d2.dmEnvelope() == dmEnvelope);
	QVERIFY(d2.dmRecipients() == dmRecipients);
	QVERIFY(d2.dmAttachments() == dmAttachments);
	QVERIFY(d2.shortMessageText() == shortMessageText);
	QVERIFY(d2.shortMessageTextFmt() == shortMessageTextFmt);
	QVERIFY(d2.cntrlDowmloadMessage() == cntrlDowmloadMessage);
	QVERIFY(d2.cntrlUploadMessageRm() == cntrlUploadMessageRm);
}

void TestDraftDb::testDraftDbInsert(void)
{
	bool ret = false;
	QList<DraftEntry> draftEntries;
	const AcntId aId1("username01", true);
	const AcntId aId2("username01", false);
	qint64 entryId = -1;

	_init();

	draftDbPtr = new (::std::nothrow) DraftDb(m_connectionPrefix, false);
	QVERIFY(draftDbPtr != Q_NULLPTR);
	QVERIFY(draftDbPtr->openDb(m_dbPath, SQLiteDb::CREATE_MISSING));
	QVERIFY(QFileInfo::exists(m_dbPath));

	QSignalSpy spyInserted(draftDbPtr, SIGNAL(draftsInserted(DraftEntryList)));
	QSignalSpy spyUpdated(draftDbPtr, SIGNAL(draftsUpdated(DraftEntryList)));
	QSignalSpy spyDeleted(draftDbPtr, SIGNAL(draftsDeleted(Json::Int64StringList)));
	QSignalSpy spyCountChanged(draftDbPtr, SIGNAL(draftCountChanged(DraftCounts)));

	ret = draftDbPtr->getDraftListing(Json::Int64StringList({-1}), draftEntries);
	QVERIFY(ret);
	QCOMPARE(spyInserted.count(), 0);
	QCOMPARE(spyUpdated.count(), 0);
	QCOMPARE(spyDeleted.count(), 0);
	QCOMPARE(spyCountChanged.count(), 0);
	QVERIFY(draftEntries.isEmpty());

	ret = draftDbPtr->getDraftListing(Json::Int64StringList({0}), draftEntries);
	QVERIFY(ret);
	QCOMPARE(spyInserted.count(), 0);
	QCOMPARE(spyUpdated.count(), 0);
	QCOMPARE(spyDeleted.count(), 0);
	QCOMPARE(spyCountChanged.count(), 0);
	QVERIFY(draftEntries.isEmpty());

	ret = draftDbPtr->getDraftListing(Json::Int64StringList({1}), draftEntries);
	QVERIFY(ret);
	QCOMPARE(spyInserted.count(), 0);
	QCOMPARE(spyUpdated.count(), 0);
	QCOMPARE(spyDeleted.count(), 0);
	QCOMPARE(spyCountChanged.count(), 0);
	QVERIFY(draftEntries.isEmpty());

	/* Invalid account identifier should fail. */
	ret = draftDbPtr->insertDraft(AcntId(), Json::DmDraft());
	QVERIFY(!ret);
	QCOMPARE(spyInserted.count(), 0);
	QCOMPARE(spyUpdated.count(), 0);
	QCOMPARE(spyDeleted.count(), 0);
	QCOMPARE(spyCountChanged.count(), 0);
	QVERIFY(draftEntries.isEmpty());

	Json::DmDraft d;
	{
		Isds::Envelope dmEnvelope;
		dmEnvelope.setDmAnnotation("dmAnnotation");
		const QStringList dmRecipients({"recip1", "recip2"});
		const QString shortMessageText("shortMessageText");
		const qint64 shortMessageTextFmt = 1;
		const bool cntrlDowmloadMessage = true;
		const bool cntrlUploadMessageRm = true;

		d.setDmEnvelope(dmEnvelope);
		d.setDmRecipients(dmRecipients);
		d.setShortMessageText(shortMessageText);
		d.setShortMessageTextFmt(shortMessageTextFmt);
		d.setCntrlDowmloadMessage(cntrlDowmloadMessage);
		d.setCntrlUploadMessageRm(cntrlUploadMessageRm);
	}

	/* Inserting valid draft -- data. */
	{
		Json::DmDraft::AttachmentList dmAttachments;
		{
			Json::DmDraft::Attachment a;

			a.setName("text.txt");
			a.setData("text data 001");
			dmAttachments.append(a);

			a.setName("text.txt");
			a.setData("text data 002");
			a.setCreatedFromText(true); /* Second is created fom text. */
			dmAttachments.append(a);

			a.setName("text3.txt");
			a.setData("text data 003");
			a.setCreatedFromText(false);
			dmAttachments.append(a);
		}

		d.setDmAttachments(dmAttachments);
	}

	{
		ret = draftDbPtr->insertDraft(aId1, d);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 1);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 1);

		QList<QVariant> arguments = spyInserted.takeFirst();
		QCOMPARE(arguments.size(), 1);
		QVERIFY(arguments.at(0).canConvert<DraftEntryList>());
		const DraftEntryList entries =
		    qvariant_cast<DraftEntryList>(arguments.at(0));
		QCOMPARE(entries.size(), 1);
		const DraftEntry &entry = entries.at(0);
		QVERIFY(entry.id >= 0);
		QCOMPARE(entry.dmDraft.dmAttachments().size(), 3);
		{
			QSet<QString> paths;

			int i = 0;
			for (const Json::DmDraft::Attachment &a : entry.dmDraft.dmAttachments()) {
				QVERIFY(a.name().isNull());
				QVERIFY(a.data().isNull());
				QVERIFY(!a.path().isEmpty());
				QVERIFY(QFileInfo::exists(a.path()));
				QVERIFY(QFileInfo(a.path()).isFile());

				paths.insert(a.path());

				/* Second is created fom text. */
				if (i == 1) {
					QVERIFY(a.createdFromText());
				} else {
					QVERIFY(!a.createdFromText());
				}

				++i;
			}

			QCOMPARE(paths.size(), 3);
		}

		arguments = spyCountChanged.takeFirst();
		QCOMPARE(arguments.size(), 1);
		QVERIFY(arguments.at(0).canConvert<DraftCounts>());
		const DraftCounts counts =
		    qvariant_cast<DraftCounts>(arguments.at(0));
		QCOMPARE(counts.size(), 1);
		QVERIFY(counts.keys().contains(aId1));
		QCOMPARE(counts[aId1], 1);
	}

	/* Inserting same valid draft again. */
	{
		ret = draftDbPtr->insertDraft(aId1, d);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 1);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 1);

		QList<QVariant> arguments = spyInserted.takeFirst();
		QCOMPARE(arguments.size(), 1);
		QVERIFY(arguments.at(0).canConvert<DraftEntryList>());
		const DraftEntryList entries =
		    qvariant_cast<DraftEntryList>(arguments.at(0));
		QCOMPARE(entries.size(), 1);
		const DraftEntry &entry = entries.at(0);
		QVERIFY(entry.id >= 0);
		QCOMPARE(entry.dmDraft.dmAttachments().size(), 3);

		{
			QSet<QString> paths;

			int i = 0;
			for (const Json::DmDraft::Attachment &a : entry.dmDraft.dmAttachments()) {
				QVERIFY(a.name().isNull());
				QVERIFY(a.data().isNull());
				QVERIFY(!a.path().isEmpty());
				QVERIFY(QFileInfo::exists(a.path()));
				QVERIFY(QFileInfo(a.path()).isFile());

				paths.insert(a.path());

				/* Second is created fom text. */
				if (i == 1) {
					QVERIFY(a.createdFromText());
				} else {
					QVERIFY(!a.createdFromText());
				}

				++i;
			}

			QCOMPARE(paths.size(), 3);
		}

		entryId = entry.id;

		arguments = spyCountChanged.takeFirst();
		QCOMPARE(arguments.size(), 1);
		QVERIFY(arguments.at(0).canConvert<DraftCounts>());
		const DraftCounts counts =
		    qvariant_cast<DraftCounts>(arguments.at(0));
		QCOMPARE(counts.size(), 1);
		QVERIFY(counts.keys().contains(aId1));
		QCOMPARE(counts[aId1], 2);
	}

	{
		DraftEntryList entries;
		ret = draftDbPtr->getDraftListing(
		    Json::Int64StringList({entryId}), entries);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 0);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 0);

		QCOMPARE(entries.size(), 1);
		{
			QSet<QString> paths;
			for (const DraftEntry &entry : entries) {
				QCOMPARE(entry.acntId, aId1);
				QCOMPARE(entry.dmDraft.dmEnvelope().dmAnnotation(), QString("dmAnnotation"));

				int i = 0;
				for (const Json::DmDraft::Attachment &a : entry.dmDraft.dmAttachments()) {
					QVERIFY(a.name().isNull());
					QVERIFY(a.data().isNull());
					QVERIFY(!a.path().isEmpty());
					QVERIFY(QFileInfo::exists(a.path()));
					QVERIFY(QFileInfo(a.path()).isFile());

					paths.insert(a.path());

					/* Second is created fom text. */
					if (i == 1) {
						QVERIFY(a.createdFromText());
					} else {
						QVERIFY(!a.createdFromText());
					}

					++i;
				}
			}
			QCOMPARE(paths.size(), 3);
		}
	}

	/* Inserting invalid draft -- missing data. */
	{
		Json::DmDraft::AttachmentList dmAttachments;
		{
			Json::DmDraft::Attachment a;

			a.setName("text.txt");
			a.setData("");
			dmAttachments.append(a);

			a.setName("text.txt");
			a.setData("");
			a.setCreatedFromText(true); /* Second is created fom text. */
			dmAttachments.append(a);

			a.setName("text3.txt");
			a.setData("");
			a.setCreatedFromText(false);
			dmAttachments.append(a);
		}

		d.setDmAttachments(dmAttachments);
	}

	{
		ret = draftDbPtr->insertDraft(aId1, d);
		QVERIFY(!ret);
		QCOMPARE(spyInserted.count(), 0);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 0);
	}

	/* Inserting valid draft - file. */
	{
		Json::DmDraft::AttachmentList dmAttachments;
		{
			Json::DmDraft::Attachment a;

			a.setName("text.txt"); /* Should be ignored. */
			a.setPath(m_attachPath);
			dmAttachments.append(a);
		}

		d.setDmAttachments(dmAttachments);
	}

	{
		ret = draftDbPtr->insertDraft(aId1, d);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 1);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 1);

		QList<QVariant> arguments = spyInserted.takeFirst();
		QCOMPARE(arguments.size(), 1);
		QVERIFY(arguments.at(0).canConvert<DraftEntryList>());
		const DraftEntryList entries =
		    qvariant_cast<DraftEntryList>(arguments.at(0));
		QCOMPARE(entries.size(), 1);
		const DraftEntry &entry = entries.at(0);
		QVERIFY(entry.id >= 0);
		QCOMPARE(entry.dmDraft.dmAttachments().size(), 1);
		QSet<QString> paths;
		for (const Json::DmDraft::Attachment &a : entry.dmDraft.dmAttachments()) {
			QVERIFY(a.name().isNull());
			QVERIFY(a.data().isNull());
			QVERIFY(!a.path().isEmpty());
			QVERIFY(QFileInfo::exists(a.path()));
			QVERIFY(QFileInfo(a.path()).isFile());

			paths.insert(a.path());
		}
		QCOMPARE(paths.size(), 1);

		entryId = entry.id;

		arguments = spyCountChanged.takeFirst();
		QCOMPARE(arguments.size(), 1);
		QVERIFY(arguments.at(0).canConvert<DraftCounts>());
		const DraftCounts counts =
		    qvariant_cast<DraftCounts>(arguments.at(0));
		QCOMPARE(counts.size(), 1);
		QVERIFY(counts.keys().contains(aId1));
		QCOMPARE(counts[aId1], 3);
	}


	{
		DraftEntryList entries;
		ret = draftDbPtr->getDraftListing(
		    Json::Int64StringList({entryId}), entries);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 0);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 0);

		QCOMPARE(entries.size(), 1);
		QSet<QString> paths;
		for (const DraftEntry &entry : entries) {
			QCOMPARE(entry.acntId, aId1);
			QCOMPARE(entry.dmDraft.dmEnvelope().dmAnnotation(), QString("dmAnnotation"));
			for (const Json::DmDraft::Attachment &a : entry.dmDraft.dmAttachments()) {
				QVERIFY(a.name().isNull());
				QVERIFY(a.data().isNull());
				QVERIFY(!a.path().isEmpty());
				QVERIFY(QFileInfo::exists(a.path()));
				QVERIFY(QFileInfo(a.path()).isFile());

				paths.insert(a.path());
			}
		}
		QCOMPARE(paths.size(), 1);
	}

	{
		DraftEntryList entries;
		ret = draftDbPtr->getDraftListing(QList<AcntId>({aId1}), entries);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 0);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 0);

		QCOMPARE(entries.size(), 3);
		QSet<QString> paths;
		for (const DraftEntry &entry : entries) {
			QCOMPARE(entry.acntId, aId1);
			QCOMPARE(entry.dmDraft.dmEnvelope().dmAnnotation(), QString("dmAnnotation"));
			for (const Json::DmDraft::Attachment &a : entry.dmDraft.dmAttachments()) {
				QVERIFY(a.name().isNull());
				QVERIFY(a.data().isNull());
				QVERIFY(!a.path().isEmpty());
				QVERIFY(QFileInfo::exists(a.path()));
				QVERIFY(QFileInfo(a.path()).isFile());

				paths.insert(a.path());
			}
		}
		QCOMPARE(paths.size(), 7);
	}

	{
		DraftEntryList entries;
		ret = draftDbPtr->getDraftListing(QList<AcntId>({aId2}), entries);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 0);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 0);

		QCOMPARE(entries.size(), 0);
	}

	_cleanup();
}

void TestDraftDb::testDraftDbUpdate(void)
{
	bool ret = false;
	QList<DraftEntry> draftEntries;
	const AcntId aId1("username01", true);
	const AcntId aId2("username01", false);
	qint64 entryId = -1;
	QSet<QString> origPaths;
	QString oldPath;
	QString newPath;

	_init();

	draftDbPtr = new (::std::nothrow) DraftDb(m_connectionPrefix, false);
	QVERIFY(draftDbPtr != Q_NULLPTR);
	QVERIFY(draftDbPtr->openDb(m_dbPath, SQLiteDb::CREATE_MISSING));
	QVERIFY(QFileInfo::exists(m_dbPath));

	QSignalSpy spyInserted(draftDbPtr, SIGNAL(draftsInserted(DraftEntryList)));
	QSignalSpy spyUpdated(draftDbPtr, SIGNAL(draftsUpdated(DraftEntryList)));
	QSignalSpy spyDeleted(draftDbPtr, SIGNAL(draftsDeleted(Json::Int64StringList)));
	QSignalSpy spyCountChanged(draftDbPtr, SIGNAL(draftCountChanged(DraftCounts)));

	Json::DmDraft d;
	{
		Isds::Envelope dmEnvelope;
		dmEnvelope.setDmAnnotation("dmAnnotation");
		const QStringList dmRecipients({"recip1", "recip2"});
		const QString shortMessageText("shortMessageText");
		const qint64 shortMessageTextFmt = 1;
		const bool cntrlDowmloadMessage = true;
		const bool cntrlUploadMessageRm = true;

		d.setDmEnvelope(dmEnvelope);
		d.setDmRecipients(dmRecipients);
		d.setShortMessageText(shortMessageText);
		d.setShortMessageTextFmt(shortMessageTextFmt);
		d.setCntrlDowmloadMessage(cntrlDowmloadMessage);
		d.setCntrlUploadMessageRm(cntrlUploadMessageRm);
	}

	/* Inserting valid draft -- data. */
	{
		Json::DmDraft::AttachmentList dmAttachments;
		{
			Json::DmDraft::Attachment a;

			a.setName("text.txt");
			a.setData("text data 001");
			dmAttachments.append(a);

			a.setName("text.txt");
			a.setData("text data 002");
			a.setCreatedFromText(true); /* Second is created fom text. */
			dmAttachments.append(a);

			a.setName("text3.txt");
			a.setData("text data 003");
			a.setCreatedFromText(false);
			dmAttachments.append(a);
		}

		d.setDmAttachments(dmAttachments);
	}

	{
		ret = draftDbPtr->insertDraft(aId1, d);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 1);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 1);

		QList<QVariant> arguments = spyInserted.takeFirst();
		QCOMPARE(arguments.size(), 1);
		QVERIFY(arguments.at(0).canConvert<DraftEntryList>());
		const DraftEntryList entries =
		    qvariant_cast<DraftEntryList>(arguments.at(0));
		QCOMPARE(entries.size(), 1);
		const DraftEntry &entry = entries.at(0);
		QVERIFY(entry.id >= 0);
		QCOMPARE(entry.dmDraft.dmAttachments().size(), 3);
		{
			QSet<QString> paths;

			int i = 0;
			for (const Json::DmDraft::Attachment &a : entry.dmDraft.dmAttachments()) {
				QVERIFY(a.name().isNull());
				QVERIFY(a.data().isNull());
				QVERIFY(!a.path().isEmpty());
				QVERIFY(QFileInfo::exists(a.path()));
				QVERIFY(QFileInfo(a.path()).isFile());

				paths.insert(a.path());

				/* Second is created fom text. */
				if (i == 1) {
					QVERIFY(a.createdFromText());
				} else {
					QVERIFY(!a.createdFromText());
				}

				++i;
			}
			QCOMPARE(paths.size(), 3);
		}

		entryId = entry.id;

		arguments = spyCountChanged.takeFirst();
		QCOMPARE(arguments.size(), 1);
		QVERIFY(arguments.at(0).canConvert<DraftCounts>());
		const DraftCounts counts =
		    qvariant_cast<DraftCounts>(arguments.at(0));
		QCOMPARE(counts.size(), 1);
		QVERIFY(counts.keys().contains(aId1));
		QCOMPARE(counts[aId1], 1);
	}

	/* Inserting valid draft - file. */
	{
		Json::DmDraft::AttachmentList dmAttachments;
		{
			Json::DmDraft::Attachment a;

			a.setPath(m_attachPath);
			dmAttachments.append(a);
		}

		d.setDmAttachments(dmAttachments);
	}

	{
		ret = draftDbPtr->insertDraft(aId1, d);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 1);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 1);

		QList<QVariant> arguments = spyInserted.takeFirst();
		QCOMPARE(arguments.size(), 1);
		QVERIFY(arguments.at(0).canConvert<DraftEntryList>());
		const DraftEntryList entries =
		    qvariant_cast<DraftEntryList>(arguments.at(0));
		QCOMPARE(entries.size(), 1);
		const DraftEntry &entry = entries.at(0);
		QVERIFY(entry.id >= 0);
		QCOMPARE(entry.dmDraft.dmAttachments().size(), 1);
		QSet<QString> paths;
		for (const Json::DmDraft::Attachment &a : entry.dmDraft.dmAttachments()) {
			QVERIFY(a.name().isNull());
			QVERIFY(a.data().isNull());
			QVERIFY(!a.path().isEmpty());
			QVERIFY(QFileInfo::exists(a.path()));
			QVERIFY(QFileInfo(a.path()).isFile());

			paths.insert(a.path());
		}
		QCOMPARE(paths.size(), 1);

		arguments = spyCountChanged.takeFirst();
		QCOMPARE(arguments.size(), 1);
		QVERIFY(arguments.at(0).canConvert<DraftCounts>());
		const DraftCounts counts =
		    qvariant_cast<DraftCounts>(arguments.at(0));
		QCOMPARE(counts.size(), 1);
		QVERIFY(counts.keys().contains(aId1));
		QCOMPARE(counts[aId1], 2);
	}

	{
		DraftEntryList entries;
		ret = draftDbPtr->getDraftListing(
		    Json::Int64StringList({entryId}), entries);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 0);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 0);

		QCOMPARE(entries.size(), 1);
		QSet<QString> paths;
		for (const DraftEntry &entry : entries) {
			QCOMPARE(entry.acntId, aId1);
			QCOMPARE(entry.dmDraft.dmEnvelope().dmAnnotation(), QString("dmAnnotation"));

			int i = 0;
			for (const Json::DmDraft::Attachment &a : entry.dmDraft.dmAttachments()) {
				QVERIFY(a.name().isNull());
				QVERIFY(a.data().isNull());
				QVERIFY(!a.path().isEmpty());
				QVERIFY(QFileInfo::exists(a.path()));
				QVERIFY(QFileInfo(a.path()).isFile());

				paths.insert(a.path());

				/* Second is created fom text. */
				if (i == 1) {
					QVERIFY(a.createdFromText());
				} else {
					QVERIFY(!a.createdFromText());
				}

				++i;
			}
		}
		QCOMPARE(paths.size(), 3);

		/* Get draft. */
		d = entries.at(0).dmDraft;
		origPaths = paths;
	}

	/* Modify second attachment. */
	{
		Json::DmDraft::AttachmentList dmAttachments = d.dmAttachments();
		{
			Json::DmDraft::Attachment &a = dmAttachments[1];
			oldPath = a.path();

			a.setName("text4.txt");
			a.setData("text data 004");
			a.setPath(QString());
		}

		d.setDmAttachments(dmAttachments);
	}

	{
		ret = draftDbPtr->updateDraft(entryId, aId1, d);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 0);
		QCOMPARE(spyUpdated.count(), 1);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 0);

		QList<QVariant> arguments = spyUpdated.takeFirst();
		QCOMPARE(arguments.size(), 1);
		QVERIFY(arguments.at(0).canConvert<DraftEntryList>());
		const DraftEntryList entries =
		    qvariant_cast<DraftEntryList>(arguments.at(0));
		QCOMPARE(entries.size(), 1);
		const DraftEntry &entry = entries.at(0);
		QVERIFY(entry.id >= 0);
		QCOMPARE(entry.dmDraft.dmAttachments().size(), 3);
		QSet<QString> paths;
		int i = 0;
		for (const Json::DmDraft::Attachment &a : entry.dmDraft.dmAttachments()) {
			QVERIFY(a.name().isNull());
			QVERIFY(a.data().isNull());
			QVERIFY(!a.path().isEmpty());
			QVERIFY(QFileInfo::exists(a.path()));
			QVERIFY(QFileInfo(a.path()).isFile());

			paths.insert(a.path());

			/* Second is created fom text. */
			if (i == 1) {
				QVERIFY(a.createdFromText());
			} else {
				QVERIFY(!a.createdFromText());
			}

			++i;
		}
		QCOMPARE(paths.size(), 3);

		/* One removed. */
		{
			QSet<QString> removed = origPaths;
			removed -= paths;
			QCOMPARE(removed.size(), 1);
			QCOMPARE(*(removed.begin()), oldPath);
		}
		/* One new. */
		{
			QSet<QString> added = paths;
			added -= origPaths;
			QCOMPARE(added.size(), 1);
			newPath = *(added.begin());
		}

		entryId = entry.id;

	}

	{
		DraftEntryList entries;
		ret = draftDbPtr->getDraftListing(
		    Json::Int64StringList({entryId}), entries);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 0);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 0);

		QCOMPARE(entries.size(), 1);
		QSet<QString> paths;

		for (const DraftEntry &entry : entries) {
			QCOMPARE(entry.acntId, aId1);
			QCOMPARE(entry.dmDraft.dmEnvelope().dmAnnotation(), QString("dmAnnotation"));

			int i = 0;
			for (const Json::DmDraft::Attachment &a : entry.dmDraft.dmAttachments()) {
				QVERIFY(a.name().isNull());
				QVERIFY(a.data().isNull());
				QVERIFY(!a.path().isEmpty());
				QVERIFY(QFileInfo::exists(a.path()));
				QVERIFY(QFileInfo(a.path()).isFile());

				paths.insert(a.path());

				/* Second is created fom text. */
				if (i == 1) {
					QVERIFY(a.createdFromText());
				} else {
					QVERIFY(!a.createdFromText());
				}

				++i;
			}
		}
		QCOMPARE(paths.size(), 3);

		{
			/* Remove new, add old, receive original. */
			QSet<QString> paths2 = paths;
			paths2.remove(newPath);
			QCOMPARE(paths2.size(), 2);
			paths2.insert(oldPath);
			QCOMPARE(paths2.size(), 3);
			QCOMPARE(paths2, origPaths);
		}

		/* Get draft. */
		d = entries.at(0).dmDraft;
		origPaths = paths;

	}

	/* Change account. */
	{
		ret = draftDbPtr->updateDraft(entryId, aId2, d);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 0);
		QCOMPARE(spyUpdated.count(), 1);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 1);

		QList<QVariant> arguments = spyUpdated.takeFirst();
		QCOMPARE(arguments.size(), 1);
		QVERIFY(arguments.at(0).canConvert<DraftEntryList>());
		const DraftEntryList entries =
		    qvariant_cast<DraftEntryList>(arguments.at(0));
		QCOMPARE(entries.size(), 1);
		const DraftEntry &entry = entries.at(0);
		QVERIFY(entry.id >= 0);
		QCOMPARE(entry.dmDraft.dmAttachments().size(), 3);
		QSet<QString> paths;
		int i = 0;
		for (const Json::DmDraft::Attachment &a : entry.dmDraft.dmAttachments()) {
			QVERIFY(a.name().isNull());
			QVERIFY(a.data().isNull());
			QVERIFY(!a.path().isEmpty());
			QVERIFY(QFileInfo::exists(a.path()));
			QVERIFY(QFileInfo(a.path()).isFile());

			paths.insert(a.path());

			/* Second is created fom text. */
			if (i == 1) {
				QVERIFY(a.createdFromText());
			} else {
				QVERIFY(!a.createdFromText());
			}

			++i;
		}
		QCOMPARE(paths.size(), 3);

		/* One removed. */
		{
			QSet<QString> removed = origPaths;
			removed -= paths;
			QCOMPARE(removed.size(), 3);
		}
		/* One new. */
		{
			QSet<QString> added = paths;
			added -= origPaths;
			QCOMPARE(added.size(), 3);
		}

		arguments = spyCountChanged.takeFirst();
		QCOMPARE(arguments.size(), 1);
		QVERIFY(arguments.at(0).canConvert<DraftCounts>());
		const DraftCounts counts =
		    qvariant_cast<DraftCounts>(arguments.at(0));
		QCOMPARE(counts.size(), 2);
		QVERIFY(counts.keys().contains(aId1));
		QCOMPARE(counts[aId1], 1);
		QVERIFY(counts.keys().contains(aId2));
		QCOMPARE(counts[aId2], 1);
	}

	{
		DraftEntryList entries;
		ret = draftDbPtr->getDraftListing(
		    Json::Int64StringList({entryId}), entries);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 0);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 0);

		QCOMPARE(entries.size(), 1);
		QSet<QString> paths;
		for (const DraftEntry &entry : entries) {
			QCOMPARE(entry.acntId, aId2);
			QCOMPARE(entry.dmDraft.dmEnvelope().dmAnnotation(), QString("dmAnnotation"));

			int i = 0;
			for (const Json::DmDraft::Attachment &a : entry.dmDraft.dmAttachments()) {
				QVERIFY(a.name().isNull());
				QVERIFY(a.data().isNull());
				QVERIFY(!a.path().isEmpty());
				QVERIFY(QFileInfo::exists(a.path()));
				QVERIFY(QFileInfo(a.path()).isFile());

				paths.insert(a.path());

				/* Second is created fom text. */
				if (i == 1) {
					QVERIFY(a.createdFromText());
				} else {
					QVERIFY(!a.createdFromText());
				}

				++i;
			}
		}
		QCOMPARE(paths.size(), 3);
	}

	_cleanup();
}

void TestDraftDb::testDraftDbDelete(void)
{
	bool ret = false;
	const AcntId aId1("username01", true);
	const AcntId aId2("username01", false);
	qint64 entryId1 = -1;
	qint64 entryId2 = -1;
	QSet<QString> draftPaths1;
	QSet<QString> draftPaths2;

	_init();

	draftDbPtr = new (::std::nothrow) DraftDb(m_connectionPrefix, false);
	QVERIFY(draftDbPtr != Q_NULLPTR);
	QVERIFY(draftDbPtr->openDb(m_dbPath, SQLiteDb::CREATE_MISSING));
	QVERIFY(QFileInfo::exists(m_dbPath));

	QSignalSpy spyInserted(draftDbPtr, SIGNAL(draftsInserted(DraftEntryList)));
	QSignalSpy spyUpdated(draftDbPtr, SIGNAL(draftsUpdated(DraftEntryList)));
	QSignalSpy spyDeleted(draftDbPtr, SIGNAL(draftsDeleted(Json::Int64StringList)));
	QSignalSpy spyCountChanged(draftDbPtr, SIGNAL(draftCountChanged(DraftCounts)));

	Json::DmDraft d;
	{
		Isds::Envelope dmEnvelope;
		dmEnvelope.setDmAnnotation("dmAnnotation");
		const QStringList dmRecipients({"recip1", "recip2"});
		const QString shortMessageText("shortMessageText");
		const qint64 shortMessageTextFmt = 1;
		const bool cntrlDowmloadMessage = true;
		const bool cntrlUploadMessageRm = true;

		d.setDmEnvelope(dmEnvelope);
		d.setDmRecipients(dmRecipients);
		d.setShortMessageText(shortMessageText);
		d.setShortMessageTextFmt(shortMessageTextFmt);
		d.setCntrlDowmloadMessage(cntrlDowmloadMessage);
		d.setCntrlUploadMessageRm(cntrlUploadMessageRm);
	}

	/* Inserting valid draft -- data. */
	{
		Json::DmDraft::AttachmentList dmAttachments;
		{
			Json::DmDraft::Attachment a;

			a.setName("text.txt");
			a.setData("text data 001");
			dmAttachments.append(a);

			a.setName("text.txt");
			a.setData("text data 002");
			dmAttachments.append(a);

			a.setName("text3.txt");
			a.setData("text data 003");
			dmAttachments.append(a);
		}

		d.setDmAttachments(dmAttachments);
	}

	{
		ret = draftDbPtr->insertDraft(aId1, d);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 1);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 1);

		QList<QVariant> arguments = spyInserted.takeFirst();
		QCOMPARE(arguments.size(), 1);
		QVERIFY(arguments.at(0).canConvert<DraftEntryList>());
		const DraftEntryList entries =
		    qvariant_cast<DraftEntryList>(arguments.at(0));
		QCOMPARE(entries.size(), 1);
		const DraftEntry &entry = entries.at(0);
		QVERIFY(entry.id >= 0);
		QCOMPARE(entry.dmDraft.dmAttachments().size(), 3);
		QSet<QString> paths;
		for (const Json::DmDraft::Attachment &a : entry.dmDraft.dmAttachments()) {
			QVERIFY(a.name().isNull());
			QVERIFY(a.data().isNull());
			QVERIFY(!a.path().isEmpty());
			QVERIFY(QFileInfo::exists(a.path()));
			QVERIFY(QFileInfo(a.path()).isFile());

			paths.insert(a.path());
		}
		QCOMPARE(paths.size(), 3);

		draftPaths1 = macroStdMove(paths);
		entryId1 = entry.id;

		arguments = spyCountChanged.takeFirst();
		QCOMPARE(arguments.size(), 1);
		QVERIFY(arguments.at(0).canConvert<DraftCounts>());
		const DraftCounts counts =
		    qvariant_cast<DraftCounts>(arguments.at(0));
		QCOMPARE(counts.size(), 1);
		QVERIFY(counts.keys().contains(aId1));
		QCOMPARE(counts[aId1], 1);
	}

	/* Inserting valid draft - file. */
	{
		Json::DmDraft::AttachmentList dmAttachments;
		{
			Json::DmDraft::Attachment a;

			a.setPath(m_attachPath);
			dmAttachments.append(a);
		}

		d.setDmAttachments(dmAttachments);
	}

	{
		ret = draftDbPtr->insertDraft(aId2, d);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 1);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 1);

		QList<QVariant> arguments = spyInserted.takeFirst();
		QCOMPARE(arguments.size(), 1);
		QVERIFY(arguments.at(0).canConvert<DraftEntryList>());
		const DraftEntryList entries =
		    qvariant_cast<DraftEntryList>(arguments.at(0));
		QCOMPARE(entries.size(), 1);
		const DraftEntry &entry = entries.at(0);
		QVERIFY(entry.id >= 0);
		QCOMPARE(entry.dmDraft.dmAttachments().size(), 1);
		QSet<QString> paths;
		for (const Json::DmDraft::Attachment &a : entry.dmDraft.dmAttachments()) {
			QVERIFY(a.name().isNull());
			QVERIFY(a.data().isNull());
			QVERIFY(!a.path().isEmpty());
			QVERIFY(QFileInfo::exists(a.path()));
			QVERIFY(QFileInfo(a.path()).isFile());

			paths.insert(a.path());
		}
		QCOMPARE(paths.size(), 1);

		draftPaths2 = macroStdMove(paths);
		entryId2 = entry.id;

		arguments = spyCountChanged.takeFirst();
		QCOMPARE(arguments.size(), 1);
		QVERIFY(arguments.at(0).canConvert<DraftCounts>());
		const DraftCounts counts =
		    qvariant_cast<DraftCounts>(arguments.at(0));
		QCOMPARE(counts.size(), 1);
		QVERIFY(counts.keys().contains(aId2));
		QCOMPARE(counts[aId2], 1);
	}

	{
		DraftEntryList entries;
		ret = draftDbPtr->getDraftListing(
		    Json::Int64StringList({entryId1}), entries);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 0);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 0);

		QCOMPARE(entries.size(), 1);
		QSet<QString> paths;
		for (const DraftEntry &entry : entries) {
			QCOMPARE(entry.acntId, aId1);
			QCOMPARE(entry.dmDraft.dmEnvelope().dmAnnotation(), QString("dmAnnotation"));
			for (const Json::DmDraft::Attachment &a : entry.dmDraft.dmAttachments()) {
				QVERIFY(a.name().isNull());
				QVERIFY(a.data().isNull());
				QVERIFY(!a.path().isEmpty());
				QVERIFY(QFileInfo::exists(a.path()));
				QVERIFY(QFileInfo(a.path()).isFile());

				paths.insert(a.path());
			}
		}
		QCOMPARE(paths.size(), 3);
		QCOMPARE(draftPaths1, paths);
	}

	{
		DraftEntryList entries;
		ret = draftDbPtr->getDraftListing(
		    Json::Int64StringList({entryId2}), entries);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 0);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 0);

		QCOMPARE(entries.size(), 1);
		QSet<QString> paths;
		for (const DraftEntry &entry : entries) {
			QCOMPARE(entry.acntId, aId2);
			QCOMPARE(entry.dmDraft.dmEnvelope().dmAnnotation(), QString("dmAnnotation"));
			for (const Json::DmDraft::Attachment &a : entry.dmDraft.dmAttachments()) {
				QVERIFY(a.name().isNull());
				QVERIFY(a.data().isNull());
				QVERIFY(!a.path().isEmpty());
				QVERIFY(QFileInfo::exists(a.path()));
				QVERIFY(QFileInfo(a.path()).isFile());

				paths.insert(a.path());
			}
		}
		QCOMPARE(paths.size(), 1);
		QCOMPARE(draftPaths2, paths);
	}

	/* Empty list should fail. */
	{
		ret = draftDbPtr->deleteDrafts(Json::Int64StringList());
		QVERIFY(!ret);
		QCOMPARE(spyInserted.count(), 0);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 0);
	}

	/* Empty list should fail. */
	{
		ret = draftDbPtr->deleteDrafts(QList<AcntId>());
		QVERIFY(!ret);
		QCOMPARE(spyInserted.count(), 0);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 0);
	}

	/* Delete first draft. */
	{
		ret = draftDbPtr->deleteDrafts(Json::Int64StringList({entryId1}));
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 0);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 1);
		QCOMPARE(spyCountChanged.count(), 1);

		QList<QVariant> arguments = spyDeleted.takeFirst();
		QCOMPARE(arguments.size(), 1);
		QVERIFY(arguments.at(0).canConvert<Json::Int64StringList>());
		const Json::Int64StringList identifiers =
		    qvariant_cast<Json::Int64StringList>(arguments.at(0));
		QCOMPARE(identifiers.size(), 1);
		QCOMPARE(identifiers.first(), entryId1);

		arguments = spyCountChanged.takeFirst();
		QCOMPARE(arguments.size(), 1);
		QVERIFY(arguments.at(0).canConvert<DraftCounts>());
		const DraftCounts counts =
		    qvariant_cast<DraftCounts>(arguments.at(0));
		QCOMPARE(counts.size(), 1);
		QVERIFY(counts.keys().contains(aId1));
		QCOMPARE(counts[aId1], 0);

		for (const QString &path : draftPaths1) {
			QVERIFY(!QFileInfo::exists(path));
		}
	}

	{
		DraftEntryList entries;
		ret = draftDbPtr->getDraftListing(QList<AcntId>({aId1}), entries);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 0);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 0);

		QCOMPARE(entries.size(), 0);
	}

	/* Delete entries of account with no drafts. */
	{
		ret = draftDbPtr->deleteDrafts(QList<AcntId>({aId1}));
		QVERIFY(!ret);
		QCOMPARE(spyInserted.count(), 0);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 0);
	}

	/* Delete second draft. */
	{
		ret = draftDbPtr->deleteDrafts(QList<AcntId>({aId2}));
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 0);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 1);
		QCOMPARE(spyCountChanged.count(), 1);

		QList<QVariant> arguments = spyDeleted.takeFirst();
		QCOMPARE(arguments.size(), 1);
		QVERIFY(arguments.at(0).canConvert<Json::Int64StringList>());
		const Json::Int64StringList identifiers =
		    qvariant_cast<Json::Int64StringList>(arguments.at(0));
		QCOMPARE(identifiers.size(), 1);
		QCOMPARE(identifiers.first(), entryId2);

		arguments = spyCountChanged.takeFirst();
		QCOMPARE(arguments.size(), 1);
		QVERIFY(arguments.at(0).canConvert<DraftCounts>());
		const DraftCounts counts =
		    qvariant_cast<DraftCounts>(arguments.at(0));
		QCOMPARE(counts.size(), 1);
		QVERIFY(counts.keys().contains(aId2));
		QCOMPARE(counts[aId2], 0);
	}

	{
		DraftEntryList entries;
		ret = draftDbPtr->getDraftListing(QList<AcntId>({aId2}), entries);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 0);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 0);

		QCOMPARE(entries.size(), 0);
	}

	_cleanup();
}

void TestDraftDb::testsReadWhenMissingFiles1(void)
{
	bool ret = false;
	const AcntId aId1("username01", true);
	const AcntId aId2("username01", false);
	qint64 entryId1 = -1;
	qint64 entryId2 = -1;
	QSet<QString> draftPaths1;
	QSet<QString> draftPaths2;
	QSet<QString> missingPaths1;

	_init();

	draftDbPtr = new (::std::nothrow) DraftDb(m_connectionPrefix, false);
	QVERIFY(draftDbPtr != Q_NULLPTR);
	QVERIFY(draftDbPtr->openDb(m_dbPath, SQLiteDb::CREATE_MISSING));
	QVERIFY(QFileInfo::exists(m_dbPath));

	QSignalSpy spyInserted(draftDbPtr, SIGNAL(draftsInserted(DraftEntryList)));
	QSignalSpy spyUpdated(draftDbPtr, SIGNAL(draftsUpdated(DraftEntryList)));
	QSignalSpy spyDeleted(draftDbPtr, SIGNAL(draftsDeleted(Json::Int64StringList)));
	QSignalSpy spyCountChanged(draftDbPtr, SIGNAL(draftCountChanged(DraftCounts)));

	Json::DmDraft d;
	{
		Isds::Envelope dmEnvelope;
		dmEnvelope.setDmAnnotation("dmAnnotation");
		const QStringList dmRecipients({"recip1", "recip2"});
		const QString shortMessageText("shortMessageText");
		const qint64 shortMessageTextFmt = 1;
		const bool cntrlDowmloadMessage = true;
		const bool cntrlUploadMessageRm = true;

		d.setDmEnvelope(dmEnvelope);
		d.setDmRecipients(dmRecipients);
		d.setShortMessageText(shortMessageText);
		d.setShortMessageTextFmt(shortMessageTextFmt);
		d.setCntrlDowmloadMessage(cntrlDowmloadMessage);
		d.setCntrlUploadMessageRm(cntrlUploadMessageRm);
	}

	/* Inserting valid draft -- data. */
	{
		Json::DmDraft::AttachmentList dmAttachments;
		{
			Json::DmDraft::Attachment a;

			a.setName("text.txt");
			a.setData("text data 001");
			dmAttachments.append(a);

			a.setName("text.txt");
			a.setData("text data 002");
			dmAttachments.append(a);

			a.setName("text3.txt");
			a.setData("text data 003");
			dmAttachments.append(a);
		}

		d.setDmAttachments(dmAttachments);
	}

	{
		ret = draftDbPtr->insertDraft(aId1, d);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 1);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 1);

		QList<QVariant> arguments = spyInserted.takeFirst();
		QCOMPARE(arguments.size(), 1);
		QVERIFY(arguments.at(0).canConvert<DraftEntryList>());
		const DraftEntryList entries =
		    qvariant_cast<DraftEntryList>(arguments.at(0));
		QCOMPARE(entries.size(), 1);
		const DraftEntry &entry = entries.at(0);
		QVERIFY(entry.id >= 0);
		QCOMPARE(entry.dmDraft.dmAttachments().size(), 3);
		QSet<QString> paths;
		for (const Json::DmDraft::Attachment &a : entry.dmDraft.dmAttachments()) {
			QVERIFY(a.name().isNull());
			QVERIFY(a.data().isNull());
			QVERIFY(!a.path().isEmpty());
			QVERIFY(QFileInfo::exists(a.path()));
			QVERIFY(QFileInfo(a.path()).isFile());

			paths.insert(a.path());
		}
		QCOMPARE(paths.size(), 3);

		draftPaths1 = macroStdMove(paths);
		entryId1 = entry.id;

		arguments = spyCountChanged.takeFirst();
		QCOMPARE(arguments.size(), 1);
		QVERIFY(arguments.at(0).canConvert<DraftCounts>());
		const DraftCounts counts =
		    qvariant_cast<DraftCounts>(arguments.at(0));
		QCOMPARE(counts.size(), 1);
		QVERIFY(counts.keys().contains(aId1));
		QCOMPARE(counts[aId1], 1);
	}

	/* Inserting valid draft - file. */
	{
		Json::DmDraft::AttachmentList dmAttachments;
		{
			Json::DmDraft::Attachment a;

			a.setPath(m_attachPath);
			dmAttachments.append(a);
		}

		d.setDmAttachments(dmAttachments);
	}

	{
		ret = draftDbPtr->insertDraft(aId2, d);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 1);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 1);

		QList<QVariant> arguments = spyInserted.takeFirst();
		QCOMPARE(arguments.size(), 1);
		QVERIFY(arguments.at(0).canConvert<DraftEntryList>());
		const DraftEntryList entries =
		    qvariant_cast<DraftEntryList>(arguments.at(0));
		QCOMPARE(entries.size(), 1);
		const DraftEntry &entry = entries.at(0);
		QVERIFY(entry.id >= 0);
		QCOMPARE(entry.dmDraft.dmAttachments().size(), 1);
		QSet<QString> paths;
		for (const Json::DmDraft::Attachment &a : entry.dmDraft.dmAttachments()) {
			QVERIFY(a.name().isNull());
			QVERIFY(a.data().isNull());
			QVERIFY(!a.path().isEmpty());
			QVERIFY(QFileInfo::exists(a.path()));
			QVERIFY(QFileInfo(a.path()).isFile());

			paths.insert(a.path());
		}
		QCOMPARE(paths.size(), 1);

		draftPaths2 = macroStdMove(paths);
		entryId2 = entry.id;

		arguments = spyCountChanged.takeFirst();
		QCOMPARE(arguments.size(), 1);
		QVERIFY(arguments.at(0).canConvert<DraftCounts>());
		const DraftCounts counts =
		    qvariant_cast<DraftCounts>(arguments.at(0));
		QCOMPARE(counts.size(), 1);
		QVERIFY(counts.keys().contains(aId2));
		QCOMPARE(counts[aId2], 1);
	}

	{
		DraftEntryList entries;
		ret = draftDbPtr->getDraftListing(
		    Json::Int64StringList({entryId1}), entries);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 0);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 0);

		QCOMPARE(entries.size(), 1);
		QSet<QString> paths;
		for (const DraftEntry &entry : entries) {
			QCOMPARE(entry.acntId, aId1);
			QCOMPARE(entry.dmDraft.dmEnvelope().dmAnnotation(), QString("dmAnnotation"));
			for (const Json::DmDraft::Attachment &a : entry.dmDraft.dmAttachments()) {
				QVERIFY(a.name().isNull());
				QVERIFY(a.data().isNull());
				QVERIFY(!a.path().isEmpty());
				QVERIFY(QFileInfo::exists(a.path()));
				QVERIFY(QFileInfo(a.path()).isFile());

				paths.insert(a.path());
			}
		}
		QCOMPARE(paths.size(), 3);
		QCOMPARE(draftPaths1, paths);
	}

	{
		DraftEntryList entries;
		ret = draftDbPtr->getDraftListing(
		    Json::Int64StringList({entryId2}), entries);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 0);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 0);

		QCOMPARE(entries.size(), 1);
		QSet<QString> paths;
		for (const DraftEntry &entry : entries) {
			QCOMPARE(entry.acntId, aId2);
			QCOMPARE(entry.dmDraft.dmEnvelope().dmAnnotation(), QString("dmAnnotation"));
			for (const Json::DmDraft::Attachment &a : entry.dmDraft.dmAttachments()) {
				QVERIFY(a.name().isNull());
				QVERIFY(a.data().isNull());
				QVERIFY(!a.path().isEmpty());
				QVERIFY(QFileInfo::exists(a.path()));
				QVERIFY(QFileInfo(a.path()).isFile());

				paths.insert(a.path());
			}
		}
		QCOMPARE(paths.size(), 1);
		QCOMPARE(draftPaths2, paths);
	}

	/* Delete one attachment. */
	{
		QString deletedPath = *(draftPaths1.begin());
		QVERIFY(QFileInfo::exists(deletedPath));
		QVERIFY(QFile(deletedPath).remove());
		QVERIFY(!QFileInfo::exists(deletedPath));

		missingPaths1.insert(deletedPath);
	}

	{
		DraftEntryList entries;
		ret = draftDbPtr->getDraftListing(
		    Json::Int64StringList({entryId1}), entries);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 0);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 0);

		QCOMPARE(entries.size(), 1);
		QSet<QString> paths;
		for (const DraftEntry &entry : entries) {
			QCOMPARE(entry.acntId, aId1);
			QCOMPARE(entry.dmDraft.dmEnvelope().dmAnnotation(), QString("dmAnnotation"));
			for (const Json::DmDraft::Attachment &a : entry.dmDraft.dmAttachments()) {
				QVERIFY(a.name().isNull());
				QVERIFY(a.data().isNull());
				QVERIFY(!a.path().isEmpty());
				QVERIFY(QFileInfo::exists(a.path()));
				QVERIFY(QFileInfo(a.path()).isFile());

				paths.insert(a.path());
			}
		}
		QCOMPARE(paths.size(), 2);

		paths += missingPaths1;
		QCOMPARE(draftPaths1, paths);
	}

	{
		DraftEntryList entries;
		ret = draftDbPtr->getDraftListing(
		    QList<AcntId>({aId1}), entries);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 0);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 0);

		QCOMPARE(entries.size(), 1);
		QSet<QString> paths;
		for (const DraftEntry &entry : entries) {
			QCOMPARE(entry.acntId, aId1);
			QCOMPARE(entry.dmDraft.dmEnvelope().dmAnnotation(), QString("dmAnnotation"));
			for (const Json::DmDraft::Attachment &a : entry.dmDraft.dmAttachments()) {
				QVERIFY(a.name().isNull());
				QVERIFY(a.data().isNull());
				QVERIFY(!a.path().isEmpty());
				QVERIFY(QFileInfo::exists(a.path()));
				QVERIFY(QFileInfo(a.path()).isFile());

				paths.insert(a.path());
			}
		}
		QCOMPARE(paths.size(), 2);

		draftPaths1 -= missingPaths1;
		QCOMPARE(draftPaths1, paths);
	}

	/* Delete all attachments. */
	{
		for (const QString &deletedPath : draftPaths1) {
			QVERIFY(QFileInfo::exists(deletedPath));
			QVERIFY(QFile(deletedPath).remove());
			QVERIFY(!QFileInfo::exists(deletedPath));

			missingPaths1.insert(deletedPath);
		}
	}

	{
		DraftEntryList entries;
		ret = draftDbPtr->getDraftListing(
		    Json::Int64StringList({entryId1}), entries);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 0);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 0);

		QCOMPARE(entries.size(), 1);
		QSet<QString> paths;
		for (const DraftEntry &entry : entries) {
			QCOMPARE(entry.acntId, aId1);
			QCOMPARE(entry.dmDraft.dmEnvelope().dmAnnotation(), QString("dmAnnotation"));
			for (const Json::DmDraft::Attachment &a : entry.dmDraft.dmAttachments()) {
				QVERIFY(a.name().isNull());
				QVERIFY(a.data().isNull());
				QVERIFY(!a.path().isEmpty());
				QVERIFY(QFileInfo::exists(a.path()));
				QVERIFY(QFileInfo(a.path()).isFile());

				paths.insert(a.path());
			}
		}
		QCOMPARE(paths.size(), 0);
	}

	{
		DraftEntryList entries;
		ret = draftDbPtr->getDraftListing(
		    QList<AcntId>({aId1}), entries);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 0);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 0);

		QCOMPARE(entries.size(), 1);
		QSet<QString> paths;
		for (const DraftEntry &entry : entries) {
			QCOMPARE(entry.acntId, aId1);
			QCOMPARE(entry.dmDraft.dmEnvelope().dmAnnotation(), QString("dmAnnotation"));
			for (const Json::DmDraft::Attachment &a : entry.dmDraft.dmAttachments()) {
				QVERIFY(a.name().isNull());
				QVERIFY(a.data().isNull());
				QVERIFY(!a.path().isEmpty());
				QVERIFY(QFileInfo::exists(a.path()));
				QVERIFY(QFileInfo(a.path()).isFile());

				paths.insert(a.path());
			}
		}
		QCOMPARE(paths.size(), 0);
	}

	/* Delete first draft. */
	{
		ret = draftDbPtr->deleteDrafts(Json::Int64StringList({entryId1}));
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 0);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 1);
		QCOMPARE(spyCountChanged.count(), 1);

		QList<QVariant> arguments = spyDeleted.takeFirst();
		QCOMPARE(arguments.size(), 1);
		QVERIFY(arguments.at(0).canConvert<Json::Int64StringList>());
		const Json::Int64StringList identifiers =
		    qvariant_cast<Json::Int64StringList>(arguments.at(0));
		QCOMPARE(identifiers.size(), 1);
		QCOMPARE(identifiers.first(), entryId1);

		arguments = spyCountChanged.takeFirst();
		QCOMPARE(arguments.size(), 1);
		QVERIFY(arguments.at(0).canConvert<DraftCounts>());
		const DraftCounts counts =
		    qvariant_cast<DraftCounts>(arguments.at(0));
		QCOMPARE(counts.size(), 1);
		QVERIFY(counts.keys().contains(aId1));
		QCOMPARE(counts[aId1], 0);

		for (const QString &path : draftPaths1) {
			QVERIFY(!QFileInfo::exists(path));
		}
	}

	_cleanup();
}

void TestDraftDb::testsReadWhenMissingFiles2(void)
{
	bool ret = false;
	const AcntId aId1("username01", true);
	const AcntId aId2("username01", false);
	qint64 entryId1 = -1;
	qint64 entryId2 = -1;
	QSet<QString> draftPaths1;
	QSet<QString> draftPaths2;

	_init();

	draftDbPtr = new (::std::nothrow) DraftDb(m_connectionPrefix, false);
	QVERIFY(draftDbPtr != Q_NULLPTR);
	QVERIFY(draftDbPtr->openDb(m_dbPath, SQLiteDb::CREATE_MISSING));
	QVERIFY(QFileInfo::exists(m_dbPath));

	QSignalSpy spyInserted(draftDbPtr, SIGNAL(draftsInserted(DraftEntryList)));
	QSignalSpy spyUpdated(draftDbPtr, SIGNAL(draftsUpdated(DraftEntryList)));
	QSignalSpy spyDeleted(draftDbPtr, SIGNAL(draftsDeleted(Json::Int64StringList)));
	QSignalSpy spyCountChanged(draftDbPtr, SIGNAL(draftCountChanged(DraftCounts)));

	Json::DmDraft d;
	{
		Isds::Envelope dmEnvelope;
		dmEnvelope.setDmAnnotation("dmAnnotation");
		const QStringList dmRecipients({"recip1", "recip2"});
		const QString shortMessageText("shortMessageText");
		const qint64 shortMessageTextFmt = 1;
		const bool cntrlDowmloadMessage = true;
		const bool cntrlUploadMessageRm = true;

		d.setDmEnvelope(dmEnvelope);
		d.setDmRecipients(dmRecipients);
		d.setShortMessageText(shortMessageText);
		d.setShortMessageTextFmt(shortMessageTextFmt);
		d.setCntrlDowmloadMessage(cntrlDowmloadMessage);
		d.setCntrlUploadMessageRm(cntrlUploadMessageRm);
	}

	/* Inserting valid draft -- data. */
	{
		Json::DmDraft::AttachmentList dmAttachments;
		{
			Json::DmDraft::Attachment a;

			a.setName("text.txt");
			a.setData("text data 001");
			dmAttachments.append(a);

			a.setName("text.txt");
			a.setData("text data 002");
			dmAttachments.append(a);

			a.setName("text3.txt");
			a.setData("text data 003");
			dmAttachments.append(a);
		}

		d.setDmAttachments(dmAttachments);
	}

	{
		ret = draftDbPtr->insertDraft(aId1, d);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 1);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 1);

		QList<QVariant> arguments = spyInserted.takeFirst();
		QCOMPARE(arguments.size(), 1);
		QVERIFY(arguments.at(0).canConvert<DraftEntryList>());
		const DraftEntryList entries =
		    qvariant_cast<DraftEntryList>(arguments.at(0));
		QCOMPARE(entries.size(), 1);
		const DraftEntry &entry = entries.at(0);
		QVERIFY(entry.id >= 0);
		QCOMPARE(entry.dmDraft.dmAttachments().size(), 3);
		QSet<QString> paths;
		for (const Json::DmDraft::Attachment &a : entry.dmDraft.dmAttachments()) {
			QVERIFY(a.name().isNull());
			QVERIFY(a.data().isNull());
			QVERIFY(!a.path().isEmpty());
			QVERIFY(QFileInfo::exists(a.path()));
			QVERIFY(QFileInfo(a.path()).isFile());

			paths.insert(a.path());
		}
		QCOMPARE(paths.size(), 3);

		draftPaths1 = macroStdMove(paths);
		entryId1 = entry.id;

		arguments = spyCountChanged.takeFirst();
		QCOMPARE(arguments.size(), 1);
		QVERIFY(arguments.at(0).canConvert<DraftCounts>());
		const DraftCounts counts =
		    qvariant_cast<DraftCounts>(arguments.at(0));
		QCOMPARE(counts.size(), 1);
		QVERIFY(counts.keys().contains(aId1));
		QCOMPARE(counts[aId1], 1);
	}

	/* Inserting valid draft - file. */
	{
		Json::DmDraft::AttachmentList dmAttachments;
		{
			Json::DmDraft::Attachment a;

			a.setPath(m_attachPath);
			dmAttachments.append(a);
		}

		d.setDmAttachments(dmAttachments);
	}

	{
		ret = draftDbPtr->insertDraft(aId2, d);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 1);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 1);

		QList<QVariant> arguments = spyInserted.takeFirst();
		QCOMPARE(arguments.size(), 1);
		QVERIFY(arguments.at(0).canConvert<DraftEntryList>());
		const DraftEntryList entries =
		    qvariant_cast<DraftEntryList>(arguments.at(0));
		QCOMPARE(entries.size(), 1);
		const DraftEntry &entry = entries.at(0);
		QVERIFY(entry.id >= 0);
		QCOMPARE(entry.dmDraft.dmAttachments().size(), 1);
		QSet<QString> paths;
		for (const Json::DmDraft::Attachment &a : entry.dmDraft.dmAttachments()) {
			QVERIFY(a.name().isNull());
			QVERIFY(a.data().isNull());
			QVERIFY(!a.path().isEmpty());
			QVERIFY(QFileInfo::exists(a.path()));
			QVERIFY(QFileInfo(a.path()).isFile());

			paths.insert(a.path());
		}
		QCOMPARE(paths.size(), 1);

		draftPaths2 = macroStdMove(paths);
		entryId2 = entry.id;

		arguments = spyCountChanged.takeFirst();
		QCOMPARE(arguments.size(), 1);
		QVERIFY(arguments.at(0).canConvert<DraftCounts>());
		const DraftCounts counts =
		    qvariant_cast<DraftCounts>(arguments.at(0));
		QCOMPARE(counts.size(), 1);
		QVERIFY(counts.keys().contains(aId2));
		QCOMPARE(counts[aId2], 1);
	}

	{
		DraftEntryList entries;
		ret = draftDbPtr->getDraftListing(
		    Json::Int64StringList({entryId1}), entries);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 0);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 0);

		QCOMPARE(entries.size(), 1);
		QSet<QString> paths;
		for (const DraftEntry &entry : entries) {
			QCOMPARE(entry.acntId, aId1);
			QCOMPARE(entry.dmDraft.dmEnvelope().dmAnnotation(), QString("dmAnnotation"));
			for (const Json::DmDraft::Attachment &a : entry.dmDraft.dmAttachments()) {
				QVERIFY(a.name().isNull());
				QVERIFY(a.data().isNull());
				QVERIFY(!a.path().isEmpty());
				QVERIFY(QFileInfo::exists(a.path()));
				QVERIFY(QFileInfo(a.path()).isFile());

				paths.insert(a.path());
			}
		}
		QCOMPARE(paths.size(), 3);
		QCOMPARE(draftPaths1, paths);
	}

	{
		DraftEntryList entries;
		ret = draftDbPtr->getDraftListing(
		    Json::Int64StringList({entryId2}), entries);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 0);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 0);

		QCOMPARE(entries.size(), 1);
		QSet<QString> paths;
		for (const DraftEntry &entry : entries) {
			QCOMPARE(entry.acntId, aId2);
			QCOMPARE(entry.dmDraft.dmEnvelope().dmAnnotation(), QString("dmAnnotation"));
			for (const Json::DmDraft::Attachment &a : entry.dmDraft.dmAttachments()) {
				QVERIFY(a.name().isNull());
				QVERIFY(a.data().isNull());
				QVERIFY(!a.path().isEmpty());
				QVERIFY(QFileInfo::exists(a.path()));
				QVERIFY(QFileInfo(a.path()).isFile());

				paths.insert(a.path());
			}
		}
		QCOMPARE(paths.size(), 1);
		QCOMPARE(draftPaths2, paths);
	}

	/* Delete whole database directory. */
	{
		QVERIFY(QFileInfo::exists(m_dbAssociatedDirPath));
		QVERIFY(QDir(m_dbAssociatedDirPath).removeRecursively());
		QVERIFY(!QFileInfo::exists(m_dbAssociatedDirPath));
	}

	{
		DraftEntryList entries;
		ret = draftDbPtr->getDraftListing(
		    QList<AcntId>({aId1}), entries);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 0);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 0);

		QCOMPARE(entries.size(), 1);
		QSet<QString> paths;
		for (const DraftEntry &entry : entries) {
			QCOMPARE(entry.acntId, aId1);
			QCOMPARE(entry.dmDraft.dmEnvelope().dmAnnotation(), QString("dmAnnotation"));
			for (const Json::DmDraft::Attachment &a : entry.dmDraft.dmAttachments()) {
				QVERIFY(a.name().isNull());
				QVERIFY(a.data().isNull());
				QVERIFY(!a.path().isEmpty());
				QVERIFY(QFileInfo::exists(a.path()));
				QVERIFY(QFileInfo(a.path()).isFile());

				paths.insert(a.path());
			}
		}
		QCOMPARE(paths.size(), 0);
	}

	{
		DraftEntryList entries;
		ret = draftDbPtr->getDraftListing(
		    QList<AcntId>({aId2}), entries);
		QVERIFY(ret);
		QCOMPARE(spyInserted.count(), 0);
		QCOMPARE(spyUpdated.count(), 0);
		QCOMPARE(spyDeleted.count(), 0);
		QCOMPARE(spyCountChanged.count(), 0);

		QCOMPARE(entries.size(), 1);
		QSet<QString> paths;
		for (const DraftEntry &entry : entries) {
			QCOMPARE(entry.acntId, aId2);
			QCOMPARE(entry.dmDraft.dmEnvelope().dmAnnotation(), QString("dmAnnotation"));
			for (const Json::DmDraft::Attachment &a : entry.dmDraft.dmAttachments()) {
				QVERIFY(a.name().isNull());
				QVERIFY(a.data().isNull());
				QVERIFY(!a.path().isEmpty());
				QVERIFY(QFileInfo::exists(a.path()));
				QVERIFY(QFileInfo(a.path()).isFile());

				paths.insert(a.path());
			}
		}
		QCOMPARE(paths.size(), 0);
	}

	_cleanup();
}

void TestDraftDb::_init(void)
{
	QVERIFY(draftDbPtr == Q_NULLPTR);

	QFile(m_dbPath).remove();
	QDir(m_dbAssociatedDirPath).removeRecursively();

	QVERIFY(!QFileInfo::exists(m_dbPath));
	QVERIFY(!QFileInfo::exists(m_dbAssociatedDirPath));
}

void TestDraftDb::_cleanup(void)
{
	delete draftDbPtr; draftDbPtr = Q_NULLPTR;
}

QObject *newTestDraftDb(void)
{
	return new (::std::nothrow) TestDraftDb();
}

//QTEST_MAIN(TestDraftDb)
#include "test_draft_db.moc"
