diff options
17 files changed, 645 insertions, 1 deletions
diff --git a/gentoobrowse-api/service/Jamfile.jam b/gentoobrowse-api/service/Jamfile.jam index a065393..7300b68 100644 --- a/gentoobrowse-api/service/Jamfile.jam +++ b/gentoobrowse-api/service/Jamfile.jam @@ -4,10 +4,11 @@ lib icetray : : : : <include>/usr/include/icetray ; lib git2 ; lib gentoobrowse-service : - [ glob-tree *.cpp : bin ] + [ glob-tree *.cpp : bin notifications ] [ glob-tree *.sql ] : <library>..//adhocutil + <library>notifications <library>icetray <library>git2 <library>..//dbppcore @@ -15,6 +16,8 @@ lib gentoobrowse-service : <library>..//slicer <library>..//slicer-db <library>../api//gentoobrowse-api + <library>notifications//gentoobrowse-service-notifications + <implicit-dependency>notifications//gentoobrowse-service-notifications <implicit-dependency>../api//gentoobrowse-api <library>..//boost_system <library>..//boost_thread @@ -27,6 +30,8 @@ lib gentoobrowse-service : : : <include>. <implicit-dependency>../api//gentoobrowse-api + <library>notifications//gentoobrowse-service-notifications + <implicit-dependency>notifications//gentoobrowse-service-notifications <library>../api//gentoobrowse-api ; diff --git a/gentoobrowse-api/service/main.cpp b/gentoobrowse-api/service/main.cpp index bdf6ed5..b7b18a2 100644 --- a/gentoobrowse-api/service/main.cpp +++ b/gentoobrowse-api/service/main.cpp @@ -4,6 +4,8 @@ #include "portageimpl.h" #include "usersimpl.h" #include "maintenanceimpl.h" +#include "notificationsimpl.h" +#include "mailserverimpl.h" namespace Gentoo { namespace Service { @@ -15,6 +17,8 @@ class Api : public IceTray::Service { adp->add(new Portage(db), ic->stringToIdentity("portage")); adp->add(new Maintenance(db), ic->stringToIdentity("maintenance")); adp->add(new Users(db), ic->stringToIdentity("users")); + adp->add(new Notifications(), ic->stringToIdentity("notifications")); + adp->add(new MailServer(), ic->stringToIdentity("mailserver")); } }; diff --git a/gentoobrowse-api/service/notifications/Jamfile.jam b/gentoobrowse-api/service/notifications/Jamfile.jam new file mode 100644 index 0000000..a168649 --- /dev/null +++ b/gentoobrowse-api/service/notifications/Jamfile.jam @@ -0,0 +1,44 @@ +import type : register ; +import generators : register-standard ; + +type.register XSLT : xslt ; + +generators.register-standard xslt.h : XSLT : H ; + +h base-xslt : xslt/base.xslt : <slicer>yes ; +h signup-xslt : xslt/signup.xslt : <slicer>yes ; +h news-xslt : xslt/news.xslt : <slicer>yes ; + +lib exslt ; +lib xslt : : : : <include>/usr/include/libxslt ; +lib xml2 : : : : <include>/usr/include/libxml2 ; +lib esmtp ; +lib slicer-xml ; +lib boost_system ; + +lib gentoobrowse-service-notifications : + [ glob *.ice *.cpp ] + : + <slicer>yes + <library>xslt + <library>xml2 + <library>esmtp + <library>../../..//libxmlpp + <library>boost_system + <library>slicer-xml + <library>../../domain//gentoobrowse-domain + <implicit-dependency>../../domain//gentoobrowse-domain + <dependency>base-xslt + <dependency>signup-xslt + <dependency>news-xslt + : : + <include>. + ; + +actions xslt.h +{ + ( cd $(2:D) ; xxd -i $(2:B)$(2:S) ) > $(1) +} + +IMPORT $(__name__) : xslt.h : : xslt.h ; + diff --git a/gentoobrowse-api/service/notifications/mailserverimpl.cpp b/gentoobrowse-api/service/notifications/mailserverimpl.cpp new file mode 100644 index 0000000..c3dfdfa --- /dev/null +++ b/gentoobrowse-api/service/notifications/mailserverimpl.cpp @@ -0,0 +1,64 @@ +#include "mailserverimpl.h" +#include <libesmtp.h> +#include <Ice/ObjectAdapter.h> +#include <Ice/Communicator.h> + +namespace Gentoo { + namespace Service { + typedef std::vector<std::string> Parts; + typedef std::pair<Parts::iterator, Parts::const_iterator> PartsProgress; + + void + MailServer::sendEmail(const Gentoo::EmailPtr & msg, const Ice::Current & c) + { + auto props = c.adapter->getCommunicator()->getProperties(); + smtp_session_t session = smtp_create_session(); + smtp_message_t message = smtp_add_message(session); + auto server = props->getPropertyWithDefault("GentooBrowseAPI.MailServer", "localhost"); + smtp_set_server(session, server.c_str()); + smtp_set_header(message, "To", msg->to.name.c_str(), msg->to.address.c_str()); + smtp_set_header(message, "From", msg->from.name.c_str(), msg->from.address.c_str()); + smtp_set_header(message, "Subject", msg->subject.c_str()); + Parts parts; + parts.emplace_back("Content-Type: multipart/alternative; boundary=\"<<divider>>\"\r\n"); + parts.emplace_back("MIME-Version: 1.0\r\n"); + parts.emplace_back("Content-Transfer-Encoding: binary\r\n"); + smtp_add_recipient(message, msg->to.address.c_str()); + for (const auto & p : msg->body) { + parts.emplace_back("\r\n--<<divider>>\r\nContent-Type: " + p->mimetype + "; utf-8\r\n\r\n"); + parts.emplace_back(p->payload); + } + parts.emplace_back("\r\n--<<divider>>--\r\n"); + PartsProgress pp { parts.begin(), parts.end() }; + smtp_set_messagecb(message, writeBody, &pp); + if (!smtp_start_session(session)) { + char buf[BUFSIZ]; + auto b = smtp_strerror(smtp_errno(), buf, sizeof(buf)); + assert(b); + SendEmailFailed e(b); + smtp_destroy_session(session); + throw e; + } + smtp_destroy_session(session); + } + + const char * + MailServer::writeBody(void **, int * len, void * arg) + { + auto parts = static_cast<PartsProgress *>(arg); + if (len == NULL || parts->first == parts->second) { + return NULL; + } + const auto & p = *parts->first++; + *len = p.length(); + return p.data(); + } + } + + void + SendEmailFailed::ice_print(std::ostream & buf) const + { + buf << "Failed to send email: " << message; + } +} + diff --git a/gentoobrowse-api/service/notifications/mailserverimpl.h b/gentoobrowse-api/service/notifications/mailserverimpl.h new file mode 100644 index 0000000..23b17c2 --- /dev/null +++ b/gentoobrowse-api/service/notifications/mailserverimpl.h @@ -0,0 +1,20 @@ +#ifndef MAILSERVERIMPL_H +#define MAILSERVERIMPL_H + +#include <notifications.h> +#include <visibility.h> + +namespace Gentoo { + namespace Service { + class DLL_PUBLIC MailServer : public Gentoo::MailServer { + public: + void sendEmail(const Gentoo::EmailPtr & msg, const Ice::Current &) override; + + private: + static const char * writeBody(void ** buf, int * len, void * arg); + }; + } +} + +#endif + diff --git a/gentoobrowse-api/service/notifications/notifications.ice b/gentoobrowse-api/service/notifications/notifications.ice new file mode 100644 index 0000000..a60fa28 --- /dev/null +++ b/gentoobrowse-api/service/notifications/notifications.ice @@ -0,0 +1,49 @@ +#ifndef GENTOO_NOTIFICATIONS +#define GENTOO_NOTIFICATIONS + +#include <user-models.ice> +#include <portage-models.ice> + +module Gentoo { + class MimePart { + string mimetype; + string payload; + }; + + sequence<MimePart> MimeParts; + + struct Address { + string name; + string address; + }; + + class Email { + Address to; + Address from; + string subject; + MimeParts body; + }; + + ["cpp:ice_print"] + exception SendEmailFailed { + string message; + }; + + struct NewsContent { + Categories categories; + Packages packages; + Ebuilds ebuilds; + }; + + interface Notifications { + Email getSignup(NewUser user); + Email getNews(User user, NewsContent nc); + }; + + interface MailServer { + idempotent void sendEmail(Email msg) throws SendEmailFailed; + }; +}; + +#endif + diff --git a/gentoobrowse-api/service/notifications/notificationsimpl.cpp b/gentoobrowse-api/service/notifications/notificationsimpl.cpp new file mode 100644 index 0000000..d7dbee4 --- /dev/null +++ b/gentoobrowse-api/service/notifications/notificationsimpl.cpp @@ -0,0 +1,59 @@ +#include "notificationsimpl.h" +#include <libxml/parser.h> +#include <base-xslt.h> +#include <news-xslt.h> +#include <signup-xslt.h> +#include <string.h> +#include <slicer/slicer.h> +#include "xsltStreamSerializer.h" + +namespace Gentoo { + namespace Service { + xmlDocPtr xsltDocLoaderFunc (const xmlChar * URI, xmlDictPtr, int, void *, xsltLoadType) + { +#define MATCH(name) \ + if (xmlStrcmp(URI, BAD_CAST #name ".xslt") == 0) { \ + return xmlParseMemory((char *)name ## _xslt, name ## _xslt_len); \ + } + MATCH(base) + MATCH(news) + MATCH(signup) + return NULL; +#undef MATCH + } + + Notifications::Notifications() + { + xsltSetLoaderFunc(&xsltDocLoaderFunc); + news = xsltSSPtr(xsltParseStylesheetFile(BAD_CAST "news.xslt"), xsltFreeStylesheet); + signup = xsltSSPtr(xsltParseStylesheetFile(BAD_CAST "signup.xslt"), xsltFreeStylesheet); + xsltSetLoaderFunc(NULL); + } + + Gentoo::EmailPtr Notifications::getSignup(const Gentoo::NewUserPtr & u, const Ice::Current &) + { + auto e = basicMail("Welcome", u); + Slicer::SerializeAny<XsltStreamSerializer>(u, e, signup.get()); + return e; + } + + Gentoo::EmailPtr Notifications::getNews(const Gentoo::UserPtr & u, const Gentoo::NewsContent & c, const Ice::Current &) + { + auto e = basicMail("Latest updates", u); + Slicer::SerializeAny<XsltStreamSerializer>(c, e, news.get()); + return e; + } + + Gentoo::EmailPtr Notifications::basicMail(const std::string & subject, Gentoo::UserPtr u) + { + Gentoo::EmailPtr e = new Gentoo::Email(); + e->subject = "Gentoo Browse: " + subject; + e->from.name = "Gentoo Browse"; + e->from.address = "noreply@gentoobrowse.randomdan.homeip.net"; + e->to.name = u->userrealname; + e->to.address = u->useremail; + return e; + } + } +} + diff --git a/gentoobrowse-api/service/notifications/notificationsimpl.h b/gentoobrowse-api/service/notifications/notificationsimpl.h new file mode 100644 index 0000000..1d3b1fc --- /dev/null +++ b/gentoobrowse-api/service/notifications/notificationsimpl.h @@ -0,0 +1,29 @@ +#ifndef NOTIFICATIONSIMPL_H +#define NOTIFICATIONSIMPL_H + +#include <notifications.h> +#include <visibility.h> +#include <memory> +#include <libxslt/documents.h> + +namespace Gentoo { + namespace Service { + class DLL_PUBLIC Notifications : public Gentoo::Notifications { + public: + Notifications(); + + Gentoo::EmailPtr getSignup(const Gentoo::NewUserPtr &, const Ice::Current &) override; + Gentoo::EmailPtr getNews(const Gentoo::UserPtr &, const Gentoo::NewsContent &, const Ice::Current &) override; + + private: + Gentoo::EmailPtr basicMail(const std::string &, Gentoo::UserPtr u); + + typedef std::shared_ptr<xsltStylesheet> xsltSSPtr; + xsltSSPtr news; + xsltSSPtr signup; + }; + } +} + +#endif + diff --git a/gentoobrowse-api/service/notifications/xslt/base.xslt b/gentoobrowse-api/service/notifications/xslt/base.xslt new file mode 100644 index 0000000..9db7752 --- /dev/null +++ b/gentoobrowse-api/service/notifications/xslt/base.xslt @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + xmlns:project2="http://project2.randomdan.homeip.net" exclude-result-prefixes="project2" > + <xsl:output encoding="utf-8" method="html" media-type="text/html" indent="yes" /> + + <xsl:template match="/*"> + <xsl:text disable-output-escaping="yes"><!DOCTYPE html> </xsl:text> + <html lang="en"> + <head> + <meta charset="utf-8" /> + <link href="http://gentoobrowse.randomdan.homeip.net/css/bootstrap.min.css" rel="stylesheet" media="screen" /> + <link href="http://gentoobrowse.randomdan.homeip.net/css/bootstrap-responsive.min.css" rel="stylesheet" media="screen" /> + <link href="http://gentoobrowse.randomdan.homeip.net/css/general.css" rel="stylesheet" media="screen" /> + <title><xsl:call-template name="title" /></title> + </head> + <body data-spy="scroll" data-target=".bs-docs-sidebar"> + <!-- nav --> + <nav class="navbar navbar-inverse navbar-fixed-top"> + <div class="navbar-inner"> + <div class="container"> + <a class="brand" href="http://gentoobrowse.randomdan.homeip.net/">Gentoo Browse</a> + </div> + </div> + </nav> + + <!-- page content --> + <header class="jumbotron subhead"> + <div class="container"> + <h1> + <xsl:call-template name="title" /> + </h1> + </div> + </header> + <div class="container"> + <article class="row"> + <xsl:call-template name="content" /> + <section class="signoff"> + </section> + </article> + </div> + + <!-- footer --> + <footer class="footer"> + <div class="container"> + <p>Gentoo Browse is not an official Gentoo website. The name "Gentoo" and the "g" logo are trademarks of the Gentoo Foundation, Inc.</p> + </div> + </footer> + + <script src="http://code.jquery.com/jquery-latest.js"></script> + <script src="http://gentoobrowse.randomdan.homeip.net/js/bootstrap.min.js"></script> + <script src="http://gentoobrowse.randomdan.homeip.net/js/js.js"></script> + </body> + </html> + </xsl:template> + + <xsl:template name="head"> + </xsl:template> + + <xsl:template name="header"> + </xsl:template> +</xsl:stylesheet> diff --git a/gentoobrowse-api/service/notifications/xslt/news.xslt b/gentoobrowse-api/service/notifications/xslt/news.xslt new file mode 100644 index 0000000..6b2ea29 --- /dev/null +++ b/gentoobrowse-api/service/notifications/xslt/news.xslt @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://exslt.org/strings" xmlns:project2="http://project2.randomdan.homeip.net" version="1.0" exclude-result-prefixes="project2 fn"> + <xsl:import href="base.xslt"/> + <xsl:output encoding="utf-8" method="html" media-type="text/html" indent="yes"/> + + <xsl:template name="title">Latest news</xsl:template> + + <xsl:template name="content"> + <xsl:variable name="packages" select="packages/package[packageid = ../../ebuilds/ebuild/packageid]"/> + <xsl:variable name="categories" select="categories/category[categoryid = $packages/categoryid]"/> + <section> + <h2 class="page-header">New versions of tracked packages</h2> + <div class="span3 bs-docs-sidebar"> + <ul class="nav nav-list bs-docs-sidenav"> + <xsl:for-each select="$categories"> + <xsl:sort select="name" data-type="text" order="ascending"/> + <li> + <a> + <xsl:attribute name="href">http://gentoobrowse.randomdan.homeip.net/packages/<xsl:value-of select="name" /></xsl:attribute> + <i class="icon-chevron-right"></i> + <xsl:value-of select="name" /> + </a> + </li> + </xsl:for-each> + </ul> + </div> + <div class="span9"> + <xsl:for-each select="$categories"> + <xsl:sort select="name" data-type="text" order="ascending"/> + <xsl:variable name="category" select="." /> + <xsl:for-each select="$packages[categoryid = current()/categoryid]"> + <xsl:sort select="name" data-type="text" order="ascending"/> + <xsl:variable name="package" select="." /> + <p> + <a> + <xsl:attribute name="href">http://gentoobrowse.randomdan.homeip.net/packages/<xsl:value-of select="$category/name" /></xsl:attribute> + <xsl:value-of select="$category/name" /> + </a> + / + <a> + <xsl:attribute name="href">http://gentoobrowse.randomdan.homeip.net/packages/<xsl:value-of select="$category/name" />/<xsl:value-of select="name" /></xsl:attribute> + <xsl:value-of select="name" /> + </a> + : + <xsl:value-of select="description" /> + </p> + <xsl:for-each select="../../ebuilds/ebuild[packageid = current()/packageid]"> + <p> + v<xsl:value-of select="version" /> + </p> + </xsl:for-each> + </xsl:for-each> + </xsl:for-each> + </div> + </section> + </xsl:template> +</xsl:stylesheet> diff --git a/gentoobrowse-api/service/notifications/xslt/signup.xslt b/gentoobrowse-api/service/notifications/xslt/signup.xslt new file mode 100644 index 0000000..e579835 --- /dev/null +++ b/gentoobrowse-api/service/notifications/xslt/signup.xslt @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> + <xsl:import href="base.xslt"/> + + <xsl:template name="title">Welcome to Gentoo Browse</xsl:template> + + <xsl:template name="content"> + <section> + <h2 class="page-header">Confirmation</h2> + <p> + Click <a> + <xsl:attribute name="href"> + <xsl:text>http://gentoobrowse.randomdan.homeip.net/user/verify/</xsl:text> + <xsl:value-of select="verifyguid" /> + </xsl:attribute> + <xsl:text>http://gentoobrowse.randomdan.homeip.net/user/verify/</xsl:text> + <xsl:value-of select="verifyguid" /> + </a> to confirm your account. + </p> + <p>Or alternatively, copy and paste this code into the confirmation page: <xsl:value-of select="verifyguid" /></p> + </section> + </xsl:template> +</xsl:stylesheet> + diff --git a/gentoobrowse-api/service/notifications/xsltStreamSerializer.cpp b/gentoobrowse-api/service/notifications/xsltStreamSerializer.cpp new file mode 100644 index 0000000..5a71b35 --- /dev/null +++ b/gentoobrowse-api/service/notifications/xsltStreamSerializer.cpp @@ -0,0 +1,82 @@ +#include "xsltStreamSerializer.h" +#include <libxslt/xsltInternals.h> +#include <libxml/HTMLtree.h> +#include <factory.impl.h> +#include <processPipes.h> +#include <sys/wait.h> + +namespace Gentoo { + static int xmlstrmclosecallback(void * context) + { + ((std::ostream*)context)->flush(); + return 0; + } + + static int xmlstrmwritecallback(void * context, const char * buffer, int len) + { + ((std::ostream*)context)->write(buffer, len); + return len; + } + + XsltStreamSerializer::XsltStreamSerializer(Gentoo::EmailPtr e, xsltStylesheet * ss) : + Slicer::XmlDocumentSerializer(doc), + mail(e), + doc(nullptr), + stylesheet(ss) + { + } + + XsltStreamSerializer::~XsltStreamSerializer() + { + delete doc; + } + + void + XsltStreamSerializer::Serialize(Slicer::ModelPartForRootPtr mp) + { + Slicer::XmlDocumentSerializer::Serialize(mp); + auto result = std::shared_ptr<xmlDoc>(xsltApplyStylesheet(stylesheet, doc->cobj(), nullptr), xmlFreeDoc); + if (!result) { + throw xmlpp::exception("Failed to apply XSL transform"); + } + appendText(result.get()); + appendHtml(result.get()); + } + + void XsltStreamSerializer::appendHtml(xmlDoc * result) const + { + std::stringstream strm; + xmlOutputBufferPtr buf = xmlOutputBufferCreateIO(xmlstrmwritecallback, xmlstrmclosecallback, &strm, NULL); + htmlDocContentDumpFormatOutput(buf, result, "utf-8", 0); + xmlOutputBufferClose(buf); + mail->body.push_back(new Gentoo::MimePart("text/html", strm.str())); + } + + void XsltStreamSerializer::appendText(xmlDoc * result) const + { + std::stringstream strm; + std::vector<std::string> callLynx; + callLynx.push_back("/usr/bin/lynx"); + callLynx.push_back("-dump"); + callLynx.push_back("-stdin"); + std::string widthArg = "-width=105"; + callLynx.push_back(widthArg); + AdHoc::System::ProcessPipes fds(callLynx, true, true, false); + FILE * lynxIn = fdopen(fds.fdIn(), "w"); + // Fixed encoding as we want the result to go back into a ustring + htmlNodeDumpFileFormat(lynxIn, result, xmlDocGetRootElement(result), "utf-8", 0); + fclose(lynxIn); + char buf[1024]; + int r; + while ((r = read(fds.fdOut(), buf, sizeof(buf))) > 0) { + strm.write(buf, r); + } + int status; + waitpid(fds.pid(), &status, 0); + if (status != 0) { + throw std::runtime_error("Lynx failed"); + } + mail->body.push_back(new Gentoo::MimePart("text/plain", strm.str())); + } +} + diff --git a/gentoobrowse-api/service/notifications/xsltStreamSerializer.h b/gentoobrowse-api/service/notifications/xsltStreamSerializer.h new file mode 100644 index 0000000..492b954 --- /dev/null +++ b/gentoobrowse-api/service/notifications/xsltStreamSerializer.h @@ -0,0 +1,28 @@ +#ifndef ICESPIDER_CORE_XSLTSTREAMSERIALIZER_H +#define ICESPIDER_CORE_XSLTSTREAMSERIALIZER_H + +#include <slicer/xml/serializer.h> +#include <visibility.h> +#include <libxslt/transform.h> +#include <notifications.h> + +namespace Gentoo { + class DLL_PUBLIC XsltStreamSerializer : public Slicer::XmlDocumentSerializer { + public: + XsltStreamSerializer(Gentoo::EmailPtr, xsltStylesheet *); + ~XsltStreamSerializer(); + + void Serialize(Slicer::ModelPartForRootPtr mp) override; + + protected: + void appendHtml(xmlDoc *) const; + void appendText(xmlDoc *) const; + + Gentoo::EmailPtr mail; + xmlpp::Document * doc; + xsltStylesheet * stylesheet; + }; +} + +#endif + diff --git a/gentoobrowse-api/unittests/Jamfile.jam b/gentoobrowse-api/unittests/Jamfile.jam index 92501c0..70fa90f 100644 --- a/gentoobrowse-api/unittests/Jamfile.jam +++ b/gentoobrowse-api/unittests/Jamfile.jam @@ -98,6 +98,14 @@ run : testUsers ; run + testNotifications.cpp + : : : + <dependency>../db/schema.sql + <define>BOOST_TEST_DYN_LINK + <library>testCommon + : testNotifications ; + +run testBugs.cpp : : : <dependency>../db/schema.sql diff --git a/gentoobrowse-api/unittests/mockDefs.cpp b/gentoobrowse-api/unittests/mockDefs.cpp index 70f5b69..eeab5f2 100644 --- a/gentoobrowse-api/unittests/mockDefs.cpp +++ b/gentoobrowse-api/unittests/mockDefs.cpp @@ -2,6 +2,9 @@ #include <definedDirs.h> Service::Service() : + IceTray::DryIce({ + "--GentooBrowseAPI.MailServer=smtp.random.lan:25", + }), PQ::Mock("user=postgres dbname=postgres", "GentooBrowseAPI", { rootDir.parent_path() / "db" / "schema.sql", rootDir / "data.sql" }) @@ -22,6 +25,8 @@ Maintenance::Maintenance() : TestClient::TestClient() : m(getProxy<Gentoo::MaintenancePrx>("maintenance")), p(getProxy<Gentoo::PortagePrx>("portage")), + n(getProxy<Gentoo::NotificationsPrx>("notifications")), + ms(getProxy<Gentoo::MailServerPrx>("mailserver")), u(getProxy<Gentoo::UsersPrx>("users")) { } diff --git a/gentoobrowse-api/unittests/mockDefs.h b/gentoobrowse-api/unittests/mockDefs.h index 9a35c1f..a0c044a 100644 --- a/gentoobrowse-api/unittests/mockDefs.h +++ b/gentoobrowse-api/unittests/mockDefs.h @@ -7,6 +7,7 @@ #include <portage.h> #include <users.h> #include <maintenance.h> +#include <notifications.h> #include <selectcommandUtil.impl.h> class DLL_PUBLIC Service : public IceTray::DryIce, PQ::Mock { @@ -25,6 +26,8 @@ class DLL_PUBLIC TestClient : public IceTray::DryIceClient { Gentoo::MaintenancePrx m; Gentoo::PortagePrx p; + Gentoo::NotificationsPrx n; + Gentoo::MailServerPrx ms; Gentoo::UsersPrx u; }; diff --git a/gentoobrowse-api/unittests/testNotifications.cpp b/gentoobrowse-api/unittests/testNotifications.cpp new file mode 100644 index 0000000..81b28d0 --- /dev/null +++ b/gentoobrowse-api/unittests/testNotifications.cpp @@ -0,0 +1,102 @@ +#define BOOST_TEST_MODULE TestNotifications +#include <boost/test/unit_test.hpp> + +#include "mockDefs.h" +#include <fstream> + +BOOST_GLOBAL_FIXTURE( Service ); + +BOOST_FIXTURE_TEST_SUITE(tp, TestClient); + +bool +isHtml(const std::string & body) +{ + return body.find("<html") != std::string::npos; +} + +void +save(const std::string & name, Gentoo::EmailPtr e) +{ + std::ofstream text("/tmp/" + name + ".txt"); + text << e->body[0]->payload; + std::ofstream html("/tmp/" + name + ".html"); + html << e->body[1]->payload; +} + +void +commonAssert(Gentoo::EmailPtr e) +{ + BOOST_REQUIRE_EQUAL(e->body.size(), 2); + BOOST_REQUIRE_EQUAL(e->body[0]->mimetype, "text/plain"); + BOOST_REQUIRE(!isHtml(e->body[0]->payload)); + BOOST_REQUIRE_EQUAL(e->body[1]->mimetype, "text/html"); + BOOST_REQUIRE(isHtml(e->body[1]->payload)); +} + +#if 0 +BOOST_AUTO_TEST_CASE( testSend ) +{ + Gentoo::NewUserPtr u = new Gentoo::NewUser(1, "testuser", "Test User", "dangoodliffe@gmail.com", "some-guid"); + auto e = n->getSignup(u); + BOOST_REQUIRE(e); + ms->sendEmail(e); +} +#endif + +BOOST_AUTO_TEST_CASE( testSignup ) +{ + Gentoo::NewUserPtr u = new Gentoo::NewUser(1, "testuser", "Test User", "test@user.com", "some-guid"); + auto e = n->getSignup(u); + BOOST_REQUIRE(e); + BOOST_REQUIRE_EQUAL(e->subject, "Gentoo Browse: Welcome"); + BOOST_REQUIRE_EQUAL(e->from.name, "Gentoo Browse"); + BOOST_REQUIRE_EQUAL(e->from.address, "noreply@gentoobrowse.randomdan.homeip.net"); + BOOST_REQUIRE_EQUAL(e->to.name, "Test User"); + BOOST_REQUIRE_EQUAL(e->to.address, "test@user.com"); + commonAssert(e); + save("signup", e); + for (auto p : e->body) { + BOOST_REQUIRE(p->payload.find("Welcome to Gentoo Browse") != std::string::npos); + BOOST_REQUIRE(p->payload.find("http://gentoobrowse.randomdan.homeip.net/user/verify/some-guid") != std::string::npos); + BOOST_REQUIRE(p->payload.find("confirmation page: some-guid") != std::string::npos); + } +} + +BOOST_AUTO_TEST_CASE( testNews ) +{ + Gentoo::UserPtr u = new Gentoo::User(1, "testuser", "Test User", "test@user.com"); + Gentoo::NewsContent nc { + { + new Gentoo::Category(1, "app-test", "Test cat1"), + new Gentoo::Category(2, "sys-test", "Test cat2"), + new Gentoo::Category(3, "no-show", "Unused cat") + }, + { + new Gentoo::Package(1, 1, "app1", "first-seen", "Test app desc one", "Test summary 1", IceUtil::None, IceUtil::None, IceUtil::None, IceUtil::None), + new Gentoo::Package(2, 1, "app2", "first-seen", "Test app desc two", "Test summary 1", IceUtil::None, IceUtil::None, IceUtil::None, IceUtil::None), + new Gentoo::Package(3, 2, "app3", "first-seen", "Test app desc three", "Test summary 1", IceUtil::None, IceUtil::None, IceUtil::None, IceUtil::None), + new Gentoo::Package(4, 2, "app4", "first-seen", "Test app no-show", "Test summary 1", IceUtil::None, IceUtil::None, IceUtil::None, IceUtil::None) + }, + { + new Gentoo::Ebuild(1, 1, 1, "1.0a", "slot", "first-seen", "last-mod", IceUtil::None), + new Gentoo::Ebuild(2, 1, 1, "1.1a", "slot", "first-seen", "last-mod", IceUtil::None), + new Gentoo::Ebuild(3, 1, 1, "1.2a", "slot", "first-seen", "last-mod", IceUtil::None), + new Gentoo::Ebuild(4, 2, 1, "2.0a", "slot", "first-seen", "last-mod", IceUtil::None), + new Gentoo::Ebuild(5, 3, 1, "3.0a", "slot", "first-seen", "last-mod", IceUtil::None), + new Gentoo::Ebuild(6, 2, 1, "2.1a", "slot", "first-seen", "last-mod", IceUtil::None), + new Gentoo::Ebuild(7, 3, 1, "3.1a", "slot", "first-seen", "last-mod", IceUtil::None) + } + }; + auto e = n->getNews(u, nc); + commonAssert(e); + save("news", e); + for (auto p : e->body) { + BOOST_REQUIRE(p->payload.find("Latest news") != std::string::npos); + // BOOST_REQUIRE(p->payload.find("http://gentoobrowse.randomdan.homeip.net/packages/app-test") != std::string::npos); + // BOOST_REQUIRE(p->payload.find("http://gentoobrowse.randomdan.homeip.net/packages/app-test/app") != std::string::npos); + BOOST_REQUIRE(p->payload.find("no-show") == std::string::npos); + } +} + +BOOST_AUTO_TEST_SUITE_END() + |