text: """Tests for core ``sopel.irc``"""
  from __future__ import generator_stop
  
  import pytest
  
  from sopel.tests import rawlist
  from sopel.tools import Identifier
  from sopel.tools.target import User
  
  
  TMP_CONFIG = """
  [core]
  owner = Exirel
  nick = Sopel
  user = sopel
  name = Sopel (https://sopel.chat)
  # we don't want flood protection here
  flood_burst_lines = 1000
  """
  
  
  @pytest.fixture
  def tmpconfig(configfactory):
      return configfactory('conf.ini', TMP_CONFIG)
  
  
  @pytest.fixture
  def bot(tmpconfig, botfactory):
      return botfactory(tmpconfig)
  
  
  def prefix_length(bot):
      # ':', nick, '!', '~', ident/username, '@', maximum hostname length, <0x20>
      return 1 + len(bot.nick) + 1 + 1 + len(bot.user) + 1 + 63 + 1
  
  
  def test_safe_text_length_hostmask_unknown(bot):
      assert bot.hostmask is None
      assert bot.safe_text_length('#channel') == 414
  
  
  def test_safe_text_length(bot):
      bot.users[Identifier(bot.nick)] = User(bot.nick, bot.user, 'hostname')
      assert bot.hostmask is not None
      assert bot.safe_text_length('#channel') == 470
  
  
  def test_on_connect(bot):
      bot.on_connect()
  
      assert bot.backend.message_sent == rawlist(
          'CAP LS 302',
          'NICK Sopel',
          'USER sopel 0 * :Sopel (https://sopel.chat)'
      )
  
  
  def test_on_connect_auth_password(bot):
      bot.settings.core.auth_method = 'server'
      bot.settings.core.auth_password = 'auth_secret'
      bot.on_connect()
  
      assert bot.backend.message_sent == rawlist(
          'CAP LS 302',
          'PASS auth_secret',
          'NICK Sopel',
          'USER sopel 0 * :Sopel (https://sopel.chat)'
      )
  
  
  def test_on_connect_server_auth_password(bot):
      bot.settings.core.server_auth_method = 'server'
      bot.settings.core.server_auth_password = 'server_secret'
      bot.on_connect()
  
      assert bot.backend.message_sent == rawlist(
          'CAP LS 302',
          'PASS server_secret',
          'NICK Sopel',
          'USER sopel 0 * :Sopel (https://sopel.chat)'
      )
  
  
  def test_on_connect_auth_password_override_server_auth(bot):
      bot.settings.core.auth_method = 'server'
      bot.settings.core.auth_password = 'auth_secret'
      bot.settings.core.server_auth_method = 'server'
      bot.settings.core.server_auth_password = 'server_secret'
      bot.on_connect()
  
      assert bot.backend.message_sent == rawlist(
          'CAP LS 302',
          'PASS auth_secret',
          'NICK Sopel',
          'USER sopel 0 * :Sopel (https://sopel.chat)'
      )
  
  
  def test_write(bot):
      bot.write(['INFO'])
  
      assert bot.backend.message_sent == rawlist('INFO')
  
  
  def test_write_args(bot):
      bot.write(['NICK', 'Sopel'])
  
      assert bot.backend.message_sent == rawlist('NICK Sopel')
  
  
  def test_write_text(bot):
      bot.write(['HELP'], '?')
  
      assert bot.backend.message_sent == rawlist('HELP :?')
  
  
  def test_write_args_text_safe(bot):
      bot.write(['CMD\nUNSAFE'], 'Unsafe\rtext')
  
      assert bot.backend.message_sent == rawlist('CMDUNSAFE :Unsafetext')
  
  
  def test_write_args_many(bot):
      bot.write(['NICK', 'Sopel'])
      bot.write(['JOIN', '#sopel'])
  
      assert bot.backend.message_sent == rawlist(
          'NICK Sopel',
          'JOIN #sopel',
      )
  
  
  def test_write_text_many(bot):
      bot.write(['NICK', 'Sopel'])
      bot.write(['HELP'], '?')
  
      assert bot.backend.message_sent == rawlist(
          'NICK Sopel',
          'HELP :?',
      )
  
  
  def test_action(bot):
      bot.action('is doing some tests', '#sopel')
  
      assert bot.backend.message_sent == rawlist(
          'PRIVMSG #sopel :\001ACTION is doing some tests\001',
      )
  
  
  def test_join(bot):
      bot.join('#sopel')
  
      assert bot.backend.message_sent == rawlist(
          'JOIN #sopel',
      )
  
  
  def test_join_password(bot):
      bot.join('#sopel', 'secret_password')
  
      assert bot.backend.message_sent == rawlist(
          'JOIN #sopel secret_password',
      )
  
  
  def test_kick(bot):
      bot.kick('spambot', '#channel')
  
      assert bot.backend.message_sent == rawlist(
          'KICK #channel spambot',
      )
  
  
  def test_kick_reason(bot):
      bot.kick('spambot', '#channel', 'Flood!')
  
      assert bot.backend.message_sent == rawlist(
          'KICK #channel spambot :Flood!',
      )
  
  
  def test_notice(bot):
      bot.notice('Hello world!', '#sopel')
  
      assert bot.backend.message_sent == rawlist(
          'NOTICE #sopel :Hello world!',
      )
  
  
  def test_part(bot):
      bot.part('#channel')
  
      assert bot.backend.message_sent == rawlist(
          'PART #channel',
      )
  
  
  def test_part_reason(bot):
      bot.part('#channel', 'Bye!')
  
      assert bot.backend.message_sent == rawlist(
          'PART #channel :Bye!',
      )
  
  
  def test_reply(bot):
      bot.reply('Thank you!', '#sopel', 'dgw')
  
      assert bot.backend.message_sent == rawlist(
          'PRIVMSG #sopel :dgw: Thank you!',
      )
  
  
  def test_reply_notice(bot):
      bot.reply('Thank you!', '#sopel', 'dgw', notice=True)
  
      assert bot.backend.message_sent == rawlist(
          'NOTICE #sopel :dgw: Thank you!',
      )
  
  
  def test_say(bot):
      bot.say('Hello world!', '#sopel')
  
      assert bot.backend.message_sent == rawlist(
          'PRIVMSG #sopel :Hello world!',
      )
  
  
  def test_say_safe(bot):
      bot.say('Hello\r\nworld!\r\n', '#sopel')
  
      assert bot.backend.message_sent == rawlist(
          'PRIVMSG #sopel :Helloworld!',
      )
  
  
  def test_say_long_fit(bot):
      """Test a long message that fits into the 512 bytes limit."""
      text = 'a' * (512 - len('PRIVMSG #sopel :\r\n'))
      bot.say(text, '#sopel')
  
      assert bot.backend.message_sent == rawlist(
          'PRIVMSG #sopel :%s' % text,
      )
  
  
  def test_say_long_extra(bot):
      """Test a long message that doesn't fit into the 512 bytes limit."""
      text = 'a' * (512 - len('PRIVMSG #sopel :\r\n'))
      bot.say(text + 'b', '#sopel')
  
      assert bot.backend.message_sent == rawlist(
          'PRIVMSG #sopel :%s' % text,  # the 'b' is truncated out
      )
  
  
  def test_say_long_extra_multi_message(bot):
      """Test a long message that doesn't fit, with split allowed."""
      text = 'a' * (512 - prefix_length(bot) - len('PRIVMSG #sopel :\r\n'))
      bot.say(text + 'b', '#sopel', max_messages=2)
  
      assert bot.backend.message_sent == rawlist(
          'PRIVMSG #sopel :%s' % text,  # the 'b' is split from message
          'PRIVMSG #sopel :b',
      )
  
  
  def test_say_long_extra_multi_message_multibyte_recipient(bot):
      """Test a split-allowed message sent to recipient with multi-byte char."""
      text = 'a' * (
          512 - prefix_length(bot) - len('PRIVMSG #sopel² :\r\n'.encode('utf-8')))
      bot.say(text + 'b', '#sopel²', max_messages=2)
  
      assert bot.backend.message_sent == rawlist(
          'PRIVMSG #sopel² :%s' % text,  # the 'b' is split from message
          'PRIVMSG #sopel² :b',
      )
  
  
  def test_say_long_truncation_fit(bot):
      """Test optional truncation indicator with message that fits in one line."""
      text = 'a' * (512 - prefix_length(bot) - len('PRIVMSG #sopel :\r\n') - 3)
      bot.say(text + 'ttt', '#sopel', truncation='...')
  
      assert bot.backend.message_sent == rawlist(
          'PRIVMSG #sopel :%s' % text + 'ttt',  # nothing is truncated or replaced
      )
  
  
  def test_say_long_truncation_extra(bot):
      """Test optional truncation indicator with message that is too long."""
      text = 'a' * (512 - prefix_length(bot) - len('PRIVMSG #sopel :\r\n') - 3)
      bot.say(text + 'ttt' + 'b', '#sopel', truncation='...')
  
      assert bot.backend.message_sent == rawlist(
          # 'b' is truncated; 'ttt' is replaced by `truncation`
          'PRIVMSG #sopel :%s' % text + '...',
      )
  
  
  def test_say_long_truncation_extra_multi_message(bot):
      """Test optional truncation indicator with message splitting."""
      msg1 = 'a' * (512 - prefix_length(bot) - len('PRIVMSG #sopel :\r\n'))
      msg2 = msg1[:-3] + 'ttt'
      bot.say(msg1 + msg2 + 'b', '#sopel', max_messages=2, truncation='...')
  
      assert bot.backend.message_sent == rawlist(
          # split as expected
          'PRIVMSG #sopel :%s' % msg1,
          # 'b' is truncated; 'ttt' is replaced by `truncation`
          'PRIVMSG #sopel :%s' % msg2.replace('ttt', '...'),
      )
  
  
  def test_say_long_truncation_extra_multi_message_multibyte(bot):
      """Test optional truncation indicator with message splitting."""
      msg1 = 'a' * (512 - prefix_length(bot) - len('PRIVMSG #sopel :\r\n'))
      msg2 = msg1[:-3] + 'ttt'
      bot.say(msg1 + msg2 + 'b', '#sopel', max_messages=2, truncation='…')
  
      assert bot.backend.message_sent == rawlist(
          # split as expected
          'PRIVMSG #sopel :%s' % msg1,
          # 'b' is truncated; 'ttt' is replaced by `truncation`
          'PRIVMSG #sopel :%s' % msg2.replace('ttt', '…'),
      )
  
  
  def test_say_trailing(bot):
      """Test optional trailing string."""
      text = '"This is a test quote.'
      bot.say(text, '#sopel', trailing='"')
  
      assert bot.backend.message_sent == rawlist(
          # combined
          'PRIVMSG #sopel :%s' % text + '"'
      )
  
  
  def test_say_long_truncation_trailing(bot):
      """Test optional truncation indicator AND trailing string together."""
      msg1 = 'a' * (512 - prefix_length(bot) - len('PRIVMSG #sopel :\r\n'))
      msg2 = msg1[:-4] + 'bbbc'
      bot.say(msg1 + msg2 + 'd', '#sopel', max_messages=2, truncation='…', trailing='q')
  
      assert bot.backend.message_sent == rawlist(
          # split as expected
          'PRIVMSG #sopel :%s' % msg1,
          # 'd' is truncated; 'bbb' is replaced by `truncation`; 'c' is replaced by `trailing`
          'PRIVMSG #sopel :%s' % msg2.replace('bbb', '…').replace('c', 'q')
      )
  
  
  def test_say_no_repeat_protection(bot):
      # five is fine
      bot.say('hello', '#sopel')
      bot.say('hello', '#sopel')
      bot.say('hello', '#sopel')
      bot.say('hello', '#sopel')
      bot.say('hello', '#sopel')
  
      assert bot.backend.message_sent == rawlist(
          'PRIVMSG #sopel :hello',
          'PRIVMSG #sopel :hello',
          'PRIVMSG #sopel :hello',
          'PRIVMSG #sopel :hello',
          'PRIVMSG #sopel :hello',
      )
  
      # six: replaced by '...'
      bot.say('hello', '#sopel')
  
      assert bot.backend.message_sent == rawlist(
          'PRIVMSG #sopel :hello',
          'PRIVMSG #sopel :hello',
          'PRIVMSG #sopel :hello',
          'PRIVMSG #sopel :hello',
          'PRIVMSG #sopel :hello',
          # the extra hello is replaced by '...'
          'PRIVMSG #sopel :...',
      )
  
      # these one will add more '...'
      bot.say('hello', '#sopel')
      bot.say('hello', '#sopel')
  
      assert bot.backend.message_sent == rawlist(
          'PRIVMSG #sopel :hello',
          'PRIVMSG #sopel :hello',
          'PRIVMSG #sopel :hello',
          'PRIVMSG #sopel :hello',
          'PRIVMSG #sopel :hello',
          'PRIVMSG #sopel :...',
          # the new ones are also replaced by '...'
          'PRIVMSG #sopel :...',
          'PRIVMSG #sopel :...',
      )
  
      # but at some point it just stops talking
      bot.say('hello', '#sopel')
  
      assert bot.backend.message_sent == rawlist(
          'PRIVMSG #sopel :hello',
          'PRIVMSG #sopel :hello',
          'PRIVMSG #sopel :hello',
          'PRIVMSG #sopel :hello',
          'PRIVMSG #sopel :hello',
          #  three time, then stop
          'PRIVMSG #sopel :...',
          'PRIVMSG #sopel :...',
          'PRIVMSG #sopel :...',
      )
  