Example #1
0
 def make_dataset(self):
     citizens = [
         # Первого жителя создаем с родственником. В запросе к
         # PATCH-обработчику список relatives будет содержать только другого
         # жителя, что потребует выполнения максимального кол-ва запросов
         # (как на добавление новой родственной связи, так и на удаление
         # существующей).
         generate_citizen(citizen_id=1, relatives=[2]),
         generate_citizen(citizen_id=2, relatives=[1]),
         *generate_citizens(
             citizens_num=9998, relations_num=1000, start_citizen_id=3)
     ]
     return {citizen['citizen_id']: citizen for citizen in citizens}
async def test_get_citizens_birthdays(api_client, dataset):
    # Перед прогоном каждого теста добавляем в БД дополнительную выгрузку с
    # двумя родственниками, чтобы убедиться, что обработчик различает жителей
    # разных выгрузок.
    await import_data(api_client, [
        generate_citizen(citizen_id=1, relatives=[2]),
        generate_citizen(citizen_id=2, relatives=[1])
    ])

    # Проверяем обработчик на указанных данных
    import_id = await import_data(api_client, dataset['citizens'])
    result = await get_citizens_birthdays(api_client, import_id)

    for month in dataset['expected']:
        assert month in result, f'Month {month} is missing'

        actual = {(citizen['citizen_id'], citizen['presents'])
                  for citizen in result[month]}
        expected = {(citizen['citizen_id'], citizen['presents'])
                    for citizen in dataset['expected'][month]}
        assert actual == expected
async def test_get_ages(api_client, dataset):
    # Перед прогоном каждого теста добавим в БД дополнительную выгрузку с
    # жителем из другого города, чтобы убедиться, что обработчик различает
    # жителей разных выгрузок.
    await import_data(api_client,
                      [generate_citizen(citizen_id=1, town='Санкт-Петербург')])

    import_id = await import_data(api_client, dataset['citizens'])
    result = await get_citizens_ages(api_client, import_id)

    assert len(dataset['expected']) == len(result), 'Towns number is different'
    actual_towns_map = {town['town']: town for town in result}

    for town in dataset['expected']:
        assert town['town'] in actual_towns_map
        actual_town = actual_towns_map[town['town']]

        for percentile in ['p50', 'p75', 'p99']:
            assert town[percentile] == actual_town[percentile], (
                f"{town['town']} {percentile} {actual_town[percentile]} does "
                f"not match expected value {town[percentile]}")
async def test_patch_self_relative(api_client):
    """
    Проверяем что жителю можно указать себя родственником.
    Напоминает фильм Патруль времени, не так ли?
    """
    dataset = [
        generate_citizen(citizen_id=1,
                         name='Джейн',
                         gender='male',
                         birth_date='13.09.1945',
                         town='Нью-Йорк',
                         relatives=[]),
    ]
    import_id = await import_data(api_client, dataset)

    dataset[0]['relatives'] = [dataset[0]['citizen_id']]
    actual = await patch_citizen(
        api_client,
        import_id,
        dataset[0]['citizen_id'],
        data={k: v
              for k, v in dataset[0].items() if k != 'citizen_id'})
    assert compare_citizens(dataset[0], actual)
