public void Cache_hit_and_expire() { Plug deki = Plug.New("http://mock/deki"); AutoMockPlug autoMock = MockPlug.Register(deki.Uri); Action expire = null; PageChangeCache cache = new PageChangeCache(deki, (key, trigger) => expire = trigger); DateTime timestamp = DateTime.UtcNow; XUri pageUri = deki.Uri.At("pages", "10").With("redirects", "0"); XDoc pageResponse = new XDoc("page") .Elem("uri.ui", "http://foo.com/@api/deki/pages/10") .Elem("title", "foo") .Elem("path", "foo/bar"); XUri feedUri = deki.Uri .At("pages", "10", "feed") .With("redirects", "0") .With("format", "raw") .With("since", timestamp.Subtract(TimeSpan.FromSeconds(10)).ToString("yyyyMMddHHmmss")); XDoc changeResponse = new XDoc("table") .Start("change") .Elem("rc_summary", "Two edits") .Elem("rc_comment", "edit 1") .Elem("rc_comment", "edit 2") .End(); _log.Debug("first get"); autoMock.Expect("GET", pageUri, (XDoc)null, DreamMessage.Ok(pageResponse)); autoMock.Expect("GET", feedUri, (XDoc)null, DreamMessage.Ok(changeResponse)); Assert.IsNotNull(Coroutine.Invoke(cache.GetPageData, (uint)10, "foo", timestamp, CultureInfo.InvariantCulture, (string)null, new Result <PageChangeData>()).Wait()); Assert.IsTrue(autoMock.WaitAndVerify(10.Seconds())); _log.Debug("second get, cache hit"); autoMock.Reset(); Assert.IsNotNull(Coroutine.Invoke(cache.GetPageData, (uint)10, "foo", timestamp, CultureInfo.InvariantCulture, (string)null, new Result <PageChangeData>()).Wait()); Assert.IsTrue(autoMock.WaitAndVerify(2.Seconds())); _log.Debug("third get, cache miss"); autoMock.Reset(); autoMock.Expect("GET", pageUri, (XDoc)null, DreamMessage.Ok(pageResponse)); autoMock.Expect("GET", feedUri, (XDoc)null, DreamMessage.Ok(changeResponse)); expire(); Assert.IsNotNull(Coroutine.Invoke(cache.GetPageData, (uint)10, "foo", timestamp, CultureInfo.InvariantCulture, (string)null, new Result <PageChangeData>()).Wait()); Assert.IsTrue(autoMock.WaitAndVerify(10.Seconds())); }
public void Get_page_data_in_user_timezone() { Plug deki = Plug.New("http://mock/deki"); AutoMockPlug autoMock = MockPlug.Register(deki.Uri); PageChangeCache cache = new PageChangeCache(deki, (key, trigger) => { }); DateTime timestamp = DateTime.Parse("2009/02/01 10:10:00"); XUri pageUri = deki.Uri.At("pages", "10").With("redirects", "0"); XDoc pageResponse = new XDoc("page") .Elem("uri.ui", "http://foo.com/@api/deki/pages/10") .Elem("title", "foo") .Elem("path", "foo/bar"); XUri feedUri = deki.Uri .At("pages", "10", "feed") .With("redirects", "0") .With("format", "raw") .With("since", timestamp.Subtract(TimeSpan.FromSeconds(10)).ToString("yyyyMMddHHmmss")); XDoc changeResponse = new XDoc("table") .Start("change") .Elem("rc_summary", "Two edits") .Elem("rc_comment", "edit 1") .Elem("rc_comment", "edit 2") .Elem("rc_timestamp", "20090201101000") .End(); _log.Debug("first get"); autoMock.Expect("GET", pageUri, (XDoc)null, DreamMessage.Ok(pageResponse)); autoMock.Expect("GET", feedUri, (XDoc)null, DreamMessage.Ok(changeResponse)); PageChangeData data = Coroutine.Invoke(cache.GetPageData, (uint)10, "foo", timestamp, CultureInfo.InvariantCulture, "-09:00", new Result <PageChangeData>()).Wait(); Assert.IsTrue(autoMock.WaitAndVerify(TimeSpan.FromSeconds(10))); //string plainbody = "foo\r\n[ http://foo.com/@api/deki/pages/10 ]\r\n\r\n - edit 1 (Sun, 01 Feb 2009 01:10:00 -09:00)\r\n [ http://foo.com/@api/deki/pages/10?revision ]\r\n\r\n"; XDoc htmlBody = XDocFactory.From("<html><p><b><a href=\"http://foo.com/@api/deki/pages/10\">foo</a></b> ( Last edited by <a href=\"http://foo.com/User%3A\" /> )<br /><small><a href=\"http://foo.com/@api/deki/pages/10\">http://foo.com/@api/deki/pages/10</a></small><br /><small><a href=\"http://foo.com/index.php?title=Special%3APageAlerts&id=10\">Unsubscribe</a></small></p><p><ol><li>edit 1 ( <a href=\"http://foo.com/@api/deki/pages/10?revision\">Sun, 01 Feb 2009 01:10:00 -09:00</a> by <a href=\"http://foo.com/User%3A\" /> )</li></ol></p><br /></html>", MimeType.TEXT_XML); Assert.AreEqual(htmlBody.ToString(), data.HtmlBody.ToString()); }
//--- Methods --- protected override Yield Start(XDoc config, IContainer container, Result result) { yield return(Coroutine.Invoke(base.Start, config, new Result())); // set up plug for phpscript that will handle the notifications _emailer = Plug.New(config["uri.emailer"].AsUri); // set up plug deki, so we can validate users _deki = Plug.New(config["uri.deki"].AsUri); // get the apikey, which we will need as a subscription auth token for subscriptions not done on behalf of a user _apikey = config["apikey"].AsText; _cache = new PageChangeCache(_deki.With("apikey", _apikey), TimeSpan.FromSeconds(config["page-cache-ttl"].AsInt ?? 2)); if (!container.IsRegistered <IPageSubscriptionInstance>()) { var builder = new ContainerBuilder(); builder.Register <PageSubscriptionInstance>().As <IPageSubscriptionInstance>().FactoryScoped(); builder.Build(container); } // TODO (arnec): this should be hitting the API to retrieve resources // resource manager for email template var resourcePath = Config["resources-path"].AsText; if (!string.IsNullOrEmpty(resourcePath)) { _resourceManager = new PlainTextResourceManager(Environment.ExpandEnvironmentVariables(resourcePath)); } else { // creating a test resource manager _log.WarnFormat("'resource-path' was not defined in Config, using a test resource manager for email templating"); var testSet = new TestResourceSet { { "Notification.Page.email-subject", "Page Modified" }, { "Notification.Page.email-header", "The following pages have changed:" } }; _resourceManager = new PlainTextResourceManager(testSet); } // set up subscription for pubsub var subscriptionSet = new XDoc("subscription-set") .Elem("uri.owner", Self.Uri.AsServerUri().ToString()) .Start("subscription") .Elem("channel", "event://*/deki/users/*") .Add(DreamCookie.NewSetCookie("service-key", InternalAccessKey, Self.Uri).AsSetCookieDocument) .Start("recipient") .Attr("authtoken", _apikey) .Elem("uri", Self.Uri.AsServerUri().At("updateuser").ToString()) .End() .End() .Start("subscription") .Elem("channel", "event://*/deki/pages/create") .Elem("channel", "event://*/deki/pages/update") .Elem("channel", "event://*/deki/pages/delete") .Elem("channel", "event://*/deki/pages/revert") .Elem("channel", "event://*/deki/pages/move") .Elem("channel", "event://*/deki/pages/tags/update") .Elem("channel", "event://*/deki/pages/dependentschanged/comments/create") .Elem("channel", "event://*/deki/pages/dependentschanged/comments/update") .Elem("channel", "event://*/deki/pages/dependentschanged/comments/delete") .Elem("channel", "event://*/deki/pages/dependentschanged/files/create") .Elem("channel", "event://*/deki/pages/dependentschanged/files/update") .Elem("channel", "event://*/deki/pages/dependentschanged/files/delete") .Elem("channel", "event://*/deki/pages/dependentschanged/files/properties/*") .Elem("channel", "event://*/deki/pages/dependentschanged/files/restore") .Add(DreamCookie.NewSetCookie("service-key", InternalAccessKey, Self.Uri).AsSetCookieDocument) .Start("recipient") .Attr("authtoken", _apikey) .Elem("uri", Self.Uri.AsServerUri().At("notify").ToString()) .End() .End(); Result <DreamMessage> subscribe; yield return(subscribe = PubSub.At("subscribers").PostAsync(subscriptionSet)); string accessKey = subscribe.Value.ToDocument()["access-key"].AsText; XUri location = subscribe.Value.Headers.Location; Cookies.Update(DreamCookie.NewSetCookie("access-key", accessKey, location), null); _subscriptionLocation = Plug.New(location.AsLocalUri().WithoutQuery()); _log.DebugFormat("set up initial subscription location at {0}", _subscriptionLocation.Uri); // set up notification accumulator queue TimeSpan accumulationMinutes = TimeSpan.FromSeconds(config["accumulation-time"].AsInt ?? 10 * 60); _log.DebugFormat("Initializing queue with {0:0.00} minute accumulation", accumulationMinutes.TotalMinutes); _notificationQueue = new NotificationDelayQueue(accumulationMinutes, SendEmail); result.Return(); }