#include "eetca.h" #include #include #include #include #include #include #include #include namespace { class MockCaServer : public QSslServer { public: explicit MockCaServer(const QByteArray &pkcs12) : m_pkcs12(pkcs12) { connect(this, &QSslServer::pendingConnectionAvailable, this, [this] { QSslSocket *socket = qobject_cast(nextPendingConnection()); if (socket == nullptr) { return; } connect(socket, &QSslSocket::readyRead, this, [this, socket] { const QByteArray request = socket->readAll(); const QByteArray firstLine = request.left(request.indexOf("\r\n")); const QList parts = firstLine.split(' '); if (parts.size() < 2 || !request.contains("Authorization: Bearer ")) { socket->disconnectFromHost(); return; } QByteArray body; int status = 200; if (parts.at(1) == "/api/request/renew") { body = "{\"reqId\":\"123e4567-e89b-42d3-a456-426614174000\"}"; status = 201; } else if (parts.at(1) == "/api/request/123e4567-e89b-42d3-a456-426614174000/status") { body = "{\"reqId\":\"123e4567-e89b-42d3-a456-426614174000\"," "\"state\":\"ISSUED\"}"; } else if (parts.at(1) == "/api/request/not-finished") { body = "{\"count\":1,\"data\":[\"123e4567-e89b-42d3-a456-426614174000\"]}"; } else if (parts.at(1) == "/api/request/123e4567-e89b-42d3-a456-426614174000/claim-download") { body = "{\"pkcs12\":\"" + m_pkcs12.toBase64() + "\",\"password\":\"ABcd1234\",\"certificate\":{" "\"sn\":\"12345\",\"validFrom\":\"2026-01-01T00:00:00+00:00\"," "\"validTo\":\"2027-01-01T00:00:00+00:00\",\"state\":\"VALID\"}}"; } else if (parts.at(1) == "/api/request/123e4567-e89b-42d3-a456-426614174000/ack-download") { status = 204; } else { status = 404; body = "{\"code\":4,\"message\":\"Unknown object.\"}"; } const QByteArray response = "HTTP/1.1 " + QByteArray::number(status) + " OK\r\n" "Content-Type: application/json\r\nContent-Length: " + QByteArray::number(body.size()) + "\r\nConnection: close\r\n\r\n" + body; socket->write(response); socket->disconnectFromHost(); }); }); } private: QByteArray m_pkcs12; }; } class EetCaIntegrationTest : public QObject { Q_OBJECT private slots: void runsCertificateWorkflow(); }; void EetCaIntegrationTest::runsCertificateWorkflow() { QTemporaryDir directory; QVERIFY(directory.isValid()); const QString keyPath = directory.filePath(QStringLiteral("key.pem")); const QString certPath = directory.filePath(QStringLiteral("certificate.pem")); const QString p12Path = directory.filePath(QStringLiteral("certificate.p12")); auto runOpenSsl = [](const QStringList &arguments) { QProcess process; process.start(QStringLiteral("openssl"), arguments); QVERIFY2(process.waitForFinished(30000), qPrintable(process.errorString())); QVERIFY2(process.exitStatus() == QProcess::NormalExit && process.exitCode() == 0, process.readAllStandardError().constData()); }; runOpenSsl({QStringLiteral("req"), QStringLiteral("-x509"), QStringLiteral("-newkey"), QStringLiteral("rsa:2048"), QStringLiteral("-nodes"), QStringLiteral("-subj"), QStringLiteral("/CN=localhost"), QStringLiteral("-keyout"), keyPath, QStringLiteral("-out"), certPath, QStringLiteral("-days"), QStringLiteral("1")}); runOpenSsl({QStringLiteral("pkcs12"), QStringLiteral("-export"), QStringLiteral("-out"), p12Path, QStringLiteral("-inkey"), keyPath, QStringLiteral("-in"), certPath, QStringLiteral("-passout"), QStringLiteral("pass:test")}); QFile p12File(p12Path); QVERIFY(p12File.open(QIODevice::ReadOnly)); const QByteArray p12 = p12File.readAll(); MockCaServer server(p12); QSslConfiguration ssl = QSslConfiguration::defaultConfiguration(); QFile keyFile(keyPath); QFile certificateFile(certPath); QVERIFY(keyFile.open(QIODevice::ReadOnly)); QVERIFY(certificateFile.open(QIODevice::ReadOnly)); ssl.setPrivateKey(QSslKey(keyFile.readAll(), QSsl::Rsa, QSsl::Pem)); ssl.setLocalCertificate(QSslCertificate(certificateFile.readAll(), QSsl::Pem)); server.setSslConfiguration(ssl); QVERIFY(server.listen(QHostAddress::LocalHost)); QSslConfiguration clientSsl = QSslConfiguration::defaultConfiguration(); clientSsl.setCaCertificates({ssl.localCertificate()}); EetCaClient client({ QUrl(QStringLiteral("https://localhost:%1/api").arg(server.serverPort())), p12Path, QByteArrayLiteral("test"), 5000, clientSsl }); QString error; QUuid requestId; QVERIFY2(client.requestRenewal(&requestId, &error), qPrintable(error)); QCOMPARE(requestId, QUuid(QStringLiteral("123e4567-e89b-42d3-a456-426614174000"))); EetCaRequestStatus status; QVERIFY2(client.requestStatus(requestId, &status, &error), qPrintable(error)); QCOMPARE(status.state, QStringLiteral("ISSUED")); QList unfinished; QVERIFY2(client.listUnfinished(&unfinished, &error), qPrintable(error)); QCOMPARE(unfinished, QList{requestId}); EetCaPkcs12 package; QVERIFY2(client.claimDownload(requestId, &package, &error), qPrintable(error)); QCOMPARE(package.pkcs12, p12); QCOMPARE(package.password, QByteArrayLiteral("ABcd1234")); QVERIFY2(client.acknowledgeDownload(requestId, &error), qPrintable(error)); } QTEST_MAIN(EetCaIntegrationTest) #include "eetca_integration_test.moc"