Changeset

9266:c5267f5fd6e6

spec/scansion: Add scansion scripts used for testing All past, except issue978-multi which fails randomly based on ordering of elements - this is a scansion issue to be resolved.
author Matthew Wild <mwild1@gmail.com>
date Thu, 06 Sep 2018 16:44:48 +0100
parents 9265:585ef5c1b226
children 9267:37e2cace1f2a
files spec/scansion/basic.scs spec/scansion/basic_message.scs spec/scansion/basic_roster.scs spec/scansion/issue505.scs spec/scansion/issue978-multi.scs spec/scansion/issue978.scs spec/scansion/muc_mediated_invite.scs spec/scansion/muc_password-trunk.scs spec/scansion/muc_password.scs spec/scansion/muc_register.scs spec/scansion/pubsub_advanced.scs spec/scansion/pubsub_basic.scs spec/scansion/pubsub_createdelete.scs
diffstat 13 files changed, 1474 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spec/scansion/basic.scs	Thu Sep 06 16:44:48 2018 +0100
@@ -0,0 +1,16 @@
+[Client] Romeo
+	jid: user@localhost
+	password: password
+
+---------
+
+Romeo connects
+
+Romeo sends:
+	<presence/>
+
+Romeo receives:
+	<presence/>
+
+Romeo disconnects
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spec/scansion/basic_message.scs	Thu Sep 06 16:44:48 2018 +0100
@@ -0,0 +1,174 @@
+# A script testing basic message routing and delivery
+
+[Client] Romeo
+	jid: user@localhost
+	password: password
+
+[Client] Juliet
+	jid: juliet@localhost
+	password: password
+
+[Client] Juliet's phone
+	jid: juliet@localhost
+	password: password
+	resource: mobile
+
+---------
+
+# Act 1, scene 1
+# The clients connect
+
+Romeo connects
+
+Juliet connects
+
+Juliet's phone connects
+
+# Romeo publishes his presence. Juliet has not, and so does not receive presence.
+
+Romeo sends:
+	<presence/>
+
+Romeo receives:
+	<presence from="${Romeo's full JID}" />
+
+# Romeo sends a message to Juliet's full JID
+
+Romeo sends:
+	<message to="${Juliet's full JID}" type="chat">
+		<body>Hello Juliet!</body>
+	</message>
+
+Juliet receives:
+	<message to="${Juliet's full JID}" from="${Romeo's full JID}" type="chat">
+		<body>Hello Juliet!</body>
+	</message>
+
+# Romeo sends a message to Juliet's phone
+
+Romeo sends:
+	<message to="${Juliet's phone's full JID}" type="chat">
+		<body>Hello Juliet, on your phone.</body>
+	</message>
+
+Juliet's phone receives:
+	<message to="${Juliet's phone's full JID}" from="${Romeo's full JID}" type="chat">
+		<body>Hello Juliet, on your phone.</body>
+	</message>
+
+# Scene 2
+# This requires the server to support offline messages (which is optional).
+
+# Romeo sends a message to Juliet's bare JID. This is not immediately delivered, as she
+# has not published presence on either of her resources.
+
+Romeo sends:
+	<message to="juliet@localhost" type="chat">
+		<body>Hello Juliet, are you there?</body>
+	</message>
+
+# Juliet sends presence on her phone, and should receive the message there
+
+Juliet's phone sends:
+	<presence/>
+
+Juliet's phone receives:
+	<presence/>
+
+Juliet's phone receives:
+	<message from="${Romeo's full JID}" type="chat">
+		<body>Hello Juliet, are you there?</body>
+		<delay xmlns='urn:xmpp:delay' from='localhost' />
+	</message>	
+
+# Romeo sends another bare-JID message, it should be delivered
+# instantly to Juliet's phone
+
+Romeo sends:
+	<message to="juliet@localhost" type="chat">
+		<body>Oh, hi!</body>
+	</message>
+
+Juliet's phone receives:
+	<message from="${Romeo's full JID}" type="chat">
+		<body>Oh, hi!</body>
+	</message>	
+
+# Juliet's laptop goes online, but with a negative priority
+
+Juliet sends:
+	<presence>
+		<priority>-1</priority>
+	</presence>
+
+Juliet receives:
+	<presence from="${Juliet's full JID}">
+		<priority>-1</priority>
+	</presence>
+
+Juliet's phone receives:
+	<presence from="${Juliet's full JID}">
+		<priority>-1</priority>
+	</presence>
+
+# Again, Romeo sends a message to her bare JID, but it should
+# only get delivered to her phone:
+
+Romeo sends:
+	<message to="juliet@localhost" type="chat">
+		<body>How are you?</body>
+	</message>
+
+Juliet's phone receives:
+	<message from="${Romeo's full JID}" type="chat">
+		<body>How are you?</body>
+	</message>	
+
+# Romeo sends direct to Juliet's full JID, and she should receive it
+
+Romeo sends:
+	<message to="${Juliet's full JID}" type="chat">
+		<body>Are you hiding?</body>
+	</message>
+
+Juliet receives:
+	<message from="${Romeo's full JID}" type="chat">
+		<body>Are you hiding?</body>
+	</message>
+
+# Juliet publishes non-negative presence
+
+Juliet sends:
+	<presence/>
+
+Juliet receives:
+	<presence from="${Juliet's full JID}"/>
+
+Juliet's phone receives:
+	<presence from="${Juliet's full JID}"/>
+
+# And now Romeo's bare JID messages get delivered to both resources
+# (server behaviour may vary here)
+
+Romeo sends:
+	<message to="juliet@localhost" type="chat">
+		<body>There!</body>
+	</message>
+
+Juliet receives:
+	<message from="${Romeo's full JID}" type="chat">
+		<body>There!</body>
+	</message>
+
+Juliet's phone receives:
+	<message from="${Romeo's full JID}" type="chat">
+		<body>There!</body>
+	</message>
+
+# The End
+
+Romeo disconnects
+
+Juliet disconnects
+
+Juliet's phone disconnects
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spec/scansion/basic_roster.scs	Thu Sep 06 16:44:48 2018 +0100
@@ -0,0 +1,39 @@
+[Client] Romeo
+	jid: user@localhost
+	password: password
+
+[Client] Juliet
+	jid: juliet@localhost
+	password: password
+
+---------
+
+Romeo connects
+
+Juliet connects
+
+Romeo sends:
+	<presence/>
+
+Romeo receives:
+	<presence from="${Romeo's full JID}" />
+
+Romeo sends:
+	<iq type="get" id="roster1">
+		<query xmlns='jabber:iq:roster'/>
+	</iq>
+
+Romeo receives:
+	<iq type="result" id="roster1">
+		<query xmlns="jabber:iq:roster"/>
+	</iq>
+
+Romeo disconnects
+
+#Juliet receives:
+#	<presence from="${Romeo's full JID}" />
+#
+#Juliet receives:
+#	<presence from="${Romeo's full JID}" type="unavailable" />
+#
+#Juliet disconnects
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spec/scansion/issue505.scs	Thu Sep 06 16:44:48 2018 +0100
@@ -0,0 +1,77 @@
+[Client] Romeo
+	jid: user@localhost
+	password: password
+
+[Client] Juliet
+	jid: user2@localhost
+	password: password
+
+-----
+
+Romeo connects
+
+Romeo sends:
+	<presence to="room@conference.localhost/Romeo">
+		<x xmlns="http://jabber.org/protocol/muc"/>
+	</presence>
+
+Romeo receives:
+	<presence from='room@conference.localhost/Romeo'>
+		<x xmlns='http://jabber.org/protocol/muc#user'>
+			<status code='201'/>
+			<item jid="${Romeo's full JID}" affiliation='owner' role='moderator'/>
+			<status code='110'/>
+		</x>
+	</presence>
+
+Romeo receives:
+	<message type='groupchat' from='room@conference.localhost'><subject/></message>
+
+# Submit config form
+Romeo sends:
+	<iq id='config1' to='room@conference.localhost' type='set'>
+		<query xmlns='http://jabber.org/protocol/muc#owner'>
+			<x xmlns='jabber:x:data' type='submit'>
+				<field var='FORM_TYPE'>
+					<value>http://jabber.org/protocol/muc#roomconfig</value>
+				</field>
+			</x>
+		</query>
+	</iq>
+
+Romeo receives:
+	<iq id="config1" from="room@conference.localhost" type="result">
+	</iq>
+
+# Juliet connects, and joins the room
+Juliet connects
+
+Juliet sends:
+	<presence to="room@conference.localhost/Juliet">
+		<x xmlns="http://jabber.org/protocol/muc"/>
+	</presence>
+
+Juliet receives:
+	<presence from="room@conference.localhost/Romeo" />
+
+Juliet receives:
+	<presence from="room@conference.localhost/Juliet" />
+
+Juliet receives:
+	<message type='groupchat' from='room@conference.localhost'><subject/></message>
+
+Romeo receives:
+	<presence from="room@conference.localhost/Juliet" />
+
+Juliet sends:
+	<presence type='unavailable' to='room@conference.localhost'>
+		<status>Farewell</status>
+	</presence>
+
+Romeo receives:
+	<presence type='unavailable' from='room@conference.localhost/Juliet'>
+		<status>Farewell</status>
+		<x xmlns='http://jabber.org/protocol/muc#user'>
+			<item jid="${Juliet's full JID}" affiliation='none' role='none'/>
+		</x>
+	</presence>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spec/scansion/issue978-multi.scs	Thu Sep 06 16:44:48 2018 +0100
@@ -0,0 +1,109 @@
+[Client] Romeo
+	jid: user@localhost
+	password: password
+
+[Client] Juliet
+	jid: user2@localhost
+	password: password
+
+[Client] Juliet's phone
+	jid: user2@localhost
+	password: password
+
+-----
+
+Romeo connects
+
+Romeo sends:
+	<presence to="room@conference.localhost/Romeo">
+		<x xmlns="http://jabber.org/protocol/muc"/>
+	</presence>
+
+Romeo receives:
+	<presence from='room@conference.localhost/Romeo'>
+		<x xmlns='http://jabber.org/protocol/muc#user'>
+			<status code='201'/>
+			<item jid="${Romeo's full JID}" affiliation='owner' role='moderator'/>
+			<status code='110'/>
+		</x>
+	</presence>
+
+Romeo receives:
+	<message type='groupchat' from='room@conference.localhost'><subject/></message>
+
+# Submit config form
+Romeo sends:
+	<iq id='config1' to='room@conference.localhost' type='set'>
+		<query xmlns='http://jabber.org/protocol/muc#owner'>
+			<x xmlns='jabber:x:data' type='submit'>
+				<field var='FORM_TYPE'>
+					<value>http://jabber.org/protocol/muc#roomconfig</value>
+				</field>
+			</x>
+		</query>
+	</iq>
+
+Romeo receives:
+	<iq id="config1" from="room@conference.localhost" type="result">
+	</iq>
+
+# Juliet connects, and joins the room
+Juliet connects
+
+Juliet sends:
+	<presence to="room@conference.localhost/Juliet">
+		<x xmlns="http://jabber.org/protocol/muc"/>
+	</presence>
+
+Juliet receives:
+	<presence from="room@conference.localhost/Romeo" />
+
+Juliet receives:
+	<presence from="room@conference.localhost/Juliet" />
+
+Juliet receives:
+	<message type='groupchat' from='room@conference.localhost'><subject/></message>
+
+Romeo receives:
+	<presence from="room@conference.localhost/Juliet" />
+
+# Juliet's phone connects, and joins the room
+Juliet's phone connects
+
+Juliet's phone sends:
+	<presence to="room@conference.localhost/Juliet">
+		<x xmlns="http://jabber.org/protocol/muc"/>
+	</presence>
+
+Juliet's phone receives:
+	<presence from="room@conference.localhost/Romeo" />
+
+Juliet's phone receives:
+	<presence from="room@conference.localhost/Juliet" />
+
+Juliet's phone receives:
+	<message type='groupchat' from='room@conference.localhost'><subject/></message>
+
+Romeo receives:
+	<presence from='room@conference.localhost/Juliet'>
+		<x xmlns='http://jabber.org/protocol/muc#user'>
+			<item affiliation='none' jid="${Juliet's phone's full JID}" role='participant'/>
+			<item affiliation='none' jid="${Juliet's full JID}" role='participant'/>
+		</x>
+	</presence>
+
+# Juliet leaves with an error
+Juliet sends:
+	<presence type='error' to='room@conference.localhost'>
+		<error type='cancel'>
+			<service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
+			<text xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'>Test error</text>
+		</error>
+	</presence>
+
+Romeo receives:
+	<presence from='room@conference.localhost/Juliet'>
+		<x xmlns='http://jabber.org/protocol/muc#user'>
+			<item jid="${Juliet's phone's full JID}" affiliation='none' role='participant'/>
+		</x>
+	</presence>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spec/scansion/issue978.scs	Thu Sep 06 16:44:48 2018 +0100
@@ -0,0 +1,84 @@
+[Client] Romeo
+	jid: user@localhost
+	password: password
+
+[Client] Juliet
+	jid: user2@localhost
+	password: password
+
+-----
+
+Romeo connects
+
+Romeo sends:
+	<presence to="room@conference.localhost/Romeo">
+		<x xmlns="http://jabber.org/protocol/muc"/>
+	</presence>
+
+Romeo receives:
+	<presence from='room@conference.localhost/Romeo'>
+		<x xmlns='http://jabber.org/protocol/muc#user'>
+			<status code='201'/>
+			<item jid="${Romeo's full JID}" affiliation='owner' role='moderator'/>
+			<status code='110'/>
+		</x>
+	</presence>
+
+Romeo receives:
+	<message type='groupchat' from='room@conference.localhost'><subject/></message>
+
+# Submit config form
+Romeo sends:
+	<iq id='config1' to='room@conference.localhost' type='set'>
+		<query xmlns='http://jabber.org/protocol/muc#owner'>
+			<x xmlns='jabber:x:data' type='submit'>
+				<field var='FORM_TYPE'>
+					<value>http://jabber.org/protocol/muc#roomconfig</value>
+				</field>
+				<field var='muc#roomconfig_whois'>
+					<value>anyone</value>
+				</field>
+			</x>
+		</query>
+	</iq>
+
+Romeo receives:
+	<iq id="config1" from="room@conference.localhost" type="result">
+	</iq>
+
+# Juliet connects, and joins the room
+Juliet connects
+
+Juliet sends:
+	<presence to="room@conference.localhost/Juliet">
+		<x xmlns="http://jabber.org/protocol/muc"/>
+	</presence>
+
+Juliet receives:
+	<presence from="room@conference.localhost/Romeo" />
+
+Juliet receives:
+	<presence from="room@conference.localhost/Juliet" />
+
+Juliet receives:
+	<message type='groupchat' from='room@conference.localhost'><subject/></message>
+
+Romeo receives:
+	<presence from="room@conference.localhost/Juliet" />
+
+Juliet sends:
+	<presence type='error' to='room@conference.localhost'>
+		<error type='cancel'>
+			<service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
+			<text xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'>Test error</text>
+		</error>
+	</presence>
+
+Romeo receives:
+	<presence type='unavailable' from='room@conference.localhost/Juliet'>
+		<status>Kicked: service unavailable: Test error</status>
+		<x xmlns='http://jabber.org/protocol/muc#user'>
+			<status code='333'/>
+			<item jid="${Juliet's full JID}" affiliation='none' role='none'/>
+		</x>
+	</presence>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spec/scansion/muc_mediated_invite.scs	Thu Sep 06 16:44:48 2018 +0100
@@ -0,0 +1,74 @@
+[Client] Romeo
+	jid: user@localhost
+	password: password
+
+[Client] Juliet
+	jid: user2@localhost
+	password: password
+
+-----
+
+Romeo connects
+
+Romeo sends:
+	<presence to="room@conference.localhost/Romeo">
+		<x xmlns="http://jabber.org/protocol/muc"/>
+	</presence>
+
+Romeo receives:
+	<presence from='room@conference.localhost/Romeo'>
+		<x xmlns='http://jabber.org/protocol/muc#user'>
+			<status code='201'/>
+			<item jid="${Romeo's full JID}" affiliation='owner' role='moderator'/>
+			<status code='110'/>
+		</x>
+	</presence>
+
+Romeo receives:
+	<message type='groupchat' from='room@conference.localhost'><subject/></message>
+
+# Submit config form
+Romeo sends:
+	<iq id='config1' to='room@conference.localhost' type='set'>
+		<query xmlns='http://jabber.org/protocol/muc#owner'>
+			<x xmlns='jabber:x:data' type='submit'>
+				<field var='FORM_TYPE'>
+					<value>http://jabber.org/protocol/muc#roomconfig</value>
+				</field>
+			</x>
+		</query>
+	</iq>
+
+Romeo receives:
+	<iq id="config1" from="room@conference.localhost" type="result">
+	</iq>
+
+# Juliet connects
+Juliet connects
+
+Juliet sends:
+	<presence/>
+
+Juliet receives:
+	<presence/>
+
+
+# Romeo invites Juliet to join the room
+
+Romeo sends:
+	<message to="room@conference.localhost" id="invite1">
+		<x xmlns="http://jabber.org/protocol/muc#user">
+			<invite to="${Juliet's JID}" />
+		</x>
+	</message>
+
+Juliet receives:
+	<message from="room@conference.localhost" id="invite1">
+		<x xmlns="http://jabber.org/protocol/muc#user">
+			<invite from="room@conference.localhost/Romeo">
+				<reason/>
+			</invite>
+		</x>
+		<body>room@conference.localhost/Romeo invited you to the room room@conference.localhost</body>
+		<x xmlns="jabber:x:conference" jid="room@conference.localhost"/>
+	</message>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spec/scansion/muc_password-trunk.scs	Thu Sep 06 16:44:48 2018 +0100
@@ -0,0 +1,141 @@
+[Client] Romeo
+	jid: user@localhost
+	password: password
+
+[Client] Juliet
+	jid: user2@localhost
+	password: password
+
+-----
+
+Romeo connects
+
+Romeo sends:
+	<presence to="room@conference.localhost/Romeo">
+		<x xmlns="http://jabber.org/protocol/muc"/>
+	</presence>
+
+Romeo receives:
+	<presence from='room@conference.localhost/Romeo'>
+		<x xmlns='http://jabber.org/protocol/muc#user'>
+			<status code='201'/>
+			<item jid="${Romeo's full JID}" affiliation='owner' role='moderator'/>
+			<status code='110'/>
+		</x>
+	</presence>
+
+Romeo receives:
+	<message type='groupchat' from='room@conference.localhost'><subject/></message>
+
+# Submit config form
+Romeo sends:
+	<iq id='config1' to='room@conference.localhost' type='set'>
+		<query xmlns='http://jabber.org/protocol/muc#owner'>
+			<x xmlns='jabber:x:data' type='submit'>
+				<field var='FORM_TYPE'>
+					<value>http://jabber.org/protocol/muc#roomconfig</value>
+				</field>
+				<field var='muc#roomconfig_roomsecret'>
+					<value>cauldronburn</value>
+				</field>
+			</x>
+		</query>
+	</iq>
+
+Romeo receives:
+	<iq id="config1" from="room@conference.localhost" type="result">
+	</iq>
+
+# Juliet connects, and tries to join the room (password-protected)
+Juliet connects
+
+Juliet sends:
+	<presence to="room@conference.localhost/Juliet">
+		<x xmlns="http://jabber.org/protocol/muc"/>
+	</presence>
+
+Juliet receives:
+	<presence from="room@conference.localhost/Juliet" type="error">
+		<error type="auth">
+			<not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
+		</error>
+	</presence>
+
+# Retry with the correct password
+Juliet sends:
+	<presence to="room@conference.localhost/Juliet">
+		<x xmlns="http://jabber.org/protocol/muc">
+			<password>cauldronburn</password>
+		</x>
+	</presence>
+
+Juliet receives:
+	<presence from="room@conference.localhost/Romeo" />
+
+Juliet receives:
+	<presence from="room@conference.localhost/Juliet" />
+
+Juliet receives:
+	<message type='groupchat' from='room@conference.localhost'><subject/></message>
+
+Romeo receives:
+	<presence from="room@conference.localhost/Juliet" />
+
+# Ok, now Juliet leaves, and Romeo unsets the password
+
+Juliet sends:
+	<presence type="unavailable" to="room@conference.localhost"/>
+
+Romeo receives:
+	<presence type="unavailable" from="room@conference.localhost/Juliet"/>
+
+Juliet receives:
+	<presence type="unavailable" from="room@conference.localhost/Juliet"/>
+
+# Remove room password
+Romeo sends:
+	<iq id='config2' to='room@conference.localhost' type='set'>
+		<query xmlns='http://jabber.org/protocol/muc#owner'>
+			<x xmlns='jabber:x:data' type='submit'>
+				<field var='FORM_TYPE'>
+					<value>http://jabber.org/protocol/muc#roomconfig</value>
+				</field>
+				<field var='muc#roomconfig_roomsecret'>
+				</field>
+			</x>
+		</query>
+	</iq>
+
+# Config change success
+Romeo receives:
+	<iq id="config2" from="room@conference.localhost" type="result">
+	</iq>
+
+# Notification of room configuration update
+Romeo receives:
+	<message type='groupchat' from='room@conference.localhost'>
+		<x xmlns='http://jabber.org/protocol/muc#user'>
+			<status code='104'/>
+		</x>
+	</message>
+
+# Juliet tries to join (should succeed)
+Juliet sends:
+	<presence to="room@conference.localhost/Juliet">
+		<x xmlns="http://jabber.org/protocol/muc"/>
+	</presence>
+
+# Notification of Romeo's presence in the room
+Juliet receives:
+	<presence from="room@conference.localhost/Romeo" />
+
+Juliet receives:
+	<presence from="room@conference.localhost/Juliet" />
+
+# Room topic
+Juliet receives:
+	<message type='groupchat' from='room@conference.localhost'><subject/></message>
+
+Romeo receives:
+	<presence from="room@conference.localhost/Juliet" />
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spec/scansion/muc_password.scs	Thu Sep 06 16:44:48 2018 +0100
@@ -0,0 +1,149 @@
+# MUC password test
+# Ensures that setting and unsetting a MUC password works, and that the password is
+# required to join a password-protected room.
+
+## prosody-0.9 result: fails because of missing 201 status code (related to issue #328)
+## prosody-0.10 result: fails because of missing 201 status code (related to issue #328)
+
+[Client] Romeo
+	jid: user@localhost
+	password: password
+
+[Client] Juliet
+	jid: user2@localhost
+	password: password
+
+-----
+
+Romeo connects
+
+Romeo sends:
+	<presence to="room@conference.localhost/Romeo">
+		<x xmlns="http://jabber.org/protocol/muc"/>
+	</presence>
+
+Romeo receives:
+	<presence from='room@conference.localhost/Romeo'>
+		<x xmlns='http://jabber.org/protocol/muc#user'>
+			<status code='201'/>
+			<item jid="${Romeo's full JID}" affiliation='owner' role='moderator'/>
+			<status code='110'/>
+		</x>
+	</presence>
+
+Romeo receives:
+	<message type='groupchat' from='room@conference.localhost'><subject/></message>
+
+# Submit config form
+Romeo sends:
+	<iq id='config1' to='room@conference.localhost' type='set'>
+		<query xmlns='http://jabber.org/protocol/muc#owner'>
+			<x xmlns='jabber:x:data' type='submit'>
+				<field var='FORM_TYPE'>
+					<value>http://jabber.org/protocol/muc#roomconfig</value>
+				</field>
+				<field var='muc#roomconfig_roomsecret'>
+					<value>cauldronburn</value>
+				</field>
+			</x>
+		</query>
+	</iq>
+
+Romeo receives:
+	<iq id="config1" from="room@conference.localhost" type="result">
+	</iq>
+
+# Juliet connects, and tries to join the room (password-protected)
+Juliet connects
+
+Juliet sends:
+	<presence to="room@conference.localhost/Juliet">
+		<x xmlns="http://jabber.org/protocol/muc"/>
+	</presence>
+
+Juliet receives:
+	<presence from="room@conference.localhost/Juliet" type="error">
+		<error type="auth">
+			<not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
+		</error>
+	</presence>
+
+# Retry with the correct password
+Juliet sends:
+	<presence to="room@conference.localhost/Juliet">
+		<x xmlns="http://jabber.org/protocol/muc">
+			<password>cauldronburn</password>
+		</x>
+	</presence>
+
+Juliet receives:
+	<presence from="room@conference.localhost/Romeo" />
+
+Juliet receives:
+	<presence from="room@conference.localhost/Juliet" />
+
+Juliet receives:
+	<message type='groupchat' from='room@conference.localhost'><subject/></message>
+
+Romeo receives:
+	<presence from="room@conference.localhost/Juliet" />
+
+# Ok, now Juliet leaves, and Romeo unsets the password
+
+Juliet sends:
+	<presence type="unavailable" to="room@conference.localhost"/>
+
+Romeo receives:
+	<presence type="unavailable" from="room@conference.localhost/Juliet"/>
+
+Juliet receives:
+	<presence type="unavailable" from="room@conference.localhost/Juliet"/>
+
+# Remove room password
+Romeo sends:
+	<iq id='config2' to='room@conference.localhost' type='set'>
+		<query xmlns='http://jabber.org/protocol/muc#owner'>
+			<x xmlns='jabber:x:data' type='submit'>
+				<field var='FORM_TYPE'>
+					<value>http://jabber.org/protocol/muc#roomconfig</value>
+				</field>
+				<field var='muc#roomconfig_roomsecret'>
+					<value></value>
+				</field>
+			</x>
+		</query>
+	</iq>
+
+# Config change success
+Romeo receives:
+	<iq id="config2" from="room@conference.localhost" type="result">
+	</iq>
+
+# Notification of room configuration update
+Romeo receives:
+	<message type='groupchat' from='room@conference.localhost'>
+		<x xmlns='http://jabber.org/protocol/muc#user'>
+			<status code='104'/>
+		</x>
+	</message>
+
+# Juliet tries to join (should succeed)
+Juliet sends:
+	<presence to="room@conference.localhost/Juliet">
+		<x xmlns="http://jabber.org/protocol/muc"/>
+	</presence>
+
+# Notification of Romeo's presence in the room
+Juliet receives:
+	<presence from="room@conference.localhost/Romeo" />
+
+Juliet receives:
+	<presence from="room@conference.localhost/Juliet" />
+
+# Room topic
+Juliet receives:
+	<message type='groupchat' from='room@conference.localhost'><subject/></message>
+
+Romeo receives:
+	<presence from="room@conference.localhost/Juliet" />
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spec/scansion/muc_register.scs	Thu Sep 06 16:44:48 2018 +0100
@@ -0,0 +1,286 @@
+[Client] Romeo
+	jid: user@localhost
+	password: password
+
+[Client] Juliet
+	jid: user2@localhost
+	password: password
+
+[Client] Rosaline
+	jid: user3@localhost
+	password: password
+
+-----
+
+Romeo connects
+
+Romeo sends:
+	<presence to="room@conference.localhost/Romeo">
+		<x xmlns="http://jabber.org/protocol/muc"/>
+	</presence>
+
+Romeo receives:
+	<presence from='room@conference.localhost/Romeo'>
+		<x xmlns='http://jabber.org/protocol/muc#user'>
+			<status code='201'/>
+			<item jid="${Romeo's full JID}" affiliation='owner' role='moderator'/>
+			<status code='110'/>
+		</x>
+	</presence>
+
+Romeo receives:
+	<message type='groupchat' from='room@conference.localhost'><subject/></message>
+
+# Submit config form
+Romeo sends:
+	<iq id='config1' to='room@conference.localhost' type='set'>
+		<query xmlns='http://jabber.org/protocol/muc#owner'>
+			<x xmlns='jabber:x:data' type='submit'>
+				<field var='FORM_TYPE'>
+					<value>http://jabber.org/protocol/muc#roomconfig</value>
+				</field>
+			</x>
+		</query>
+	</iq>
+
+Romeo receives:
+	<iq id="config1" from="room@conference.localhost" type="result">
+	</iq>
+
+Romeo sends:
+	<iq id='member1' to='room@conference.localhost' type='set'>
+		<query xmlns='http://jabber.org/protocol/muc#admin'>
+			<item affiliation='member' jid="${Juliet's JID}" />
+		</query>
+	</iq>
+
+Romeo receives:
+	<iq from='room@conference.localhost' id='member1' type='result'/>
+
+# Juliet connects, and joins the room
+Juliet connects
+
+Juliet sends:
+	<presence to="room@conference.localhost/Juliet">
+		<x xmlns="http://jabber.org/protocol/muc"/>
+	</presence>
+
+Juliet receives:
+	<presence from="room@conference.localhost/Romeo" />
+
+Juliet receives:
+	<presence from="room@conference.localhost/Juliet" />
+
+Juliet receives:
+	<message type='groupchat' from='room@conference.localhost'><subject/></message>
+
+Romeo receives:
+	<presence from="room@conference.localhost/Juliet" />
+
+# Juliet retrieves the registration form
+
+Juliet sends:
+	<iq id='jw81b36f' to='room@conference.localhost' type='get'>
+		<query xmlns='jabber:iq:register'/>
+	</iq>
+
+Juliet receives:
+	<iq type='result' from='room@conference.localhost' id='jw81b36f'>
+		<query xmlns='jabber:iq:register'>
+			<x type='form' xmlns='jabber:x:data'>
+				<field type='hidden' var='FORM_TYPE'>
+					<value>http://jabber.org/protocol/muc#register</value>
+				</field>
+				<field type='text-single' label='Nickname' var='muc#register_roomnick'/>
+			</x>
+		</query>
+	</iq>
+
+Juliet sends:
+	<iq id='nv71va54' to='room@conference.localhost' type='set'>
+		<query xmlns='jabber:iq:register'>
+			<x xmlns='jabber:x:data' type='submit'>
+				<field var='FORM_TYPE'>
+					<value>http://jabber.org/protocol/muc#register</value>
+				</field>
+				<field var='muc#register_roomnick'>
+					<value>Juliet</value>
+				</field>
+			</x>
+		</query>
+	</iq>
+
+Juliet receives:
+	<presence from='room@conference.localhost/Juliet'>
+		<x xmlns='http://jabber.org/protocol/muc#user'>
+			<item affiliation='member' jid="${Juliet's full JID}" role='participant'/>
+			<status code='110'/>
+		</x>
+	</presence>
+
+Juliet receives:
+	<iq type='result' from='room@conference.localhost' id='nv71va54'/>
+
+# Juliet discovers her reserved nick
+
+Juliet sends:
+	<iq id='getnick1' to='room@conference.localhost' type='get'>
+		<query xmlns='http://jabber.org/protocol/disco#info' node='x-roomuser-item'/>
+	</iq>
+
+Juliet receives:
+	<iq type='result' from='room@conference.localhost' id='getnick1'>
+		<query xmlns='http://jabber.org/protocol/disco#info'>
+			<identity category='conference' name='Juliet' type='text'/>
+		</query>
+	</iq>
+
+# Juliet leaves the room:
+
+Juliet sends:
+	<presence type="unavailable" to="room@conference.localhost/Juliet" />
+
+Juliet receives:
+	<presence type='unavailable' from='room@conference.localhost/Juliet'>
+		<x xmlns='http://jabber.org/protocol/muc#user'>
+			<item jid="${Juliet's full JID}" affiliation='member' role='none'/>
+			<status code='110'/>
+		</x>
+	</presence>
+
+Romeo receives:
+	<presence from='room@conference.localhost/Juliet'>
+		<x xmlns='http://jabber.org/protocol/muc#user'>
+			<item jid="${Juliet's full JID}" affiliation='member' role='participant'/>
+		</x>
+	</presence>
+
+# Rosaline connect and tries to join the room as Juliet
+
+Rosaline connects
+
+Rosaline sends:
+	<presence to="room@conference.localhost/Juliet">
+		<x xmlns="http://jabber.org/protocol/muc"/>
+	</presence>
+
+Rosaline receives:
+	<presence type='error' from='room@conference.localhost/Juliet'>
+		<error type='cancel'>
+			<conflict xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
+		</error>
+		<x xmlns='http://jabber.org/protocol/muc'/>
+	</presence>
+
+# In a heated moment, Juliet unregisters from the room
+
+Juliet sends:
+	<iq type='set' to='room@conference.localhost' id='unreg1'>
+		<query xmlns='jabber:iq:register'>
+			<remove/>
+		</query>
+	</iq>
+
+Juliet receives:
+	<iq type='result' from='room@conference.localhost' id='unreg1'/>
+
+# Rosaline attempts once more to sneak into the room, disguised as Juliet
+
+Rosaline sends:
+	<presence to="room@conference.localhost/Juliet">
+		<x xmlns="http://jabber.org/protocol/muc"/>
+	</presence>
+
+Rosaline receives:
+	<presence from='room@conference.localhost/Romeo'>
+		<x xmlns='http://jabber.org/protocol/muc#user'>
+			<item affiliation='owner' role='moderator'/>
+		</x>
+	</presence>
+
+Rosaline receives:
+	<presence from='room@conference.localhost/Juliet'>
+		<x xmlns='http://jabber.org/protocol/muc#user'>
+			<item affiliation='none' jid="${Rosaline's full JID}" role='participant'/>
+			<status code='110'/>
+		</x>
+	</presence>
+
+Romeo receives:
+	<presence from='room@conference.localhost/Juliet'>
+		<x xmlns='http://jabber.org/protocol/muc#user'>
+			<item affiliation='none' jid="${Rosaline's full JID}" role='participant'/>
+		</x>
+	</presence>
+
+# On discovering the ruse, Romeo restores Juliet's nick and status within the room
+
+Romeo sends:
+	<iq id='member1' to='room@conference.localhost' type='set'>
+		<query xmlns='http://jabber.org/protocol/muc#admin'>
+			<item affiliation='member' jid="${Juliet's JID}" nick='Juliet' />
+		</query>
+	</iq>
+
+# Rosaline is evicted from the room
+
+Romeo receives:
+	<presence from='room@conference.localhost/Juliet' type='unavailable'>
+		<x xmlns='http://jabber.org/protocol/muc#user'>
+			<status code='307'/>
+			<item affiliation='none' role='none' jid="${Rosaline's full JID}">
+				<reason>This nickname is reserved</reason>
+			</item>
+		</x>
+	</presence>
+
+Romeo receives:
+	<iq type='result' id='member1' from='room@conference.localhost' />
+
+Rosaline receives:
+	<presence type='unavailable' from='room@conference.localhost/Juliet'>
+		<x xmlns='http://jabber.org/protocol/muc#user'>
+			<status code='307'/>
+			<item affiliation='none' jid="${Rosaline's full JID}" role='none'>
+				<reason>This nickname is reserved</reason>
+			</item>
+			<status code='110'/>
+		</x>
+	</presence>
+
+# Rosaline, frustrated, attempts to get back into the room...
+
+Rosaline sends:
+	<presence to="room@conference.localhost/Juliet">
+		<x xmlns="http://jabber.org/protocol/muc"/>
+	</presence>
+
+# ...but once again, is denied
+
+Rosaline receives:
+	<presence type='error' from='room@conference.localhost/Juliet'>
+		<error type='cancel'>
+			<conflict xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
+		</error>
+		<x xmlns='http://jabber.org/protocol/muc'/>
+	</presence>
+
+# Juliet, however, quietly joins the room with success
+
+Juliet sends:
+	<presence to="room@conference.localhost/Juliet">
+		<x xmlns="http://jabber.org/protocol/muc"/>
+	</presence>
+
+Juliet receives:
+	<presence from="room@conference.localhost/Romeo" />
+
+Juliet receives:
+	<presence from="room@conference.localhost/Juliet" />
+
+Juliet receives:
+	<message type='groupchat' from='room@conference.localhost'><subject/></message>
+
+Romeo receives:
+	<presence from="room@conference.localhost/Juliet" />
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spec/scansion/pubsub_advanced.scs	Thu Sep 06 16:44:48 2018 +0100
@@ -0,0 +1,162 @@
+[Client] Balthasar
+	jid: admin@localhost
+	password: password
+
+[Client] Romeo
+	jid: romeo@localhost
+	password: password
+
+[Client] Juliet
+	jid: juliet@localhost
+	password: password
+
+---------
+
+Romeo connects
+
+Romeo sends:
+	<iq type="set" to="pubsub.localhost" id='create1'>
+		<pubsub xmlns="http://jabber.org/protocol/pubsub">
+			<create node="princely_musings"/>
+		</pubsub>
+	</iq>
+
+Romeo receives:
+	<iq type="error" id='create1'>
+		<error type="auth">
+			<forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
+		</error>
+	</iq>
+
+Balthasar connects
+
+Balthasar sends:
+	<iq type='set' to='pubsub.localhost' id='create2'>
+		<pubsub xmlns='http://jabber.org/protocol/pubsub'>
+			<create node='princely_musings'/>
+		</pubsub>
+	</iq>
+
+Balthasar receives:
+	<iq type="result" id='create2'/>
+
+Balthasar sends:
+	<iq type="set" to="pubsub.localhost" id='create3'>
+		<pubsub xmlns="http://jabber.org/protocol/pubsub">
+			<create node="princely_musings"/>
+		</pubsub>
+	</iq>
+
+Balthasar receives:
+	<iq type="error" id='create3'>
+		<error type="cancel">
+			<conflict xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>
+		</error>
+	</iq>
+
+Juliet connects
+
+Juliet sends:
+	<iq type="set" to="pubsub.localhost" id='sub1'>
+		<pubsub xmlns="http://jabber.org/protocol/pubsub">
+			<subscribe node="princely_musings" jid="${Romeo's full JID}"/>
+		</pubsub>
+	</iq>
+
+Juliet receives:
+	<iq type="error" id='sub1'/>
+
+Juliet sends:
+	<iq type="set" to="pubsub.localhost" id='sub2'>
+		<pubsub xmlns="http://jabber.org/protocol/pubsub">
+			<subscribe node="princely_musings" jid="${Juliet's full JID}"/>
+		</pubsub>
+	</iq>
+
+Juliet receives:
+	<iq type="result" id='sub2'>
+		<pubsub xmlns='http://jabber.org/protocol/pubsub'>
+			<subscription jid="${Juliet's full JID}" node='princely_musings' subscription='subscribed'/>
+		</pubsub>
+	</iq>
+
+Balthasar sends:
+	<iq type="get" id='aff1' to='pubsub.localhost'>
+		<pubsub xmlns="http://jabber.org/protocol/pubsub#owner">
+			<affiliations node="princely_musings"/>
+		</pubsub>
+	</iq>
+
+Balthasar receives:
+	<iq type="result" id='aff1' from='pubsub.localhost'>
+		<pubsub xmlns="http://jabber.org/protocol/pubsub#owner">
+			<affiliations node="princely_musings"/>
+		</pubsub>
+	</iq>
+
+Balthasar sends:
+	<iq type="set" id='aff2' to='pubsub.localhost'>
+		<pubsub xmlns="http://jabber.org/protocol/pubsub#owner">
+			<affiliations node="princely_musings">
+				<affiliation jid="${Romeo's JID}" affiliation="publisher"/>
+			</affiliations>
+		</pubsub>
+	</iq>
+
+Balthasar receives:
+	<iq type="result" id='aff2' from='pubsub.localhost'/>
+
+Romeo sends:
+	<iq type="set" to="pubsub.localhost" id='pub1'>
+		<pubsub xmlns="http://jabber.org/protocol/pubsub">
+			<publish node="princely_musings">
+				<item id="current">
+					<entry xmlns="http://www.w3.org/2005/Atom">
+						<title>Soliloquy</title>
+						<summary>Lorem ipsum dolor sit amet</summary>
+					</entry>
+				</item>
+			</publish>
+		</pubsub>
+	</iq>
+
+Juliet receives:
+	<message type="headline" from="pubsub.localhost">
+		<event xmlns="http://jabber.org/protocol/pubsub#event">
+			<items node="princely_musings">
+				<item id="current">
+					<entry xmlns="http://www.w3.org/2005/Atom">
+						<title>Soliloquy</title>
+						<summary>Lorem ipsum dolor sit amet</summary>
+					</entry>
+				</item>
+			</items>
+		</event>
+	</message>
+
+Romeo receives:
+	<iq type="result" id='pub1'/>
+
+Juliet sends:
+	<iq type="set" to="pubsub.localhost" id='unsub1'>
+		<pubsub xmlns="http://jabber.org/protocol/pubsub">
+			<unsubscribe node="princely_musings" jid="${Juliet's full JID}"/>
+		</pubsub>
+	</iq>
+
+Juliet receives:
+	<iq type="result" id='unsub1'/>
+
+Balthasar sends:
+	<iq type="set" to="pubsub.localhost" id='del1'>
+		<pubsub xmlns="http://jabber.org/protocol/pubsub#owner">
+			<delete node="princely_musings"/>
+		</pubsub>
+	</iq>
+
+Balthasar receives:
+	<iq type="result" from='pubsub.localhost' id='del1'/>
+
+Romeo disconnects
+
+// vim: syntax=xml:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spec/scansion/pubsub_basic.scs	Thu Sep 06 16:44:48 2018 +0100
@@ -0,0 +1,102 @@
+[Client] Romeo
+	jid: admin@localhost
+	password: password
+
+// admin@localhost is assumed to have node creation privileges
+
+[Client] Juliet
+	jid: juliet@localhost
+	password: password
+
+---------
+
+Romeo connects
+
+Romeo sends:
+	<iq type="set" to="pubsub.localhost" id='create1'>
+		<pubsub xmlns="http://jabber.org/protocol/pubsub">
+			<create node="princely_musings"/>
+		</pubsub>
+	</iq>
+
+Romeo receives:
+	<iq type="result" id='create1'/>
+
+Juliet connects
+
+-- Juliet sends:
+-- 	<iq type="set" to="pubsub.localhost">
+-- 		<pubsub xmlns="http://jabber.org/protocol/pubsub">
+-- 			<subscribe node="princely_musings" jid="${Romeo's full JID}"/>
+-- 		</pubsub>
+-- 	</iq>
+-- 
+-- Juliet receives:
+-- 	<iq type="error"/>
+
+Juliet sends:
+	<iq type="set" to="pubsub.localhost" id='sub1'>
+		<pubsub xmlns="http://jabber.org/protocol/pubsub">
+			<subscribe node="princely_musings" jid="${Juliet's full JID}"/>
+		</pubsub>
+	</iq>
+
+Juliet receives:
+	<iq type="result" id='sub1'/>
+
+Romeo sends:
+	<iq type="set" to="pubsub.localhost" id='pub1'>
+		<pubsub xmlns="http://jabber.org/protocol/pubsub">
+			<publish node="princely_musings">
+				<item id="current">
+					<entry xmlns="http://www.w3.org/2005/Atom">
+						<title>Soliloquy</title>
+						<summary>Lorem ipsum dolor sit amet</summary>
+					</entry>
+				</item>
+			</publish>
+		</pubsub>
+	</iq>
+
+Romeo receives:
+	<iq type="result" id='pub1'/>
+
+Juliet receives:
+	<message type="headline" from="pubsub.localhost">
+		<event xmlns="http://jabber.org/protocol/pubsub#event">
+			<items node="princely_musings">
+				<item id="current">
+					<entry xmlns="http://www.w3.org/2005/Atom">
+						<title>Soliloquy</title>
+						<summary>Lorem ipsum dolor sit amet</summary>
+					</entry>
+				</item>
+			</items>
+		</event>
+	</message>
+
+Juliet sends:
+	<iq type="set" to="pubsub.localhost" id='unsub1'>
+		<pubsub xmlns="http://jabber.org/protocol/pubsub">
+			<unsubscribe node="princely_musings" jid="${Juliet's full JID}"/>
+		</pubsub>
+	</iq>
+
+Juliet receives:
+	<iq type="result" id='unsub1'/>
+
+Juliet disconnects
+
+Romeo sends:
+	<iq type="set" to="pubsub.localhost" id='del1'>
+		<pubsub xmlns="http://jabber.org/protocol/pubsub#owner">
+			<delete node="princely_musings"/>
+		</pubsub>
+	</iq>
+
+Romeo receives:
+	<iq type="result" id='del1'/>
+
+Romeo disconnects
+
+// vim: syntax=xml:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spec/scansion/pubsub_createdelete.scs	Thu Sep 06 16:44:48 2018 +0100
@@ -0,0 +1,61 @@
+[Client] Romeo
+	jid: admin@localhost
+	password: password
+
+// admin@localhost is assumed to have node creation privileges
+
+---------
+
+Romeo connects
+
+Romeo sends:
+	<iq type="set" to="pubsub.localhost" id='create1'>
+		<pubsub xmlns="http://jabber.org/protocol/pubsub">
+			<create node="princely_musings"/>
+		</pubsub>
+	</iq>
+
+Romeo receives:
+	<iq type="result" id='create1'/>
+
+Romeo sends:
+	<iq type="set" to="pubsub.localhost" id='create2'>
+		<pubsub xmlns="http://jabber.org/protocol/pubsub">
+			<create node="princely_musings"/>
+		</pubsub>
+	</iq>
+
+Romeo receives:
+	<iq type="error" id='create2'>
+		<error type="cancel">
+			<conflict xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>
+		</error>
+	</iq>
+
+Romeo sends:
+	<iq type="set" to="pubsub.localhost" id='delete1'>
+		<pubsub xmlns="http://jabber.org/protocol/pubsub#owner">
+			<delete node="princely_musings"/>
+		</pubsub>
+	</iq>
+
+Romeo receives:
+	<iq type="result" id='delete1'/>
+
+Romeo sends:
+	<iq type="set" to="pubsub.localhost" id='delete2'>
+		<pubsub xmlns="http://jabber.org/protocol/pubsub#owner">
+			<delete node="princely_musings"/>
+		</pubsub>
+	</iq>
+
+Romeo receives:
+	<iq type="error" id='delete2'>
+		<error type="cancel">
+			<item-not-found xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>
+		</error>
+	</iq>
+
+Romeo disconnects
+
+// vim: syntax=xml: