def test_get_nick_id_migration(db: SopelDB): """Test nicks with wrong casemapping are properly migrated.""" nick = 'Test[User]' old_nick = Identifier._lower_swapped(nick) # sanity check assert Identifier(nick).lower() != old_nick, ( 'Previous casemapping should be different from the new one') # insert old version with db.session() as session: nickname = Nicknames( nick_id=42, slug=Identifier._lower_swapped(nick), canonical=nick, ) session.add(nickname) session.commit() assert db.get_nick_id(nick) == 42, 'Old nick must be converted.' with db.session() as session: nicknames = session.execute(select(Nicknames)).scalars().fetchall() assert len(nicknames) == 1, ( 'There should be only one instance of Nicknames.') nickname_found = nicknames[0] assert nickname_found.nick_id == 42 assert nickname_found.slug == Identifier(nick).lower() assert nickname_found.canonical == nick
def get_nick_id(self, nick, create=True): """Return the internal identifier for a given nick. :param nick: the nickname for which to fetch an ID :type nick: :class:`~sopel.tools.Identifier` :param bool create: whether to create an ID if one does not exist :raise ValueError: if no ID exists for the given ``nick`` and ``create`` is set to ``False`` :raise ~sqlalchemy.exc.SQLAlchemyError: if there is a database error The nick ID is shared across all of a user's aliases, assuming their nicks have been grouped together. .. seealso:: Alias/group management functions: :meth:`alias_nick`, :meth:`unalias_nick`, :meth:`merge_nick_groups`, and :meth:`delete_nick_group`. """ session = self.ssession() slug = nick.lower() try: nickname = session.query(Nicknames) \ .filter(Nicknames.slug == slug) \ .one_or_none() if nickname is None: # see if it needs case-mapping migration nickname = session.query(Nicknames) \ .filter(Nicknames.slug == Identifier._lower_swapped(nick)) \ .one_or_none() if nickname is not None: # it does! nickname.slug = slug session.commit() if nickname is None: # "is /* still */ None", if Python had inline comments if not create: raise ValueError('No ID exists for the given nick') # Generate a new ID nick_id = NickIDs() session.add(nick_id) session.commit() # Create a new Nickname nickname = Nicknames(nick_id=nick_id.nick_id, slug=slug, canonical=nick) session.add(nickname) session.commit() return nickname.nick_id except SQLAlchemyError: session.rollback() raise finally: self.ssession.remove()
def get_channel_slug(self, chan): """Return the case-normalized representation of ``channel``. :param str channel: the channel name to normalize, with prefix (required) :return str: the case-normalized channel name (or "slug" representation) This is useful to make sure that a channel name is stored consistently in both the bot's own database and third-party plugins' databases/files, without regard for variation in case between different clients and/or servers on the network. """ chan = Identifier(chan) slug = chan.lower() session = self.ssession() try: count = session.query(ChannelValues) \ .filter(ChannelValues.channel == slug) \ .count() if count == 0: # see if it needs case-mapping migration old_rows = session.query(ChannelValues) \ .filter(ChannelValues.channel == Identifier._lower_swapped(chan)) old_count = old_rows.count() if old_count > 0: # it does! old_rows.update({ChannelValues.channel: slug}) session.commit() return slug except SQLAlchemyError: session.rollback() raise finally: self.ssession.remove()
def test_get_channel_slug_with_migration(db: SopelDB): channel = db.make_identifier('#[channel]') db.set_channel_value(channel, 'testkey', 'cval') assert db.get_channel_slug(channel) == channel.lower() assert db.get_channel_value(channel, 'testkey') == 'cval' # insert a value with the wrong casemapping old_channel = Identifier._lower_swapped('#[channel]') assert old_channel == '#[channel]' assert channel.lower() == '#{channel}' with db.session() as session: channel_value = ChannelValues( channel=old_channel, key='oldkey', value='"value"' # result from json.dumps ) session.add(channel_value) session.commit() assert db.get_channel_slug(old_channel) == channel.lower(), ( 'Channel with previous casemapping must return the new version.') assert db.get_channel_value(old_channel, 'oldkey') == 'value', ( 'Key associated to an old version must be migrated to the new one')