Пример #1
0
class TopHeroesHandlerTestCase(unittest.TestCase):
    """Top Heroes handler"""
    def setUp(self):
        """SetUp é chamado no inicio de cada teste"""
        self.mock_db = MockFirestore()
        self.patcher = patch('modules.main.MainModule.get_firestore_db',
                             return_value=self.mock_db)
        self.patcher.start()
        # Nessa linha vamos iniciar a API nos testes
        self.app = app.test_client()

    def tearDown(self):
        """O tearDown é chamado no final de cada teste"""
        self.patcher.stop()
        self.mock_db.reset()

    @staticmethod
    def create_hero(hero_name, universe):
        """Create a hero for tests"""
        hero = Hero()
        hero.name = hero_name
        hero.description = '{0} description'.format(hero_name)
        hero.universe = universe
        hero.save()
        return hero

    def test_get_top_heroes(self):
        """Test get top heroes"""
        # Aqui vamos fazer um loop e criar 20 herois
        # E o nome vai ser hero + index do loop, ex: "Hero 1"
        for index in range(1, 21):
            self.create_hero('Hero {0}'.format(index), 'marvel')

        # Fazendo a primeira consulta a url e conferindo a resposta
        response = self.app.get(path='/top-heroes')
        first_hero_list = response.get_json()['heroes']
        self.assertEqual(len(first_hero_list), 4)
        self.assertEqual(response.status_code, 200)

        # Fazendo a segunda consulta a url e conferindo a resposta
        response = self.app.get(path='/top-heroes')
        self.assertEqual(response.status_code, 200)
        second_hero_list = response.get_json()['heroes']
        self.assertEqual(len(second_hero_list), 4)

        # Comparando as duas listas para ver se são diferentes
        # Pois essa url precisa sempre retornar herois diferentes
        self.assertNotEqual(first_hero_list, second_hero_list)
class HeroesSearchHandlerTestCase(unittest.TestCase):
    """Heroes search handler"""

    def setUp(self):
        """SetUp é chamado no inicio de cada teste"""
        self.mock_db = MockFirestore()
        self.patcher = patch(
            'modules.main.MainModule.get_firestore_db',
            return_value=self.mock_db)
        self.patcher.start()
        self.app = app.test_client()

    def tearDown(self):
        """O tearDown é chamado no final de cada teste"""
        self.patcher.stop()
        self.mock_db.reset()

    def test_search_hero(self):
        """Test search hero by name"""
        # Criando dois herois com nomes diferentes
        self.create_hero('Superman', 'dc')
        self.create_hero('Batman', 'dc')

        # Pesquisando o heroi Superman
        response = self.app.get(path='/search?name=' + 'Superman')

        # Conferindo o status da requisição
        self.assertEqual(response.status_code, 200)

        # Conferindo se voltou somente um heroi
        self.assertEqual(len(response.get_json()), 1)
        # Conferindo o nome do heroi que voltou
        self.assertEqual(response.get_json()[0]['name'], 'Superman')

    def test_search_hero_with_lowercase_param_name(self):
        """Test search hero by lowercase param name"""
        # Criando dois herois com nomes diferentes
        self.create_hero('Superman', 'dc')
        self.create_hero('Batman', 'dc')

        # Pesquisando o heroi Superman com a primeira letra minuscula
        # No metodo view vocês só precisam chamar o metodo title para deixar
        # a primeira letra maiuscula e o teste ira passar
        response = self.app.get(path='/search?name=' + 'superman')

        # Conferindo o status da requisição
        self.assertEqual(response.status_code, 200)

        # Conferindo se voltou somente um heroi
        self.assertEqual(len(response.get_json()), 1)
        # Conferindo o nome do heroi que voltou
        self.assertEqual(response.get_json()[0]['name'], 'Superman')

    def test_search_hero_max_returned_items(self):
        """Test search hero max items returned in query"""
        # Criando 15 herois com o mesmo nome para testar o limite da consulta
        for _ in range(15):
            self.create_hero('Superman', 'dc')

        # Pesquisando o heroi
        response = self.app.get(path='/search?name=' + 'Superman')

        # Conferindo o status da requisição
        self.assertEqual(response.status_code, 200)

        # Conferindo se voltou somente 10 herois que é o limite mencionado
        self.assertEqual(len(response.get_json()), 10)

    def test_search_hero_without_param_name(self):
        """Test search hero without param name"""

        # Pesquisando o heroi sem o parametro name, fazendo isso a requisição
        # deve retornar o status 400(Bad request) e retornar um json com
        # a mensagem de erro
        response = self.app.get(path='/search?name=')

        # Conferindo o status da requisição
        self.assertEqual(response.status_code, 400)

        # Conferindo a mensagem de erro que voltou
        self.assertDictEqual(
            response.get_json(),
            {'message': 'Bad request, param name is required'}
        )

    @staticmethod
    def create_hero(hero_name, universe):
        hero = Hero()
        hero.name = hero_name
        hero.description = '{0} description'.format(hero_name)
        hero.universe = universe
        hero.save()
        return hero
Пример #3
0
class TestHeroModel(unittest.TestCase):
    """Test hero model"""
    def setUp(self):
        """SetUp é chamado no inicio de cada teste"""
        self.mock_db = MockFirestore()
        self.patcher = patch('modules.main.MainModule.get_firestore_db',
                             return_value=self.mock_db)
        self.patcher.start()

    def tearDown(self):
        """O tearDown é chamado no final de cada teste"""
        self.patcher.stop()
        self.mock_db.reset()

    def test_save_and_get_hero(self):
        """Test save and get hero"""
        # Criando o novo heroi
        new_hero = Hero()
        new_hero.name = 'Superman'
        new_hero.description = 'Superman'
        new_hero.universe = 'dc'
        new_hero.save()

        # Obtendo o heroi pelo id
        hero = Hero.get_hero(new_hero.id)
        self.assertEqual(hero.name, 'Superman')
        self.assertEqual(hero.id, new_hero.id)

    def test_get_hero_not_found(self):
        """Test get hero not found"""
        hero = Hero.get_hero('ID_TEST')
        self.assertIsNone(hero)

    @staticmethod
    def create_hero(hero_name, universe):
        hero = Hero()
        hero.name = hero_name
        hero.description = '{0} description'.format(hero_name)
        hero.universe = universe
        hero.save()
        return hero

    def test_get_heroes(self):
        """Test get heroes"""
        # Aqui vamos fazer um loop e criar 20 herois
        for index in range(1, 21):
            self.create_hero('Hero {0}'.format(index), 'marvel')

        # Chamando o metodo para obter os herois
        heroes = Hero.get_heroes()
        # Percorrendo todos os herois e transformando eles em dict(json)
        heroes_dict = [hero.to_dict() for hero in heroes]
        # Consultando a quantidade de item que retornou
        self.assertEqual(len(heroes_dict), 16)
        for hero in heroes_dict:
            self.assertTrue(hero['name'].startswith('Hero'))

    def test_delete_hero(self):
        """Test delete hero"""
        # Criando o heroi
        hero = self.create_hero('Joker', 'dc')
        # excluindo o heroi
        Hero.delete(hero.id)

        # consultando se o heroi foi mesmo excluido
        self.assertIsNone(Hero.get_hero(hero.id))
