def test_add_valid_action(self): Log.objects().delete() body = [{ 'userId': '1234', 'sessionId': '12345', 'actions': [{ "time": "2018-10-20T21:37:28-06:00", "type": "VIEW", "properties": { "viewedId": "12345" } }] }] action = Action(_type="VIEW", _properties=ViewProperties(viewedId="12345"), _time="2018-10-20T21:37:28-06:00") result = LogService.add_logs(body) logs = Log.objects(userId="1234") assert logs is not None assert len(logs) == 1 assert logs[0].userId == "1234" assert logs[0].sessionId == "12345" assert logs[0].actions == [action] Log.objects().delete()
def test_status_code_with_valid_get_request(self): Log.objects().delete() r = self.w.get('/logs/?') # Assert there was no messages flushed: assert r is not None assert r.content_type == 'application/json' assert r.status_int < 400
def test_add_no_log(self): Log.objects().delete() try: result = LogService.add_logs(None) assert False except Exception as e: assert str(e) == "NO_LOGS_PROVIDED"
def test_retrieving_with_invalid_to_date(self): Log.objects().delete() r = self.w.get('/logs/?to=2018-10-18T21:37:28-04:00', expect_errors=True) # Assert there was no messages flushed: assert r.status_int == 400 assert r.json == {'code': 'INVALID_TIME_FORMAT', 'success': False}
def test_empty_actions(self): Log.objects().delete() body = [{'userId': '12345', 'sessionId': '1234', 'actions': []}] r1 = self.w.post_json('/logs/', body, expect_errors=True) assert r1.status_int == 400 assert r1.json == {'success': False, 'code': 'MISSING_ACTIONS'}
def add_logs(self, logs): if not logs: raise ValueError('NO_LOGS_PROVIDED') # Run through all received logs. This for-loop does two things # - Validates all inputs # - Once all is validated, returns an array of actions well constructed by our helper below. # # This way if any log in the body is invalid, then the entire body doesn't get inserted. Simulating a rollback. # logArray = [] for log in logs: userId = log.get('userId') sessionId = log.get('sessionId') actions = log.get('actions') if not userId: raise ValueError('MISSING_USERID') if not sessionId: raise ValueError('MISSING_SESSIONID') if not actions: raise ValueError('MISSING_ACTIONS') actionArray = [] for action in actions: actionArray.append(self._createActionModel(action)) logArray.append({ 'userId': userId, 'sessionId': sessionId, 'actionArray': actionArray}) # Validation and creation of array objects complete. Insert them all into Log collections for _log in logArray: Log.objects( userId=_log.get('userId'), sessionId=_log.get('sessionId') ).update_one(push_all__actions=_log.get('actionArray'), upsert=True) return { 'success': True }
def test_invalid_datetime_format_logs_3(self): Log.objects().delete() try: result = LogService.get_logs(None, '2018-10-20T21:37:28-04:00', '2018-10-20T21:37:28-04:00', None) assert False except Exception as e: assert str(e) == "INVALID_TIME_FORMAT"
def test_miss_action(self): Log.objects().delete() try: body = [{'userId': '12345', 'sessionId': '12345', 'actions': []}] result = LogService.add_logs(body) assert False except ValueError as e: assert str(e) == "MISSING_ACTIONS"
def test_db_doesnt_add_body_if_any_log_is_invalid(self): Log.objects().delete() body = [{ "userId": "ABC123XYZ", "sessionId": "XYZ456ABC", "actions": [{ "time": "2018-10-18T21:37:28-06:00", "type": "CLICK", "properties": { "locationX": 52, "locationY": 11 } }, { "time": "2018-10-18T21:37:30-06:00", "type": "VIEW", "properties": { "viewedId": "FDJKLHSLD" } }, { "time": "2018-10-18T21:37:30-06:00", "type": "NAVIGATE", "properties": { "pageFrom": "communities", "pageTo": "inventory" } }] }, { "userId": "asd", "sessionId": "asdfg", "actions": [{ "time": "2018-10-18T21:37:28-06:00", "type": "CLICK", "properties": { "locationX": 60, "locationY": 70 } }, { "time": "2018-10-20T21:37:28-06:00", "type": "NAVIGATE", "properties": { "viewedId": "1234", } }] }] try: result = LogService.add_logs(body) False except Exception as e: assert str(e) == "MISSING_PAGEFROM_VALUE" log = Log.objects() assert log is not None assert len(log) == 0
def test_retrieving_valid_log(self): Log.objects().delete() answer = [{ 'userId': '12345', 'sessionId': 'asdfg', 'actions': [{ 'type': 'CLICK', 'properties': { 'locationX': 52, 'locationY': 22 }, 'time': '2018-10-18T21:37:28-06:00' }] }] action = Action(_time='2018-10-18T21:37:28-06:00', _type='CLICK', _properties=ClickProperties(locationX=52, locationY=22)) log = Log(userId='12345', sessionId='asdfg', actions=[action]) log.save() r = self.w.get('/logs/?') log.delete() assert r.json == answer
def test_retrieving_with_invalid_type(self): Log.objects().delete() answer = [{ 'userId': '12345', 'sessionId': 'asdfg', 'actions': [{ 'type': 'CLICK', 'properties': { 'locationX': 52, 'locationY': 22 }, 'time': '2018-10-17T21:37:28-06:00' }] }] action1 = Action(_time='2018-10-17T21:37:28-06:00', _type='CLICK', _properties=ClickProperties(locationX=52, locationY=22)) action2 = Action(_time='2018-10-19T21:37:28-06:00', _type='NAVIGATE', _properties=NavigateProperties(pageFrom='X', pageTo='Y')) log = Log(userId='12345', sessionId='asdfg', actions=[action1, action2]) log.save() r1 = self.w.get('/logs/?types=CLICK,WRONGTYPE,VIEW') assert r1.status_int == 200 assert r1.json == answer log.delete()
def test_logs_with_an_existing_log(self): Log.objects().delete() answer = [{ 'userId': '12345', 'sessionId': 'asdfg', 'actions': [{ 'time': '2018-10-18T21:37:28-06:00', 'type': 'CLICK', 'properties': { 'locationX': 52, 'locationY': 22 } }] }] action = Action(_time='2018-10-18T21:37:28-06:00', _type='CLICK', _properties=ClickProperties(locationX=52, locationY=22)) log = Log(userId='12345', sessionId='asdfg', actions=[action]) log.save() result = LogService.get_logs(None, None, None, None) assert result is not None assert result == answer log.delete()
def test_add_valid_action_non_upserted_different_session(self): Log.objects().delete() body = [{ 'userId': '1234', 'sessionId': '12345', 'actions': [{ "time": "2018-10-20T21:37:28-06:00", "type": "VIEW", "properties": { "viewedId": "12345" } }] }, { 'userId': '1234', 'sessionId': 'ASDF', 'actions': [{ "time": "2018-10-20T21:37:28-06:00", "type": "NAVIGATE", "properties": { "pageFrom": "X", "pageTo": "Y" } }] }] action1 = Action(_type="VIEW", _properties=ViewProperties(viewedId="12345"), _time="2018-10-20T21:37:28-06:00") action2 = Action(_type="NAVIGATE", _properties=NavigateProperties(pageFrom="X", pageTo="Y"), _time="2018-10-20T21:37:28-06:00") result = LogService.add_logs(body) logs = Log.objects() assert logs is not None assert len(logs) == 2 assert logs[0].userId == "1234" assert logs[0].sessionId == "12345" assert logs[0].actions == [action1] assert logs[1].userId == "1234" assert logs[1].sessionId == "ASDF" assert logs[1].actions == [action2] Log.objects().delete()
def test_missing_time(self): Log.objects().delete() body = [{ 'userId': '12345', 'sessionId': '1234', 'actions': [{ 'type': 'VIEW', 'properties': { 'asdf': 'asdf' } }] }] r = self.w.post_json('/logs/', body, expect_errors=True) assert r.status_int == 400 assert r.json == {'success': False, 'code': 'MISSING_TIME_VALUE'}
def test_miss_properties(self): Log.objects().delete() try: body = [{ 'userId': '12345', 'sessionId': '12345', 'actions': [{ "time": "2018-10-20T21:37:28-06:00", "type": "VIEW" }] }] result = LogService.add_logs(body) assert False except ValueError as e: assert str(e) == "MISSING_PROPERTIES_VALUE"
def test_no_sessionId(self): Log.objects().delete() body = [{ 'userId': '12345', 'actions': [{ 'time': '2018-10-18T21:37:28-06:00', 'type': 'CLICK', 'properties': { 'locationX': 52, 'locationY': 22 } }] }] r1 = self.w.post_json('/logs/', body, expect_errors=True) assert r1.status_int == 400 assert r1.json == {'success': False, 'code': 'MISSING_SESSIONID'}
def test_miss_sessionId(self): Log.objects().delete() try: body = [{ 'userId': '12345', 'actions': [{ "time": "2018-10-20T21:37:28-06:00", "type": "VIEW", "properties": { "viewedId": "12345" } }] }] result = LogService.add_logs(body) assert False except ValueError as e: assert str(e) == "MISSING_SESSIONID"
def test_result_success(self): Log.objects().delete() body = [{ 'userId': '12345', 'sessionId': '12345', 'actions': [{ "time": "2018-10-20T21:37:28-06:00", "type": "VIEW", "properties": { "viewedId": "12345" } }] }] result = LogService.add_logs(body) assert result is not None assert result.get('success') is True Log.objects().delete()
def test_miss_time(self): Log.objects().delete() try: body = [{ 'userId': '12345', 'sessionId': '12345', 'actions': [{ "type": "VIEW", "properties": { "viewedId": "12345" } }] }] result = LogService.add_logs(body) assert False except ValueError as e: assert str(e) == "MISSING_TIME_VALUE"
def test_invalid_click(self): Log.objects().delete() body = [{ 'userId': '12345', 'sessionId': '1234', 'actions': [{ 'time': '2018-10-18T21:37:28-06:00', 'type': 'VIEW', 'properties': { 'asdf': 'asdf' } }] }] r = self.w.post_json('/logs/', body, expect_errors=True) assert r.status_int == 400 assert r.json == {'success': False, 'code': 'MISSING_VIEWEDID_VALUE'}
def test_invalid_click(self): Log.objects().delete() body1 = [{ 'userId': '12345', 'sessionId': '1234', 'actions': [{ 'time': '2018-10-18T21:37:28-06:00', 'type': 'CLICK', 'properties': { 'locationX': 22 } }] }] body2 = [{ 'userId': '12345', 'sessionId': '1234', 'actions': [{ 'time': '2018-10-18T21:37:28-06:00', 'type': 'CLICK', 'properties': { 'locationY': 23 } }] }] r1 = self.w.post_json('/logs/', body1, expect_errors=True) assert r1.status_int == 400 assert r1.json == { 'success': False, 'code': 'MISSING_LOCATION_Y_VALUE' } r2 = self.w.post_json('/logs/', body2, expect_errors=True) assert r2.status_int == 400 assert r2.json == { 'success': False, 'code': 'MISSING_LOCATION_X_VALUE' }
def test_invalid_type(self): Log.objects().delete() body = [{ 'userId': '12345', 'sessionId': '1234', 'actions': [{ 'time': '2018-10-18T21:37:28-06:00', 'type': 'INVALID', 'properties': { 'locationX': 52, 'locationY': 22 } }] }] r1 = self.w.post_json('/logs/', body, expect_errors=True) assert r1.status_int == 400 assert r1.json == {'success': False, 'code': 'INVALID_ACTION_TYPE'}
def test_invalid_navigate(self): Log.objects().delete() body1 = [{ 'userId': '12345', 'sessionId': '1234', 'actions': [{ 'time': '2018-10-18T21:37:28-06:00', 'type': 'NAVIGATE', 'properties': { 'pageTo': '22' } }] }] body2 = [{ 'userId': '12345', 'sessionId': '1234', 'actions': [{ 'time': '2018-10-18T21:37:28-06:00', 'type': 'NAVIGATE', 'properties': { 'pageFrom': '22' } }] }] r1 = self.w.post_json('/logs/', body1, expect_errors=True) assert r1.status_int == 400 assert r1.json == {'success': False, 'code': 'MISSING_PAGEFROM_VALUE'} r2 = self.w.post_json('/logs/', body2, expect_errors=True) assert r2.status_int == 400 assert r2.json == {'success': False, 'code': 'MISSING_PAGETO_VALUE'}
def test_logs_with_an_matching_log_userId(self): Log.objects().delete() answer = [{ "userId": "12345", "sessionId": "asdfg", "actions": [{ "time": "2018-10-18T21:37:28-06:00", "properties": { "locationX": 52, "locationY": 22 }, "type": "CLICK", }, { "time": "2018-10-20T21:37:28-06:00", "properties": { "pageFrom": "X", "pageTo": "Y" }, "type": "NAVIGATE" }] }] action1 = Action(_time='2018-10-18T21:37:28-06:00', _type='CLICK', _properties=ClickProperties(locationX=52, locationY=22)) action2 = Action(_time='2018-10-20T21:37:28-06:00', _type='NAVIGATE', _properties=NavigateProperties(pageFrom='X', pageTo='Y')) log = Log(userId='12345', sessionId='asdfg', actions=[action1, action2]) log.save() result1 = LogService.get_logs('12345', None, None, None) assert result1 is not None assert result1 == answer result2 = LogService.get_logs('asdf', None, None, None) assert result2 is not None assert len(result2) == 0 log.delete()
def test_no_body(self): Log.objects().delete() r1 = self.w.post_json('/logs/', None, expect_errors=True) assert r1.status_int == 400 assert r1.json == {'success': False, 'code': 'NO_LOGS_PROVIDED'}
def add_valid_log(self): Log.objects().delete() body = [{ "userId": "ABC123XYZ", "sessionId": "XYZ456ABC", "actions": [{ "time": "2018-10-18T21:37:28-06:00", "type": "CLICK", "properties": { "locationX": 52, "locationY": 11 } }, { "time": "2018-10-18T21:37:30-06:00", "type": "VIEW", "properties": { "viewedId": "FDJKLHSLD" } }, { "time": "2018-10-18T21:37:30-06:00", "type": "NAVIGATE", "properties": { "pageFrom": "communities", "pageTo": "inventory" } }] }, { "userId": "asd", "sessionId": "asdfg", "actions": [{ "time": "2018-10-18T21:37:28-06:00", "type": "CLICK", "properties": { "locationX": 60, "locationY": 70 } }, { "time": "2018-10-20T21:37:28-06:00", "type": "NAVIGATE", "properties": { "pageFrom": "X", "pageTo": "Y" } }] }] r = self.w.post_json('/logs/', body, expect_errors=True) assert r.status_int == 200 assert r.json == {'success': True} log = Log.objects() assert log is not None assert len(log) == 2 assert log[0].get('userId') == 'ABC123XYZ' assert log[0].get('sessionId') == 'XYZ456ABC' actions = log[0].get('actions') assert actions is not None assert len(actions) == 3 assert actions[0] == Action(_time="2018-10-18T21:37:28-06:00", _type="CLICK", _properties=ClickProperties(locationX=52, locationY=11)) assert actions[1] == Action( _time="2018-10-18T21:37:30-06:00", _type="VIEW", _properties=ViewProperties(viewedId="FDJKLHSLD")) assert actions[2] == Action(_time="2018-10-18T21:37:30-06:00", _type="NAVIGATE", _properties=NavigateProperties( pageFrom="communities", pageTo="inventory")) Log.objects().delete() assert log[1].get('userId') == 'asd' assert log[1].get('sessionId') == 'asdfg' actions = log[1].get('actions') assert actions is not None assert len(actions) == 2 assert actions[0] == Action(_time="2018-10-18T21:37:28-06:00", _type="CLICK", _properties=ClickProperties(locationX=60, locationY=70)) assert actions[1] == Action(_time="2018-10-20T21:37:28-06:00", _type="NAVIGATE", _properties=NavigateProperties( pageFrom="X", pageTo="Y")) Log.objects().delete()
def test_empty_logs(self): Log.objects().delete() result = LogService.get_logs(None, None, None, None) assert len(result) == 0
def test_logs_with_a_from_and_to(self): Log.objects().delete() answer1 = [ { "userId": "12345", "sessionId": "asdfg", "actions": [{ "time": "2018-10-17T21:37:28-06:00", "properties": { "locationX": 52, "locationY": 22 }, "type": "CLICK" }] }, { "userId": "ASDFG", "sessionId": "12345", "actions": [{ "time": "2018-10-18T21:37:28-06:00", "properties": { "viewedId": "12345" }, "type": "VIEW" }] }, ] answer2 = [ { "userId": "12345", "sessionId": "asdfg", "actions": [{ "time": "2018-10-19T21:37:28-06:00", "properties": { "pageFrom": 'X', "pageTo": 'Y' }, "type": "NAVIGATE" }] }, { "userId": "ASDFG", "sessionId": "12345", "actions": [{ "time": "2018-10-20T21:37:28-06:00", "properties": { "locationX": 60, "locationY": 30 }, "type": "CLICK" }] }, ] answer3 = [{ "userId": "ASDFG", "sessionId": "12345", "actions": [{ "time": "2018-10-18T21:37:28-06:00", "properties": { "viewedId": "12345", }, "type": "VIEW" }] }] action1 = Action(_time='2018-10-17T21:37:28-06:00', _type='CLICK', _properties=ClickProperties(locationX=52, locationY=22)) action2 = Action(_time='2018-10-19T21:37:28-06:00', _type='NAVIGATE', _properties=NavigateProperties(pageFrom='X', pageTo='Y')) log1 = Log(userId='12345', sessionId='asdfg', actions=[action1, action2]) action3 = Action(_time='2018-10-18T21:37:28-06:00', _type='VIEW', _properties=ViewProperties(viewedId='12345')) action4 = Action(_time='2018-10-20T21:37:28-06:00', _type='CLICK', _properties=ClickProperties(locationX=60, locationY=30)) log2 = Log(userId='ASDFG', sessionId='12345', actions=[action3, action4]) log1.save() log2.save() result1 = LogService.get_logs(None, None, '2018-10-19T00:00:00-06:00', None) assert result1 is not None assert result1 == answer1 result2 = LogService.get_logs(None, '2018-10-19T00:00:00-06:00', None, None) assert result2 is not None assert result2 == answer2 result3 = LogService.get_logs(None, '2018-10-18T00:00:00-06:00', '2018-10-19T00:00:00-06:00', None) assert result3 is not None assert result3 == answer3 log1.delete() log2.delete()
def test_retrieving_log_by_user(self): Log.objects().delete() answer1 = [{ 'userId': 'ASDF', 'sessionId': '123', 'actions': [{ 'type': 'CLICK', 'properties': { 'locationX': 52, 'locationY': 22 }, 'time': '2018-10-18T21:37:28-06:00' }] }] answer2 = [{ 'userId': '12345', 'sessionId': 'asdfg', 'actions': [{ 'type': 'CLICK', 'properties': { 'locationX': 52, 'locationY': 22 }, 'time': '2018-10-18T21:37:28-06:00' }] }] action = Action(_time='2018-10-18T21:37:28-06:00', _type='CLICK', _properties=ClickProperties(locationX=52, locationY=22)) log1 = Log(userId='12345', sessionId='asdfg', actions=[action]) log2 = Log(userId='ASDF', sessionId='123', actions=[action]) log1.save() log2.save() r1 = self.w.get('/logs/?userId=ASDF') assert r1.json == answer1 r2 = self.w.get('/logs/?userId=12345') assert r2.json == answer2 r2 = self.w.get('/logs/?userId=12') assert r2.json == [] log1.delete() log2.delete()
def test_retrieving_logs_with_matching_types(self): Log.objects().delete() answer1 = [ { "userId": "12345", "sessionId": "asdfg", "actions": [{ "time": "2018-10-20T21:37:28-06:00", "properties": { "locationX": 52, "locationY": 22 }, "type": "CLICK" }] }, { "userId": "ASDFG", "sessionId": "12345", "actions": [{ "time": "2019-10-20T21:37:28-06:00", "properties": { "locationX": 60, "locationY": 30 }, "type": "CLICK" }] }, ] answer2 = [ { "userId": "12345", "sessionId": "asdfg", "actions": [{ "time": "2018-10-20T21:37:28-06:00", "properties": { "locationX": 52, "locationY": 22 }, "type": "CLICK" }] }, { "userId": "ASDFG", "sessionId": "12345", "actions": [{ "time": "2019-10-19T21:37:28-06:00", "properties": { "viewedId": "12345", }, "type": "VIEW" }, { "time": "2019-10-20T21:37:28-06:00", "properties": { "locationX": 60, "locationY": 30 }, "type": "CLICK" }] }, ] answer3 = [{ "userId": "12345", "sessionId": "asdfg", "actions": [{ "time": "2018-10-20T21:37:28-06:00", "properties": { "pageFrom": "X", "pageTo": "Y" }, "type": "NAVIGATE" }] }] action1 = Action(_time='2018-10-20T21:37:28-06:00', _type='CLICK', _properties=ClickProperties(locationX=52, locationY=22)) action2 = Action(_time='2018-10-20T21:37:28-06:00', _type='NAVIGATE', _properties=NavigateProperties(pageFrom='X', pageTo='Y')) log1 = Log(userId='12345', sessionId='asdfg', actions=[action1, action2]) action3 = Action(_time='2019-10-19T21:37:28-06:00', _type='VIEW', _properties=ViewProperties(viewedId='12345')) action4 = Action(_time='2019-10-20T21:37:28-06:00', _type='CLICK', _properties=ClickProperties(locationX=60, locationY=30)) log2 = Log(userId='ASDFG', sessionId='12345', actions=[action3, action4]) log1.save() log2.save() r1 = self.w.get('/logs/?types=CLICK') assert r1.json == answer1 r2 = self.w.get('/logs/?types=CLICK,VIEW') assert r2.json == answer2 r3 = self.w.get('/logs/?types=NAVIGATE') assert r3.json == answer3 log1.delete() log2.delete()