home · contact · privacy
On testing, split server message log treatment into two separate directives.
authorChristian Heller <c.heller@plomlompom.de>
Tue, 30 Sep 2025 22:49:22 +0000 (00:49 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Tue, 30 Sep 2025 22:49:22 +0000 (00:49 +0200)
src/ircplom/testing.py
src/tests/pingpong.txt
src/tests/test.txt

index 33c4eb5b097d879ab81b950202f6098bb3d1c948..48f98d6ba1afccec446cbf93a98fda45fd259aba 100644 (file)
@@ -101,9 +101,11 @@ _CHAR_COMMENT = '#'
 _CHAR_CONTEXT_SEP = ' '
 _CHAR_ID_TYPE_SEP = ':'
 _CHAR_PROMPT = '>'
+_CHAR_SERVER_MSG = '<'
 _CHAR_RANGE = ':'
 _CHAR_RANGE_DATA_SEP = ' '
 _CHAR_WIN_ID_SEP = ','
+_TOK_REPEAT = 'repeat'
 
 
 class _Playbook:
@@ -113,30 +115,50 @@ class _Playbook:
         self._get_client = get_client
         with path.open('r', encoding='utf8') as f:
             self._lines = [line.rstrip() for line in f.readlines()]
-        while True:
+
+        def expand_parsed(marker, parse_into, **kwargs) -> bool:
             inserts: list[str] = []
-            anchors: dict[str, int] = {}
             for idx, line in enumerate(self._lines):
-                if line[:1] == _CHAR_ANCHOR:
-                    anchors[line[1:]] = idx
-            for idx, line in enumerate(self._lines):
-                split = self._split_active_line(line)
-                if (not split) or split[0] != 'repeat':
+                if not line.startswith(marker):
                     continue
-                range_data = split[1].split(_CHAR_RANGE_DATA_SEP, maxsplit=2)
-                start_key, end_key = range_data[:2]
-                start = anchors[start_key] + 1
-                end = anchors[end_key]
-                inserts = self._lines[int(start):int(end)]
-                if len(range_data) == 3:
-                    for jdx, insert in enumerate(inserts):
-                        if (res := self._split_active_line(insert)):
-                            inserts[jdx] = ' '.join([range_data[2]] + [res[1]])
+                inserts = parse_into(line, **kwargs)
                 self._lines =\
                     self._lines[:idx] + inserts + self._lines[idx + 1:]
                 break
-            if not inserts:
+            return bool(inserts)
+
+        def split_server_put_and_log(line: str, **_) -> list[str]:
+            context, msg = line.split(_CHAR_CONTEXT_SEP, maxsplit=1)
+            assert msg[0] in '.*' and msg[1:3] == f'{LOG_PREFIX_IN} '
+            c_id, win_ids = context[1:].split(_CHAR_ID_TYPE_SEP, maxsplit=1)
+            return [f'{_CHAR_SERVER_MSG}{c_id}{_CHAR_CONTEXT_SEP}{msg[3:]}',
+                    win_ids + _CHAR_CONTEXT_SEP + msg]
+
+        def repeat(line: str, anchors: dict[str, int], **_) -> list[str]:
+            range_data = line[len(_TOK_REPEAT) + 1:].split(
+                    _CHAR_RANGE_DATA_SEP, maxsplit=2)
+            start_key, end_key = range_data[:2]
+            start = anchors[start_key] + 1
+            end = anchors[end_key]
+            inserts = self._lines[int(start):int(end)]
+            if len(range_data) == 2:
+                return inserts
+            for jdx, insert in enumerate(inserts):
+                if (result := self._split_active_line(insert)):
+                    inserts[jdx] = _CHAR_CONTEXT_SEP.join([range_data[2]]
+                                                          + [result[1]])
+            return inserts
+
+        while expand_parsed(_CHAR_ID_TYPE_SEP, split_server_put_and_log):
+            pass
+        while True:
+            anchors: dict[str, int] = {}
+            for idx, line in enumerate(self._lines):
+                if line[:1] == _CHAR_ANCHOR:
+                    anchors[line[1:]] = idx
+            if not expand_parsed(_TOK_REPEAT, repeat, anchors=anchors):
                 break
+        self._lines = [ln for ln in self._lines if ln[:1] != _CHAR_ANCHOR]
         self._idx = 0
 
     @property
@@ -169,22 +191,17 @@ class _Playbook:
                     for c in msg:
                         self.put_keypress(c)
                     self.put_keypress('KEY_ENTER')
-                    continue
-                cmd_prefix = f'.{LOG_PREFIX_IN} '
-                if _CHAR_ID_TYPE_SEP in context and msg.startswith(cmd_prefix):
-                    client_id, win_ids = context.split(_CHAR_ID_TYPE_SEP)
-                    client = self._get_client(int(client_id))
+                elif context.startswith(_CHAR_SERVER_MSG):
+                    client = self._get_client(int(context[1:]))
                     assert isinstance(client.conn, _FakeIrcConnection)
-                    client.conn.put_server_msg(msg[len(cmd_prefix):])
-                    if not win_ids:
-                        continue
-                break
+                    client.conn.put_server_msg(msg)
+                else:
+                    break
 
     @staticmethod
     def _split_active_line(line: str) -> Optional[tuple[str, ...]]:
-        'Return two-items tuple of split line, or None if inactive one.'
-        if line[:1] in {_CHAR_COMMENT, _CHAR_ANCHOR}\
-                or _CHAR_CONTEXT_SEP not in line:
+        'Return 2-items tuple of split line, or None if empty/comment/anchor.'
+        if line[:1] == _CHAR_COMMENT or _CHAR_CONTEXT_SEP not in line:
             return None
         return tuple(line.split(_CHAR_CONTEXT_SEP, maxsplit=1))
 
index d0429bf0436cd2c4a495c9a303cbcef76ad730c0..ac3d0cd635908273984e1ff01e6588c346238003 100644 (file)
@@ -1,5 +1,3 @@
-#
-
 #
 > /connect foo.bar.baz foo:bar baz:foobarbazquux
 1 .$ isupport cleared
 1 .> NICK :foo
 
 # ensure we PONG properly
-0:1 .< PING :?
+:0:1 .< PING :?
 1 .> PONG :?
 
 # ping on timeout, go on as normal if PONG received 
-0: .< FAKE_IRC_CONN_TIMEOUT_EXCEPTION
+<0 FAKE_IRC_CONN_TIMEOUT_EXCEPTION
 1 .> PING :what's up?
-0:1 .< :*.?.net PONG *.?.net :what's up?
-0:1 .< :*.?.net NOTICE * :*** Looking up your ident...
+:0:1 .< :*.?.net PONG *.?.net :what's up?
+:0:1 .< :*.?.net NOTICE * :*** Looking up your ident...
 2 .< *** [ server] *** Looking up your ident...
 
 # another timeout instead of pong? disconnect
-0: .< FAKE_IRC_CONN_TIMEOUT_EXCEPTION
+<0 FAKE_IRC_CONN_TIMEOUT_EXCEPTION
 1 .> PING :what's up?
-0: .< FAKE_IRC_CONN_TIMEOUT_EXCEPTION
+<0 FAKE_IRC_CONN_TIMEOUT_EXCEPTION
 1 .$ connection_state set to: [broken: no timely PONG from server]
 1 .$ isupport cleared
 1 .$ isupport:CHANTYPES set to: [#&]
index 10109422d1d0e2bbd90ecfbd742a18b38519c683..babd6e2560f4e0bd121ae025bd071e02a343c80b 100644 (file)
 1 .> NICK :foo
 
 # expect some NOTICE and PING to process/reply during initiation
-0:1 .< :*.?.net NOTICE * :*** Looking up your ident...
+:0:1 .< :*.?.net NOTICE * :*** Looking up your ident...
 2 .< *** [ server] *** Looking up your ident...
-0:1 .< :*.?.net NOTICE * :*** Looking up your hostname...
+:0:1 .< :*.?.net NOTICE * :*** Looking up your hostname...
 2 .< *** [ server] *** Looking up your hostname...
-0:1 .< :*.?.net NOTICE * :*** Found your hostname (foo.bar.baz)
+:0:1 .< :*.?.net NOTICE * :*** Found your hostname (foo.bar.baz)
 2 .< *** [ server] *** Found your hostname (foo.bar.baz)
-0:1 .< PING :?
+:0:1 .< PING :?
 1 .> PONG :?
 
 # handle 433
-0:1 .< :*.?.net 433 * foo :Nickname already in use
+:0:1 .< :*.?.net 433 * foo :Nickname already in use
 1 .!$ nickname already in use, trying increment
 1 .> NICK :foo0
-0:1 .< :*.?.net 433 * foo0 :Nickname already in use
+:0:1 .< :*.?.net 433 * foo0 :Nickname already in use
 1 .!$ nickname already in use, trying increment
 1 .> NICK :foo1
 
 # collect server capabilities
-0:1 .< :*.?.net CAP * LS : foo bar sasl=PLAIN,EXTERNAL baz cap-notify
+:0:1 .< :*.?.net CAP * LS : foo bar sasl=PLAIN,EXTERNAL baz cap-notify
 1 .> CAP REQ :sasl
 1 .> CAP :LIST
-0:1 .< :*.?.net CAP * ACK :sasl
-0:1 .< :*.?.net CAP * LIST :cap-notify sasl
+:0:1 .< :*.?.net CAP * ACK :sasl
+:0:1 .< :*.?.net CAP * LIST :cap-notify sasl
 1 .$ caps:bar:data set to: []
 1 .$ caps:baz:data set to: []
 1 .$ caps:cap-notify:data set to: []
 # authenticate via SASL, collect items of user identity
 1 .$ sasl_auth_state set to: [attempting]
 1 .> AUTHENTICATE :PLAIN
-0:1 .< AUTHENTICATE +
+:0:1 .< AUTHENTICATE +
 1 .> AUTHENTICATE :Zm9vAGZvbwBiYXI=
-0:1 .< :foo.bar.baz 900 foo1 foo1!foobarbazq@baz.bar.foo foo :You are now logged in as foo
+:0:1 .< :foo.bar.baz 900 foo1 foo1!foobarbazq@baz.bar.foo foo :You are now logged in as foo
 1 .$ users:me:nick set to: [?]
 1 .$ users:me:nick set to: [foo1]
 1 .$ users:me:user set to: [foobarbazq]
 1 .$ users:me:host set to: [baz.bar.foo]
 1 .$ sasl_account set to: [foo]
-0:1 .< :foo.bar.baz 903 foo1 :SASL authentication successful
+:0:1 .< :foo.bar.baz 903 foo1 :SASL authentication successful
 1 .$ sasl_auth_state set to: [SASL authentication successful]
 
 # finish CAP negotation, thus login procedure
 1 .> CAP :END
 
 # of all pre-MOTD greeting messages, only process isupports
-0:1 .< :foo.bar.baz 001 foo1 :Welcome to the foo.bar.baz network
+:0:1 .< :foo.bar.baz 001 foo1 :Welcome to the foo.bar.baz network
 |conn3
-0:1 .< :foo.bar.baz 002 foo1 :Your host is foo.bar.baz
-0:1 .< :foo.bar.baz 003 foo1 :This server was created Jan 1 2020
-0:1 .< :foo.bar.baz 004 foo1 foo.bar.baz ircserver-1.0 abc def ghi
-0:1 .< :foo.bar.baz 005 foo1 ABC=DEF GHI=JKL :are supported by this server
+:0:1 .< :foo.bar.baz 002 foo1 :Your host is foo.bar.baz
+:0:1 .< :foo.bar.baz 003 foo1 :This server was created Jan 1 2020
+:0:1 .< :foo.bar.baz 004 foo1 foo.bar.baz ircserver-1.0 abc def ghi
+:0:1 .< :foo.bar.baz 005 foo1 ABC=DEF GHI=JKL :are supported by this server
 1 .$ isupport:ABC set to: [DEF]
 1 .$ isupport:GHI set to: [JKL]
-0:1 .< :foo.bar.baz 005 foo1 MNO=PQR STU=VWX Y=Z :are supported by this server
+:0:1 .< :foo.bar.baz 005 foo1 MNO=PQR STU=VWX Y=Z :are supported by this server
 1 .$ isupport:MNO set to: [PQR]
 1 .$ isupport:STU set to: [VWX]
 1 .$ isupport:Y set to: [Z]
-0:1 .< :foo.bar.baz 251 foo1 :There are 10 users and 1000 invisible on 5 servers
-0:1 .< :foo.bar.baz 252 foo1 7 :IRC Operators online
-0:1 .< :foo.bar.baz 253 foo1 4 :unknown connection(s)
-0:1 .< :foo.bar.baz 254 foo1 800 :channels formed
-0:1 .< :foo.bar.baz 255 foo1 :I have 100 clients and 1 serveres
-0:1 .< :foo.bar.baz 265 foo1 100 150 :Current local users 100, max 150
-0:1 .< :foo.bar.baz 266 foo1 1010 1050 :Current global users 1010, max 1050
-0:1 .< :foo.bar.baz 250 foo1 :Highest connection count: 151 (150 clients) (1080 connections received)
+:0:1 .< :foo.bar.baz 251 foo1 :There are 10 users and 1000 invisible on 5 servers
+:0:1 .< :foo.bar.baz 252 foo1 7 :IRC Operators online
+:0:1 .< :foo.bar.baz 253 foo1 4 :unknown connection(s)
+:0:1 .< :foo.bar.baz 254 foo1 800 :channels formed
+:0:1 .< :foo.bar.baz 255 foo1 :I have 100 clients and 1 serveres
+:0:1 .< :foo.bar.baz 265 foo1 100 150 :Current local users 100, max 150
+:0:1 .< :foo.bar.baz 266 foo1 1010 1050 :Current global users 1010, max 1050
+:0:1 .< :foo.bar.baz 250 foo1 :Highest connection count: 151 (150 clients) (1080 connections received)
 
 # collect MOTD into a single output (rather than line-by-line)
-0:1 .< :foo.bar.baz 375 foo1 :- foo.bar.baz Message of the Day -
-0:1 .< :foo.bar.baz 372 foo1 :- Howdy! -
-0:1 .< :foo.bar.baz 372 foo1 :- Welcome! -
-0:1 .< :foo.bar.baz 372 foo1 :- (to this server) -
-0:1 .< :foo.bar.baz 376 foo1 :End of /MOTD command
+:0:1 .< :foo.bar.baz 375 foo1 :- foo.bar.baz Message of the Day -
+:0:1 .< :foo.bar.baz 372 foo1 :- Howdy! -
+:0:1 .< :foo.bar.baz 372 foo1 :- Welcome! -
+:0:1 .< :foo.bar.baz 372 foo1 :- (to this server) -
+:0:1 .< :foo.bar.baz 376 foo1 :End of /MOTD command
 1 .$ motd set to:
 1 .$   - Howdy! -
 1 .$   - Welcome! -
 1 .$   - (to this server) -
 
 # collect user mode
-0:1 .< :foo1 MODE foo1 :+Ziw
+:0:1 .< :foo1 MODE foo1 :+Ziw
 1 .$ users:me:modes set to: [+Ziw]
 
 # handle bot query NOTICE
-0:1 .< :SaslServ!SaslServ@services.bar.baz NOTICE foo1 :Last login from ~foobarbaz@foo.bar.baz on Jan 1 22:00:00 2021 +0000.
+:0:1 .< :SaslServ!SaslServ@services.bar.baz NOTICE foo1 :Last login from ~foobarbaz@foo.bar.baz on Jan 1 22:00:00 2021 +0000.
 3 .< *** [SaslServ] Last login from ~foobarbaz@foo.bar.baz on Jan 1 22:00:00 2021 +0000.
 |conn4
 
 1 .#   /window.reconnect
 
 # test lack of implementation
-0:1 .< foo bar baz
+:0:1 .< foo bar baz
 1 .!$ No handler implemented for: foo bar baz
 
 # test recoverable 432
 > /nick @foo
 1 .> NICK :@foo
-0:1 .< :*.?.net 432 foo1 @foo :Erroneous nickname
+:0:1 .< :*.?.net 432 foo1 @foo :Erroneous nickname
 1 .!$ nickname refused for bad format, keeping current one
 
 # join channel, collect topic, residents; update me:user from JOIN message; ensure topic.who not affecting users DB
 > /join #test
 1 .> JOIN :#test
-0:1 .< :foo1!~foobarbaz@baz.bar.foo JOIN #test
+:0:1 .< :foo1!~foobarbaz@baz.bar.foo JOIN #test
 1 .$ users:me:user set to: [~foobarbaz]
-0:1 .< :foo.bar.baz 332 foo1 #test :foo bar baz
+:0:1 .< :foo.bar.baz 332 foo1 #test :foo bar baz
 1 .$ channels:#test:exits cleared
-0:1 .< :foo.bar.baz 333 foo1 #test bar!~bar@OLD.bar.bar 1234567890
+:0:1 .< :foo.bar.baz 333 foo1 #test bar!~bar@OLD.bar.bar 1234567890
 1 .$ channels:#test:topic set to: [Topic(what='foo bar baz', who=NickUserHost(nick='bar', user='~bar', host='OLD.bar.bar'))]
 4 .$ bar!~bar@OLD.bar.bar set topic: foo bar baz
-0:1 .< :foo.bar.baz 353 foo1 @ #test :foo1 @bar
+:0:1 .< :foo.bar.baz 353 foo1 @ #test :foo1 @bar
 1 .$ users:1:nick set to: [?]
 1 .$ users:1:nick set to: [bar]
-0:1 .< :foo.bar.baz 366 foo1 #test :End of /NAMES list.
+:0:1 .< :foo.bar.baz 366 foo1 #test :End of /NAMES list.
 1 .$ channels:#test:user_ids set to:
 1 .$   1
 1 .$   me
 4 .$ residents: bar, foo1
 
 # deliver PRIVMSG to channel window, update sender's user+host from metadata
-0:1 .< :bar!~bar@bar.bar PRIVMSG #test :hi there
+:0:1 .< :bar!~bar@bar.bar PRIVMSG #test :hi there
 1 .$ users:1:user set to: [~bar]
 1 .$ users:1:host set to: [bar.bar]
 4 .< [bar] hi there
 
 # check _changing_ TOPIC message is communicated to channel window, as long as either content or who change
-0:1 .< :bar!~bar@bar.bar TOPIC #test :foo bar baz
+:0:1 .< :bar!~bar@bar.bar TOPIC #test :foo bar baz
 1 .$ channels:#test:topic set to: [Topic(what='foo bar baz', who=NickUserHost(nick='bar', user='~bar', host='bar.bar'))]
 4 .$ bar!~bar@bar.bar set topic: foo bar baz
-0:1 .< :bar!~bar@bar.bar TOPIC #test :foo bar baz
-0:1 .< :bar!~bar@bar.bar TOPIC #test :abc def ghi
+:0:1 .< :bar!~bar@bar.bar TOPIC #test :foo bar baz
+:0:1 .< :bar!~bar@bar.bar TOPIC #test :abc def ghi
 1 .$ channels:#test:topic set to: [Topic(what='abc def ghi', who=NickUserHost(nick='bar', user='~bar', host='bar.bar'))]
 4 .$ bar!~bar@bar.bar set topic: abc def ghi
 
 # process non-self channel JOIN
-0:1 .< :baz!~baz@baz.baz JOIN :#test
+:0:1 .< :baz!~baz@baz.baz JOIN :#test
 1 .$ users:2:nick set to: [?]
 1 .$ users:2:nick set to: [baz]
 1 .$ users:2:user set to: [~baz]
 # join second channel with partial residents identity to compare distribution of resident-specific messages
 > /join #testtest
 1 .> JOIN :#testtest
-0:1 .< :foo1!~foobarbaz@baz.bar.foo JOIN #testtest
-0:1 .< :foo.bar.baz 332 foo1 #testtest :baz bar foo
+:0:1 .< :foo1!~foobarbaz@baz.bar.foo JOIN #testtest
+:0:1 .< :foo.bar.baz 332 foo1 #testtest :baz bar foo
 1 .$ channels:#testtest:exits cleared
-0:1 .< :foo.bar.baz 333 foo1 #testtest bar!~bar@OLD.bar.bar 1234567890
+:0:1 .< :foo.bar.baz 333 foo1 #testtest bar!~bar@OLD.bar.bar 1234567890
 1 .$ channels:#testtest:topic set to: [Topic(what='baz bar foo', who=NickUserHost(nick='bar', user='~bar', host='OLD.bar.bar'))]
 5 .$ bar!~bar@OLD.bar.bar set topic: baz bar foo
-0:1 .< :foo.bar.baz 353 foo1 @ #testtest :foo1 baz
-0:1 .< :foo.bar.baz 366 foo1 #testtest :End of /NAMES list.
+:0:1 .< :foo.bar.baz 353 foo1 @ #testtest :foo1 baz
+:0:1 .< :foo.bar.baz 366 foo1 #testtest :End of /NAMES list.
 1 .$ channels:#testtest:user_ids set to:
 1 .$   2
 1 .$   me
 5 .$ residents: baz, foo1
 
 # handle query window with known user
-0:1 .< :baz!~baz@baz.baz PRIVMSG foo1 :hi there
+:0:1 .< :baz!~baz@baz.baz PRIVMSG foo1 :hi there
 6 .< [baz] hi there
 > /privmsg baz hello, how is it going
 1 .> PRIVMSG baz :hello, how is it going
 6 .> [foo1] hello, how is it going
-0:1 .< :baz!~baz@baz.baz PRIVMSG foo1 :fine!
+:0:1 .< :baz!~baz@baz.baz PRIVMSG foo1 :fine!
 6 .< [baz] fine!
 
 # handle failure to query absent user
 > /privmsg barbar hello!
 1 .> PRIVMSG barbar :hello!
 7 .> [foo1] hello!
-0:1 .< :*.?.net 401 foo1 barbar :No such nick/channel
+:0:1 .< :*.?.net 401 foo1 barbar :No such nick/channel
 7 .!$ barbar not online
 
 # handle non-self renaming
-0:1 .< :baz!~baz@baz.baz NICK :bazbaz
+:0:1 .< :baz!~baz@baz.baz NICK :bazbaz
 1 .$ users:2:nick set to: [bazbaz]
 4,5,6 .$ baz!~baz@baz.baz renames bazbaz
 
 # handle non-self PART in one of two inhabited channels, preserve identity into re-JOIN
-0:1 .< :bazbaz!~baz@baz.baz PART :#test
+:0:1 .< :bazbaz!~baz@baz.baz PART :#test
 1 .$ channels:#test:exits:2 set to: [P]
 1 .$ channels:#test:user_ids set to:
 1 .$   1
 1 .$   me
 4 .$ bazbaz!~baz@baz.baz parts
 1 .$ channels:#test:exits:2 cleared
-0:1 .< :bazbaz!~baz@baz.baz JOIN :#test
+:0:1 .< :bazbaz!~baz@baz.baz JOIN :#test
 1 .$ channels:#test:user_ids set to:
 1 .$   1
 1 .$   2
 4 .$ bazbaz!~baz@baz.baz joins
 
 # handle non-self PART in only inhabited channel, lose identity, re-join as new identity
-0:1 .< :bar!~bar@bar.bar PART :#test
+:0:1 .< :bar!~bar@bar.bar PART :#test
 1 .$ channels:#test:exits:1 set to: [P]
 1 .$ channels:#test:user_ids set to:
 1 .$   2
 4 .$ bar!~bar@bar.bar parts
 1 .$ channels:#test:exits:1 cleared
 1 .$ users:1 cleared
-0:1 .< :bar!~bar@bar.bar JOIN :#test
+:0:1 .< :bar!~bar@bar.bar JOIN :#test
 1 .$ users:3:nick set to: [?]
 1 .$ users:3:nick set to: [bar]
 1 .$ users:3:user set to: [~bar]
 4 .$ bar!~bar@bar.bar joins
 
 # handle non-self QUIT
-0:1 .< :bazbaz!~baz@baz.baz QUIT :Client Quit
+:0:1 .< :bazbaz!~baz@baz.baz QUIT :Client Quit
 1 .$ users:2:exit_msg set to: [QClient Quit]
 6 .$ bazbaz!~baz@baz.baz quits: Client Quit
 1 .$ channels:#test:exits:2 set to: [QClient Quit]
 1 .$ users:2 cleared
 
 # handle self-PART: clear channel, and its squatters
-0:1 .< :foo1!~foobarbaz@baz.bar.foo PART :#test
+:0:1 .< :foo1!~foobarbaz@baz.bar.foo PART :#test
 1 .$ channels:#test:exits:me set to: [P]
 1 .$ channels:#test:user_ids set to:
 1 .$   3
 # handle /disconnect, clear all
 > /disconnect
 1 .> QUIT :ircplom says bye
-0:1 .< :foo1!~foobarbaz@baz.bar.foo QUIT :Client Quit
+:0:1 .< :foo1!~foobarbaz@baz.bar.foo QUIT :Client Quit
 1 .$ users:me:exit_msg set to: [QClient Quit]
 2,3,6,7 .$ foo1!~foobarbaz@baz.bar.foo quits: Client Quit
 1 .$ channels:#testtest:exits:me set to: [QClient Quit]
 1 .$ channels:#testtest:user_ids set to:
 5 .$ foo1!~foobarbaz@baz.bar.foo quits: Client Quit
 1 .$ channels:#testtest:exits:me cleared
-0:1 .< ERROR :Closing link: (~foobarbaz@baz.bar.foo) [Quit: ircplom says bye]
+:0:1 .< ERROR :Closing link: (~foobarbaz@baz.bar.foo) [Quit: ircplom says bye]
 1 .$ connection_state set to: [Closing link: (~foobarbaz@baz.bar.foo) [Quit: ircplom says bye]]
 repeat isupport-clear-in isupport-clear-out
 1 .$ caps cleared
@@ -369,7 +369,7 @@ repeat conn0 conn1 8
 1:8 .> CAP LS :302
 1:8 .> USER foo 0 * :foo
 1:8 .> NICK :?foo
-1:8 .< :*.?.net 432 * ?foo :Erroneous nickname
+:1:8 .< :*.?.net 432 * ?foo :Erroneous nickname
 repeat isupport-clear-in isupport-clear-out 8
 8 .$ connection_state set to: []
 , .$ DISCONNECTED
@@ -390,7 +390,7 @@ repeat conn0 conn1 9
 2:9 .> CAP LS :302
 2:9 .> USER baz 0 * :baz
 2:9 .> NICK :baz
-2: .< FAKE_IRC_CONN_ABORT_EXCEPTION
+<2 FAKE_IRC_CONN_ABORT_EXCEPTION
 9 .$ connection_state set to: [broken: FAKE_IRC_CONN_ABORT_EXCEPTION]
 repeat isupport-clear-in isupport-clear-out 9
 9 .$ connection_state set to: []