Пример #4
0
class HeroesHandlerTestCase(unittest.TestCase):
    """Heroes handler"""
    def setUp(self):
        """SetUp é chamado no inicio de cada teste"""
        self.mock_db = MockFirestore()
        self.patcher = patch('modules.main.MainModule.get_firestore_db',
                             return_value=self.mock_db)
        self.patcher.start()
        self.app = app.test_client()

    def tearDown(self):
        """O tearDown é chamado no final de cada teste"""
        self.patcher.stop()
        self.mock_db.reset()

    def test_create_a_new_hero(self):
        """This test should create a new hero"""
        hero_dict = {
            'hero': {
                'name':
                'Superman',
                'description':
                'Superman description',
                'universe':
                'DC',
                'imageUrl':
                'https://super.abril.com.br/wp-content/uploads/2018/09/superman.png?w=1024'
            }
        }

        response = self.app.post(path='/heroes', json=hero_dict)

        # Conferindo se voltou 200
        self.assertEqual(response.status_code, 200)

        # Conferindo a resposta da requisição
        self.assertIsNotNone(response.get_json())
        self.assertIsNotNone(response.get_json()['id'])

    def test_get_heroes(self):
        """Test get this"""
        # Aqui vamos fazer um loop e criar 20 herois
        # E o nome vai ser hero + index do loop, ex: "Hero 1"
        for index in range(1, 21):
            self.create_hero('Hero {0}'.format(index), 'marvel')

        response = self.app.get(path='/heroes')
        # conferindo se voltou 200
        # print(response)
        self.assertEqual(response.status_code, 200)
        # conferindo se a chave do cursor esta retornando um json
        self.assertIn('cursor', response.get_json())
        # Conferindo a quantidade de herois que voltou no json
        self.assertEqual(len(response.get_json()['heroes']), 16)

        # Fazendo a segunda consulta enviando o cursor retornado
        cursor = response.get_json()['cursor']

        response = self.app.get(path='/heroes?cursor=' + cursor)

        # Conferindo se voltou 200
        self.assertEqual(response.status_code, 200)

        # Conferindo a quantidade de herois que voltou no json
        # Na primeira requisiçao voltou 16 herois entao precisa retornar mais 4
        self.assertEqual(len(response.get_json()['heroes']), 4)

    @staticmethod
    def create_hero(hero_name, universe):
        hero = Hero()
        hero.name = hero_name
        hero.description = '{0} description'.format(hero_name)
        hero.universe = universe
        hero.save()
        return hero

    def test_get_hero(self):
        """Test get hero"""
        # Criando o heroi e salvando o id
        hero_id = self.create_hero('Hero', 'DC').id

        # Enviando a requisição para obter o heroi
        response = self.app.get('/hero/{0}'.format(hero_id))
        self.assertEqual(response.status_code, 200)

        # Pegando o json da resposta
        hero_dict = response.get_json()
        self.assertEqual(hero_dict['name'], 'Hero')
        self.assertEqual(hero_dict['id'], hero_id)

    def test_get_hero_not_found(self):
        """Test get hero not found"""
        # Enviando a requisição para obter o heroi
        response = self.app.get('/hero/id_aleatorio')

        # A requisição vai voltar 404 pois não existe nenhum heroi com esse id
        self.assertEqual(response.status_code, 404)

        # Json retornado
        self.assertDictEqual(response.get_json(),
                             {'message': 'Hero not found'})

    def test_update_hero(self):
        """Test update hero"""
        # Criando o heroi com o nome hero
        hero = self.create_hero('Hero', 'DC')
        # Enviando a requisição para atualizar nome do heroi para "Hawkwoman"
        params = {
            'hero': {
                'name':
                'Hawkwoman',
                'description':
                hero.description,
                'universe':
                hero.universe,
                'imageUrl':
                'https://gartic.com.br/imgs/mural/ti/tica_/pantera-negra.png'
            }
        }
        response = self.app.post(path='/hero/{0}'.format(hero.id), json=params)
        # Resposta da requisição
        self.assertEqual(response.status_code, 200)

        # Obtendo o heroi atualizado para conferir o novo nome
        hero_updated = Hero.get_hero(hero.id)
        self.assertEqual(hero_updated.name, 'Hawkwoman')

    def test_delete_hero(self):
        """Test delete hero"""
        # Criando o heroi
        hero = self.create_hero('Hero', 'DC')

        # Enviando a requisição para excluir o heroi
        response = self.app.delete(path='/hero/{0}'.format(hero.id))

        # Resposta da requisição
        self.assertEqual(response.status_code, 200)

        # Conferindo a mensagem que voltou
        self.assertEqual(response.get_json(), {'message': 'Hero deleted'})
        # Obtendo o heroi diretamente no banco de dados para conferir se foi
        # excluido mesmo
        self.assertIsNone(Hero.get_hero(hero.id))

    def test_create_hero_without_name(self):
        """Test create hero without name"""
        params = {
            'hero': {
                'name': '',
                'description': '',
                'universe': 'DC',
                'imageUrl': 'https://image.com.br/image.jpg'
            }
        }
        response = self.app.post(path='/heroes', json=params)
        self.assertEqual(response.status_code, 500)
        self.assertEqual(response.get_json()['details'],
                         'Bad request, name is required')

    def test_create_hero_with_name_formatted(self):
        """Test create hero with uppercase name and blank spaces"""
        params = {
            'hero': {
                'name': ' SUPERMAN ',
                'description': 'Hero description',
                'universe': 'DC',
                'imageUrl': 'https://image.com.br/image.jpg'
            }
        }
        response = self.app.post(path='/heroes', json=params)
        self.assertEqual(response.status_code, 200)

        # Obtendo o heroi no banco de dados para conferir o nome
        hero_updated = Hero.get_hero(response.get_json()['id'])
        self.assertEqual(hero_updated.name, 'Superman')

    def test_create_hero_with_invalid_universe(self):
        """Test create hero with invalid universe"""
        params = {
            'hero': {
                'name': ' SUPERMAN ',
                'description': 'Hero description',
                'universe': 'x-men',
                'imageUrl': 'https://image.com.br/image.jpg'
            }
        }
        response = self.app.post(path='/heroes', json=params)
        self.assertEqual(response.status_code, 500)
        self.assertEqual(response.get_json()['details'],
                         'Bad request, invalid universe')

    def test_create_hero_with_invalid_url(self):
        """Test create hero with invalid url"""
        params = {
            'hero': {
                'name': 'Superman',
                'description': 'Superman description',
                'universe': 'DC',
                'imageUrl': ''
            }
        }
        response = self.app.post(path='/heroes', json=params)
        self.assertEqual(response.status_code, 500)
        self.assertEqual(response.get_json()['details'],
                         'Bad request, invalid image url')

    def test_create_hero_with_formatted_description(self):
        params = {
            'hero': {
                'name': 'SUPERMAN',
                'description': '          hero description         ',
                'universe': 'DC',
                'imageUrl': 'https://image.com.br/image.jpg'
            }
        }
        response = self.app.post(path='/heroes', json=params)
        self.assertEqual(response.status_code, 200)

        # Obtendo o heroi no banco de dados para conferir a descrição
        hero_updated = Hero.get_hero(response.get_json()['id'])
        self.assertEqual(hero_updated.description, 'Hero description')
