From dda10fdc0e8ae61b68635924b4dcb47a80484df1 Mon Sep 17 00:00:00 2001 From: David Rousselie Date: Sat, 28 Oct 2006 19:55:54 +0200 Subject: [PATCH] Add complex Account class darcs-hash:20061028175554-86b55-a286c0e397f7bc3d9c9616db0c3f2f0c39a70bfa.gz --- run_tests.py | 12 +- src/jcl/jabber/component.py | 38 ++-- src/jcl/jabber/x.py | 2 +- src/jcl/model/account.py | 29 ++- tests/jcl/jabber/test_component.py | 316 +++++++++++++++++++++-------- tests/jcl/jabber/test_feeder.py | 1 + 6 files changed, 286 insertions(+), 112 deletions(-) diff --git a/run_tests.py b/run_tests.py index 1555de9..3105919 100644 --- a/run_tests.py +++ b/run_tests.py @@ -50,14 +50,14 @@ if __name__ == '__main__': feeder_suite = unittest.makeSuite(Feeder_TestCase, "test") sender_suite = unittest.makeSuite(Sender_TestCase, "test") jcl_suite = unittest.TestSuite() - jcl_suite.addTest(FeederComponent_TestCase('test_handle_set_register_new')) -# jcl_suite.addTest(FeederComponent_TestCase('test_handle_presence_available_to_account_live_password')) +# jcl_suite.addTest(FeederComponent_TestCase('test_handle_get_register_exist2')) +# jcl_suite.addTest(FeederComponent_TestCase('test_constructor')) # jcl_suite = unittest.TestSuite((feeder_component_suite)) # jcl_suite = unittest.TestSuite((component_suite)) -# jcl_suite = unittest.TestSuite((component_suite, -# feeder_component_suite, -# feeder_suite, -# sender_suite)) + jcl_suite = unittest.TestSuite((component_suite, + feeder_component_suite, + feeder_suite, + sender_suite)) test_support.run_suite(jcl_suite) diff --git a/src/jcl/jabber/component.py b/src/jcl/jabber/component.py index e1c5068..1a712e6 100644 --- a/src/jcl/jabber/component.py +++ b/src/jcl/jabber/component.py @@ -56,7 +56,7 @@ VERSION = "0.1" ############################################################################### # JCL implementation ############################################################################### -class JCLComponent(Component): +class JCLComponent(Component, object): """Implement default JCL component behavior: - regular interval behavior - Jabber register process (add, delete, update accounts) @@ -66,21 +66,6 @@ class JCLComponent(Component): timeout = 1 - def set_account_class(self, account_class): - """account_class attribut setter - create associated table via SQLObject""" - self.__account_class = account_class - self.db_connect() - self.__account_class.createTable() # TODO: ifNotExists = True) - self.db_disconnect() - - def get_account_class(self): - """account_class attribut getter""" - return self.__account_class - - account_class = property(get_account_class, set_account_class) - - def __init__(self, jid, secret, @@ -101,7 +86,6 @@ class JCLComponent(Component): self.spool_dir = "." self.db_connection_str = db_connection_str self.__account_class = None - self.set_account_class(Account) self.version = VERSION self.accounts = [] self.time_unit = 60 @@ -116,6 +100,22 @@ class JCLComponent(Component): signal.signal(signal.SIGINT, self.signal_handler) signal.signal(signal.SIGTERM, self.signal_handler) + def set_account_class(self, account_class): + """account_class attribut setter + create associated table via SQLObject""" + self.__account_class = account_class + self.db_connect() + self.__account_class.createTable(ifNotExists = True) + self.db_disconnect() + + def get_account_class(self): + """account_class attribut getter""" + if self.__account_class is None: + self.set_account_class(Account) + return self.__account_class + + account_class = property(get_account_class, set_account_class) + def run(self): """Main loop Connect to Jabber server @@ -591,8 +591,6 @@ class JCLComponent(Component): def get_reg_form(self, lang_class): """Return register form based on language and account class """ - # TODO : handle text-private for password - # TODO : handle list-single reg_form = X() reg_form.xmlns = "jabber:x:data" reg_form.title = lang_class.register_title @@ -604,7 +602,7 @@ class JCLComponent(Component): label = lang_class.account_name, \ var = "name") - for (field, field_type) in \ + for (field, field_type, post_func, default_func) in \ self.account_class.get_register_fields(): lang_label_attr = self.account_class.__name__.lower() \ + "_" + field diff --git a/src/jcl/jabber/x.py b/src/jcl/jabber/x.py index f855834..d014f7c 100644 --- a/src/jcl/jabber/x.py +++ b/src/jcl/jabber/x.py @@ -113,7 +113,7 @@ class X(object): or return default func processing if field does not exist""" if self.fields.has_key(field_name): return post_func(self.fields[field_name].value) - return default_func() + return default_func(field_name) def attach_xml(self, info_query): """Attach this Xdata form to iq node diff --git a/src/jcl/model/account.py b/src/jcl/model/account.py index b93b277..cbc6412 100644 --- a/src/jcl/model/account.py +++ b/src/jcl/model/account.py @@ -31,12 +31,15 @@ from sqlobject.col import StringCol, BoolCol from sqlobject.dbconnection import ConnectionHub from jcl.lang import Lang +from jcl.jabber.error import FieldError OFFLINE = "offline" ONLINE = "online" # create a hub to attach a per thread connection hub = ConnectionHub() +class Account2(SQLObject): + pass class Account(SQLObject): """Base Account class""" @@ -90,7 +93,7 @@ class Account(SQLObject): status = property(get_status, set_status) - def get_register_fields(cls): + def _get_register_fields(cls): """Return a list of tuples for X Data Form composition A tuple is composed of: - field_name: might be the name of one of the class attribut @@ -102,7 +105,7 @@ class Account(SQLObject): """ return [] # "name" field is mandatory - get_register_fields = classmethod(get_register_fields) + get_register_fields = classmethod(_get_register_fields) def get_new_message_subject(self, lang_class): """Get localized message subject for new account""" @@ -119,4 +122,26 @@ class Account(SQLObject): def get_update_message_body(self, lang_class): """Return localized message body for existing account""" return lang_class.new_account_message_body + + def default_post_func(self, field_value): + """Default post process function: do nothing""" + return field_value + + def boolean_post_func(self, field_value): + """Return a boolean from boolean field value""" + return (field_value == "1" or field_value.lower() == "true") + def int_post_func(self, field_value): + """Return an integer from integer field value""" + return int(field_value) + + def string_not_null_post_func(self, field_value): + """Post process function for not null/empty string""" + if field_value is None or field_value == "": + raise FieldError # TODO : add translated message + return field_value + + def mandatory_field(self, field_name): + """Used as default function for field that must be specified + and cannot have default value""" + raise FieldError # TODO : add translated message diff --git a/tests/jcl/jabber/test_component.py b/tests/jcl/jabber/test_component.py index 706c862..5062800 100644 --- a/tests/jcl/jabber/test_component.py +++ b/tests/jcl/jabber/test_component.py @@ -43,6 +43,8 @@ from jcl.model.account import Account from jcl.lang import Lang from jcl.jabber.x import X +from tests.jcl.model.account import AccountExample + DB_PATH = "/tmp/test.db" DB_URL = DB_PATH# + "?debug=1&debugThreading=1" @@ -119,11 +121,13 @@ class JCLComponent_TestCase(unittest.TestCase): "localhost", "5347", 'sqlite://' + DB_URL) + self.comp.account_class = Account self.max_tick_count = 2 self.saved_time_handler = None def tearDown(self): account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) + AccountExample.dropTable(ifExists = True) Account.dropTable(ifExists = True) del TheURIOpener.cachedURIs['sqlite://' + DB_URL] account.hub.threadConnection.close() @@ -321,15 +325,61 @@ class JCLComponent_TestCase(unittest.TestCase): fields = iq_sent.xpath_eval("jir:query/jxd:x/jxd:field", \ {"jir" : "jabber:iq:register", \ "jxd" : "jabber:x:data"}) + print str([str(field) for field in fields]) self.assertEquals(len(fields), 1) - self.assertEquals(len([field - for field in fields \ - if field.prop("type") == "text-single" \ - and field.prop("var") == "name" \ - and field.prop("label") == \ - Lang.en.account_name]), \ - 1) + self.assertEquals(fields[0].prop("type"), "text-single") + self.assertEquals(fields[0].prop("var"), "name") + self.assertEquals(fields[0].prop("label"), Lang.en.account_name) + def test_handle_get_register_new2(self): + self.comp.account_class = AccountExample + self.comp.stream = MockStream() + self.comp.stream_class = MockStream + self.comp.handle_get_register(Iq(stanza_type = "get", \ + from_jid = "user1@test.com", \ + to_jid = "jcl.test.com")) + self.assertEquals(len(self.comp.stream.sent), 1) + iq_sent = self.comp.stream.sent[0] + self.assertEquals(iq_sent.get_to(), "user1@test.com") + titles = iq_sent.xpath_eval("jir:query/jxd:x/jxd:title", \ + {"jir" : "jabber:iq:register", \ + "jxd" : "jabber:x:data"}) + self.assertEquals(len(titles), 1) + self.assertEquals(titles[0].content, \ + Lang.en.register_title) + instructions = iq_sent.xpath_eval("jir:query/jxd:x/jxd:instructions", \ + {"jir" : "jabber:iq:register", \ + "jxd" : "jabber:x:data"}) + self.assertEquals(len(instructions), 1) + self.assertEquals(instructions[0].content, \ + Lang.en.register_instructions) + fields = iq_sent.xpath_eval("jir:query/jxd:x/jxd:field", \ + {"jir" : "jabber:iq:register", \ + "jxd" : "jabber:x:data"}) + self.assertEquals(len(fields), 6) + self.assertEquals(fields[0].prop("type"), "text-single") + self.assertEquals(fields[0].prop("var"), "name") + self.assertEquals(fields[0].prop("label"), Lang.en.account_name) + + self.assertEquals(fields[1].prop("type"), "text-single") + self.assertEquals(fields[1].prop("var"), "login") + self.assertEquals(fields[1].prop("label"), "login") + + self.assertEquals(fields[2].prop("type"), "text-private") + self.assertEquals(fields[2].prop("var"), "password") + self.assertEquals(fields[2].prop("label"), "password") + + self.assertEquals(fields[3].prop("type"), "boolean") + self.assertEquals(fields[3].prop("var"), "store_password") + self.assertEquals(fields[3].prop("label"), "store_password") + + self.assertEquals(fields[4].prop("type"), "list-single") + self.assertEquals(fields[4].prop("var"), "test_enum") + self.assertEquals(fields[4].prop("label"), "test_enum") + + self.assertEquals(fields[5].prop("type"), "text-single") + self.assertEquals(fields[5].prop("var"), "test_int") + self.assertEquals(fields[5].prop("label"), "test_int") def test_handle_get_register_exist(self): self.comp.stream = MockStream() @@ -337,7 +387,7 @@ class JCLComponent_TestCase(unittest.TestCase): account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) account1 = Account(user_jid = "user1@test.com", \ name = "account1", \ - jid = "account1@jcl.test.com") + jid = "account1@jcl.test.com") del account.hub.threadConnection self.comp.handle_get_register(Iq(stanza_type = "get", \ from_jid = "user1@test.com", \ @@ -374,6 +424,50 @@ class JCLComponent_TestCase(unittest.TestCase): self.assertEquals(len(value), 1) self.assertEquals(value[0].content, "account1") +# def test_handle_get_register_exist2(self): +# self.comp.account_class = AccountExample +# self.comp.stream = MockStream() +# self.comp.stream_class = MockStream +# account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) +# account1 = AccountExample(user_jid = "user1@test.com", \ +# name = "account1", \ +# jid = "account1@jcl.test.com") +# del account.hub.threadConnection +# self.comp.handle_get_register(Iq(stanza_type = "get", \ +# from_jid = "user1@test.com", \ +# to_jid = "account1@jcl.test.com")) +# self.assertEquals(len(self.comp.stream.sent), 1) +# iq_sent = self.comp.stream.sent[0] +# self.assertEquals(iq_sent.get_to(), "user1@test.com") +# titles = iq_sent.xpath_eval("jir:query/jxd:x/jxd:title", \ +# {"jir" : "jabber:iq:register", \ +# "jxd" : "jabber:x:data"}) +# self.assertEquals(len(titles), 1) +# self.assertEquals(titles[0].content, \ +# Lang.en.register_title) +# instructions = iq_sent.xpath_eval("jir:query/jxd:x/jxd:instructions", \ +# {"jir" : "jabber:iq:register", \ +# "jxd" : "jabber:x:data"}) +# self.assertEquals(len(instructions), 1) +# self.assertEquals(instructions[0].content, \ +# Lang.en.register_instructions) +# fields = iq_sent.xpath_eval("jir:query/jxd:x/jxd:field", \ +# {"jir" : "jabber:iq:register", \ +# "jxd" : "jabber:x:data"}) +# self.assertEquals(len(fields), 1) +# self.assertEquals(len([field +# for field in fields \ +# if field.prop("type") == "hidden" \ +# and field.prop("var") == "name" \ +# and field.prop("label") == \ +# Lang.en.account_name]), \ +# 1) +# value = iq_sent.xpath_eval("jir:query/jxd:x/jxd:field/jxd:value", \ +# {"jir" : "jabber:iq:register", \ +# "jxd" : "jabber:x:data"}) +# self.assertEquals(len(value), 1) +# self.assertEquals(value[0].content, "account1") + def test_handle_set_register_new(self): self.comp.stream = MockStream() self.comp.stream_class = MockStream @@ -402,7 +496,6 @@ class JCLComponent_TestCase(unittest.TestCase): del account.hub.threadConnection stanza_sent = self.comp.stream.sent - print str([str(stanza.get_node()) for stanza in stanza_sent]) self.assertEquals(len(stanza_sent), 4) iq_result = stanza_sent[0] self.assertTrue(isinstance(iq_result, Iq)) @@ -432,7 +525,66 @@ class JCLComponent_TestCase(unittest.TestCase): self.assertEquals(presence_account.get_to(), "user1@test.com") self.assertEquals(presence_account.get_node().prop("type"), \ "subscribe") + + def test_handle_set_register_new2(self): + # TODO : Add AccountExample fields +# self.comp.account_class = AccountExample + self.comp.stream = MockStream() + self.comp.stream_class = MockStream + x_data = X() + x_data.xmlns = "jabber:x:data" + x_data.type = "submit" + x_data.add_field(field_type = "text-single", \ + var = "name", \ + value = "account1") + iq_set = Iq(stanza_type = "set", \ + from_jid = "user1@test.com", \ + to_jid = "jcl.test.com") + query = iq_set.new_query("jabber:iq:register") + x_data.attach_xml(query) + self.comp.handle_set_register(iq_set) + + account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) + accounts = self.comp.account_class.select(\ + self.comp.account_class.q.user_jid == "user1@test.com" \ + and self.comp.account_class.q.name == "account1") + self.assertEquals(accounts.count(), 1) + _account = accounts[0] + self.assertEquals(_account.user_jid, "user1@test.com") + self.assertEquals(_account.name, "account1") + self.assertEquals(_account.jid, "account1@jcl.test.com") + del account.hub.threadConnection + stanza_sent = self.comp.stream.sent + self.assertEquals(len(stanza_sent), 4) + iq_result = stanza_sent[0] + self.assertTrue(isinstance(iq_result, Iq)) + self.assertEquals(iq_result.get_node().prop("type"), "result") + self.assertEquals(iq_result.get_from(), "jcl.test.com") + self.assertEquals(iq_result.get_to(), "user1@test.com") + + presence_component = stanza_sent[1] + self.assertTrue(isinstance(presence_component, Presence)) + self.assertEquals(presence_component.get_from(), "jcl.test.com") + self.assertEquals(presence_component.get_to(), "user1@test.com") + self.assertEquals(presence_component.get_node().prop("type"), \ + "subscribe") + + message = stanza_sent[2] + self.assertTrue(isinstance(message, Message)) + self.assertEquals(message.get_from(), "jcl.test.com") + self.assertEquals(message.get_to(), "user1@test.com") + self.assertEquals(message.get_subject(), \ + _account.get_new_message_subject(Lang.en)) + self.assertEquals(message.get_body(), \ + _account.get_new_message_body(Lang.en)) + + presence_account = stanza_sent[3] + self.assertTrue(isinstance(presence_account, Presence)) + self.assertEquals(presence_account.get_from(), "account1@jcl.test.com") + self.assertEquals(presence_account.get_to(), "user1@test.com") + self.assertEquals(presence_account.get_node().prop("type"), \ + "subscribe") def test_handle_set_register_update(self): pass @@ -534,48 +686,48 @@ class JCLComponent_TestCase(unittest.TestCase): self.assertEqual(presence.get_from_jid(), "account11@jcl.test.com") self.assertEqual(presence.get_to_jid(), "user1@test.com") -# Use it in real live password implementation -# def test_handle_presence_available_to_account_live_password(self): -# self.comp.stream = MockStream() -# self.comp.stream_class = MockStream -# account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) -# account11 = Account(user_jid = "user1@test.com", \ -# name = "account11", \ -# jid = "account11@jcl.test.com") -# account11.store_password = False -# account12 = Account(user_jid = "user1@test.com", \ -# name = "account12", \ -# jid = "account12@jcl.test.com") -# account2 = Account(user_jid = "user2@test.com", \ -# name = "account2", \ -# jid = "account2@jcl.test.com") -# del account.hub.threadConnection -# self.comp.handle_presence_available(Presence(\ -# stanza_type = "available", \ -# from_jid = "user1@test.com",\ -# to_jid = "account11@jcl.test.com")) -# messages_sent = self.comp.stream.sent -# self.assertEqual(len(messages_sent), 2) -# password_message = None -# presence = None -# for message in messages_sent: -# if isinstance(message, Message): -# password_message = message -# elif isinstance(message, Presence): -# presence = message -# self.assertTrue(password_message is not None) -# self.assertTrue(presence is not None) -# self.assertTrue(isinstance(presence, Presence)) -# self.assertEqual(presence.get_from_jid(), "account11@jcl.test.com") -# self.assertEqual(presence.get_to_jid(), "user1@test.com") + def test_handle_presence_available_to_account_live_password2(self): + self.comp.account_class = AccountExample + self.comp.stream = MockStream() + self.comp.stream_class = MockStream + account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) + account11 = AccountExample(user_jid = "user1@test.com", \ + name = "account11", \ + jid = "account11@jcl.test.com") + account11.store_password = False + account12 = AccountExample(user_jid = "user1@test.com", \ + name = "account12", \ + jid = "account12@jcl.test.com") + account2 = AccountExample(user_jid = "user2@test.com", \ + name = "account2", \ + jid = "account2@jcl.test.com") + del account.hub.threadConnection + self.comp.handle_presence_available(Presence(\ + stanza_type = "available", \ + from_jid = "user1@test.com",\ + to_jid = "account11@jcl.test.com")) + messages_sent = self.comp.stream.sent + self.assertEqual(len(messages_sent), 2) + password_message = None + presence = None + for message in messages_sent: + if isinstance(message, Message): + password_message = message + elif isinstance(message, Presence): + presence = message + self.assertTrue(password_message is not None) + self.assertTrue(presence is not None) + self.assertTrue(isinstance(presence, Presence)) + self.assertEqual(presence.get_from_jid(), "account11@jcl.test.com") + self.assertEqual(presence.get_to_jid(), "user1@test.com") -# self.assertEqual(unicode(password_message.get_from_jid()), \ -# "account11@jcl.test.com") -# self.assertEqual(unicode(password_message.get_to_jid()), \ -# "user1@test.com") -# self.assertEqual(password_message.get_subject(), \ -# "[PASSWORD] Password request") -# self.assertEqual(password_message.get_body(), None) + self.assertEqual(unicode(password_message.get_from_jid()), \ + "account11@jcl.test.com") + self.assertEqual(unicode(password_message.get_to_jid()), \ + "user1@test.com") + self.assertEqual(password_message.get_subject(), \ + "[PASSWORD] Password request") + self.assertEqual(password_message.get_body(), None) def test_handle_presence_unavailable_to_component(self): self.comp.stream = MockStream() @@ -771,39 +923,37 @@ class JCLComponent_TestCase(unittest.TestCase): messages_sent = self.comp.stream.sent self.assertEqual(len(messages_sent), 0) -# TODO Use it with real implementation live password -# def test_handle_message_password(self): -# self.comp.stream = MockStream() -# self.comp.stream_class = MockStream -# account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) -# account11 = Account(user_jid = "user1@test.com", \ -# name = "account11", \ -# jid = "account11@jcl.test.com") -# account11.waiting_password_reply = True -# account12 = Account(user_jid = "user1@test.com", \ -# name = "account12", \ -# jid = "account12@jcl.test.com") -# account2 = Account(user_jid = "user2@test.com", \ -# name = "account2", \ -# jid = "account2@jcl.test.com") -# del account.hub.threadConnection -# self.comp.handle_message(Message(\ -# from_jid = "user1@test.com", \ -# to_jid = "account11@jcl.test.com", \ -# subject = "[PASSWORD]", \ -# body = "secret")) -# messages_sent = self.comp.stream.sent -# self.assertEqual(len(messages_sent), 1) -# self.assertEqual(messages_sent[0].get_to(), "user1@test.com") -# self.assertEqual(messages_sent[0].get_from(), "account11@jcl.test.com") -# self.assertEqual(account11.password, "secret") -# self.assertEqual(account11.waiting_password_reply, False) -# self.assertEqual(messages_sent[0].get_subject(), \ -# "Password will be kept during your Jabber session") -# self.assertEqual(messages_sent[0].get_body(), \ -# "Password will be kept during your Jabber session") + def test_handle_message_password2(self): + self.comp.account_class = AccountExample + self.comp.stream = MockStream() + self.comp.stream_class = MockStream + account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) + account11 = AccountExample(user_jid = "user1@test.com", \ + name = "account11", \ + jid = "account11@jcl.test.com") + account11.waiting_password_reply = True + account12 = AccountExample(user_jid = "user1@test.com", \ + name = "account12", \ + jid = "account12@jcl.test.com") + account2 = AccountExample(user_jid = "user2@test.com", \ + name = "account2", \ + jid = "account2@jcl.test.com") + del account.hub.threadConnection + self.comp.handle_message(Message(\ + from_jid = "user1@test.com", \ + to_jid = "account11@jcl.test.com", \ + subject = "[PASSWORD]", \ + body = "secret")) + messages_sent = self.comp.stream.sent + self.assertEqual(len(messages_sent), 1) + self.assertEqual(messages_sent[0].get_to(), "user1@test.com") + self.assertEqual(messages_sent[0].get_from(), "account11@jcl.test.com") + self.assertEqual(account11.password, "secret") + self.assertEqual(account11.waiting_password_reply, False) + self.assertEqual(messages_sent[0].get_subject(), \ + "Password will be kept during your Jabber session") + self.assertEqual(messages_sent[0].get_body(), \ + "Password will be kept during your Jabber session") def test_handle_tick(self): self.assertRaises(NotImplementedError, self.comp.handle_tick) - - diff --git a/tests/jcl/jabber/test_feeder.py b/tests/jcl/jabber/test_feeder.py index 992bbda..4fb49ea 100644 --- a/tests/jcl/jabber/test_feeder.py +++ b/tests/jcl/jabber/test_feeder.py @@ -48,6 +48,7 @@ class FeederComponent_TestCase(JCLComponent_TestCase): "localhost", "5347", 'sqlite://' + DB_URL) + self.comp.account_class = Account def tearDown(self): account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL)