package org.buddycloud.channelserver.packetprocessor.iq.namespace.pubsub.set; import java.util.ArrayList; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import junit.framework.Assert; import org.buddycloud.channelserver.Configuration; import org.buddycloud.channelserver.channel.ChannelManager; import org.buddycloud.channelserver.channel.validate.AtomEntry; import org.buddycloud.channelserver.db.exception.NodeStoreException; import org.buddycloud.channelserver.packetHandler.iq.IQTestHandler; import org.buddycloud.channelserver.packetprocessor.iq.namespace.pubsub.JabberPubsub; import org.buddycloud.channelserver.pubsub.affiliation.Affiliations; import org.buddycloud.channelserver.pubsub.model.NodeMembership; import org.buddycloud.channelserver.pubsub.model.NodeSubscription; import org.buddycloud.channelserver.pubsub.model.impl.GlobalItemIDImpl; import org.buddycloud.channelserver.pubsub.model.impl.NodeItemImpl; import org.buddycloud.channelserver.pubsub.model.impl.NodeMembershipImpl; import org.buddycloud.channelserver.pubsub.model.impl.NodeSubscriptionImpl; import org.buddycloud.channelserver.pubsub.subscription.Subscriptions; import org.buddycloud.channelserver.utils.XMLConstants; import org.dom4j.Element; import org.dom4j.tree.BaseElement; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import org.xmpp.packet.IQ; import org.xmpp.packet.JID; import org.xmpp.packet.Message; import org.xmpp.packet.Packet; import org.xmpp.packet.PacketError; import org.xmpp.resultsetmanagement.ResultSetImpl; public class PublishTest extends IQTestHandler { private IQ request; private ChannelManager channelManager; private Publish publish; private JID jid; private Element element; private BlockingQueue<Packet> queue = new LinkedBlockingQueue<Packet>(); private String node = "/user/romeo@shakespeare.lit/posts"; private String server = "channels.shakespeare.lit"; private AtomEntry validateEntry; private Element entry; @Before public void setUp() throws Exception { channelManager = Mockito.mock(ChannelManager.class); validateEntry = Mockito.mock(AtomEntry.class); Configuration.getInstance().putProperty(Configuration.CONFIGURATION_LOCAL_DOMAIN_CHECKER, Boolean.TRUE.toString()); queue = new LinkedBlockingQueue<Packet>(); publish = new Publish(queue, channelManager); jid = new JID("juliet@shakespeare.lit/balcony"); request = readStanzaAsIq("/iq/pubsub/publish/request.stanza"); publish.setChannelManager(channelManager); publish.setEntryValidator(validateEntry); entry = request.getChildElement().element("publish").element("item").element("entry").createCopy(); element = new BaseElement("publish"); Mockito.when(channelManager.nodeExists(node)).thenReturn(true); NodeMembership membership = new NodeMembershipImpl(node, jid, Subscriptions.subscribed, Affiliations.publisher, null); Mockito.when(channelManager.getNodeMembership(Mockito.eq(node), Mockito.eq(jid))).thenReturn(membership); Mockito.when(channelManager.getNodeSubscriptionListeners(Mockito.eq(node))).thenReturn( new ResultSetImpl<NodeSubscription>(new ArrayList<NodeSubscription>())); validateEntry.setPayload(request.getChildElement().element("publish").element("item").createCopy()); Mockito.when(validateEntry.getGlobalItemId()).thenReturn( new GlobalItemIDImpl(new JID(request.getTo().toBareJID()), node, entry.elementText("id")).toString()); Mockito.when(validateEntry.getLocalItemId()).thenCallRealMethod(); Mockito.when(validateEntry.isValid()).thenReturn(true); Mockito.when(validateEntry.getPayload()).thenReturn(entry); } @Test public void passingRetractAsElementNameReturnsTrue() { Element element = new BaseElement("publish"); Assert.assertTrue(publish.accept(element)); } @Test public void passingNotRetractAsElementNameReturnsFalse() { Element element = new BaseElement("not-publish"); Assert.assertFalse(publish.accept(element)); } @Test public void passingNoNodeResultsInErrorStanza() throws Exception { IQ request = this.request.createCopy(); request.getChildElement().element("publish").attribute("node").detach(); publish.process(element, jid, request, null); Packet response = queue.poll(); PacketError error = response.getError(); Assert.assertNotNull(error); Assert.assertEquals(PacketError.Type.modify, error.getType()); Assert.assertEquals(XMLConstants.NODE_ID_REQUIRED, error.getApplicationConditionName()); } @Test public void nodeStoreExceptionReturnsErrorStanza() throws Exception { Mockito.doThrow(new NodeStoreException()).when(channelManager).nodeExists(Mockito.eq(node)); publish.process(element, jid, request, null); Packet response = queue.poll(); PacketError error = response.getError(); Assert.assertNotNull(error); Assert.assertEquals(PacketError.Condition.internal_server_error, error.getCondition()); Assert.assertEquals(PacketError.Type.wait, error.getType()); } @Test public void providingNodeWhichDoesntExistReturnsError() throws Exception { Mockito.when(channelManager.nodeExists(node)).thenReturn(false); publish.process(element, jid, request, null); Packet response = queue.poll(); PacketError error = response.getError(); Assert.assertNotNull(error); Assert.assertEquals(PacketError.Type.cancel, error.getType()); Assert.assertEquals(PacketError.Condition.item_not_found, error.getCondition()); } @Test public void requestToRemoteNodeResultsInForwardedPacket() throws Exception { Configuration.getInstance().remove(Configuration.CONFIGURATION_LOCAL_DOMAIN_CHECKER); Configuration.getInstance().putProperty(Configuration.CONFIGURATION_SERVER_DOMAIN, "shakespeare.lit"); Assert.assertEquals(new JID("channels.shakespeare.lit"), request.getTo()); request.getElement().element("pubsub").element("publish").addAttribute("node", "/user/romeo@barracks.lit/posts"); publish.process(element, jid, request, null); Assert.assertEquals(1, queue.size()); Packet response = queue.poll(); Assert.assertEquals(new JID("barracks.lit"), response.getTo()); } @Test public void unsubscribedUserCanNotPublish() throws Exception { NodeMembership membership = new NodeMembershipImpl(node, jid, Subscriptions.none, Affiliations.publisher, null); Mockito.when(channelManager.getNodeMembership(Mockito.eq(node), Mockito.eq(jid))).thenReturn(membership); publish.process(element, jid, request, null); Packet response = queue.poll(); PacketError error = response.getError(); Assert.assertNotNull(error); Assert.assertEquals(PacketError.Type.auth, error.getType()); Assert.assertEquals(PacketError.Condition.forbidden, error.getCondition()); } @Test public void pendingSubscriptionCanNotPublish() throws Exception { NodeMembership membership = new NodeMembershipImpl(node, jid, Subscriptions.pending, Affiliations.publisher, null); Mockito.when(channelManager.getNodeMembership(Mockito.eq(node), Mockito.eq(jid))).thenReturn(membership); publish.process(element, jid, request, null); Packet response = queue.poll(); PacketError error = response.getError(); Assert.assertNotNull(error); Assert.assertEquals(PacketError.Type.auth, error.getType()); Assert.assertEquals(PacketError.Condition.forbidden, error.getCondition()); } @Test public void noAffiliationCanNotPublish() throws Exception { NodeMembership membership = new NodeMembershipImpl(node, jid, Subscriptions.subscribed, Affiliations.none, null); Mockito.when(channelManager.getNodeMembership(Mockito.eq(node), Mockito.eq(jid))).thenReturn(membership); publish.process(element, jid, request, null); Packet response = queue.poll(); PacketError error = response.getError(); Assert.assertNotNull(error); Assert.assertEquals(PacketError.Type.auth, error.getType()); Assert.assertEquals(PacketError.Condition.forbidden, error.getCondition()); } @Test public void memberAffiliationCanNotPublish() throws Exception { NodeMembership membership = new NodeMembershipImpl(node, jid, Subscriptions.subscribed, Affiliations.member, null); Mockito.when(channelManager.getNodeMembership(Mockito.eq(node), Mockito.eq(jid))).thenReturn(membership); publish.process(element, jid, request, null); Packet response = queue.poll(); PacketError error = response.getError(); Assert.assertNotNull(error); Assert.assertEquals(PacketError.Type.auth, error.getType()); Assert.assertEquals(PacketError.Condition.forbidden, error.getCondition()); } @Test public void outcastAffiliationCanNotPublish() throws Exception { NodeMembership membership = new NodeMembershipImpl(node, jid, Subscriptions.subscribed, Affiliations.outcast, null); Mockito.when(channelManager.getNodeMembership(Mockito.eq(node), Mockito.eq(jid))).thenReturn(membership); publish.process(element, jid, request, null); Packet response = queue.poll(); PacketError error = response.getError(); Assert.assertNotNull(error); Assert.assertEquals(PacketError.Type.auth, error.getType()); Assert.assertEquals(PacketError.Condition.forbidden, error.getCondition()); } @Test public void noItemElementReturnsError() throws Exception { IQ request = this.request.createCopy(); request.getChildElement().element("publish").element("item").detach(); publish.process(element, jid, request, null); Packet response = queue.poll(); PacketError error = response.getError(); Assert.assertNotNull(error); Assert.assertEquals(PacketError.Type.modify, error.getType()); Assert.assertEquals(PacketError.Condition.bad_request, error.getCondition()); Assert.assertEquals(XMLConstants.ITEM_REQUIRED_ELEM, error.getApplicationConditionName()); } @Test public void invalidEntryReturnsError() throws Exception { String errorMessage = "errorMessage"; Mockito.when(validateEntry.isValid()).thenReturn(false); Mockito.when(validateEntry.getErrorMessage()).thenReturn(errorMessage); publish.process(element, jid, request, null); Packet response = queue.poll(); PacketError error = response.getError(); Assert.assertNotNull(error); Assert.assertEquals(PacketError.Type.modify, error.getType()); Assert.assertEquals(PacketError.Condition.bad_request, error.getCondition()); Assert.assertEquals(errorMessage, error.getApplicationConditionName()); } @Test public void itemIsSavedAsExpected() throws Exception { IQ request = this.request.createCopy(); publish.process(element, jid, request, null); Mockito.verify(channelManager, Mockito.times(1)).addNodeItem(Mockito.any(NodeItemImpl.class)); } @Test public void expectedSuccessResponseReceived() throws Exception { IQ request = this.request.createCopy(); publish.process(element, jid, request, null); IQ response = (IQ) queue.poll(); Assert.assertEquals(IQ.Type.result, response.getType()); Assert.assertEquals(request.getFrom(), response.getTo()); Assert.assertEquals(request.getTo(), response.getFrom()); Element pubsub = response.getElement().element("pubsub"); Assert.assertEquals(JabberPubsub.NAMESPACE_URI, pubsub.getNamespaceURI()); Element publish = pubsub.element("publish"); Assert.assertEquals(node, publish.attributeValue("node")); Element item = publish.element("item"); Assert.assertNotNull(item); Assert.assertTrue(item.attributeValue("id").length() > 0); Assert.assertTrue(GlobalItemIDImpl.isGlobalId(item.attributeValue("id"))); } @Test public void sendsOutExpectedNotifications() throws Exception { NodeSubscription subscriber1 = new NodeSubscriptionImpl(node, new JID("romeo@shakespeare.lit"), Subscriptions.subscribed, null); // Expect not to see this user (subscription: 'pending') NodeSubscription subscriber2 = new NodeSubscriptionImpl(node, new JID("titania@shakespeare.lit"), Subscriptions.pending, null); NodeSubscription subscriber3 = new NodeSubscriptionImpl(node, new JID("faustus@marlowe.lit"), new JID("channels.marlowe.lit"), Subscriptions.subscribed, null); ArrayList<NodeSubscription> subscribers = new ArrayList<NodeSubscription>(); subscribers.add(subscriber1); subscribers.add(subscriber2); subscribers.add(subscriber3); Mockito.when(channelManager.getNodeSubscriptionListeners(Mockito.eq(node))).thenReturn(new ResultSetImpl<NodeSubscription>(subscribers)); IQ request = this.request.createCopy(); publish.process(element, jid, request, null); Assert.assertEquals(5, queue.size()); queue.poll(); Message notification = (Message) queue.poll(); Assert.assertEquals(Message.Type.headline, notification.getType()); Assert.assertEquals(subscriber1.getUser(), notification.getTo()); Assert.assertEquals(server, notification.getFrom().toString()); Element event = notification.getElement().element("event"); Assert.assertEquals(JabberPubsub.NS_PUBSUB_EVENT, event.getNamespaceURI()); Element items = event.element("items"); Assert.assertEquals(node, items.attributeValue("node")); Element item = items.element("item"); Assert.assertTrue(item.attributeValue("id").length() > 0); Assert.assertTrue(GlobalItemIDImpl.isGlobalId(item.attributeValue("id"))); Element responseEntry = item.element("entry"); Assert.assertEquals(entry.asXML(), responseEntry.asXML()); notification = (Message) queue.poll(); Assert.assertEquals(subscriber3.getListener(), notification.getTo()); notification = (Message) queue.poll(); Assert.assertEquals(new JID("user1@server1"), notification.getTo()); notification = (Message) queue.poll(); Assert.assertEquals(new JID("user2@server1"), notification.getTo()); } @Test public void inReplyToIdIsSavedToDatabase() throws Exception { IQ request = readStanzaAsIq("/iq/pubsub/publish/reply.stanza"); Mockito.when(validateEntry.getPayload()).thenReturn(request.getChildElement().element("publish").element("item").element("entry")); Mockito.when(validateEntry.getInReplyTo()).thenReturn( GlobalItemIDImpl.toLocalId(request.getChildElement().element("publish").element("item").element("entry").element("in-reply-to") .attributeValue("ref"))); publish.process(element, jid, request, null); Assert.assertEquals(IQ.Type.result, ((IQ) queue.poll()).getType()); ArgumentCaptor<NodeItemImpl> argument = ArgumentCaptor.forClass(NodeItemImpl.class); Mockito.verify(channelManager, Mockito.times(1)).addNodeItem(argument.capture()); Assert.assertEquals("fc362eb42085f017ed9ccd9c4004b095", argument.getValue().getInReplyTo()); Assert.assertEquals(node, argument.getValue().getNodeId()); } @Test public void replyUpdatesThreadParentDate() throws Exception { IQ request = readStanzaAsIq("/iq/pubsub/publish/reply.stanza"); Mockito.when(validateEntry.getPayload()).thenReturn(request.getChildElement().element("publish").element("item").element("entry")); Mockito.when(validateEntry.getInReplyTo()).thenReturn("fc362eb42085f017ed9ccd9c4004b095"); publish.process(element, jid, request, null); Assert.assertEquals(IQ.Type.result, ((IQ) queue.poll()).getType()); ArgumentCaptor<String> inReplyTo = ArgumentCaptor.forClass(String.class); ArgumentCaptor<String> passedNode = ArgumentCaptor.forClass(String.class); Mockito.verify(channelManager, Mockito.times(1)).updateThreadParent(passedNode.capture(), inReplyTo.capture()); Assert.assertEquals("fc362eb42085f017ed9ccd9c4004b095", inReplyTo.getValue()); Assert.assertEquals(node, passedNode.getValue()); } @Test public void doesNotUpdateParentThreadIfNotReply() throws Exception { IQ request = this.request.createCopy(); publish.process(element, jid, request, null); Mockito.verify(channelManager, Mockito.times(0)).updateThreadParent(Mockito.anyString(), Mockito.anyString()); } }