// SPDX-FileCopyrightText: 2012 Jeremy Lainé <jeremy.laine@m4x.org>
//
// SPDX-License-Identifier: LGPL-2.1-or-later

#include "QXmppRpcIq.h"

#include "util.h"

#include <QObject>

static void checkVariant(const QVariant &value, const QByteArray &xml)
{
    // serialise
    QBuffer buffer;
    buffer.open(QIODevice::ReadWrite);
    QXmlStreamWriter writer(&buffer);
    QXmppRpcMarshaller::marshall(&writer, value);
    if (xml != buffer.data()) {
        qDebug() << "expect " << xml;
        qDebug() << "writing" << buffer.data();
    }
    QCOMPARE(buffer.data(), xml);

    // parse
    QStringList errors;
    QVariant test = QXmppRpcMarshaller::demarshall(xmlToDom(xml), errors);
    if (!errors.isEmpty()) {
        qDebug() << errors;
    }
    QCOMPARE(errors, QStringList());
    QCOMPARE(test, value);
}

class tst_QXmppRpcIq : public QObject
{
    Q_OBJECT

private:
    Q_SLOT void testBase64();
    Q_SLOT void testBool();
    Q_SLOT void testDateTime();
    Q_SLOT void testDouble();
    Q_SLOT void testInt();
    Q_SLOT void testNil();
    Q_SLOT void testString();

    Q_SLOT void testArray();
    Q_SLOT void testStruct();

    Q_SLOT void testInvoke();
    Q_SLOT void testResponse();
    Q_SLOT void testResponseFault();
};
void tst_QXmppRpcIq::testBase64()
{
    checkVariant(QByteArray("\0\1\2\3", 4),
                 QByteArray("<value><base64>AAECAw==</base64></value>"));
}

void tst_QXmppRpcIq::testBool()
{
    checkVariant(false,
                 QByteArray("<value><boolean>false</boolean></value>"));
    checkVariant(true,
                 QByteArray("<value><boolean>true</boolean></value>"));
}

void tst_QXmppRpcIq::testDateTime()
{
    checkVariant(QDateTime(QDate(1998, 7, 17), QTime(14, 8, 55)),
                 QByteArray("<value><dateTime.iso8601>1998-07-17T14:08:55</dateTime.iso8601></value>"));
}

void tst_QXmppRpcIq::testDouble()
{
    checkVariant(double(-12.214),
                 QByteArray("<value><double>-12.214</double></value>"));
}

void tst_QXmppRpcIq::testInt()
{
    checkVariant(int(-12),
                 QByteArray("<value><i4>-12</i4></value>"));
}

void tst_QXmppRpcIq::testNil()
{
    checkVariant(QVariant(),
                 QByteArray("<value><nil/></value>"));
}

void tst_QXmppRpcIq::testString()
{
    checkVariant(u"hello world"_s,
                 QByteArray("<value><string>hello world</string></value>"));
}

void tst_QXmppRpcIq::testArray()
{
    checkVariant(QVariantList() << u"hello world"_s << double(-12.214),
                 QByteArray("<value><array><data>"
                            "<value><string>hello world</string></value>"
                            "<value><double>-12.214</double></value>"
                            "</data></array></value>"));
}

void tst_QXmppRpcIq::testStruct()
{
    QMap<QString, QVariant> map;
    map["bar"] = u"hello \n world"_s;
    map["foo"] = double(-12.214);
    checkVariant(map,
                 QByteArray("<value><struct>"
                            "<member>"
                            "<name>bar</name>"
                            "<value><string>hello \n world</string></value>"
                            "</member>"
                            "<member>"
                            "<name>foo</name>"
                            "<value><double>-12.214</double></value>"
                            "</member>"
                            "</struct></value>"));
}

void tst_QXmppRpcIq::testInvoke()
{
    const QByteArray xml(
        "<iq"
        " id=\"rpc1\""
        " to=\"responder@company-a.com/jrpc-server\""
        " from=\"requester@company-b.com/jrpc-client\""
        " type=\"set\">"
        "<query xmlns=\"jabber:iq:rpc\">"
        "<methodCall>"
        "<methodName>examples.getStateName</methodName>"
        "<params>"
        "<param>"
        "<value><i4>6</i4></value>"
        "</param>"
        "<param>"
        "<value><string>two\nlines</string></value>"
        "</param>"
        "<param>"
        "<value><string><![CDATA[\n\n]]></string></value>"
        "</param>"
        "</params>"
        "</methodCall>"
        "</query>"
        "</iq>");

    QXmppRpcInvokeIq iq;
    parsePacket(iq, xml);
    QCOMPARE(iq.method(), u"examples.getStateName");
    QCOMPARE(iq.arguments(), QVariantList() << int(6) << u"two\nlines"_s << u"\n\n"_s);

    const auto data = packetToXml(iq);
    if (data != xml) {
        qDebug() << "expect " << xml;
        qDebug() << "writing" << data;
    }
    QCOMPARE(data, xml);
}

void tst_QXmppRpcIq::testResponse()
{
    const QByteArray xml(
        "<iq"
        " id=\"rpc1\""
        " to=\"requester@company-b.com/jrpc-client\""
        " from=\"responder@company-a.com/jrpc-server\""
        " type=\"result\">"
        "<query xmlns=\"jabber:iq:rpc\">"
        "<methodResponse>"
        "<params>"
        "<param>"
        "<value><string>Colorado</string></value>"
        "</param>"
        "</params>"
        "</methodResponse>"
        "</query>"
        "</iq>");

    QXmppRpcResponseIq iq;
    parsePacket(iq, xml);
    QCOMPARE(iq.faultCode(), 0);
    QCOMPARE(iq.faultString(), QString());
    QCOMPARE(iq.values(), QVariantList() << u"Colorado"_s);
    serializePacket(iq, xml);
}

void tst_QXmppRpcIq::testResponseFault()
{
    const QByteArray xml(
        "<iq"
        " id=\"rpc1\""
        " to=\"requester@company-b.com/jrpc-client\""
        " from=\"responder@company-a.com/jrpc-server\""
        " type=\"result\">"
        "<query xmlns=\"jabber:iq:rpc\">"
        "<methodResponse>"
        "<fault>"
        "<value>"
        "<struct>"
        "<member>"
        "<name>faultCode</name>"
        "<value><i4>404</i4></value>"
        "</member>"
        "<member>"
        "<name>faultString</name>"
        "<value><string>Not found</string></value>"
        "</member>"
        "</struct>"
        "</value>"
        "</fault>"
        "</methodResponse>"
        "</query>"
        "</iq>");

    QXmppRpcResponseIq iq;
    parsePacket(iq, xml);
    QCOMPARE(iq.faultCode(), 404);
    QCOMPARE(iq.faultString(), QLatin1String("Not found"));
    QCOMPARE(iq.values(), QVariantList());
    serializePacket(iq, xml);
}

QTEST_MAIN(tst_QXmppRpcIq)
#include "tst_qxmpprpciq.moc"