Пример #5
0
class TestSurveyService(unittest.TestCase):

  def setUp(self):
    super().setUp()
    self.client = MockFirestore()
    self.collection = self.client.collection('test_surveys')

  def tearDown(self):
    self.client.reset()
    super().tearDown()

  def test_get_all(self):
    # given
    survey_collection.get_all = mock.Mock()
    # when
    survey_service.get_all()
    # then
    assert survey_collection.get_all.call_count == 1

  def test_get_by_id(self):
    # given
    survey_collection.get_by_id = mock.Mock()
    # when
    survey_service.get_by_id('mockSurveyId')
    # then
    survey_collection.get_by_id.assert_called_once_with('mockSurveyId')

  def test_get_doc_by_id(self):
    # given
    survey_collection.get_doc_by_id = mock.Mock()
    # when
    survey_service.get_doc_by_id('mockSurveyId')
    # then
    survey_collection.get_doc_by_id.assert_called_once_with('mockSurveyId')

  def test_delete_by_id(self):
    # given
    survey_collection.delete_by_id = mock.Mock()
    # when
    survey_service.delete_by_id('mockSurveyId')
    # then
    survey_collection.delete_by_id.assert_called_once_with('mockSurveyId')

  def test_set_form_data_copies_values_from_a_document(self):
    form = {
        'field1': mock.Mock(data=None),
        'field2': mock.Mock(data='value2'),
        'field3': mock.Mock(data='untouched')
    }

    ref = self.collection.document()
    ref.set({'field1': 'something', 'field2': 'new_value2'})
    doc = ref.get()

    survey_service.set_form_data(form, doc)
    self.assertEqual(form['field1'].data, 'something')
    self.assertEqual(form['field2'].data, 'new_value2')
    self.assertEqual(form['field3'].data, 'untouched')

  def test_zip_file_returns_user_readable_filename(self):
    survey_id = 'some_test_id'
    survey_dict = {
        'surveyname': 'test survey',
        'question1': 'q1 text',
    }

    filename, _ = survey_service.zip_file(survey_id, survey_dict)
    self.assertRegex(filename, '.*_test-survey.zip$')

  def test_zip_file_returns_nested_zips_with_content(self):
    survey_id = 'some_test_id'
    survey_dict = {
        'surveyname': 'test survey',
        'question1': 'q1 text',
    }

    _, data = survey_service.zip_file(survey_id, survey_dict)
    zf = zipfile.ZipFile(data)
    names = sorted([nzf.filename for nzf in zf.filelist])
    self.assertRegex(names[0], '.*_test-survey_default_control.zip$')
    self.assertRegex(names[1], '.*_test-survey_default_expose.zip$')

  def test_write_html_template(self):
    segments = ['segment1', 'segment2', 'segment3']
    survey_dict = {'question1': 'q1 text', 'answer1a': 'a1a test'}
    survey_service.render_template = mock.Mock(
        return_value='<html>placeholder</html>')

    zips = survey_service.write_html_template('id1', survey_dict, 'test_prefix',
                                              segments)

    self.assertEqual(len(zips), len(segments))
    self.assertEqual(zips[0].filename, '/tmp/test_prefix_segment1.zip')
    self.assertEqual(zips[1].filename, '/tmp/test_prefix_segment2.zip')
    self.assertEqual(zips[2].filename, '/tmp/test_prefix_segment3.zip')

  def test_delete_tmp_zip_files(self):
    os.remove = mock.Mock()
    with tempfile.TemporaryDirectory() as tmpdir:
      zip1 = zipfile.ZipFile(tmpdir + 'mock-file-name.zip', 'w')
      zip2 = zipfile.ZipFile(tmpdir + 'mock-file-name2.zip', 'w')

      survey_service.delete_tmp_zip_files([zip1, zip2])

      expected_calls = [mock.call(zip1.filename), mock.call(zip2.filename)]
      os.remove.assert_has_calls(expected_calls)
      assert os.remove.call_count == 2

  def test_get_all_question_text(self):
    survey_dict = {
        'question1': 'q1 text',
        'question2': 'q2 text',
    }

    result = survey_service.get_all_question_text(survey_dict)

    self.assertEqual(result, ['q1 text', 'q2 text'])

  def test_get_all_question_text_includes_text_after_missed_questions(self):
    survey_dict = {
        'question1': 'q1 text',
        'question2': 'q3 text',
    }

    result = survey_service.get_all_question_text(survey_dict)

    self.assertEqual(result, ['q1 text', 'q3 text'])

  def test_get_question_json_converts_to_format_expected_by_creative(self):
    survey = {
        'question1': 'q1 text',
        'question1type': 'q1type',
        'question1order': 'ORDERED',
        'answer1a': 'a1a',
        'answer1anext': 'end',
        'answer1b': 'a1b',
        'answer1bnext': '2',
        'question2': 'q2 text',
    }
    json = survey_service.get_question_json(survey)
    self.assertDictEqual(
        json[0], {
            'answersOrder': 'ORDERED',
            'id':
                1,
            'text':
                'q1 text',
            'type':
                'q1type',
            'next_question': {
                'A': 'end',
                'B': '2'
            },
            'options': [
                {
                    'id': 'A',
                    'role': 'option',
                    'text': 'a1a'
                },
                {
                    'id': 'B',
                    'role': 'option',
                    'text': 'a1b'
                },
            ]
        })
    self.assertEqual(json[1]['text'], 'q2 text')

  def test_get_thank_you_text_default(self):
    text = survey_service.get_thank_you_text({'language': 'ms'})
    self.assertEqual(text, 'Terima Kasih')

  def test_get_thank_you_text_defaults_to_english_if_no_lang(self):
    text = survey_service.get_thank_you_text({})
    self.assertEqual(text, 'Thank You')

  def test_get_thank_you_text_defaults_to_english_if_unknown_lang(self):
    text = survey_service.get_thank_you_text({'language': 'ZZ'})
    self.assertEqual(text, 'Thank You')

  def test_get_next_text_default(self):
    text = survey_service.get_next_text({'language': 'ja'})
    self.assertEqual(text, '次へ')

  def test_get_next_text_defaults_to_english_if_no_lang(self):
    text = survey_service.get_next_text({})
    self.assertEqual(text, 'Next')

  def test_get_next_text_defaults_to_english_if_unknown_lang(self):
    text = survey_service.get_next_text({'language': 'ZZ'})
    self.assertEqual(text, 'Next')

  def test_get_comment_text_default(self):
    text = survey_service.get_comment_text({'language': 'zh'})
    self.assertEqual(text, '选择所有适用的')

  def test_get_comment_text_defaults_to_english_if_no_lang(self):
    text = survey_service.get_comment_text({})
    self.assertEqual(text, 'Choose all applicable')

  def test_get_comment_text_defaults_to_english_if_unknown_lang(self):
    text = survey_service.get_comment_text({'language': 'ZZ'})
    self.assertEqual(text, 'Choose all applicable')

  def test_get_survey_responses_limits_to_given_survey(self):
    mock_bigquery_client = mock.create_autospec(bigquery.Client)

    survey_service.get_survey_responses(12345, client=mock_bigquery_client)

    query_call = mock_bigquery_client.query.mock_calls[0]
    sql = query_call.args[0]
    survey_param = query_call.kwargs['job_config'].query_parameters[0]
    self.assertRegex(sql, 'WHERE ID = @survey_id')
    self.assertEqual(survey_param.name, 'survey_id')
    self.assertEqual(survey_param.value, 12345)

  @mock.patch.object(survey_service, 'get_survey_responses')
  def test_download_responses(self, responses_mock):
    t1 = datetime.datetime.now()
    t2 = t1 + datetime.timedelta(hours=-1)
    responses_mock.return_value = pandas.DataFrame({
        'CreatedAt': [t1, t2],
        'Segmentation': ['seg1', 'seg2'],
        'Response': ['1:r1a', '1:r2b']
    })

    raw_csv = survey_service.download_responses(1)
    responses = list(csv.DictReader(raw_csv.splitlines()))

    self.assertEqual(responses[0]['Date'], str(t1))
    self.assertEqual(responses[0]['Control/Expose'], 'seg1')
    self.assertEqual(responses[0]['Response 1'], 'r1a')
    self.assertEqual(responses[1]['Date'], str(t2))
    self.assertEqual(responses[1]['Control/Expose'], 'seg2')
    self.assertEqual(responses[1]['Response 1'], 'r2b')

  @mock.patch.object(survey_service, 'get_survey_responses')
  def test_download_responses_with_multi_question_responses(
      self, responses_mock):
    responses_mock.return_value = pandas.DataFrame({
        'CreatedAt': [datetime.datetime.now()],
        'Segmentation': ['seg1'],
        'Response': ['1:r1a|2:r1b||4:r1e|']
    })

    raw_csv = survey_service.download_responses(surveyid=1234)
    responses = list(csv.DictReader(raw_csv.splitlines()))

    self.assertEqual(responses[0]['Response 1'], 'r1a')
    self.assertEqual(responses[0]['Response 2'], 'r1b')
    self.assertEqual(responses[0]['Response 3'], '')
    self.assertEqual(responses[0]['Response 4'], 'r1e')
    self.assertEqual(responses[0]['Response 5'], '')

  @mock.patch.object(survey_service, 'get_survey_responses')
  def test_download_responses_returns_empty_csv_with_no_responses(
      self, responses_mock):
    responses_mock.return_value = pandas.DataFrame({
        'CreatedAt': [],
        'Segmentation': [],
        'Response': []
    })

    raw_csv = survey_service.download_responses(surveyid=1234)
    self.assertEqual(raw_csv.strip(), 'Date,Control/Expose,Dimension 2')

  @mock.patch.object(survey_service, 'get_survey_responses')
  def test_get_brand_lift_results(self, responses_mock):
    responses_mock.return_value = pandas.DataFrame({
        'CreatedAt': 4 * [datetime.datetime.now()],
        'Segmentation': ['expose', 'expose', 'control', 'control'],
        'Response': ['1:A', '1:A', '1:A', '1:B']
    })

    results = survey_service.get_brand_lift_results(1)
    q1_expose_lift = results[0][0]
    q1_control_lift = results[0][1]
    q1_lift = results[0][2]

    # Expose group has A for 2/2 of answers
    self.assertEqual(1, q1_expose_lift[0])
    # Expose group has B for 0/2 of answers
    self.assertEqual(0, q1_expose_lift[1])

    # Control group has A for 1/2 of answers
    self.assertEqual(0.5, q1_control_lift[0])
    # Control group has B for 1/2 of answers
    self.assertEqual(0.5, q1_control_lift[1])

    # Answer A is 100% more likely in expose group
    self.assertEqual(1, q1_lift[0])
    # Answer B is 100% less likely in expose group
    self.assertEqual(-1, q1_lift[1])

  @mock.patch.object(survey_service, 'get_survey_responses')
  def test_get_brand_lift_results_with_multi_question_responses(
      self, responses_mock):
    responses_mock.return_value = pandas.DataFrame({
        'CreatedAt': 4 * [datetime.datetime.now()],
        'Segmentation': ['expose', 'expose', 'control', 'control'],
        'Response': ['1:A|2:A', '1:A|2:B', '1:A|2:B', '1:B|2:B']
    })

    results = survey_service.get_brand_lift_results(1)
    q2_expose_lift = results[1][0]
    q2_control_lift = results[1][1]
    q2_lift = results[1][2]

    # Expose group has A for 1/2 of answers
    self.assertEqual(0.5, q2_expose_lift[0])
    # Expose group has B for 1/2 of answers
    self.assertEqual(0.5, q2_expose_lift[1])

    # Control group has A for 0/2 of answers
    self.assertEqual(0, q2_control_lift[0])
    # Control group has B for 2/2 of answers
    self.assertEqual(1, q2_control_lift[1])

    # Answer A is infinitely more likely in expose group (since 0 in control)
    self.assertEqual(math.inf, q2_lift[0])
    # Answer B is 50% less likely in expose group
    self.assertEqual(-0.5, q2_lift[1])

  @mock.patch.object(survey_service, 'get_survey_responses')
  def test_get_brand_lift_results_with_no_responses(self, responses_mock):
    responses_mock.return_value = pandas.DataFrame({
        'CreatedAt': [],
        'Segmentation': [],
        'Response': []
    })

    results = survey_service.get_brand_lift_results(1)

    self.assertEqual(results, [])

  @mock.patch.object(survey_service, 'get_survey_responses')
  def test_get_brand_lift_results_corrects_segment_names(self, responses_mock):
    responses_mock.return_value = pandas.DataFrame({
        'CreatedAt': 3 * [datetime.datetime.now()],
        'Segmentation': ['default_expose', 'default_expose', 'default_control'],
        'Response': ['1:A', '1:A', '1:B']
    })

    results = survey_service.get_brand_lift_results(1)
    q1_expose_lift = results[0][0]
    q1_control_lift = results[0][1]

    # Expose group has A for 2/2 of answers
    self.assertEqual(1, q1_expose_lift[0])
    # Expose group has B for 0/2 of answers
    self.assertEqual(0, q1_expose_lift[1])

    # Control group has A for 0/1 of answers
    self.assertEqual(0, q1_control_lift[0])
    # Control group has B for 1/1 of answers
    self.assertEqual(1, q1_control_lift[1])