async def test_patch_citizen(api_client):
    """
    Проверяет, что данные о жителе и его родственниках успешно обновляются.
    """

    # Перед прогоном каждого теста добавляем в БД дополнительную выгрузку с
    # тремя жителями и одинаковыми идентификаторами, чтобы убедиться, что
    # обработчик различает жителей разных выгрузок и изменения не затронут
    # жителей другой выгрузки.
    side_dataset = [
        generate_citizen(citizen_id=1),
        generate_citizen(citizen_id=2),
        generate_citizen(citizen_id=3)
    ]
    side_dataset_id = await import_data(api_client, side_dataset)

    # Создаем выгрузку с тремя жителями, два из которых родственники для
    # тестирования изменений.
    dataset = [
        generate_citizen(citizen_id=1,
                         name='Иванов Иван Иванович',
                         gender=Gender.male.value,
                         birth_date='01.01.2000',
                         town='Некий город',
                         street='Некая улица',
                         building='Некое строение',
                         apartment=1,
                         relatives=[2]),
        generate_citizen(citizen_id=2, relatives=[1]),
        generate_citizen(citizen_id=3, relatives=[]),
    ]
    import_id = await import_data(api_client, dataset)

    # Обновляем часть полей о жителе, чтобы убедиться что PATCH позволяет
    # передавать только некоторые поля.
    # Данные меняем сразу в выгрузке, чтобы потом было легче сравнивать.
    dataset[0]['name'] = 'Иванова Иванна Ивановна'
    await patch_citizen(api_client,
                        import_id,
                        dataset[0]['citizen_id'],
                        data={'name': dataset[0]['name']})

    # Обновляем другую часть данных, чтобы проверить что данные обновляются.
    dataset[0]['gender'] = Gender.female.value
    dataset[0]['birth_date'] = '02.02.2002'
    dataset[0]['town'] = 'Другой город'
    dataset[0]['street'] = 'Другая улица'
    dataset[0]['building'] = 'Другое строение'
    dataset[0]['apartment'] += 1
    # У жителя #1 одна родственная связь должна исчезнуть (с жителем #2),
    # и одна появиться (с жителем #3).
    dataset[0]['relatives'] = [dataset[2]['citizen_id']]
    # Родственные связи должны быть двусторонними:
    # - у жителя #2 родственная связь с жителем #1 удаляется
    # - у жителя #3 родственная связь с жителем #1 добавляется.
    dataset[2]['relatives'].append(dataset[0]['citizen_id'])
    dataset[1]['relatives'].remove(dataset[0]['citizen_id'])

    actual = await patch_citizen(api_client,
                                 import_id,
                                 dataset[0]['citizen_id'],
                                 data={
                                     'gender': dataset[0]['gender'],
                                     'birth_date': dataset[0]['birth_date'],
                                     'town': dataset[0]['town'],
                                     'street': dataset[0]['street'],
                                     'building': dataset[0]['building'],
                                     'apartment': dataset[0]['apartment'],
                                     'relatives': dataset[0]['relatives'],
                                 })

    # Проверяем, что житель корректно обновился
    assert compare_citizens(dataset[0], actual)

    # Проверяем всю выгрузку, чтобы убедиться что родственные связи всех
    # жителей изменились корректно.
    actual_citizens = await get_citizens(api_client, import_id)
    assert compare_citizen_groups(actual_citizens, dataset)

    # Проверяем, что изменение жителя в тестируемой выгрузке не испортило
    # данные в дополнительной выгрузке.
    actual_citizens = await get_citizens(api_client, side_dataset_id)
    assert compare_citizen_groups(actual_citizens, side_dataset)
from analyzer.api.schema import BIRTH_DATE_FORMAT
from analyzer.utils.pg import MAX_INTEGER
from analyzer.utils.testing import (
    compare_citizen_groups,
    generate_citizen,
    generate_citizens,
    get_citizens,
    import_data,
)

LONGEST_STR = 'ё' * 256
CASES = (
    # Житель без родственников.
    # Обработчик должен корректно создавать выгрузку с одним жителем.
    ([generate_citizen(relatives=[])], HTTPStatus.CREATED),

    # Житель с несколькими родственниками.
    # Обработчик должен корректно добавлять жителей и создавать
    # родственные связи.
    ([
        generate_citizen(citizen_id=1, relatives=[2, 3]),
        generate_citizen(citizen_id=2, relatives=[1]),
        generate_citizen(citizen_id=3, relatives=[1])
    ], HTTPStatus.CREATED),

    # Житель сам себе родственник.
    # Обработчик должен позволять создавать такие родственные связи.
    ([
        generate_citizen(citizen_id=1,
                         name='Джейн',
    Позволяет записать ожидаемый ответ в краткой форме.
    """
    return {
        str(month): values.get(str(month), []) if values else []
        for month in range(1, 13)
    }


datasets = [
    # Житель, у которого несколько родственников.
    # Обработчик должен корректно показывать сколько подарков приобретет
    # житель #1 своим родственникам в каждом месяце.
    {
        'citizens': [
            generate_citizen(citizen_id=1,
                             birth_date='31.12.2019',
                             relatives=[2, 3]),
            generate_citizen(citizen_id=2,
                             birth_date='11.02.2020',
                             relatives=[1]),
            generate_citizen(citizen_id=3,
                             birth_date='17.02.2020',
                             relatives=[1])
        ],
        'expected':
        make_response({
            '2': [{
                'citizen_id': 1,
                'presents': 2
            }],
            '12': [{
    Позволяет представлять дату рождения жителя в виде возраста человека в днях
    и годах, что гораздо нагляднее в тестах.
    """
    birth_date = copy(base_date).replace(year=base_date.year - years)
    birth_date -= timedelta(days=days)
    return birth_date.strftime(BIRTH_DATE_FORMAT)


datasets = [
    # Несколько жителей у которых завтра день рождения.
    # Проверяется что обработчик использует в рассчетах количество полных лет.
    {
        'citizens': [
            generate_citizen(birth_date=age2date(years=10, days=364),
                             town='Москва',
                             citizen_id=1),
            generate_citizen(birth_date=age2date(years=30, days=364),
                             town='Москва',
                             citizen_id=2),
            generate_citizen(birth_date=age2date(years=50, days=364),
                             town='Москва',
                             citizen_id=3)
        ],
        'expected': [{
            'town': 'Москва',
            'p50': 30.,
            'p75': 40.,
            'p99': 49.6
        }]
    },