From ddfd7e10e1755c7eca26525865cbae8a5bd4f22e Mon Sep 17 00:00:00 2001 From: Daniel DiPaolo Date: Thu, 16 Oct 2003 02:03:35 +0000 Subject: [PATCH] * LOTS of updates to the MoobotFactoids test suite, parts of which aren't ready yet, but the test targets should be pretty static from here on out, except adding new tests. * Fixed up lock and coded unlock in MoobotFactoids (actually, factored the common code out into one helper function that each call). * Added the changeFactoids (=~) portion. * Changed the table structure again (should be the last time). Locked_by was redundant if we're only going to let factoid creators lock/unlock. Removed it. --- plugins/MoobotFactoids.py | 128 ++++++++++++++++++++++++++---------- test/test_MoobotFactoids.py | 100 ++++++++++++++++++++++++++-- 2 files changed, 187 insertions(+), 41 deletions(-) diff --git a/plugins/MoobotFactoids.py b/plugins/MoobotFactoids.py index 46a92e91a..e90bc34e2 100644 --- a/plugins/MoobotFactoids.py +++ b/plugins/MoobotFactoids.py @@ -125,7 +125,7 @@ def pick(L, recursed=False): class MoobotFactoids(callbacks.PrivmsgCommandAndRegexp): priority = 1000 - addressedRegexps = sets.Set(['addFactoids']) + addressedRegexps = sets.Set(['addFactoids', 'changeFactoids']) def __init__(self): callbacks.PrivmsgCommandAndRegexp.__init__(self) self.makeDB(dbfilename) @@ -149,7 +149,6 @@ class MoobotFactoids(callbacks.PrivmsgCommandAndRegexp): created_at TIMESTAMP, modified_by INTEGER, modified_at TIMESTAMP, - locked_by INTEGER, locked_at TIMESTAMP, last_requested_by TEXT, last_requested_at TIMESTAMP, @@ -237,12 +236,47 @@ class MoobotFactoids(callbacks.PrivmsgCommandAndRegexp): return # Otherwise, cursor.execute("""INSERT INTO factoids VALUES - (%s, %s, %s, NULL, NULL, NULL, NULL, NULL, NULL, + (%s, %s, %s, NULL, NULL, NULL, NULL, NULL, %s, 0)""", key, id, int(time.time()), fact) self.db.commit() irc.reply(msg, conf.replySuccess) + def changeFactoids(self, irc, msg, match): + r"(\S+)\s+=~\s+(\S+)" + # Must be registered! + try: + id = ircdb.users.getUserId(msg.prefix) + except KeyError: + irc.error(msg, conf.replyNotRegistered) + return + key, regexp = match.groups() + cursor = self.db.cursor() + # Check and make sure it's in the DB + cursor.execute("""SELECT locked_at, fact FROM factoids + WHERE key = %s""", key) + if cursor.rowcount == 0: + irc.error(msg, "Factoid '%s' not found." % key) + return + # No dice if it's locked, no matter who it is + (locked_at, fact) = cursor.fetchone() + if locked_at is not None: + irc.error(msg, "Factoid '%s' is locked." % key) + return + # It's fair game if we get to here + try: + r = utils.perlReToReplacer(regexp) + except ValueError, e: + irc.error(msg, "Invalid regexp: %s" % regexp) + return + new_fact = r(fact) + cursor.execute("""UPDATE factoids + SET fact = %s, modified_by = %s, + modified_at = %s WHERE key = %s""", + new_fact, id, int(time.time()), key) + self.db.commit() + irc.reply(msg, conf.replySuccess) + def literal(self, irc, msg, args): """ @@ -271,14 +305,13 @@ class MoobotFactoids(callbacks.PrivmsgCommandAndRegexp): cursor = self.db.cursor() cursor.execute("""SELECT created_by, created_at, modified_by, modified_at, last_requested_by, last_requested_at, - requested_count, locked_by, locked_at FROM + requested_count, locked_at FROM factoids WHERE key = %s""", key) if cursor.rowcount == 0: irc.error(msg, "No such factoid: %s" % key) return (created_by, created_at, modified_by, modified_at, last_requested_by, - last_requested_at, requested_count, locked_by, locked_at) = \ - cursor.fetchone() + last_requested_at, requested_count, locked_at) = cursor.fetchone() # First, creation info. # Map the integer created_by to the username creat_by = ircdb.users.getUser(created_by).name @@ -297,45 +330,72 @@ class MoobotFactoids(callbacks.PrivmsgCommandAndRegexp): last_at = time.strftime(conf.humanTimestampFormat, time.localtime(int(last_requested_at))) req_count = requested_count - s += " Last requested by %s on %s, requested %s times." % \ - (last_by, last_at, req_count) + times_str = utils.nItems(requested_count, 'time') + s += " Last requested by %s on %s, requested %s." % \ + (last_by, last_at, times_str) # Last, locked info - if locked_by is not None: - lock_by = ircdb.users.getUser(locked_by).name + if locked_at is not None: lock_at = time.strftime(conf.humanTimestampFormat, time.localtime(int(locked_at))) - s += " Locked by %s on %s." % (lock_by, lock_at) + s += " Locked on %s." % lock_at irc.reply(msg, s) + + def _lock(self, irc, msg, args, lock=True): + try: + id = ircdb.users.getUserId(msg.prefix) + except KeyError: + irc.error(msg, conf.replyNotRegistered) + return + key = privmsgs.getArgs(args, needed=1) + cursor = self.db.cursor() + cursor.execute("""SELECT created_by, locked_at FROM factoids + WHERE key = %s""", key) + if cursor.rowcount == 0: + irc.error(msg, "No such factoid: %s" % key) + return + (created_by, locked_at) = cursor.fetchone() + # Don't perform redundant operations + if lock: + if locked_at is not None: + irc.error(msg, "Factoid '%s' is already locked." % key) + return + else: + if locked_at is None: + irc.error(msg, "Factoid '%s' is not locked." % key) + return + # Can only lock/unlock own factoids + if created_by != id: + s = "unlock" + if lock: + s = "lock" + irc.error(msg, "Cannot %s someone else's factoid." % s) + return + # Okay, we're done, ready to lock/unlock + if lock: + locked_at = int(time.time()) + else: + locked_at = None + cursor.execute("""UPDATE factoids SET locked_at = %s + WHERE key = %s""", locked_at, key) + self.db.commit() + irc.reply(msg, conf.replySuccess) + def lock(self, irc, msg, args): """ Locks the factoid with the given factoid key. Requires that the user be registered and have created the factoid originally. """ - try: - id = ircdb.users.getUserId(msg.prefix) - except KeyError: - irc.error(msg, conf.replyNotRegistered) - return - key = privmsgs.getArgs(args, needed=1) - cursor = self.db.cursor() - cursor.execute("""SELECT created_by, locked_by FROM factoids - WHERE key = %s""", key) - if cursor.rowcount == 0: - irc.error(msg, "No such factoid: %s" % key) - return - (created_by, locked_by) = cursor.fetchone() - if locked_by is not None: - irc.error(msg, "Factoid '%s' is already locked." % key) - return - if created_by != id: - irc.error(msg, "Cannot lock someone else's factoid." % key) - return - cursor.execute("""UPDATE factoids SET locked_by = %s, locked_at = %s - WHERE key = %s""", id, int(time.time()), key) - self.db.commit() - irc.reply(msg, conf.replySuccess) + self._lock(irc, msg, args, True) + + def unlock(self, irc, msg, args): + """ + + Unlocks the factoid with the given factoid key. Requires that the + user be registered and have locked the factoid. + """ + self._lock(irc, msg, args, False) Class = MoobotFactoids diff --git a/test/test_MoobotFactoids.py b/test/test_MoobotFactoids.py index 80d93cc5f..e7f291a25 100644 --- a/test/test_MoobotFactoids.py +++ b/test/test_MoobotFactoids.py @@ -44,24 +44,110 @@ if sqlite is not None: # Create a valid user to use self.prefix = 'foo!bar@baz' self.assertNotError('register tester moo') - + def testLiteral(self): self.assertError('literal moo') # no factoids yet self.assertNotError('moo is foo') - self.assertRegexp('literal moo', 'foo') + self.assertResponse('literal moo', 'foo') self.assertNotError('moo2 is moo!') - self.assertRegexp('literal moo2', 'moo!') + self.assertResponse('literal moo2', 'moo!') self.assertNotError('moo3 is foo') - self.assertRegexp('literal moo3', 'foo') + self.assertResponse('literal moo3', 'foo') def testGetFactoid(self): self.assertNotError('moo is foo') - self.assertRegexp('moo', 'foo') + self.assertResponse('moo', 'foo') self.assertNotError('moo2 is moo!') - self.assertRegexp('moo2', 'moo2 is moo!') + self.assertResponse('moo2', 'moo2 is moo!') self.assertNotError('moo3 is foo') self.assertAction('moo3', 'foo') + # Test and make sure it's parsing + self.assertNotError('moo4 is (1|2|3)') + self.assertRegexp('moo4', '^(1|2|3)$') + def testFactinfo(self): + self.assertNotError('moo is foo') + self.assertRegexp('factinfo moo', '^moo: Created by tester on.*$') + self.assertNotError('moo') + self.assertRegexp('factinfo moo', '^moo: Created by tester on' + '.*?\. Last requested by foo!bar@baz on .*?, ' + 'requested 1 time.$') + self.assertNotError('moo') + self.assertRegexp('factinfo moo', '^moo: Created by tester on' + '.*?\. Last requested by foo!bar@baz on .*?, ' + 'requested 2 times.$') + self.assertNotError('moo =~ s/foo/bar/') + self.assertRegexp('factinfo moo', '^moo: Created by tester on' + '.*?\. Last modified by tester on .*?\. ' + 'Last requested by foo!bar@baz on .*?, ' + 'requested 2 times.$') + self.assertNotError('lock moo') + self.assertRegexp('factinfo moo', '^moo: Created by tester on' + '.*?\. Last modified by tester on .*?\. ' + 'Last requested by foo!bar@baz on .*?, ' + 'requested 2 times. Locked on .*\.$') + self.assertNotError('unlock moo') + self.assertRegexp('factinfo moo', '^moo: Created by tester on' + '.*?\. Last modified by tester on .*?\. ' + 'Last requested by foo!bar@baz on .*?, ' + 'requested 2 times.$') + + def testLockUnlock(self): + self.assertNotError('moo is moo') + self.assertNotError('lock moo') + self.assertRegexp('factinfo moo', '^moo: Created by tester on' + '.*?\. Locked on .*?\.') + # switch user + self.prefix = 'moo!moo@moo' + self.assertNotError('register nottester moo') + self.assertError('unlock moo') + self.assertRegexp('factinfo moo', '^moo: Created by tester on' + '.*?\. Locked on .*?\.') + # switch back + self.prefix = 'foo!bar@baz' + self.assertNotError('identify tester moo') + self.assertNotError('unlock moo') + self.assertRegexp('factinfo moo', '^moo: Created by tester on' + '.*?\.') + + def testChangeFactoid(self): + self.assertNotError('moo is moo') + self.assertNotError('moo =~ s/moo/moos/') + self.assertResponse('moo', 'moos') + self.assertNotError('moo =~ s/reply/action/') + self.assertAction('moo', 'moos') + self.assertNotError('moo =~ s/moos/(moos|woofs)/') + self.assertActionRegexp('moo', '^(moos|woofs)$') + self.assertError('moo =~ s/moo/') + + def testListkeys(self): + self.assertResponse('listkeys *', 'No keys found matching \'*\'.') + self.assertNotError('moo is moo') + self.assertResponse('listkeys moo', 'Key search for \'moo\' ' + '(1 found): moo') + self.assertResponse('listkeys foo', 'No keys found matching ' + '\'foo\'.') + # Throw in a bunch more + for i in range(10): + self.assertNotError('moo%s is moo' % i) + self.assertRegexp('listkeys moo', '^Key search for \'moo\' ' + '(11 found): (moo\d*, )+ and moo9$') + self.assertRegexp('listkeys *', '^Key search for \'*\' ' + '(12 found): foo, (moo\d*, )+ and moo9$') + + def testListvalues(self): + self.assertNotError('moo is moo') + self.assertResponse('listvalues moo', 'Value search for \'moo\' ' + '(1 found): moo') + + def testListauth(self): + self.assertNotError('moo is moo') + self.assertResponse('listauth tester', 'Author search for tester ' + '(1 found): moo') + + class DunnoTestCase(PluginTestCase, PluginDocumentation): + plugins = ('MiscCommands', 'MoobotFactoids', 'UserCommands') + def testDunno(self): + self.assertNotError('apfasdfjoia') # Should say a dunno, no error # vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: -