def _test_updater(disable): if disable: self.env.config.set('notification','always_notify_updater', 'false') ticket = Ticket(self.env) ticket['reporter'] = '*****@*****.**' ticket['summary'] = u'This is a súmmäry' ticket['cc'] = '*****@*****.**' ticket.insert() ticket['component'] = 'dummy' now = time.time() ticket.save_changes('*****@*****.**', 'This is a change', when=now) tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=False, modtime=now) message = notifysuite.smtpd.get_message() (headers, body) = parse_smtp_message(message) # checks for header existence self.failIf(not headers) # checks for updater in the 'To' recipient list self.failIf('To' not in headers) tolist = [addr.strip() for addr in headers['To'].split(',')] if disable: self.failIf('*****@*****.**' in tolist) else: self.failIf('*****@*****.**' not in tolist)
def test_recipients(self): """To/Cc recipients""" ticket = Ticket(self.env) ticket['reporter'] = '"Joe User" <*****@*****.**>' ticket['owner'] = '*****@*****.**' ticket['cc'] = '[email protected], [email protected], ' \ '*****@*****.**' ticket['summary'] = 'Foo' ticket.insert() tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=True) recipients = notifysuite.smtpd.get_recipients() # checks there is no duplicate in the recipient list rcpts = [] for r in recipients: self.failIf(r in rcpts) rcpts.append(r) # checks that all cc recipients have been notified cc_list = self.env.config.get('notification', 'smtp_always_cc') cc_list = "%s, %s" % (cc_list, ticket['cc']) for r in cc_list.replace(',', ' ').split(): self.failIf(r not in recipients) # checks that owner has been notified self.failIf(smtp_address(ticket['owner']) not in recipients) # checks that reporter has been notified self.failIf(smtp_address(ticket['reporter']) not in recipients)
def _test_default_domain(enabled): self.env.config.set('notification', 'always_notify_owner', 'false') self.env.config.set('notification', 'always_notify_reporter', 'false') self.env.config.set('notification', 'smtp_always_cc', '') ticket = Ticket(self.env) ticket['cc'] = 'joenodom, [email protected]' ticket['summary'] = 'This is a summary' ticket.insert() # Be sure that at least one email address is valid, so that we # send a notification even if other addresses are not valid self.env.config.set('notification', 'smtp_always_cc', '*****@*****.**') if enabled: self.env.config.set('notification', 'smtp_default_domain', 'example.org') tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=True) message = notifysuite.smtpd.get_message() (headers, body) = parse_smtp_message(message) # Msg should always have a 'Cc' field self.failIf('Cc' not in headers) cclist = [addr.strip() for addr in headers['Cc'].split(',')] self.failIf('*****@*****.**' not in cclist) self.failIf('*****@*****.**' not in cclist) if not enabled: self.failIf(len(cclist) != 2) self.failIf('joenodom' in cclist) else: self.failIf(len(cclist) != 3) self.failIf('*****@*****.**' not in cclist)
def create_ticket(env): t = Ticket(env) t["summary"] = "test" t["description"] = "ticket for test" t.insert() assert t.exists return t
def test_email_map(self): """Login-to-email mapping""" self.env.config.set("notification", "always_notify_owner", "true") self.env.config.set("notification", "always_notify_reporter", "true") self.env.config.set("notification", "smtp_always_cc", "*****@*****.**") self.env.known_users = [ ("joeuser", "Joe User", "*****@*****.**"), ("jim@domain", "Jim User", "*****@*****.**"), ] ticket = Ticket(self.env) ticket["reporter"] = "joeuser" ticket["owner"] = "jim@domain" ticket["summary"] = "This is a summary" ticket.insert() tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=True) message = notifysuite.smtpd.get_message() (headers, body) = parse_smtp_message(message) # Msg should always have a 'To' field self.failIf("To" not in headers) tolist = [addr.strip() for addr in headers["To"].split(",")] # 'To' list should have been resolved to the real email address self.failIf("*****@*****.**" not in tolist) self.failIf("*****@*****.**" not in tolist) self.failIf("joeuser" in tolist) self.failIf("jim@domain" in tolist)
def _insert_ticket(self, when=None, **kwargs): ticket = Ticket(self.env) for name, value in kwargs.iteritems(): ticket[name] = value ticket['status'] = 'new' ticket.insert(when=when) return ticket
def _test_short_login(enabled): ticket = Ticket(self.env) ticket["reporter"] = "joeuser" ticket["summary"] = "This is a summary" ticket.insert() # Be sure that at least one email address is valid, so that we # send a notification even if other addresses are not valid self.env.config.set("notification", "smtp_always_cc", "*****@*****.**") if enabled: self.env.config.set("notification", "use_short_addr", "true") tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=True) message = notifysuite.smtpd.get_message() (headers, body) = parse_smtp_message(message) # Msg should not have a 'To' header if not enabled: self.failIf("To" in headers) else: tolist = [addr.strip() for addr in headers["To"].split(",")] # Msg should have a 'Cc' field self.failIf("Cc" not in headers) cclist = [addr.strip() for addr in headers["Cc"].split(",")] if enabled: # Msg should be delivered to the reporter self.failIf(ticket["reporter"] not in tolist) else: # Msg should not be delivered to joeuser self.failIf(ticket["reporter"] in cclist) # Msg should still be delivered to the always_cc list self.failIf(self.env.config.get("notification", "smtp_always_cc") not in cclist)
def _test_short_login(enabled): ticket = Ticket(self.env) ticket['reporter'] = 'joeuser' ticket['summary'] = 'This is a summary' ticket.insert() # Be sure that at least one email address is valid, so that we # send a notification even if other addresses are not valid self.env.config.set('notification', 'smtp_always_cc', '*****@*****.**') if enabled: self.env.config.set('notification', 'use_short_addr', 'true') tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=True) message = notifysuite.smtpd.get_message() (headers, body) = parse_smtp_message(message) # Msg should not have a 'To' header if not enabled: self.failIf('To' in headers) else: tolist = [addr.strip() for addr in headers['To'].split(',')] # Msg should have a 'Cc' field self.failIf('Cc' not in headers) cclist = [addr.strip() for addr in headers['Cc'].split(',')] if enabled: # Msg should be delivered to the reporter self.failIf(ticket['reporter'] not in tolist) else: # Msg should not be delivered to joeuser self.failIf(ticket['reporter'] in cclist) # Msg should still be delivered to the always_cc list self.failIf(self.env.config.get('notification', 'smtp_always_cc') not in cclist)
def test_ignore_domains(self): """Non-SMTP domain exclusion""" self.env.config.set('notification', 'ignore_domains', 'example.com, example.org') self.env.known_users = \ [('*****@*****.**', 'No Email', ''), ('*****@*****.**', 'With Email', '*****@*****.**')] ticket = Ticket(self.env) ticket['reporter'] = '*****@*****.**' ticket['owner'] = '*****@*****.**' ticket['summary'] = 'This is a summary' ticket.insert() tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=True) message = notifysuite.smtpd.get_message() (headers, body) = parse_smtp_message(message) # Msg should always have a 'To' field self.failIf('To' not in headers) tolist = [addr.strip() for addr in headers['To'].split(',')] # 'To' list should not contain addresses with non-SMTP domains self.failIf('*****@*****.**' in tolist) self.failIf('*****@*****.**' in tolist) # 'To' list should have been resolved to the actual email address self.failIf('*****@*****.**' not in tolist) self.failIf(len(tolist) != 1)
def test_props_format_wrap_leftside_unicode(self): self.env.config.set("notification", "mime_encoding", "none") ticket = Ticket(self.env) ticket["summary"] = u"This is a summary" ticket["reporter"] = u"anonymous" ticket["status"] = u"new" ticket["owner"] = u"somebody" ticket["type"] = u"defect" ticket["priority"] = u"major" ticket["milestone"] = u"milestone1" ticket["component"] = ( u"Trac は BSD ライセンスのもとで配" u"布されています。[1:]このライセ" u"ンスの全文は、配布ファイルに" u"含まれている [3:COPYING] ファイル" u"と同じものが[2:オンライン]で参" u"照できます。" ) ticket["version"] = u"2.0" ticket["resolution"] = u"fixed" ticket["keywords"] = u"" ticket.insert() formatted = """\ Reporter: anonymous | Owner: somebody Type: defect | Status: new Priority: major | Milestone: milestone1 Component: Trac は BSD ライセンスのもとで配布 | Version: 2.0 されています。[1:]このライセンスの全文は、配 | Keywords: 布ファイルに含まれている [3:COPYING] ファイル | と同じものが[2:オンライン]で参照できます。 | Resolution: fixed |""" self._validate_props_format(formatted, ticket)
def add_tickets(self, project, customerrequest, tickets, reporter, notify=False): from trac.ticket.notification import TicketNotifyEmail from trac.util.text import exception_to_unicode from penelope.core.models.dashboard import User settings = get_current_registry().settings tracenvs = settings.get('penelope.trac.envs') request = get_current_request() for trac in project.tracs: for t in tickets: owner = DBSession.query(User).get(t['owner']) ticket = {'summary': t['summary'], 'description': t['description'], 'customerrequest': customerrequest.id, 'reporter': reporter.email, 'type': 'task', 'priority': 'major', 'milestone': 'Backlog', 'owner': owner.email, 'status': 'new'} tracenv = Environment('%s/%s' % (tracenvs, trac.trac_name)) tracenv.abs_href.base = trac.api_uri t = Ticket(tracenv) t.populate(ticket) t.insert() if notify: try: tn = TicketNotifyEmail(tracenv) tn.notify(t, newticket=True) except Exception, e: request.add_message('Failure sending notification on creation ' 'of a ticket #%s: %s' % (t.id, exception_to_unicode(e)), 'error')
def create(self, req, summary, description, attributes = {}, notify=False): """ Create a new ticket, returning the ticket ID. PS: Borrowed from XmlRpcPlugin. """ t = Ticket(self.env) t['summary'] = summary t['description'] = description t['reporter'] = req.authname for k, v in attributes.iteritems(): t[k] = v t['status'] = 'new' t['resolution'] = '' t.insert() # Call ticket change listeners ts = TicketSystem(self.env) for listener in ts.change_listeners: listener.ticket_created(t) if notify: try: tn = TicketNotifyEmail(self.env) tn.notify(t, newticket=True) except Exception, e: self.log.exception("Failure sending notification on creation " "of ticket #%s: %s" % (t.id, e))
def _test_default_domain(enabled): self.env.config.set("notification", "always_notify_owner", "false") self.env.config.set("notification", "always_notify_reporter", "false") self.env.config.set("notification", "smtp_always_cc", "") ticket = Ticket(self.env) ticket["cc"] = "joenodom, [email protected]" ticket["summary"] = "This is a summary" ticket.insert() # Be sure that at least one email address is valid, so that we # send a notification even if other addresses are not valid self.env.config.set("notification", "smtp_always_cc", "*****@*****.**") if enabled: self.env.config.set("notification", "smtp_default_domain", "example.org") tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=True) message = notifysuite.smtpd.get_message() (headers, body) = parse_smtp_message(message) # Msg should always have a 'Cc' field self.failIf("Cc" not in headers) cclist = [addr.strip() for addr in headers["Cc"].split(",")] self.failIf("*****@*****.**" not in cclist) self.failIf("*****@*****.**" not in cclist) if not enabled: self.failIf(len(cclist) != 2) self.failIf("joenodom" in cclist) else: self.failIf(len(cclist) != 3) self.failIf("*****@*****.**" not in cclist)
def test_ignore_domains(self): """Non-SMTP domain exclusion""" self.env.config.set("notification", "ignore_domains", "example.com, example.org") self.env.known_users = [ ("*****@*****.**", "No Email", ""), ("*****@*****.**", "With Email", "*****@*****.**"), ] ticket = Ticket(self.env) ticket["reporter"] = "*****@*****.**" ticket["owner"] = "*****@*****.**" ticket["summary"] = "This is a summary" ticket.insert() tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=True) message = notifysuite.smtpd.get_message() (headers, body) = parse_smtp_message(message) # Msg should always have a 'To' field self.failIf("To" not in headers) tolist = [addr.strip() for addr in headers["To"].split(",")] # 'To' list should not contain addresses with non-SMTP domains self.failIf("*****@*****.**" in tolist) self.failIf("*****@*****.**" in tolist) # 'To' list should have been resolved to the actual email address self.failIf("*****@*****.**" not in tolist) self.failIf(len(tolist) != 1)
def create(self, req, summary, description, attributes={}, notify=False): """ Create a new ticket, returning the ticket ID. PS: Borrowed from XmlRpcPlugin. """ if 'product' in attributes: env = self.env.parent or self.env if attributes['product']: env = ProductEnvironment(env, attributes['product']) else: env = self.env t = Ticket(env) t['summary'] = summary t['description'] = description t['reporter'] = req.authname for k, v in attributes.iteritems(): t[k] = v t['status'] = 'new' t['resolution'] = '' t.insert() if notify: try: tn = TicketNotifyEmail(env) tn.notify(t, newticket=True) except Exception, e: self.log.exception("Failure sending notification on creation " "of ticket #%s: %s" % (t.id, e))
def ticket_setup(tc): ticket = Ticket(tc.env) ticket['reporter'] = 'santa' ticket['summary'] = 'This is the summary' ticket.insert() ticket['status'] = 'new' ticket.save_changes('claus', 'set status', 0)
def test_date(self): """Date format compliance (RFC822) we do not support 'military' format""" date_str = r"^((?P<day>\w{3}),\s*)*(?P<dm>\d{2})\s+" \ r"(?P<month>\w{3})\s+(?P<year>200\d)\s+" \ r"(?P<hour>\d{2}):(?P<min>[0-5][0-9])" \ r"(:(?P<sec>[0-5][0-9]))*\s" \ r"((?P<tz>\w{2,3})|(?P<offset>[+\-]\d{4}))$" date_re = re.compile(date_str) # python time module does not detect incorrect time values days = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'] months = ['Jan','Feb','Mar','Apr','May','Jun', \ 'Jul','Aug','Sep','Oct','Nov','Dec'] tz = ['UT','GMT','EST','EDT','CST','CDT','MST','MDT''PST','PDT'] ticket = Ticket(self.env) ticket['reporter'] = '"Joe User" <*****@*****.**>' ticket['summary'] = 'This is a summary' ticket.insert() tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=True) message = notifysuite.smtpd.get_message() (headers, body) = parse_smtp_message(message) self.failIf('Date' not in headers) mo = date_re.match(headers['Date']) self.failIf(not mo) if mo.group('day'): self.failIf(mo.group('day') not in days) self.failIf(int(mo.group('dm')) not in range(1,32)) self.failIf(mo.group('month') not in months) self.failIf(int(mo.group('hour')) not in range(0,24)) if mo.group('tz'): self.failIf(mo.group('tz') not in tz)
def test_date(self): """Date format compliance (RFC822) we do not support 'military' format""" date_str = ( r"^((?P<day>\w{3}),\s*)*(?P<dm>\d{2})\s+" r"(?P<month>\w{3})\s+(?P<year>\d{4})\s+" r"(?P<hour>\d{2}):(?P<min>[0-5][0-9])" r"(:(?P<sec>[0-5][0-9]))*\s" r"((?P<tz>\w{2,3})|(?P<offset>[+\-]\d{4}))$" ) date_re = re.compile(date_str) # python time module does not detect incorrect time values days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] tz = ["UT", "GMT", "EST", "EDT", "CST", "CDT", "MST", "MDT", "PST", "PDT"] ticket = Ticket(self.env) ticket["reporter"] = '"Joe User" <*****@*****.**>' ticket["summary"] = "This is a summary" ticket.insert() tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=True) message = notifysuite.smtpd.get_message() (headers, body) = parse_smtp_message(message) self.failIf("Date" not in headers) mo = date_re.match(headers["Date"]) self.failIf(not mo) if mo.group("day"): self.failIf(mo.group("day") not in days) self.failIf(int(mo.group("dm")) not in range(1, 32)) self.failIf(mo.group("month") not in months) self.failIf(int(mo.group("hour")) not in range(0, 24)) if mo.group("tz"): self.failIf(mo.group("tz") not in tz)
def test_props_format_wrap_bothsides_unicode(self): self.env.config.set("notification", "mime_encoding", "none") self.env.config.set("notification", "ambiguous_char_width", "double") ticket = Ticket(self.env) ticket["summary"] = u"This is a summary" ticket["reporter"] = u"anonymous" ticket["status"] = u"new" ticket["owner"] = u"somebody" ticket["type"] = u"defect" ticket["priority"] = u"major" ticket["milestone"] = u"Trac 在经过修改的BSD协议下发布。" u"[1:]协议的完整文本可以[2:在线查" u"看]也可在发布版的 [3:COPYING] 文" u"件中找到。" ticket["component"] = ( u"Trac は BSD ライセンスのもとで配" u"布されています。[1:]このライセ" u"ンスの全文は、𠀋配布ファイル" u"に含まれている[3:CОPYING]ファイ" u"ルと同じものが[2:オンライン]で" u"参照できます。" ) ticket["version"] = u"2.0" ticket["resolution"] = u"fixed" ticket["keywords"] = u"" ticket.insert() formatted = """\ Reporter: anonymous | Owner: somebody Type: defect | Status: new Priority: major | Milestone: Trac 在经过修改的BSD协 Component: Trac は BSD ライセンス | 议下发布。[1:]协议的完整文本可以[2: のもとで配布されています。[1:]こ | 在线查看]也可在发布版的 [3:COPYING] のライセンスの全文は、𠀋配布ファ | 文件中找到。 イルに含まれている[3:CОPYING]フ | Version: 2.0 ァイルと同じものが[2:オンライン] | Keywords: で参照できます。 | Resolution: fixed |""" self._validate_props_format(formatted, ticket)
def test_mimebody_none_8bit(self): """MIME None encoding resulting in 8bit""" self.env.config.set("notification", "mime_encoding", "none") ticket = Ticket(self.env) ticket["reporter"] = "joe.user" ticket["summary"] = u"This is a summary for Jöe Usèr" ticket.insert() self._validate_mimebody((None, "8bit", "utf-8"), ticket, True)
def test_prop_whitespace_change_is_not_saved(self): ticket = Ticket(self.env) ticket.populate({'summary': 'ticket summary'}) ticket.insert() ticket['summary'] = ' ticket summary ' ticket.save_changes() self.assertEqual(0, len(ticket.get_changelog()))
def _make_tickets(self, num): self.tickets = [] for i in xrange(0, num): ticket = Ticket(self.env) ticket['reporter'] = 'someone' ticket['summary'] = random_sentence() ticket.insert() self.tickets.append(ticket)
def _latest_splits(self, req): """ Return a list of tickets being split from a given ticket as part of an ongoing split action. N.B. This may involve the creation of new tickets required for the split. Where new tickets need to be created, the user will have specified a summary, milestone and component for each one using the 'split' action interface provided by the plugin. :param req: the current trac request object (contains HTTP form data) """ splits = [] # process splits to existing tickets if 'field_split_existing' in req.args: for ticket_id in re.findall(r"\d+", req.args.get("field_split_existing")): # check ticket id is valid by attempting to retrieve the ticket try: ticket = Ticket(self.env, ticket_id) splits.append(int(ticket.id)) except ResourceNotFound: self.log.debug('Split to invalid ticket ID [%s] ignored' % ticket_id) # process splits to new tickets new_tickets = {} # grab all submitted information about new tickets from the request object regex = r'field_split_new_(?P<temp_ticket_id>\d+)_(?P<field_name>\w+)' for field_name, value in req.args.items(): match = re.match(regex, field_name) if match: temp_ticket_id = match.group('temp_ticket_id') field_name = match.group('field_name') field_value = value ticket = new_tickets.setdefault(temp_ticket_id, {}) ticket[field_name] = field_value # create each new ticket required and add it's new ID to the list of splits for _, ticket_values in sorted(new_tickets.items()): ticket = Ticket(self.env) ticket['reporter'] = req.authname ticket['cc'] = '' ticket['version'] = '' ticket['keywords'] = '' ticket['description'] = '' ticket['summary'] = ticket_values['summary'] ticket['milestone'] = ticket_values['milestone'] ticket['component'] = ticket_values['component'] ticket['status'] = 'new' ticket.insert() splits.append(int(ticket.id)) return splits
def test_mimebody_none_8bit(self): """MIME None encoding resulting in 8bit""" self.env.config.set('notification','mime_encoding', 'none') ticket = Ticket(self.env) ticket['reporter'] = 'joe.user' ticket['summary'] = u'This is a summary for Jöe Usèr' ticket.insert() self._validate_mimebody((None, '8bit', 'utf-8'), \ ticket, True)
def test_notification_get_message_id_unicode(self): ticket = Ticket(self.env) ticket["summary"] = "My Summary" ticket["description"] = "Some description" ticket.insert() self.env.config.set("project", "url", u"пиво Müller ") tn = TicketNotifyEmail(self.env) tn.ticket = ticket tn.get_message_id("foo")
def _create_a_ticket(self): # 1. Creating ticket ticket = Ticket(self.env) ticket['reporter'] = 'santa' ticket['summary'] = 'Foo' ticket['description'] = 'Bar' ticket['foo'] = 'This is a custom field' ticket.insert() return ticket
def _create_a_ticket_with_email(self): ticket = Ticket(self.env) ticket['owner'] = '*****@*****.**' ticket['reporter'] = '*****@*****.**' ticket['cc'] = 'cc1, [email protected]' ticket['summary'] = 'Foo' ticket['description'] = 'Bar' ticket.insert() return ticket
def test_can_save_ticket_without_explicit_username(self): ticket = Ticket(self.env) ticket.insert() ticket['summary'] = 'another summary' ticket.save_changes() for change in ticket.get_changelog(): self.assertEqual(None, change[1])
def test_mimebody_none(self): """MIME None/ascii encoding""" self.env.config.set('notification','mime_encoding', 'none') ticket = Ticket(self.env) ticket['reporter'] = '*****@*****.**' ticket['summary'] = u'This is a summary' ticket.insert() self._validate_mimebody((None, '7bit', 'ascii'), \ ticket, True)
def _create_ticket(self, tags, **kwargs): ticket = Ticket(self.env) ticket['keywords'] = u' '.join(sorted(map(to_unicode, tags))) ticket['summary'] = 'summary' ticket['reporter'] = 'admin' for name, value in kwargs.iteritems(): ticket[name] = value ticket.insert() return ticket
def test_delete_milestone_retarget_tickets(self): self.env.db_transaction("INSERT INTO milestone (name) VALUES ('Test')") tkt1 = Ticket(self.env) tkt1.populate({'summary': 'Foo', 'milestone': 'Test'}) tkt1.insert() tkt2 = Ticket(self.env) tkt2.populate({'summary': 'Bar', 'milestone': 'Test'}) tkt2.insert() milestone = Milestone(self.env, 'Test') milestone.delete(retarget_to='Other') self.assertEqual(False, milestone.exists) self.assertEqual('Other', Ticket(self.env, tkt1.id)['milestone']) self.assertEqual('Other', Ticket(self.env, tkt2.id)['milestone'])
def _insert_ticket(self, estimation, owner): ticket = Ticket(self.env) ticket['summary'] = 'Test Ticket' ticket['owner'] = owner ticket['hours_remaining'] = estimation ticket['milestone'] = 'milestone1' return ticket.insert()
def test_update_milestone_update_tickets(self): self.env.db_transaction("INSERT INTO milestone (name) VALUES ('Test')") tkt1 = Ticket(self.env) tkt1.populate({'summary': 'Foo', 'milestone': 'Test'}) tkt1.insert() tkt2 = Ticket(self.env) tkt2.populate({'summary': 'Bar', 'milestone': 'Test'}) tkt2.insert() milestone = Milestone(self.env, 'Test') milestone.name = 'Testing' milestone.update() self.assertEqual('Testing', Ticket(self.env, tkt1.id)['milestone']) self.assertEqual('Testing', Ticket(self.env, tkt2.id)['milestone'])
def test_new_component_has_no_owner(self): """Ticket is not disowned when the component is changed to a component with no owner. """ self._add_component('component3', 'cowner3') self._add_component('component4', '') ticket = Ticket(self.env) ticket.populate({ 'reporter': 'reporter1', 'summary': 'the summary', 'component': 'component3', 'owner': 'cowner3', 'status': 'new', }) tkt_id = ticket.insert() req = self._create_request(method='POST', args={ 'id': tkt_id, 'field_component': 'component4', 'submit': True, 'action': 'leave', 'view_time': str(to_utimestamp(ticket['changetime'])), }) self.assertRaises(RequestDone, self.ticket_module.process_request, req) ticket = Ticket(self.env, tkt_id) self.assertEqual('component4', ticket['component']) self.assertEqual('cowner3', ticket['owner'])
def _insert_ticket(self, estimation): ticket = Ticket(self.env) ticket['summary'] = 'Test Ticket' ticket['hours_remaining'] = estimation ticket['milestone'] = 'milestone1' ticket['status'] = 'new' return ticket.insert()
def test_owner_from_component(self): """ Verify that the owner of a new ticket is set to the owner of the component. """ component = Component(self.env) component.name = 'test' component.owner = 'joe' component.insert() ticket = Ticket(self.env) ticket['reporter'] = 'santa' ticket['summary'] = 'Foo' ticket['component'] = 'test' ticket.insert() self.assertEqual('joe', ticket['owner'])
def new_another_ticket(self, env, ticket, comment, author, trac): project_name = self.env.config.get('project', 'name') project_url = self.env.config.get('project', 'url') tkt_id = ticket.id tkt = Ticket(env) self.log.info("Ticket_id: %s" % (ticket.id)) tkt['status'] = 'new' tkt['reporter'] = project_name tkt['summary'] = '[' + project_name + '] #' + str( tkt_id) + ": " + ticket['summary'] description = ticket['description'] if not comment == "": comment = "\n * Comment: \n" + comment + "\n" ticket_url = "\n * Ticket: " + "[" + project_url + "/ticket/" + str( tkt_id) + " " + project_name + "]" tkt['description'] = description + comment + ticket_url if ticket['taskstatus']: tkt['taskstatus'] = trac['newstatus'] self.log.info("tkt.insert") another_tkt_id = tkt.insert() if another_tkt_id: self._insert_anotherticket(env, another_tkt_id, project_name, tkt_id) self.notify(env, tkt, True) return another_tkt_id else: return False
def test_no_recipient(self): """No recipient case""" self.env.config.set('notification', 'smtp_always_cc', '') ticket = Ticket(self.env) ticket['reporter'] = 'anonymous' ticket['summary'] = 'Foo' ticket.insert() tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=True) sender = notifysuite.smtpd.get_sender() recipients = notifysuite.smtpd.get_recipients() message = notifysuite.smtpd.get_message() # checks that no message has been sent self.failIf(recipients) self.failIf(sender) self.failIf(message)
def test_multiline_header(self): """Encoded headers split into multiple lines""" self.env.config.set('notification', 'mime_encoding', 'qp') ticket = Ticket(self.env) ticket['reporter'] = '*****@*****.**' # Forces non-ascii characters ticket['summary'] = u'A_very %s súmmäry' % u' '.join(['long'] * 20) ticket.insert() tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=True) message = notifysuite.smtpd.get_message() (headers, body) = parse_smtp_message(message) # Discards the project name & ticket number subject = headers['Subject'] summary = subject[subject.find(':') + 2:] self.failIf(ticket['summary'] != summary)
def test_no_disown_from_changed_component(self): """ Verify that a ticket is not disowned when the component is changed to a non-assigned component. """ component1 = Component(self.env) component1.name = 'test1' component1.owner = 'joe' component1.insert() component2 = Component(self.env) component2.name = 'test2' component2.owner = '' component2.insert() ticket = Ticket(self.env) ticket['reporter'] = 'santa' ticket['summary'] = 'Foo' ticket['component'] = 'test1' ticket['status'] = 'new' tktid = ticket.insert() ticket = Ticket(self.env, tktid) ticket['component'] = 'test2' ticket.save_changes('jane', 'Testing') self.assertEqual('joe', ticket['owner'])
def _insert_ticket(self, summary, **kw): """Helper for inserting a ticket into the database""" ticket = Ticket(self.env) ticket['summary'] = summary for k, v in kw.items(): ticket[k] = v return ticket.insert()
def test_old_owner_not_old_component_owner(self): """New ticket owner is not updated if old owner is not the owner of the old component. """ self._add_component('component3', 'cowner3') self._add_component('component4', 'cowner4') ticket = Ticket(self.env) ticket.populate({ 'reporter': 'reporter1', 'summary': 'the summary', 'component': 'component3', 'owner': 'owner1', 'status': 'new', }) tkt_id = ticket.insert() req = self._create_request(method='POST', args={ 'id': tkt_id, 'field_component': 'component4', 'submit': True, 'action': 'leave', 'view_time': str(to_utimestamp(ticket['changetime'])), }) self.assertRaises(RequestDone, self.ticket_module.process_request, req) ticket = Ticket(self.env, tkt_id) self.assertEqual('component4', ticket['component']) self.assertEqual('owner1', ticket['owner'])
def test_owner_from_changed_component(self): """ Verify that the owner of a new ticket is updated when the component is changed. """ component1 = Component(self.env) component1.name = 'test1' component1.owner = 'joe' component1.insert() component2 = Component(self.env) component2.name = 'test2' component2.owner = 'kate' component2.insert() ticket = Ticket(self.env) ticket['reporter'] = 'santa' ticket['summary'] = 'Foo' ticket['component'] = 'test1' ticket['status'] = 'new' tktid = ticket.insert() ticket = Ticket(self.env, tktid) ticket['component'] = 'test2' ticket.save_changes('jane', 'Testing') self.assertEqual('kate', ticket['owner'])
def test_props_format_wrap_bothsides(self): self.env.config.set('notification', 'mime_encoding', 'none') ticket = Ticket(self.env) ticket['summary'] = u'This is a summary' ticket['reporter'] = u'anonymous' ticket['status'] = u'new' ticket['owner'] = u'somebody' ticket['type'] = u'defect' ticket['priority'] = u'major' ticket['milestone'] = u'Lorem ipsum dolor sit amet, consectetur ' \ u'adipisicing elit, sed do eiusmod tempor ' \ u'incididunt ut labore et dolore magna ' \ u'aliqua. Ut enim ad minim veniam, quis ' \ u'nostrud exercitation ullamco laboris nisi ' \ u'ut aliquip ex ea commodo consequat. Duis ' \ u'aute irure dolor in reprehenderit in ' \ u'voluptate velit esse cillum dolore eu ' \ u'fugiat nulla pariatur. Excepteur sint ' \ u'occaecat cupidatat non proident, sunt in ' \ u'culpa qui officia deserunt mollit anim id ' \ u'est laborum.' ticket['component'] = ticket['milestone'] ticket['version'] = u'2.0' ticket['resolution'] = u'fixed' ticket['keywords'] = u'' ticket.insert() formatted = """\ Reporter: anonymous | Owner: somebody Type: defect | Status: new Priority: major | Milestone: Lorem ipsum dolor sit Component: Lorem ipsum dolor sit | amet, consectetur adipisicing elit, amet, consectetur adipisicing | sed do eiusmod tempor incididunt ut elit, sed do eiusmod tempor | labore et dolore magna aliqua. Ut incididunt ut labore et dolore | enim ad minim veniam, quis nostrud magna aliqua. Ut enim ad minim | exercitation ullamco laboris nisi veniam, quis nostrud exercitation | ut aliquip ex ea commodo consequat. ullamco laboris nisi ut aliquip | Duis aute irure dolor in ex ea commodo consequat. Duis | reprehenderit in voluptate velit aute irure dolor in reprehenderit | esse cillum dolore eu fugiat nulla in voluptate velit esse cillum | pariatur. Excepteur sint occaecat dolore eu fugiat nulla pariatur. | cupidatat non proident, sunt in Excepteur sint occaecat cupidatat | culpa qui officia deserunt mollit non proident, sunt in culpa qui | anim id est laborum. officia deserunt mollit anim id | Version: 2.0 est laborum. | Keywords: Resolution: fixed |""" self._validate_props_format(formatted, ticket)
def macro_setup(tc): tc.env = EnvironmentStub(enable=( 'trac.*', 'tracopt.ticket.commit_updater.*', )) ticket = Ticket(tc.env) ticket['summary'] = 'the summary' ticket['status'] = 'new' ticket.insert() def _get_repository(reponame): return Mock(get_changeset=_get_changeset, resource=None) def _get_changeset(rev=None): return Mock(message="the message. refs #1. ", rev=rev) setattr(RepositoryManager(tc.env), 'get_repository', _get_repository)
def test_status_change_with_operation(self): """Status change with operation.""" ticket = Ticket(self.env) ticket['new'] = 'status1' ticket['owner'] = 'user1' ticket.insert() req = MockRequest(self.env, path_info='/ticket', authname='user2', method='POST') label, control, hints = \ self.ctlr.render_ticket_action_control(req, ticket, 'accept') self.assertEqual('accept', label) self.assertEqual('', unicode(control)) self.assertEqual("The owner will be <span class=\"trac-author-user\">" "user2</span>. The status will be 'accepted'.", unicode(hints))
def test_status_change_with_no_operation(self): """Existing ticket status change with no operation.""" config = self.env.config config.set('ticket-workflow', 'change_status', 'status1 -> status2') self._reload_workflow() ticket = Ticket(self.env) ticket['status'] = 'status1' ticket.insert() req = MockRequest(self.env, path_info='/ticket', method='POST') label, control, hints = \ self.ctlr.render_ticket_action_control(req, ticket, 'change_status') self.assertEqual('change status', label) self.assertEqual('', unicode(control)) self.assertEqual("Next status will be 'status2'.", unicode(hints))
def comment_setup(tc): ticket1 = Ticket(tc.env) ticket1.values.update({ 'reporter': 'santa', 'summary': 'This is the summary for ticket 1', 'status': 'new' }) ticket1.insert() ticket1.save_changes(comment='This is the comment for ticket 1') ticket2 = Ticket(tc.env) ticket2.values.update({ 'reporter': 'claws', 'summary': 'This is the summary for ticket 2', 'status': 'closed' }) ticket2.insert() ticket2.save_changes(comment='This is the comment for ticket 2')
def run_bcc_feature(public): # CC list should be private self.env.config.set('notification', 'use_public_cc', 'true' if public else 'false') self.env.config.set('notification', 'smtp_always_bcc', '*****@*****.**') ticket = Ticket(self.env) ticket['reporter'] = '"Joe User" <*****@*****.**>' ticket['summary'] = 'This is a summary' ticket.insert() tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=True) message = notifysuite.smtpd.get_message() (headers, body) = parse_smtp_message(message) if public: # Msg should have a To list self.failIf('To' not in headers) # Extract the list of 'To' recipients from the message to = [rcpt.strip() for rcpt in headers['To'].split(',')] else: # Msg should not have a To list self.failIf('To' in headers) # Extract the list of 'To' recipients from the message to = [] # Extract the list of 'Cc' recipients from the message cc = [rcpt.strip() for rcpt in headers['Cc'].split(',')] # Extract the list of the actual SMTP recipients rcptlist = notifysuite.smtpd.get_recipients() # Build the list of the expected 'Cc' recipients ccrcpt = self.env.config.get('notification', 'smtp_always_cc') cclist = [ccr.strip() for ccr in ccrcpt.split(',')] for rcpt in cclist: # Each recipient of the 'Cc' list should appear # in the 'Cc' header self.failIf(rcpt not in cc) # Check the message has actually been sent to the recipients self.failIf(rcpt not in rcptlist) # Build the list of the expected 'Bcc' recipients bccrcpt = self.env.config.get('notification', 'smtp_always_bcc') bcclist = [bccr.strip() for bccr in bccrcpt.split(',')] for rcpt in bcclist: # Check none of the 'Bcc' recipients appears # in the 'To' header self.failIf(rcpt in to) # Check the message has actually been sent to the recipients self.failIf(rcpt not in rcptlist)
def test_delete_milestone_retarget_tickets(self): cursor = self.db.cursor() cursor.execute("INSERT INTO milestone (name) VALUES ('Test')") cursor.close() tkt1 = Ticket(self.env) tkt1.populate({'summary': 'Foo', 'milestone': 'Test'}) tkt1.insert() tkt2 = Ticket(self.env) tkt2.populate({'summary': 'Bar', 'milestone': 'Test'}) tkt2.insert() milestone = Milestone(self.env, 'Test') milestone.delete(retarget_to='Other') self.assertEqual('Other', Ticket(self.env, tkt1.id)['milestone']) self.assertEqual('Other', Ticket(self.env, tkt2.id)['milestone'])
def test_update_milestone_update_tickets(self): cursor = self.db.cursor() cursor.execute("INSERT INTO milestone (name) VALUES ('Test')") cursor.close() tkt1 = Ticket(self.env) tkt1.populate({'summary': 'Foo', 'milestone': 'Test'}) tkt1.insert() tkt2 = Ticket(self.env) tkt2.populate({'summary': 'Bar', 'milestone': 'Test'}) tkt2.insert() milestone = Milestone(self.env, 'Test') milestone.name = 'Testing' milestone.update() self.assertEqual('Testing', Ticket(self.env, tkt1.id)['milestone']) self.assertEqual('Testing', Ticket(self.env, tkt2.id)['milestone'])
def test_leave_operation(self): ticket = Ticket(self.env) ticket['status'] = 'assigned' ticket['owner'] = 'user2' ticket.insert() req = MockRequest(self.env, path_info='/ticket', method='POST', authname='user1') label, control, hints = \ self.ctlr.render_ticket_action_control(req, ticket, 'leave') self.assertEqual('leave', label) self.assertEqual('as assigned', unicode(control)) self.assertEqual( 'The owner will remain <span class="trac-author">' 'user2</span>.', unicode(hints))
def test_undefined_resolutions(self): config = self.env.config['ticket-workflow'] ticket = Ticket(self.env) ticket.populate({'summary': '#12882', 'status': 'new'}) ticket.insert() req = MockRequest(self.env, path_info='/ticket/%d' % ticket.id) config.set('resolve.set_resolution', 'fixed,invalid,wontfix,,duplicate,worksforme,,,,,') self._reload_workflow() self.ctlr.render_ticket_action_control(req, ticket, 'resolve') config.set('resolve.set_resolution', 'undefined,fixed') self._reload_workflow() try: self.ctlr.render_ticket_action_control(req, ticket, 'resolve') self.fail('ConfigurationError not raised') except ConfigurationError as e: self.assertIn('but uses undefined resolutions', unicode(e))
def test_ticket_comment(self): ticket = Ticket(self.env) ticket['status'] = 'new' ticket['summary'] = 'Blah blah blah' ticket['reporter'] = 'joe' ticket['description'] = '`<<<Description>>>`' ticket.insert() ticket.save_changes(author='anonymous', comment='`>>>first comment<<<`') ticket.save_changes(author='anonymous', comment='`>>>second comment<<<`') url = 'http://localhost/ticket/%d#comment:2' % ticket.id orig = message % {'url': url} result = self.mod.substitute_message(orig, ignore_exc=False) self.assertNotEqual(result, orig) self.assertTrue('\nContent-Type: text/html;' in result) self.assertTrue('<<<Description>>>' in result) self.assertFalse('>>>first comment<<<' in result) self.assertTrue('>>>second comment<<<' in result)
def testComplexNotification(self): env = Environment(ENV) ticket1 = Ticket(env) ticket1['summary'] = 'Fix please.' ticket1['status'] = 'new' ticket1_id = ticket1.insert() ticket2 = Ticket(env) ticket2['summary'] = 'This one too, thanks.' ticket2['status'] = 'new' ticket2_id = ticket2.insert() ticket1 = Ticket(env, ticket1_id) self.assertEqual(ticket1['status'], 'new') self.assertEqual(ticket1['resolution'], '') ticket2 = Ticket(env, ticket2_id) self.assertEqual(ticket2['status'], 'new') self.assertEqual(ticket2['resolution'], '') message1 = "Fix #%d: you're welcome." % ticket1_id self.makeGitCommit(ALTGIT, 'newfile', 'with some new content', message1) message2 = "See #%d: you bet." % ticket2_id self.makeGitCommit(ALTGIT, 'newfile', 'with improved content', message2) self.openGitHubHook(2, 'alt') ticket1 = Ticket(env, ticket1_id) self.assertEqual(ticket1['status'], 'closed') self.assertEqual(ticket1['resolution'], 'fixed') changelog1 = ticket1.get_changelog() self.assertEqual(len(changelog1), 4) self.assertEqual(changelog1[0][2], 'comment') self.assertIn("you're welcome", changelog1[0][4]) ticket2 = Ticket(env, ticket2_id) self.assertEqual(ticket2['status'], 'new') self.assertEqual(ticket2['resolution'], '') changelog2 = ticket2.get_changelog() self.assertEqual(len(changelog2), 1) self.assertEqual(changelog2[0][2], 'comment') self.assertIn("you bet", changelog2[0][4])
def ticket_setup(tc): tc.env.config.set('ticket-custom', 'project', 'text') ticket = Ticket(tc.env) ticket.values.update({ 'reporter': 'santa', 'summary': 'This is the summary', 'status': 'new', 'project': 'xmas' }) ticket.insert() ticket = Ticket(tc.env) ticket.values.update({ 'owner': 'elf', 'summary': 'This is another summary', 'status': 'assigned' }) ticket.insert() ticket = Ticket(tc.env) ticket.values.update({ 'owner': 'santa', 'summary': 'This is th third summary', 'status': 'closed', 'project': 'xmas' }) ticket.insert() tc.env.config.set('milestone-groups', 'closed.status', 'closed') tc.env.config.set('milestone-groups', 'closed.query_args', 'group=resolution,order=time') tc.env.config.set('milestone-groups', 'closed.overall_completion', 'true') tc.env.config.set('milestone-groups', 'active.status', '*') tc.env.config.set('milestone-groups', 'active.css_class', 'open')
def create(self, req, summary, description, attributes={}, notify=False): """ Create a new ticket, returning the ticket ID. PS: Borrowed from XmlRpcPlugin. """ t = Ticket(self.env) t['summary'] = summary t['description'] = description t['reporter'] = req.authname for k, v in attributes.iteritems(): t[k] = v t['status'] = 'new' t['resolution'] = '' t.insert() if notify: try: tn = TicketNotifyEmail(self.env) tn.notify(t, newticket=True) except Exception, e: self.log.exception("Failure sending notification on creation " "of ticket #%s: %s" % (t.id, e))
def test_email_map(self): """Login-to-email mapping""" self.env.config.set('notification', 'always_notify_owner', 'false') self.env.config.set('notification', 'always_notify_reporter', 'true') self.env.config.set('notification', 'smtp_always_cc', '*****@*****.**') self.env.known_users = [('joeuser', 'Joe User', '*****@*****.**') ] ticket = Ticket(self.env) ticket['reporter'] = 'joeuser' ticket['summary'] = 'This is a summary' ticket.insert() tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=True) message = notifysuite.smtpd.get_message() (headers, body) = parse_smtp_message(message) # Msg should always have a 'To' field self.failIf('To' not in headers) tolist = [addr.strip() for addr in headers['To'].split(',')] # 'To' list should have been resolved to the real email address self.failIf('*****@*****.**' not in tolist) self.failIf('joeuser' in tolist)
def test_structure(self): """Basic SMTP message structure (headers, body)""" ticket = Ticket(self.env) ticket['reporter'] = '"Joe User" <*****@*****.**>' ticket['owner'] = '*****@*****.**' ticket['cc'] = '[email protected], [email protected], ' \ '*****@*****.**' ticket['summary'] = 'This is a summary' ticket.insert() tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=True) message = notifysuite.smtpd.get_message() (headers, body) = parse_smtp_message(message) # checks for header existence self.failIf(not headers) # checks for body existance self.failIf(not body) # checks for expected headers self.failIf('Date' not in headers) self.failIf('Subject' not in headers) self.failIf('Message-ID' not in headers) self.failIf('From' not in headers)
def test_props_format_obfuscated_email(self): self.env.config.set('notification', 'mime_encoding', 'none') ticket = Ticket(self.env) ticket['summary'] = u'This is a summary' ticket['reporter'] = u'*****@*****.**' ticket['status'] = u'new' ticket['owner'] = u'*****@*****.**' ticket['type'] = u'defect' ticket['priority'] = u'major' ticket['milestone'] = u'milestone1' ticket['component'] = u'component1' ticket['version'] = u'2.0' ticket['resolution'] = u'fixed' ticket['keywords'] = u'' ticket.insert() formatted = """\ Reporter: joe@… | Owner: joe.bar@… Type: defect | Status: new Priority: major | Milestone: milestone1 Component: component1 | Version: 2.0 Resolution: fixed | Keywords:""" self._validate_props_format(formatted, ticket)