async def test_publish_stream(self) -> None: activity = self.make_activity() stream = activity.stream() observed = [] closed = False async def observe() -> None: nonlocal closed async for event in stream: observed.append(event) closed = True self.io_loop.add_callback(observe) # Scheduled coroutines are run in the next IO loop iteration but one await sleep(0) await sleep(0) events = [ Event.create('meow', None, app=self.app), # type:ignore Event.create('woof', None, app=self.app) # type:ignore ] # type: List[Event] activity.publish(events[0]) # type:ignore activity.publish(events[1]) # type:ignore await sleep(0) await stream.aclose() await sleep(0) self.assertEqual(observed, events) self.assertTrue(closed)
def create_meeting(self, title, time=None, location=None, description=None): """See :http:post:`/api/meetings`.""" if not self.user: raise PermissionError() e = InputError() if not str_or_none(title): e.errors['title'] = 'empty' e.trigger() meeting = Meeting(id='Meeting:' + randstr(), trashed=False, app=self, authors=[self.user.id], title=title, time=time.isoformat() + 'Z' if time else None, location=str_or_none(location), description=str_or_none(description)) self.r.oset(meeting.id, meeting) self.r.rpush('meetings', meeting.id) self.activity.publish( Event.create('create-meeting', None, {'meeting': meeting}, app=self)) return meeting
def uncheck(self): """See :http:post:`/api/lists/(list-id)/items/(id)/uncheck`.""" _check_feature(self.app.user, 'check', self) self._check_permission(self.app.user, 'item-modify') self.checked = False self.app.r.oset(self.id, self) self.list.activity.publish(Event.create('item-uncheck', self, app=self.app))
async def test_send_device_notification_invalid_push_subscription(self) -> None: push_subscription = cast(Dict[str, str], json.loads(self.push_subscription)) push_subscription['endpoint'] += 'foo' push_subscription = json.dumps(push_subscription) with self.assertRaisesRegex(ValueError, 'push_subscription_invalid'): await self.app.send_device_notification(push_subscription, Event.create('meow', None, app=self.app))
async def _create(self, title, *, text=None, resource=None, location=None): # pylint: disable=protected-access; List is a friend self.host[0]._check_permission(self.app.user, 'list-modify') attrs = await WithContent.process_attrs( { 'text': text, 'resource': resource }, app=self.app) if str_or_none(title) is None: raise micro.ValueError('title_empty') item = Item(id='Item:{}'.format(randstr()), app=self.app, authors=[self.app.user.id], trashed=False, text=attrs['text'], resource=attrs['resource'], list_id=self.host[0].id, title=title, location=location.json() if location else None, checked=False) self.app.r.oset(item.id, item) self.app.r.rpush(self.map_key, item.id) self.host[0].activity.publish( Event.create('list-create-item', self.host[0], {'item': item}, self.app)) return item
async def test_send_device_notification_invalid_push_subscription_host(self) -> None: push_subscription = json.dumps({ # type: ignore[misc] **cast(Dict[str, str], json.loads(self.push_subscription)), 'endpoint': 'http://example.invalid' }) with self.assertRaises(CommunicationError): await self.app.send_device_notification(push_subscription, Event.create('meow', None, app=self.app))
def unvote(self, *, user): """See :http:delete:`/api/lists/(list-id)/items/(id)/votes/user`.""" if not user: raise PermissionError() if 'vote' not in self.item.list.features: raise error.ValueError('Disabled item list features vote') if self.app.r.zrem(self.ids.key, user.id.encode()): self.item.list.activity.publish( Event.create('item-votes-unvote', self.item, app=self.app))
async def test_notify_invalid_push_subscription(self): await self.user.enable_device_notifications(self.push_subscription) self.user.push_subscription = 'foo' self.app.login() self.user.notify(Event.create('test', None, app=self.app)) # Scheduled coroutines are run in the next IO loop iteration but one await sleep(0) await sleep(0) self.assertEqual(self.user.device_notification_status, 'off.expired')
def test_publish(self, notify): activity = self.make_activity() activity.subscribe() self.app.login() activity.subscribe() event = Event.create('meow', None, app=self.app) activity.publish(event) self.assertIn(event, activity) notify.assert_called_once_with(self.user, event)
async def test_notify_invalid_push_subscription(self) -> None: device = self.user.devices[0] await device.enable_notifications(self.push_subscription) device.push_subscription = 'foo' context.user.set(self.app.devices.sign_in().user) self.user.notify(Event.create('test', None, app=self.app)) # Scheduled coroutines are run in the next IO loop iteration but one await sleep(0) await sleep(0) self.assertEqual(device.notification_status, 'off.expired')
def unassign(self, assignee, *, user): """See :http:delete:`/api/lists/(list-id)/items/(id)/assignees/(assignee-id)`.""" # pylint: disable=protected-access; Item is a friend self.item._check_permission(user, 'list-modify') if 'assign' not in self.item.list.features: raise error.ValueError('Disabled item list features assign') if self.item.trashed: raise error.ValueError('Trashed item') if not self.app.r.zrem(self.ids.key, assignee.id.encode()): raise error.ValueError( 'No assignee {} in assignees of item {}'.format(assignee.id, self.item.id)) self.item.list.activity.publish( Event.create('item-assignees-unassign', self.item, detail={'assignee': assignee}, app=self.app))
async def create(self, text: Optional[str], resource: Optional[str]) -> Greeting: """Create a :class:`Greeting` and return it.""" user = context.user.get() if not user: raise error.PermissionError() attrs = await WithContent.process_attrs({'text': text, 'resource': resource}, app=self.app) if not (attrs['text'] or attrs['resource']): raise error.ValueError('No text and resource') greeting = Greeting( id='Greeting:{}'.format(randstr()), app=self.app, authors=[user.id], text=attrs['text'], resource=attrs['resource']) self.app.r.oset(greeting.id, greeting) self.app.r.lpush(self.ids.key, greeting.id) self.app.activity.publish( Event.create('greetings-create', None, detail={'greeting': greeting}, app=self.app)) return greeting
def create(self, use_case=None, description=None, title=None, v=1): """See :http:post:`/api/lists`.""" if v == 1: # create(title, description=None) title = title or use_case if title is None: raise TypeError() lst = self.create('simple', v=2) lst.edit(title=title, description=description) return lst if v == 2: # create(use_case='simple') use_case = use_case or 'simple' else: raise NotImplementedError() if not self.app.user: raise PermissionError() if use_case not in _USE_CASES: raise micro.ValueError('use_case_unknown') data = _USE_CASES[use_case] id = 'List:{}'.format(randstr()) lst = List(id=id, app=self.app, authors=[self.app.user.id], title=data['title'], description=None, features=data['features'], mode='collaborate', activity=Activity('{}.activity'.format(id), self.app, subscriber_ids=[])) self.app.r.oset(lst.id, lst) self.app.r.rpush(self.map_key, lst.id) self.app.user.lists.add(lst, user=self.app.user) self.app.activity.publish( Event.create('create-list', None, {'lst': lst}, app=self.app)) return lst
def create(self, use_case='simple', *, v=2): """See :http:post:`/api/lists`.""" # pylint: disable=unused-argument; former feature toggle # Compatibility for endpoint version (deprecated since 0.22.0) if not self.app.user: raise PermissionError() if use_case not in _USE_CASES: raise micro.ValueError('use_case_unknown') data = _USE_CASES[use_case] id = 'List:{}'.format(randstr()) lst = List( id=id, app=self.app, authors=[self.app.user.id], title=data['title'], description=None, features=data['features'], mode=data.get('mode', 'collaborate'), activity=Activity('{}.activity'.format(id), self.app, subscriber_ids=[])) self.app.r.oset(lst.id, lst) self.app.r.zadd('{}.users'.format(lst.id), {self.app.user.id.encode(): -time()}) self.app.r.rpush(self.map_key, lst.id) self.app.user.lists.add(lst, user=self.app.user) self.app.activity.publish( Event.create('create-list', None, {'lst': lst}, app=self.app)) return lst
async def test_send_device_notification(self) -> None: await self.app.send_device_notification(self.push_subscription, Event.create('meow', None, app=self.app))
async def test_notify(self) -> None: await self.user.devices[0].enable_notifications(self.push_subscription) self.user.notify(Event.create('test', None, app=self.app))
def make_event(self) -> Tuple[Event, Cat]: cat = self.app.cats.create() event = Event.create('meow', cat, app=self.app) return event, cat