public void Subscribers_to_a_single_page_do_not_get_escalated_by_a_subscriber_with_infinite_depth_on_same_page()
        {
            var mockDekiUri = new XUri("http://mock/deki");
            var mockDeki    = MockPlug.Register(mockDekiUri);

            mockDeki.Expect().Verb("GET").Uri(mockDekiUri.At("pages", "10")).Response(DreamMessage.Ok(new XDoc("page")
                                                                                                      .Attr("id", "10")
                                                                                                      .Start("page.parent")
                                                                                                      .Attr("id", "9")
                                                                                                      .Start("page.parent")
                                                                                                      .Attr("id", "8")
                                                                                                      .Start("page.parent")
                                                                                                      .Attr("id", "7")
                                                                                                      .EndAll()));
            var dispatcherUri = new XUri("http://mock/dispatcher");
            var dispatcher    = new DekiDispatcher(
                new DispatcherConfig {
                ServiceUri          = dispatcherUri,
                ServiceAccessCookie = new DreamCookie("service-key", "foo", dispatcherUri),
                ServiceConfig       = new XDoc("config").Elem("uri.deki", mockDekiUri).Elem("authtoken", "abc")
            },
                _mockRepository.Object
                );
            var combinedSetUpdated = 0;

            dispatcher.CombinedSetUpdated += (o, e) => {
                combinedSetUpdated++;
            };

            // subscribe to page 7 and all children
            var mockHierarchyRecipientUri = new XUri("http://mock/recipient/hierarchy");
            var mockHierarchyRecipient    = MockPlug.Register(mockHierarchyRecipientUri);

            mockHierarchyRecipient.Expect().Verb("POST");
            dispatcher.RegisterSet(
                "location1",
                new XDoc("subscription-set")
                .Elem("uri.owner", mockHierarchyRecipientUri)
                .Start("subscription")
                .Attr("id", "3")
                .Elem("uri.resource", "deki://default/pages/7#depth=infinity")
                .Elem("channel", "event://default/deki/pages/*")
                .Start("recipient").Attr("authtoken", "abc").Elem("uri", mockHierarchyRecipientUri).End()
                .End(),
                "def"
                );

            // subscribe to only page 7
            var mockPageonlyRecipientUri    = new XUri("http://mock/recipient/pageonly");
            var mockPageonlyRecipientCalled = 0;

            MockPlug.Register(mockPageonlyRecipientUri, delegate(Plug p, string v, XUri u, DreamMessage r, Result <DreamMessage> r2) {
                _log.DebugFormat("mockPageonlyRecipient called at: {0}", u);
                mockPageonlyRecipientCalled++;
                r2.Return(DreamMessage.Ok());
            });
            dispatcher.RegisterSet(
                "location2",
                new XDoc("subscription-set")
                .Elem("uri.owner", mockPageonlyRecipientUri)
                .Start("subscription")
                .Attr("id", "3")
                .Elem("uri.resource", "deki://default/pages/7")
                .Elem("channel", "event://default/deki/pages/*")
                .Start("recipient").Attr("authtoken", "abc").Elem("uri", mockPageonlyRecipientUri).End()
                .End(),
                "def"
                );

            // wait for subscriptions to be set up
            _log.DebugFormat("wait for subscriptions to be updated");
            Assert.IsTrue(
                Wait.For(() => combinedSetUpdated == 2, TimeSpan.FromSeconds(10)),
                string.Format("timeout waiting for subscriptions, expected 2, got {0} dispatches", combinedSetUpdated));

            // fire page change for page 10 (sub-child of page 7)
            XDoc evDoc = new XDoc("deki-event")
                         .Attr("wikiid", "default")
                         .Elem("channel", "event://default/deki/pages/update")
                         .Elem("uri", "deki://default/pages/10")
                         .Elem("pageid", "10");
            var ev = new DispatcherEvent(evDoc, new XUri("event://default/deki/pages/update"), new XUri("deki://default/pages/10"), new XUri("http://default/deki/pages/10"));

            _log.DebugFormat("ready to dispatch event");
            dispatcher.Dispatch(ev);

            // wait for deki to have been called
            _log.DebugFormat("wait for deki call");
            Assert.IsTrue(mockDeki.WaitAndVerify(TimeSpan.FromSeconds(10)), mockDeki.VerificationFailure);

            // wait for recipients to be notified
            _log.DebugFormat("wait for hierarchy notification");
            Assert.IsTrue(mockHierarchyRecipient.WaitAndVerify(TimeSpan.FromSeconds(10)), mockHierarchyRecipient.VerificationFailure);

            // only 'hierarchy' subscriber should have been notified
            _log.DebugFormat("make sure page only doesn't get called");
            Assert.IsFalse(Wait.For(() => mockPageonlyRecipientCalled > 0, TimeSpan.FromSeconds(5)));
        }
        public void Page_events_uses_deki_to_check_parent_wildcard_resource_matches()
        {
            var  dekiPageAuthEvent           = new ManualResetEvent(false);
            var  authorizedRecipientCalledEv = new ManualResetEvent(false);
            var  mockMatchCalledEv           = new ManualResetEvent(false);
            var  mockWildcardCalledEv        = new ManualResetEvent(false);
            XUri mockDeki           = new XUri("http://mock/deki");
            int  dekiPageAuthCalled = 0;
            int  dekiPageCalled     = 0;

            MockPlug.Register(mockDeki, delegate(Plug p, string v, XUri u, DreamMessage r, Result <DreamMessage> r2) {
                XDoc rDoc = null;
                _log.DebugFormat("mockDeki called at: {0}", u);
                if (u.Segments.Length == 3)
                {
                    _log.DebugFormat("getting page xml");
                    if (u.LastSegment == "10")
                    {
                        dekiPageCalled++;
                    }
                    rDoc = new XDoc("page")
                           .Attr("id", "10")
                           .Start("page.parent")
                           .Attr("id", "9")
                           .Start("page.parent")
                           .Attr("id", "8")
                           .Start("page.parent")
                           .Attr("id", "7")
                           .EndAll();
                }
                else
                {
                    _log.DebugFormat("getting users for page: {0}", u.LastSegment);
                    dekiPageAuthCalled++;
                    rDoc = new XDoc("users");
                    foreach (XDoc user in r.ToDocument()["user/@id"])
                    {
                        rDoc.Start("user").Attr("id", user.AsText).End();
                    }
                    if (dekiPageAuthCalled == 2)
                    {
                        dekiPageAuthEvent.Set();
                    }
                }
                r2.Return(DreamMessage.Ok(rDoc));
            });
            XUri authorizedRecipient       = new XUri("http://mock/authorized2");
            int  authorizedRecipientCalled = 0;
            XDoc authorizedReceived        = null;

            MockPlug.Register(authorizedRecipient, delegate(Plug p, string v, XUri u, DreamMessage r, Result <DreamMessage> r2) {
                _log.DebugFormat("authorizedRecipient called at: {0}", u);
                authorizedRecipientCalled++;
                authorizedReceived = r.ToDocument();
                authorizedRecipientCalledEv.Set();
                r2.Return(DreamMessage.Ok());
            });
            XUri mockRecipient       = new XUri("http://mock/r1");
            int  mockRecipientCalled = 0;
            int  mockMatchCalled     = 0;
            int  mockWildcardCalled  = 0;

            MockPlug.Register(mockRecipient, delegate(Plug p, string v, XUri u, DreamMessage r, Result <DreamMessage> r2) {
                _log.DebugFormat("mockRecipient called at: {0}", u);
                mockRecipientCalled++;
                if (u == mockRecipient.At("match"))
                {
                    mockMatchCalled++;
                    mockMatchCalledEv.Set();
                }
                else if (u == mockRecipient.At("wildcard"))
                {
                    mockWildcardCalled++;
                    mockWildcardCalledEv.Set();
                }
                r2.Return(DreamMessage.Ok());
            });
            XUri           dispatcherUri = new XUri("http://mock/dispatcher");
            DekiDispatcher dispatcher    = new DekiDispatcher(
                new DispatcherConfig()
            {
                ServiceUri          = dispatcherUri,
                ServiceAccessCookie = new DreamCookie("service-key", "foo", dispatcherUri),
                ServiceConfig       = new XDoc("config").Elem("uri.deki", mockDeki).Elem("authtoken", "abc")
            },
                _mockRepository.Object
                );
            XDoc sub1 = new XDoc("subscription-set")
                        .Elem("uri.owner", authorizedRecipient)
                        .Start("subscription")
                        .Attr("id", "1")
                        .Elem("channel", "event://default/deki/pages/*")
                        .Start("recipient").Attr("authtoken", "abc").Elem("uri", authorizedRecipient).End()
                        .End();

            _log.DebugFormat("registering sub set 1");
            dispatcher.RegisterSet("location1", sub1, "def");
            Assert.IsTrue(Wait.For(() =>
                                   dispatcher.CombinedSet.Subscriptions
                                   .Where(s =>
                                          s.Channels.Where(c => c.ToString() == "event://default/deki/pages/*").Any() &&
                                          !s.Resources.Any()
                                          ).Any(),
                                   10.Seconds()));

            XDoc sub2 = new XDoc("subscription-set")
                        .Elem("uri.owner", mockRecipient.At("1"))
                        .Start("subscription")
                        .Attr("id", "1")
                        .Elem("uri.resource", "deki://default/pages/10")
                        .Elem("channel", "event://default/deki/pages/*")
                        .Start("recipient").Attr("userid", "1").Elem("uri", "http://mock/r1/match").End()
                        .End();

            _log.DebugFormat("registering sub set 2");
            dispatcher.RegisterSet("location2", sub2, "def");
            Assert.IsTrue(Wait.For(() =>
                                   dispatcher.CombinedSet.Subscriptions
                                   .Where(s =>
                                          s.Channels.Where(c => c.ToString() == "event://default/deki/pages/*").Any() &&
                                          s.Resources.Where(r => r.ToString() == "deki://default/pages/10").Any()
                                          ).Any(),
                                   10.Seconds()));

            XDoc sub3 = new XDoc("subscription-set")
                        .Elem("uri.owner", mockRecipient.At("2"))
                        .Start("subscription")
                        .Attr("id", "2")
                        .Elem("uri.resource", "deki://default/pages/11")
                        .Elem("channel", "event://default/deki/pages/*")
                        .Start("recipient").Attr("userid", "1").Elem("uri", "http://mock/r1/miss").End()
                        .End();

            _log.DebugFormat("registering sub set 3");
            dispatcher.RegisterSet("location3", sub3, "def");
            Assert.IsTrue(Wait.For(() =>
                                   dispatcher.CombinedSet.Subscriptions
                                   .Where(s =>
                                          s.Channels.Where(c => c.ToString() == "event://default/deki/pages/*").Any() &&
                                          s.Resources.Where(r => r.ToString() == "deki://default/pages/11").Any()
                                          ).Any(),
                                   10.Seconds()));

            XDoc sub4 = new XDoc("subscription-set")
                        .Elem("uri.owner", mockRecipient.At("3"))
                        .Start("subscription")
                        .Attr("id", "3")
                        .Elem("uri.resource", "deki://default/pages/8#depth=infinity")
                        .Elem("channel", "event://default/deki/pages/*")
                        .Start("recipient").Attr("userid", "1").Elem("uri", "http://mock/r1/wildcard").End()
                        .End();

            _log.DebugFormat("registering sub set 4");
            dispatcher.RegisterSet("location4", sub4, "def");
            Assert.IsTrue(Wait.For(() =>
                                   dispatcher.CombinedSet.Subscriptions
                                   .Where(s =>
                                          s.Channels.Where(c => c.ToString() == "event://default/deki/pages/*").Any() &&
                                          s.Resources.Where(r => r.ToString() == "deki://default/pages/8#depth%3Dinfinity").Any()
                                          ).Any(),
                                   10.Seconds()));

            XDoc evDoc = new XDoc("deki-event")
                         .Attr("wikiid", "default")
                         .Elem("channel", "event://default/deki/pages/update")
                         .Elem("uri", "deki://default/pages/10")
                         .Elem("pageid", "10");
            var ev = new DispatcherEvent(evDoc, new XUri("event://default/deki/pages/update"), new XUri("deki://default/pages/10"), new XUri("http://default/deki/pages/10"));

            _log.DebugFormat("ready to dispatch event");
            dispatcher.Dispatch(ev);
            Assert.IsTrue(dekiPageAuthEvent.WaitOne(5000, true));
            Assert.IsTrue(authorizedRecipientCalledEv.WaitOne(5000, true));
            Assert.IsTrue(mockMatchCalledEv.WaitOne(5000, true));
            Assert.IsTrue(mockWildcardCalledEv.WaitOne(5000, true));
            Assert.AreEqual(1, dekiPageCalled);
            Assert.AreEqual(2, dekiPageAuthCalled);
            Assert.AreEqual(1, authorizedRecipientCalled);
            Assert.AreEqual(2, mockRecipientCalled);
            Assert.AreEqual(1, mockMatchCalled);
            Assert.AreEqual(1, mockWildcardCalled);
            Assert.AreEqual(evDoc, authorizedReceived);
        }
        public void Page_delete_events_skip_page_auth()
        {
            XUri mockDeki   = new XUri("http://mock/deki");
            int  dekiCalled = 0;

            MockPlug.Register(mockDeki, delegate(Plug p, string v, XUri u, DreamMessage r, Result <DreamMessage> r2) {
                _log.DebugFormat("mockDeki called at: {0}", u);
                dekiCalled++;
                r2.Return(DreamMessage.BadRequest("shouldn't have called deki"));
            });

            XUri           authorizedRecipient  = new XUri("http://mock/authorized2");
            AutoResetEvent authorizedResetEvent = new AutoResetEvent(false);
            XDoc           authorizedReceived   = null;

            MockPlug.Register(authorizedRecipient, delegate(Plug p, string v, XUri u, DreamMessage r, Result <DreamMessage> r2) {
                _log.DebugFormat("authorizedRecipient called at: {0}", u);
                authorizedReceived = r.ToDocument();
                authorizedResetEvent.Set();
                r2.Return(DreamMessage.Ok());
            });

            XUri mockRecipient       = new XUri("http://mock/r1");
            int  mockRecipientCalled = 0;

            MockPlug.Register(mockRecipient, delegate(Plug p, string v, XUri u, DreamMessage r, Result <DreamMessage> r2) {
                _log.DebugFormat("mockRecipient called at: {0}", u);
                mockRecipientCalled++;
                r2.Return(DreamMessage.Ok());
            });

            XUri           dispatcherUri = new XUri("http://mock/dispatcher");
            DekiDispatcher dispatcher    = new DekiDispatcher(
                new DispatcherConfig {
                ServiceUri          = dispatcherUri,
                ServiceAccessCookie = new DreamCookie("service-key", "foo", dispatcherUri),
                ServiceConfig       = new XDoc("config").Elem("uri.deki", mockDeki).Elem("authtoken", "abc")
            },
                _mockRepository.Object
                );

            XDoc sub1 = new XDoc("subscription-set")
                        .Elem("uri.owner", authorizedRecipient)
                        .Start("subscription")
                        .Attr("id", "1")
                        .Elem("channel", "event://default/deki/pages/*")
                        .Start("recipient").Attr("authtoken", "abc").Elem("uri", authorizedRecipient).End()
                        .End();

            Thread.Sleep(100);
            _log.DebugFormat("registering sub set 1");
            dispatcher.RegisterSet("abc", sub1, "def");
            XDoc sub2 = new XDoc("subscription-set")
                        .Elem("uri.owner", mockRecipient.At("1"))
                        .Start("subscription")
                        .Attr("id", "1")
                        .Elem("uri.resource", "deki://default/pages/10")
                        .Elem("channel", "event://default/deki/pages/*")
                        .Start("recipient").Attr("userid", "1").Elem("uri", "http://mock/r1/match").End()
                        .End();

            Thread.Sleep(100);
            _log.DebugFormat("registering sub set 2");
            dispatcher.RegisterSet("abc", sub2, "def");

            XDoc evDoc = new XDoc("deki-event")
                         .Attr("wikiid", "default")
                         .Elem("channel", "event://default/deki/pages/delete")
                         .Elem("uri", "deki://default/pages/10")
                         .Elem("pageid", "10");
            var ev = new DispatcherEvent(evDoc, new XUri("event://default/deki/pages/delete"), new XUri("deki://default/pages/10"), new XUri("http://default/deki/pages/10"));

            _log.DebugFormat("ready to dispatch event");

            // Meh. Testing multithreaded code is wonky. This 1000ms sleep is required, otherwise the event below never fires
            Thread.Sleep(1000);
            dispatcher.Dispatch(ev);

            // since we're waiting for more than one dekiPageAuthEvent, we give it a chance to finish after the first triggers
            Assert.IsTrue(authorizedResetEvent.WaitOne(5000, false));
            Assert.AreEqual(0, dekiCalled);
            Assert.AreEqual(0, mockRecipientCalled);
            Assert.AreEqual(evDoc, authorizedReceived);
        }
        public void InstanceKey_authtoken_gets_verified_against_deki()
        {
            var mockDeki            = new XUri("http://mock/deki");
            var dispatcherUri       = new XUri("http://mock/dispatcher");
            var authorizedRecipient = new XUri("http://mock/authorized");
            var dispatcher          = new DekiDispatcher(
                new DispatcherConfig {
                ServiceUri          = dispatcherUri,
                ServiceAccessCookie = new DreamCookie("service-key", "foo", dispatcherUri),
                ServiceConfig       = new XDoc("config").Elem("uri.deki", mockDeki).Elem("authtoken", "abc")
            },
                _mockRepository.Object
                );
            var sub = new XDoc("subscription-set")
                      .Elem("uri.owner", authorizedRecipient)
                      .Start("subscription")
                      .Attr("id", "1")
                      .Elem("channel", "event://default/deki/pages/*")
                      .Start("recipient").Attr("authtoken", "def").Elem("uri", authorizedRecipient).End()
                      .End();

            _log.DebugFormat("registering sub set");
            dispatcher.RegisterSet("abc", sub, "def");
            Assert.IsTrue(Wait.For(
                              () => dispatcher.CombinedSet.Subscriptions
                              .Where(s =>
                                     s.Channels.Where(c => c.ToString() == "event://default/deki/pages/*").Any() &&
                                     !s.Resources.Any()
                                     ).Any(),
                              10.Seconds()));
            MockPlug.Setup(mockDeki)
            .Verb("GET")
            .At("site", "settings")
            .With("apikey", "abc")
            .WithHeader("X-Deki-Site", "id=default")
            .Returns(new XDoc("config").Start("security").Elem("api-key", "def").End())
            .ExpectCalls(Times.Once());
            MockPlug.Setup(authorizedRecipient)
            .Verb("POST")
            .WithMessage(r => {
                if (!r.HasDocument)
                {
                    return(false);
                }
                var doc = r.ToDocument();
                _log.Debug(doc.ToPrettyString());
                return(doc["channel"].AsText == "event://default/deki/pages/update" &&
                       doc["pageid"].AsText == "10");
            })
            .ExpectCalls(Times.Once());
            var evDoc = new XDoc("deki-event")
                        .Attr("wikiid", "default")
                        .Elem("channel", "event://default/deki/pages/update")
                        .Elem("uri", "deki://default/pages/10")
                        .Elem("pageid", "10");
            var ev = new DispatcherEvent(evDoc, new XUri("event://default/deki/pages/update"), new XUri("deki://default/pages/10"), new XUri("http://default/deki/pages/10"));

            _log.DebugFormat("ready to dispatch event");
            dispatcher.Dispatch(ev);
            MockPlug.VerifyAll(TimeSpan.FromSeconds(10));
        }