From 9101b953e4434d1a50aa42d7329dfb8223a0b333 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 2 Dec 2016 21:27:50 +0000 Subject: Basic notification service implementation --- gentoobrowse-api/service/Jamfile.jam | 7 +- gentoobrowse-api/service/main.cpp | 4 + gentoobrowse-api/service/notifications/Jamfile.jam | 44 +++++++++ .../service/notifications/mailserverimpl.cpp | 64 +++++++++++++ .../service/notifications/mailserverimpl.h | 20 ++++ .../service/notifications/notifications.ice | 49 ++++++++++ .../service/notifications/notificationsimpl.cpp | 59 ++++++++++++ .../service/notifications/notificationsimpl.h | 29 ++++++ .../service/notifications/xslt/base.xslt | 61 ++++++++++++ .../service/notifications/xslt/news.xslt | 57 ++++++++++++ .../service/notifications/xslt/signup.xslt | 24 +++++ .../service/notifications/xsltStreamSerializer.cpp | 82 +++++++++++++++++ .../service/notifications/xsltStreamSerializer.h | 28 ++++++ gentoobrowse-api/unittests/Jamfile.jam | 8 ++ gentoobrowse-api/unittests/mockDefs.cpp | 5 + gentoobrowse-api/unittests/mockDefs.h | 3 + gentoobrowse-api/unittests/testNotifications.cpp | 102 +++++++++++++++++++++ 17 files changed, 645 insertions(+), 1 deletion(-) create mode 100644 gentoobrowse-api/service/notifications/Jamfile.jam create mode 100644 gentoobrowse-api/service/notifications/mailserverimpl.cpp create mode 100644 gentoobrowse-api/service/notifications/mailserverimpl.h create mode 100644 gentoobrowse-api/service/notifications/notifications.ice create mode 100644 gentoobrowse-api/service/notifications/notificationsimpl.cpp create mode 100644 gentoobrowse-api/service/notifications/notificationsimpl.h create mode 100644 gentoobrowse-api/service/notifications/xslt/base.xslt create mode 100644 gentoobrowse-api/service/notifications/xslt/news.xslt create mode 100644 gentoobrowse-api/service/notifications/xslt/signup.xslt create mode 100644 gentoobrowse-api/service/notifications/xsltStreamSerializer.cpp create mode 100644 gentoobrowse-api/service/notifications/xsltStreamSerializer.h create mode 100644 gentoobrowse-api/unittests/testNotifications.cpp 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 : : : : /usr/include/icetray ; lib git2 ; lib gentoobrowse-service : - [ glob-tree *.cpp : bin ] + [ glob-tree *.cpp : bin notifications ] [ glob-tree *.sql ] : ..//adhocutil + notifications icetray git2 ..//dbppcore @@ -15,6 +16,8 @@ lib gentoobrowse-service : ..//slicer ..//slicer-db ../api//gentoobrowse-api + notifications//gentoobrowse-service-notifications + notifications//gentoobrowse-service-notifications ../api//gentoobrowse-api ..//boost_system ..//boost_thread @@ -27,6 +30,8 @@ lib gentoobrowse-service : : : . ../api//gentoobrowse-api + notifications//gentoobrowse-service-notifications + notifications//gentoobrowse-service-notifications ../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 : yes ; +h signup-xslt : xslt/signup.xslt : yes ; +h news-xslt : xslt/news.xslt : yes ; + +lib exslt ; +lib xslt : : : : /usr/include/libxslt ; +lib xml2 : : : : /usr/include/libxml2 ; +lib esmtp ; +lib slicer-xml ; +lib boost_system ; + +lib gentoobrowse-service-notifications : + [ glob *.ice *.cpp ] + : + yes + xslt + xml2 + esmtp + ../../..//libxmlpp + boost_system + slicer-xml + ../../domain//gentoobrowse-domain + ../../domain//gentoobrowse-domain + base-xslt + signup-xslt + news-xslt + : : + . + ; + +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 +#include +#include + +namespace Gentoo { + namespace Service { + typedef std::vector Parts; + typedef std::pair 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=\"<>\"\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--<>\r\nContent-Type: " + p->mimetype + "; utf-8\r\n\r\n"); + parts.emplace_back(p->payload); + } + parts.emplace_back("\r\n--<>--\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(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 +#include + +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 +#include + +module Gentoo { + class MimePart { + string mimetype; + string payload; + }; + + sequence 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 +#include +#include +#include +#include +#include +#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(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(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 +#include +#include +#include + +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 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 @@ + + + + + + <!DOCTYPE html> + + + + + + + <xsl:call-template name="title" /> + + + + + + +
+
+

+ +

+
+
+
+
+ +
+
+
+
+ + +
+
+

Gentoo Browse is not an official Gentoo website. The name "Gentoo" and the "g" logo are trademarks of the Gentoo Foundation, Inc.

+
+
+ + + + + + +
+ + + + + + +
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 @@ + + + + + + Latest news + + + + +
+ + + +
+
+
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 @@ + + + + + Welcome to Gentoo Browse + + +
+ +

+ Click + + http://gentoobrowse.randomdan.homeip.net/user/verify/ + + + http://gentoobrowse.randomdan.homeip.net/user/verify/ + + to confirm your account. +

+

Or alternatively, copy and paste this code into the confirmation page:

+
+
+
+ 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 +#include +#include +#include +#include + +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(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 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 +#include +#include +#include + +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 @@ -97,6 +97,14 @@ run testCommon : testUsers ; +run + testNotifications.cpp + : : : + ../db/schema.sql + BOOST_TEST_DYN_LINK + testCommon + : testNotifications ; + run testBugs.cpp : : : 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 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("maintenance")), p(getProxy("portage")), + n(getProxy("notifications")), + ms(getProxy("mailserver")), u(getProxy("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 #include #include +#include #include 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 + +#include "mockDefs.h" +#include + +BOOST_GLOBAL_FIXTURE( Service ); + +BOOST_FIXTURE_TEST_SUITE(tp, TestClient); + +bool +isHtml(const std::string & body) +{ + return body.find("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() + -- cgit v1.2.3