Testing corner cases
====================

Let's test some corner cases here that would otherwise clutter the
README.rst too much.

First some setup::

  >>> from zope.component import getGlobalSiteManager
  >>> from zope.annotation.attribute import AttributeAnnotations
  >>> from zope.annotation.interfaces import IAnnotations
  >>> from collective.watcherlist.watchers import WatcherList
  >>> class Dummy(object):
  ...     pass
  >>> sm = getGlobalSiteManager()
  >>> sm.registerAdapter(AttributeAnnotations, (Dummy, ), IAnnotations)

With that out of the way, we create a dummy object and a watcherlist
for it::

  >>> dummy = Dummy()
  >>> wl = WatcherList(dummy)
  >>> wl.addresses
  ()

We take care to let the watchers always be a PersistentList::

  >>> wl.watchers
  []
  >>> type(wl.watchers)
  <class 'persistent.list.PersistentList'>
  >>> wl.watchers = ['joe@example.org', 'mary@example.org']
  >>> wl.watchers
  ['joe@example.org', 'mary@example.org']
  >>> type(wl.watchers)
  <class 'persistent.list.PersistentList'>

The extra_addresses should also be persistent::

  >>> wl.extra_addresses
  []
  >>> type(wl.extra_addresses)
  <class 'persistent.list.PersistentList'>
  >>> wl.extra_addresses = ['list@example.org']
  >>> wl.extra_addresses
  ['list@example.org']
  >>> type(wl.extra_addresses)
  <class 'persistent.list.PersistentList'>

The send_emails property should always be a boolean::

  >>> wl.send_emails
  True
  >>> wl.send_emails = 0
  >>> wl.send_emails
  False
  >>> wl.addresses
  ()
  >>> wl.send_emails = 1
  >>> wl.send_emails
  True
  >>> sorted(wl.addresses)
  ['joe@example.org', 'list@example.org', 'mary@example.org']

We have no Plone Site setup, so no members can watch::

  >>> wl.isWatching()
  False
  >>> wl.toggle_watching()
  >>> wl.isWatching()
  False

Let's see if we can mock a portal_membership tool into these tests,
including mock users::

  >>> from persistent.interfaces import IPersistent
  >>> from zope.interface import classImplements

  >>> class MockMemship(object):
  ...     current_member_name = None
  ...     members = []
  ...     def isAnonymousUser(self):
  ...         return not bool(self.current_member_name)
  ...     def getMemberById(self, member_id):
  ...         for member in self.members:
  ...             if member.username == member_id:
  ...                 return member
  ...     def getAuthenticatedMember(self):
  ...         return self.getMemberById(self.current_member_name)
  ...     def login(self, username):
  ...         self.current_member_name = username
  ...     def logout(self):
  ...         self.current_member_name = None
  >>> classImplements(MockMemship, IPersistent)
  >>> memship = MockMemship()
  >>> dummy.portal_membership = memship

We add some members::

  >>> class MockMember(object):
  ...     def __init__(self, username):
  ...         self.username = username
  ...     def getId(self):
  ...         return self.username
  ...     def getProperty(self, propname):
  ...         if propname == 'email':
  ...             return self.username + '@example.org'
  >>> memship.members.append(MockMember('pete'))
  >>> memship.members.append(MockMember('ann'))

We check that anonymous and authenticated users get reported
correctly::

  >>> dummy.portal_membership.isAnonymousUser()
  True
  >>> dummy.portal_membership.getAuthenticatedMember()
  >>> memship.login('pete')
  >>> dummy.portal_membership.isAnonymousUser()
  False
  >>> member = dummy.portal_membership.getAuthenticatedMember()
  >>> member
  <MockMember object at ...>
  >>> member.username
  'pete'
  >>> member.getProperty('email')
  'pete@example.org'

We log out again::

  >>> memship.logout()
  >>> dummy.portal_membership.isAnonymousUser()
  True
  >>> dummy.portal_membership.getAuthenticatedMember()

Let's try toggle watching again; as anonymous this still has no effect::

  >>> wl.isWatching()
  False
  >>> wl.toggle_watching()
  >>> wl.isWatching()
  False

Now toggle watching as authenticated member::

  >>> memship.login('pete')
  >>> wl.isWatching()
  False
  >>> wl.toggle_watching()
  >>> wl.isWatching()
  True
  >>> wl.watchers
  ['joe@example.org', 'mary@example.org', 'pete']
  >>> sorted(wl.addresses)
  ['joe@example.org', 'list@example.org', 'mary@example.org', 'pete@example.org']
  >>> wl.toggle_watching()
  >>> sorted(wl.watchers)
  ['joe@example.org', 'mary@example.org']
  >>> sorted(wl.addresses)
  ['joe@example.org', 'list@example.org', 'mary@example.org']

In Products.Poi the issues have their own 'watchers' field.  This
returns a tuple instead of a list.  We respect that::

  >>> class PoiLikeWatcherList(WatcherList):
  ...     _watchers = tuple()
  ...     def __get_watchers(self):
  ...         return self._watchers
  ...     def __set_watchers(self, v):
  ...         self._watchers = v
  ...     watchers = property(__get_watchers, __set_watchers)
  >>> dummy2 = Dummy()
  >>> poilist = PoiLikeWatcherList(dummy2)
  >>> poilist.watchers
  ()
  >>> dummy2.portal_membership = memship
  >>> poilist.toggle_watching()
  >>> poilist.watchers
  ('pete',)
  >>> poilist.addresses
  ('pete@example.org',)
  >>> poilist.toggle_watching()
  >>> poilist.watchers
  ()
  >>> poilist.addresses
  ()

Let's test some utility functions a bit more.  Get a safe unicode
version of a string or return unchanged; basically a small shim around
safe_text from Plone, with the default charset set correctly::

  >>> from collective.watcherlist.utils import su
  >>> su(None)
  >>> su("hi")
  'hi'
  >>> su(u"hi")
  'hi'

Get the email of a member, with a few fallbacks::

  >>> from collective.watcherlist.utils import get_member_email
  >>> get_member_email(None, memship)
  'pete@example.org'
  >>> get_member_email('ann', memship)
  'ann@example.org'
  >>> get_member_email('no-one', memship)
  >>> get_member_email('address@example.com', memship)
  'address@example.com'

Allegedly, CMFMember can give some problems, so we check it::

  >>> from AccessControl import Unauthorized
  >>> class MockField(object):
  ...     def getAccessor(self, instance):
  ...         return lambda: instance.username + '@example.org'
  >>> class MockCMFMember(MockMember):
  ...     def getProperty(self, propname):
  ...         if propname == 'email':
  ...             raise Unauthorized
  ...     def getField(self, name):
  ...         if name == 'email':
  ...             return MockField()
  >>> memship.members.append(MockCMFMember('cmf'))
  >>> get_member_email('cmf', memship)
  'cmf@example.org'
