Merge branch 'master' into fix_character_names

This commit is contained in:
ErikKalkoken
2022-02-01 00:31:25 +01:00
30 changed files with 997 additions and 358 deletions

View File

@@ -1,6 +1,11 @@
import logging
from typing import Union
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from esi.models import Token
from allianceauth.notifications import notify
from . import providers
from .evelinks import eveimageserver
@@ -13,7 +18,10 @@ from .managers import (
EveCorporationProviderManager,
)
logger = logging.getLogger(__name__)
_DEFAULT_IMAGE_SIZE = 32
DOOMHEIM_CORPORATION_ID = 1000001
class EveFactionInfo(models.Model):
@@ -81,10 +89,9 @@ class EveAllianceInfo(models.Model):
EveCorporationInfo.objects.filter(corporation_id__in=alliance.corp_ids).update(
alliance=self
)
EveCorporationInfo.objects\
.filter(alliance=self)\
.exclude(corporation_id__in=alliance.corp_ids)\
.update(alliance=None)
EveCorporationInfo.objects.filter(alliance=self).exclude(
corporation_id__in=alliance.corp_ids
).update(alliance=None)
def update_alliance(self, alliance: providers.Alliance = None):
if alliance is None:
@@ -219,6 +226,14 @@ class EveCharacter(models.Model):
models.Index(fields=['faction_id',]),
]
def __str__(self):
return self.character_name
@property
def is_biomassed(self) -> bool:
"""Whether this character is dead or not."""
return self.corporation_id == DOOMHEIM_CORPORATION_ID
@property
def alliance(self) -> Union[EveAllianceInfo, None]:
"""
@@ -263,10 +278,36 @@ class EveCharacter(models.Model):
self.faction_id = character.faction.id
self.faction_name = character.faction.name
self.save()
if self.is_biomassed:
self._remove_tokens_of_biomassed_character()
return self
def __str__(self):
return self.character_name
def _remove_tokens_of_biomassed_character(self) -> None:
"""Remove tokens of this biomassed character."""
try:
user = self.character_ownership.user
except ObjectDoesNotExist:
return
tokens_to_delete = Token.objects.filter(character_id=self.character_id)
tokens_count = tokens_to_delete.count()
if not tokens_count:
return
tokens_to_delete.delete()
logger.info(
"%d tokens from user %s for biomassed character %s [id:%s] deleted.",
tokens_count,
user,
self,
self.character_id,
)
notify(
user=user,
title=f"Character {self} biomassed",
message=(
f"Your former character {self} has been biomassed "
"and has been removed from the list of your alts."
)
)
@staticmethod
def generic_portrait_url(

View File

@@ -170,7 +170,7 @@ class EveProvider:
"""
:return: an ItemType object for the given ID
"""
raise NotImplemented()
raise NotImplementedError()
class EveSwaggerProvider(EveProvider):
@@ -207,7 +207,8 @@ class EveSwaggerProvider(EveProvider):
def __str__(self):
return 'esi'
def get_alliance(self, alliance_id):
def get_alliance(self, alliance_id: int) -> Alliance:
"""Fetch alliance from ESI."""
try:
data = self.client.Alliance.get_alliances_alliance_id(alliance_id=alliance_id).result()
corps = self.client.Alliance.get_alliances_alliance_id_corporations(alliance_id=alliance_id).result()
@@ -223,7 +224,8 @@ class EveSwaggerProvider(EveProvider):
except HTTPNotFound:
raise ObjectNotFound(alliance_id, 'alliance')
def get_corp(self, corp_id):
def get_corp(self, corp_id: int) -> Corporation:
"""Fetch corporation from ESI."""
try:
data = self.client.Corporation.get_corporations_corporation_id(corporation_id=corp_id).result()
model = Corporation(
@@ -239,29 +241,43 @@ class EveSwaggerProvider(EveProvider):
except HTTPNotFound:
raise ObjectNotFound(corp_id, 'corporation')
def get_character(self, character_id):
def get_character(self, character_id: int) -> Character:
"""Fetch character from ESI."""
try:
data = self.client.Character.get_characters_character_id(character_id=character_id).result()
character_name = self._fetch_character_name(character_id)
affiliation = self.client.Character.post_characters_affiliation(characters=[character_id]).result()[0]
model = Character(
id=character_id,
name=data['name'],
name=character_name,
corp_id=affiliation['corporation_id'],
alliance_id=affiliation['alliance_id'] if 'alliance_id' in affiliation else None,
faction_id=affiliation['faction_id'] if 'faction_id' in affiliation else None,
)
return model
except (HTTPNotFound, HTTPUnprocessableEntity):
except (HTTPNotFound, HTTPUnprocessableEntity, ObjectNotFound):
raise ObjectNotFound(character_id, 'character')
def _fetch_character_name(self, character_id: int) -> str:
"""Fetch character name from ESI."""
data = self.client.Universe.post_universe_names(ids=[character_id]).result()
character = data.pop() if data else None
if (
not character
or character["category"] != "character"
or character["id"] != character_id
):
raise ObjectNotFound(character_id, 'character')
return character["name"]
def get_all_factions(self):
"""Fetch all factions from ESI."""
if not self._faction_list:
self._faction_list = self.client.Universe.get_universe_factions().result()
return self._faction_list
def get_faction(self, faction_id):
faction_id=int(faction_id)
def get_faction(self, faction_id: int):
"""Fetch faction from ESI."""
faction_id = int(faction_id)
try:
if not self._faction_list:
_ = self.get_all_factions()
@@ -273,7 +289,8 @@ class EveSwaggerProvider(EveProvider):
except (HTTPNotFound, HTTPUnprocessableEntity, KeyError):
raise ObjectNotFound(faction_id, 'faction')
def get_itemtype(self, type_id):
def get_itemtype(self, type_id: int) -> ItemType:
"""Fetch inventory item from ESI."""
try:
data = self.client.Universe.get_universe_types_type_id(type_id=type_id).result()
return ItemType(id=type_id, name=data['name'])

View File

@@ -1,12 +1,11 @@
import logging
from celery import shared_task
from .models import EveAllianceInfo
from .models import EveCharacter
from .models import EveCorporationInfo
from .models import EveAllianceInfo, EveCharacter, EveCorporationInfo
from . import providers
logger = logging.getLogger(__name__)
TASK_PRIORITY = 7
@@ -32,8 +31,8 @@ def update_alliance(alliance_id):
@shared_task
def update_character(character_id):
"""Update given character from ESI"""
def update_character(character_id: int) -> None:
"""Update given character from ESI."""
EveCharacter.objects.update_character(character_id)
@@ -65,17 +64,17 @@ def update_character_chunk(character_ids_chunk: list):
.post_characters_affiliation(characters=character_ids_chunk).result()
character_names = providers.provider.client.Universe\
.post_universe_names(ids=character_ids_chunk).result()
except:
except OSError:
logger.info("Failed to bulk update characters. Attempting single updates")
for character_id in character_ids_chunk:
update_character.apply_async(
args=[character_id], priority=TASK_PRIORITY
)
args=[character_id], priority=TASK_PRIORITY
)
return
affiliations = {
affiliation.get('character_id'): affiliation
for affiliation in affiliations_raw
affiliation.get('character_id'): affiliation
for affiliation in affiliations_raw
}
# add character names to affiliations
for character in character_names:
@@ -108,5 +107,5 @@ def update_character_chunk(character_ids_chunk: list):
if corp_changed or alliance_changed or name_changed:
update_character.apply_async(
args=[character.get('character_id')], priority=TASK_PRIORITY
)
args=[character.get('character_id')], priority=TASK_PRIORITY
)

View File

@@ -0,0 +1,168 @@
from bravado.exception import HTTPNotFound
class BravadoResponseStub:
"""Stub for IncomingResponse in bravado, e.g. for HTTPError exceptions"""
def __init__(
self, status_code, reason="", text="", headers=None, raw_bytes=None
) -> None:
self.reason = reason
self.status_code = status_code
self.text = text
self.headers = headers if headers else dict()
self.raw_bytes = raw_bytes
def __str__(self):
return f"{self.status_code} {self.reason}"
class BravadoOperationStub:
"""Stub to simulate the operation object return from bravado via django-esi"""
class RequestConfig:
def __init__(self, also_return_response):
self.also_return_response = also_return_response
class ResponseStub:
def __init__(self, headers):
self.headers = headers
def __init__(self, data, headers: dict = None, also_return_response: bool = False):
self._data = data
self._headers = headers if headers else {"x-pages": 1}
self.request_config = BravadoOperationStub.RequestConfig(also_return_response)
def result(self, **kwargs):
if self.request_config.also_return_response:
return [self._data, self.ResponseStub(self._headers)]
else:
return self._data
def results(self, **kwargs):
return self.result(**kwargs)
class EsiClientStub:
"""Stub for an ESI client."""
class Alliance:
@staticmethod
def get_alliances_alliance_id(alliance_id):
data = {
3001: {
"name": "Wayne Enterprises",
"ticker": "WYE",
"executor_corporation_id": 2001
}
}
try:
return BravadoOperationStub(data[int(alliance_id)])
except KeyError:
response = BravadoResponseStub(
404, f"Alliance with ID {alliance_id} not found"
)
raise HTTPNotFound(response)
@staticmethod
def get_alliances_alliance_id_corporations(alliance_id):
data = [2001, 2002, 2003]
return BravadoOperationStub(data)
class Character:
@staticmethod
def get_characters_character_id(character_id):
data = {
1001: {
"corporation_id": 2001,
"name": "Bruce Wayne",
},
1002: {
"corporation_id": 2001,
"name": "Peter Parker",
},
1011: {
"corporation_id": 2011,
"name": "Lex Luthor",
}
}
try:
return BravadoOperationStub(data[int(character_id)])
except KeyError:
response = BravadoResponseStub(
404, f"Character with ID {character_id} not found"
)
raise HTTPNotFound(response)
@staticmethod
def post_characters_affiliation(characters: list):
data = [
{'character_id': 1001, 'corporation_id': 2001, 'alliance_id': 3001},
{'character_id': 1002, 'corporation_id': 2001, 'alliance_id': 3001},
{'character_id': 1011, 'corporation_id': 2011},
{'character_id': 1666, 'corporation_id': 1000001},
]
return BravadoOperationStub(
[x for x in data if x['character_id'] in characters]
)
class Corporation:
@staticmethod
def get_corporations_corporation_id(corporation_id):
data = {
2001: {
"ceo_id": 1091,
"member_count": 10,
"name": "Wayne Technologies",
"ticker": "WTE",
"alliance_id": 3001
},
2002: {
"ceo_id": 1092,
"member_count": 10,
"name": "Wayne Food",
"ticker": "WFO",
"alliance_id": 3001
},
2003: {
"ceo_id": 1093,
"member_count": 10,
"name": "Wayne Energy",
"ticker": "WEG",
"alliance_id": 3001
},
2011: {
"ceo_id": 1,
"member_count": 3,
"name": "LexCorp",
"ticker": "LC",
},
1000001: {
"ceo_id": 3000001,
"creator_id": 1,
"description": "The internal corporation used for characters in graveyard.",
"member_count": 6329026,
"name": "Doomheim",
"ticker": "666",
}
}
try:
return BravadoOperationStub(data[int(corporation_id)])
except KeyError:
response = BravadoResponseStub(
404, f"Corporation with ID {corporation_id} not found"
)
raise HTTPNotFound(response)
class Universe:
@staticmethod
def post_universe_names(ids: list):
data = [
{"category": "character", "id": 1001, "name": "Bruce Wayne"},
{"category": "character", "id": 1002, "name": "Peter Parker"},
{"category": "character", "id": 1011, "name": "Lex Luthor"},
{"category": "character", "id": 1666, "name": "Hal Jordan"},
{"category": "corporation", "id": 2001, "name": "Wayne Technologies"},
{"category": "corporation","id": 2002, "name": "Wayne Food"},
{"category": "corporation","id": 1000001, "name": "Doomheim"},
]
return BravadoOperationStub([x for x in data if x['id'] in ids])

View File

@@ -1,12 +1,15 @@
from unittest.mock import Mock, patch
from django.core.exceptions import ObjectDoesNotExist
from django.test import TestCase
from esi.models import Token
from allianceauth.tests.auth_utils import AuthUtils
from ..models import (
EveCharacter, EveCorporationInfo, EveAllianceInfo, EveFactionInfo
)
from ..providers import Alliance, Corporation, Character
from ..evelinks import eveimageserver
from ..models import EveAllianceInfo, EveCharacter, EveCorporationInfo, EveFactionInfo
from ..providers import Alliance, Character, Corporation
from .esi_client_stub import EsiClientStub
class EveCharacterTestCase(TestCase):
@@ -402,8 +405,8 @@ class EveAllianceTestCase(TestCase):
my_alliance.save()
my_alliance.populate_alliance()
for corporation in EveCorporationInfo.objects\
.filter(corporation_id__in=[2001, 2002]
for corporation in (
EveCorporationInfo.objects.filter(corporation_id__in=[2001, 2002])
):
self.assertEqual(corporation.alliance, my_alliance)
@@ -587,3 +590,98 @@ class EveCorporationTestCase(TestCase):
self.my_corp.logo_url_256,
'https://images.evetech.net/corporations/2001/logo?size=256'
)
@patch('allianceauth.eveonline.providers.esi_client_factory')
@patch("allianceauth.eveonline.models.notify")
class TestCharacterUpdate(TestCase):
def test_should_update_normal_character(self, mock_notify, mock_esi_client_factory):
# given
mock_esi_client_factory.return_value = EsiClientStub()
my_character = EveCharacter.objects.create(
character_id=1001,
character_name="not my name",
corporation_id=2002,
corporation_name="Wayne Food",
corporation_ticker="WYF",
alliance_id=None
)
# when
my_character.update_character()
# then
my_character.refresh_from_db()
self.assertEqual(my_character.character_name, "Bruce Wayne")
self.assertEqual(my_character.corporation_id, 2001)
self.assertEqual(my_character.corporation_name, "Wayne Technologies")
self.assertEqual(my_character.corporation_ticker, "WTE")
self.assertEqual(my_character.alliance_id, 3001)
self.assertEqual(my_character.alliance_name, "Wayne Enterprises")
self.assertEqual(my_character.alliance_ticker, "WYE")
self.assertFalse(mock_notify.called)
def test_should_update_dead_character_with_owner(
self, mock_notify, mock_esi_client_factory
):
# given
mock_esi_client_factory.return_value = EsiClientStub()
character_1666 = EveCharacter.objects.create(
character_id=1666,
character_name="Hal Jordan",
corporation_id=2002,
corporation_name="Wayne Food",
corporation_ticker="WYF",
alliance_id=None
)
user = AuthUtils.create_user("Bruce Wayne")
token_1666 = Token.objects.create(
user=user,
character_id=character_1666.character_id,
character_name=character_1666.character_name,
character_owner_hash="ABC123-1666",
)
character_1001 = EveCharacter.objects.create(
character_id=1001,
character_name="Bruce Wayne",
corporation_id=2001,
corporation_name="Wayne Technologies",
corporation_ticker="WYT",
alliance_id=None
)
token_1001 = Token.objects.create(
user=user,
character_id=character_1001.character_id,
character_name=character_1001.character_name,
character_owner_hash="ABC123-1001",
)
# when
character_1666.update_character()
# then
character_1666.refresh_from_db()
self.assertTrue(character_1666.is_biomassed)
self.assertNotIn(token_1666, user.token_set.all())
self.assertIn(token_1001, user.token_set.all())
with self.assertRaises(ObjectDoesNotExist):
self.assertTrue(character_1666.character_ownership)
user.profile.refresh_from_db()
self.assertIsNone(user.profile.main_character)
self.assertTrue(mock_notify.called)
def test_should_handle_dead_character_without_owner(
self, mock_notify, mock_esi_client_factory
):
# given
mock_esi_client_factory.return_value = EsiClientStub()
character_1666 = EveCharacter.objects.create(
character_id=1666,
character_name="Hal Jordan",
corporation_id=1011,
corporation_name="LexCorp",
corporation_ticker='LC',
alliance_id=None
)
# when
character_1666.update_character()
# then
character_1666.refresh_from_db()
self.assertTrue(character_1666.is_biomassed)
self.assertFalse(mock_notify.called)

View File

@@ -7,6 +7,7 @@ from jsonschema.exceptions import RefResolutionError
from django.test import TestCase
from . import set_logger
from .esi_client_stub import EsiClientStub
from ..providers import (
ObjectNotFound,
Entity,
@@ -632,13 +633,7 @@ class TestEveSwaggerProvider(TestCase):
@patch(MODULE_PATH + '.esi_client_factory')
def test_get_character(self, mock_esi_client_factory):
mock_esi_client_factory.return_value \
.Character.get_characters_character_id \
= TestEveSwaggerProvider.esi_get_characters_character_id
mock_esi_client_factory.return_value \
.Character.post_characters_affiliation \
= TestEveSwaggerProvider.esi_post_characters_affiliation
mock_esi_client_factory.return_value = EsiClientStub()
my_provider = EveSwaggerProvider()
# character with alliance
@@ -649,8 +644,8 @@ class TestEveSwaggerProvider(TestCase):
self.assertEqual(my_character.alliance_id, 3001)
# character wo/ alliance
my_character = my_provider.get_character(1002)
self.assertEqual(my_character.id, 1002)
my_character = my_provider.get_character(1011)
self.assertEqual(my_character.id, 1011)
self.assertEqual(my_character.alliance_id, None)
# character not found

View File

@@ -1,245 +1,271 @@
from unittest.mock import patch, Mock
from unittest.mock import patch
from django.test import TestCase
from django.test import TestCase, TransactionTestCase, override_settings
from ..models import EveCharacter, EveCorporationInfo, EveAllianceInfo
from ..models import EveAllianceInfo, EveCharacter, EveCorporationInfo
from ..tasks import (
run_model_update,
update_alliance,
update_corp,
update_character,
run_model_update
update_character_chunk,
update_corp,
)
from .esi_client_stub import EsiClientStub
class TestTasks(TestCase):
@patch('allianceauth.eveonline.tasks.EveCorporationInfo')
def test_update_corp(self, mock_EveCorporationInfo):
update_corp(42)
self.assertEqual(
mock_EveCorporationInfo.objects.update_corporation.call_count, 1
)
self.assertEqual(
mock_EveCorporationInfo.objects.update_corporation.call_args[0][0], 42
@patch('allianceauth.eveonline.providers.esi_client_factory')
class TestUpdateTasks(TestCase):
def test_should_update_alliance(self, mock_esi_client_factory):
# given
mock_esi_client_factory.return_value = EsiClientStub()
my_alliance = EveAllianceInfo.objects.create(
alliance_id=3001,
alliance_name="Wayne Enterprises",
alliance_ticker="WYE",
executor_corp_id=2003
)
# when
update_alliance(my_alliance.alliance_id)
# then
my_alliance.refresh_from_db()
self.assertEqual(my_alliance.executor_corp_id, 2001)
@patch('allianceauth.eveonline.tasks.EveAllianceInfo')
def test_update_alliance(self, mock_EveAllianceInfo):
update_alliance(42)
self.assertEqual(
mock_EveAllianceInfo.objects.update_alliance.call_args[0][0], 42
)
self.assertEqual(
mock_EveAllianceInfo.objects
.update_alliance.return_value.populate_alliance.call_count, 1
def test_should_update_character(self, mock_esi_client_factory):
# given
mock_esi_client_factory.return_value = EsiClientStub()
my_character = EveCharacter.objects.create(
character_id=1001,
character_name="Bruce Wayne",
corporation_id=2002,
corporation_name="Wayne Food",
corporation_ticker="WYF",
alliance_id=None
)
# when
update_character(my_character.character_id)
# then
my_character.refresh_from_db()
self.assertEqual(my_character.corporation_id, 2001)
@patch('allianceauth.eveonline.tasks.EveCharacter')
def test_update_character(self, mock_EveCharacter):
update_character(42)
self.assertEqual(
mock_EveCharacter.objects.update_character.call_count, 1
def test_should_update_corp(self, mock_esi_client_factory):
# given
mock_esi_client_factory.return_value = EsiClientStub()
EveAllianceInfo.objects.create(
alliance_id=3001,
alliance_name="Wayne Enterprises",
alliance_ticker="WYE",
executor_corp_id=2003
)
self.assertEqual(
mock_EveCharacter.objects.update_character.call_args[0][0], 42
my_corporation = EveCorporationInfo.objects.create(
corporation_id=2003,
corporation_name="Wayne Food",
corporation_ticker="WFO",
member_count=1,
alliance=None,
ceo_id=1999
)
# when
update_corp(my_corporation.corporation_id)
# then
my_corporation.refresh_from_db()
self.assertEqual(my_corporation.alliance.alliance_id, 3001)
# @patch('allianceauth.eveonline.tasks.EveCharacter')
# def test_update_character(self, mock_EveCharacter):
# update_character(42)
# self.assertEqual(
# mock_EveCharacter.objects.update_character.call_count, 1
# )
# self.assertEqual(
# mock_EveCharacter.objects.update_character.call_args[0][0], 42
# )
@patch('allianceauth.eveonline.tasks.update_character')
@patch('allianceauth.eveonline.tasks.update_alliance')
@patch('allianceauth.eveonline.tasks.update_corp')
@patch('allianceauth.eveonline.providers.provider')
@override_settings(CELERY_ALWAYS_EAGER=True)
@patch('allianceauth.eveonline.providers.esi_client_factory')
@patch('allianceauth.eveonline.tasks.providers')
@patch('allianceauth.eveonline.tasks.CHUNK_SIZE', 2)
class TestRunModelUpdate(TestCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
EveCorporationInfo.objects.all().delete()
EveAllianceInfo.objects.all().delete()
EveCharacter.objects.all().delete()
class TestRunModelUpdate(TransactionTestCase):
def test_should_run_updates(self, mock_providers, mock_esi_client_factory):
# given
mock_providers.provider.client = EsiClientStub()
mock_esi_client_factory.return_value = EsiClientStub()
EveCorporationInfo.objects.create(
corporation_id=2345,
corporation_name='corp.name',
corporation_ticker='c.c.t',
corporation_id=2001,
corporation_name="Wayne Technologies",
corporation_ticker="WTE",
member_count=10,
alliance=None,
)
EveAllianceInfo.objects.create(
alliance_id=3456,
alliance_name='alliance.name',
alliance_ticker='a.t',
executor_corp_id=5,
alliance_3001 = EveAllianceInfo.objects.create(
alliance_id=3001,
alliance_name="Wayne Enterprises",
alliance_ticker="WYE",
executor_corp_id=2003
)
EveCharacter.objects.create(
character_id=1,
character_name='character.name1',
corporation_id=2345,
corporation_name='character.corp.name',
corporation_ticker='c.c.t', # max 5 chars
corporation_2003 = EveCorporationInfo.objects.create(
corporation_id=2003,
corporation_name="Wayne Energy",
corporation_ticker="WEG",
member_count=99,
alliance=None,
)
character_1001 = EveCharacter.objects.create(
character_id=1001,
character_name="Bruce Wayne",
corporation_id=2002,
corporation_name="Wayne Food",
corporation_ticker="WYF",
alliance_id=None
)
EveCharacter.objects.create(
character_id=2,
character_name='character.name2',
corporation_id=9876,
corporation_name='character.corp.name',
corporation_ticker='c.c.t', # max 5 chars
alliance_id=3456,
alliance_name='character.alliance.name',
)
EveCharacter.objects.create(
character_id=3,
character_name='character.name3',
corporation_id=9876,
corporation_name='character.corp.name',
corporation_ticker='c.c.t', # max 5 chars
alliance_id=3456,
alliance_name='character.alliance.name',
)
EveCharacter.objects.create(
character_id=4,
character_name='character.name4',
corporation_id=9876,
corporation_name='character.corp.name',
corporation_ticker='c.c.t', # max 5 chars
alliance_id=3456,
alliance_name='character.alliance.name',
)
"""
EveCharacter.objects.create(
character_id=5,
character_name='character.name5',
corporation_id=9876,
corporation_name='character.corp.name',
corporation_ticker='c.c.t', # max 5 chars
alliance_id=3456,
alliance_name='character.alliance.name',
)
"""
def setUp(self):
self.affiliations = [
{'character_id': 1, 'corporation_id': 5},
{'character_id': 2, 'corporation_id': 9876, 'alliance_id': 3456},
{'character_id': 3, 'corporation_id': 9876, 'alliance_id': 7456},
{'character_id': 4, 'corporation_id': 9876, 'alliance_id': 3456}
]
self.names = [
{'id': 1, 'name': 'character.name1'},
{'id': 2, 'name': 'character.name2'},
{'id': 3, 'name': 'character.name3'},
{'id': 4, 'name': 'character.name4_new'}
]
def test_normal_run(
self,
mock_provider,
mock_update_corp,
mock_update_alliance,
mock_update_character,
):
def get_affiliations(characters: list):
response = [x for x in self.affiliations if x['character_id'] in characters]
mock_operator = Mock(**{'result.return_value': response})
return mock_operator
def get_names(ids: list):
response = [x for x in self.names if x['id'] in ids]
mock_operator = Mock(**{'result.return_value': response})
return mock_operator
mock_provider.client.Character.post_characters_affiliation.side_effect \
= get_affiliations
mock_provider.client.Universe.post_universe_names.side_effect = get_names
# when
run_model_update()
# then
character_1001.refresh_from_db()
self.assertEqual(
mock_provider.client.Character.post_characters_affiliation.call_count, 2
character_1001.corporation_id, 2001 # char has new corp
)
corporation_2003.refresh_from_db()
self.assertEqual(
mock_provider.client.Universe.post_universe_names.call_count, 2
corporation_2003.alliance.alliance_id, 3001 # corp has new alliance
)
alliance_3001.refresh_from_db()
self.assertEqual(
alliance_3001.executor_corp_id, 2001 # alliance has been updated
)
# character 1 has changed corp
# character 2 no change
# character 3 has changed alliance
# character 4 has changed name
self.assertEqual(mock_update_corp.apply_async.call_count, 1)
self.assertEqual(
int(mock_update_corp.apply_async.call_args[1]['args'][0]), 2345
)
self.assertEqual(mock_update_alliance.apply_async.call_count, 1)
self.assertEqual(
int(mock_update_alliance.apply_async.call_args[1]['args'][0]), 3456
)
characters_updated = {
x[1]['args'][0] for x in mock_update_character.apply_async.call_args_list
@override_settings(CELERY_ALWAYS_EAGER=True)
@patch('allianceauth.eveonline.tasks.update_character', wraps=update_character)
@patch('allianceauth.eveonline.providers.esi_client_factory')
@patch('allianceauth.eveonline.tasks.providers')
@patch('allianceauth.eveonline.tasks.CHUNK_SIZE', 2)
class TestUpdateCharacterChunk(TestCase):
@staticmethod
def _updated_character_ids(spy_update_character) -> set:
"""Character IDs passed to update_character task for update."""
return {
x[1]["args"][0] for x in spy_update_character.apply_async.call_args_list
}
excepted = {1, 3, 4}
self.assertSetEqual(characters_updated, excepted)
def test_ignore_character_not_in_affiliations(
self,
mock_provider,
mock_update_corp,
mock_update_alliance,
mock_update_character,
def test_should_update_corp_change(
self, mock_providers, mock_esi_client_factory, spy_update_character
):
def get_affiliations(characters: list):
response = [x for x in self.affiliations if x['character_id'] in characters]
mock_operator = Mock(**{'result.return_value': response})
return mock_operator
# given
mock_providers.provider.client = EsiClientStub()
mock_esi_client_factory.return_value = EsiClientStub()
character_1001 = EveCharacter.objects.create(
character_id=1001,
character_name="Bruce Wayne",
corporation_id=2003,
corporation_name="Wayne Energy",
corporation_ticker="WEG",
alliance_id=3001,
alliance_name="Wayne Enterprises",
alliance_ticker="WYE",
)
character_1002 = EveCharacter.objects.create(
character_id=1002,
character_name="Peter Parker",
corporation_id=2001,
corporation_name="Wayne Technologies",
corporation_ticker="WTE",
alliance_id=3001,
alliance_name="Wayne Enterprises",
alliance_ticker="WYE",
)
# when
update_character_chunk([
character_1001.character_id, character_1002.character_id
])
# then
character_1001.refresh_from_db()
self.assertEqual(character_1001.corporation_id, 2001)
self.assertSetEqual(self._updated_character_ids(spy_update_character), {1001})
def get_names(ids: list):
response = [x for x in self.names if x['id'] in ids]
mock_operator = Mock(**{'result.return_value': response})
return mock_operator
del self.affiliations[0]
mock_provider.client.Character.post_characters_affiliation.side_effect \
= get_affiliations
mock_provider.client.Universe.post_universe_names.side_effect = get_names
run_model_update()
characters_updated = {
x[1]['args'][0] for x in mock_update_character.apply_async.call_args_list
}
excepted = {3, 4}
self.assertSetEqual(characters_updated, excepted)
def test_ignore_character_not_in_names(
self,
mock_provider,
mock_update_corp,
mock_update_alliance,
mock_update_character,
def test_should_update_name_change(
self, mock_providers, mock_esi_client_factory, spy_update_character
):
def get_affiliations(characters: list):
response = [x for x in self.affiliations if x['character_id'] in characters]
mock_operator = Mock(**{'result.return_value': response})
return mock_operator
# given
mock_providers.provider.client = EsiClientStub()
mock_esi_client_factory.return_value = EsiClientStub()
character_1001 = EveCharacter.objects.create(
character_id=1001,
character_name="Batman",
corporation_id=2001,
corporation_name="Wayne Technologies",
corporation_ticker="WTE",
alliance_id=3001,
alliance_name="Wayne Technologies",
alliance_ticker="WYT",
)
# when
update_character_chunk([character_1001.character_id])
# then
character_1001.refresh_from_db()
self.assertEqual(character_1001.character_name, "Bruce Wayne")
self.assertSetEqual(self._updated_character_ids(spy_update_character), {1001})
def get_names(ids: list):
response = [x for x in self.names if x['id'] in ids]
mock_operator = Mock(**{'result.return_value': response})
return mock_operator
def test_should_update_alliance_change(
self, mock_providers, mock_esi_client_factory, spy_update_character
):
# given
mock_providers.provider.client = EsiClientStub()
mock_esi_client_factory.return_value = EsiClientStub()
character_1001 = EveCharacter.objects.create(
character_id=1001,
character_name="Bruce Wayne",
corporation_id=2001,
corporation_name="Wayne Technologies",
corporation_ticker="WTE",
alliance_id=None,
)
# when
update_character_chunk([character_1001.character_id])
# then
character_1001.refresh_from_db()
self.assertEqual(character_1001.alliance_id, 3001)
self.assertSetEqual(self._updated_character_ids(spy_update_character), {1001})
del self.names[3]
def test_should_not_update_when_not_changed(
self, mock_providers, mock_esi_client_factory, spy_update_character
):
# given
mock_providers.provider.client = EsiClientStub()
mock_esi_client_factory.return_value = EsiClientStub()
character_1001 = EveCharacter.objects.create(
character_id=1001,
character_name="Bruce Wayne",
corporation_id=2001,
corporation_name="Wayne Technologies",
corporation_ticker="WTE",
alliance_id=3001,
alliance_name="Wayne Technologies",
alliance_ticker="WYT",
)
# when
update_character_chunk([character_1001.character_id])
# then
self.assertSetEqual(self._updated_character_ids(spy_update_character), set())
mock_provider.client.Character.post_characters_affiliation.side_effect \
= get_affiliations
mock_provider.client.Universe.post_universe_names.side_effect = get_names
run_model_update()
characters_updated = {
x[1]['args'][0] for x in mock_update_character.apply_async.call_args_list
}
excepted = {1, 3}
self.assertSetEqual(characters_updated, excepted)
def test_should_fall_back_to_single_updates_when_bulk_update_failed(
self, mock_providers, mock_esi_client_factory, spy_update_character
):
# given
mock_providers.provider.client.Character.post_characters_affiliation\
.side_effect = OSError
mock_esi_client_factory.return_value = EsiClientStub()
character_1001 = EveCharacter.objects.create(
character_id=1001,
character_name="Bruce Wayne",
corporation_id=2001,
corporation_name="Wayne Technologies",
corporation_ticker="WTE",
alliance_id=3001,
alliance_name="Wayne Technologies",
alliance_ticker="WYT",
)
# when
update_character_chunk([character_1001.character_id])
# then
self.assertSetEqual(self._updated_character_ids(spy_update_character), {1001})