wbcore 2.2.3__py2.py3-none-any.whl → 2.2.5__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. wbcore/contrib/color/admin.py +28 -0
  2. wbcore/contrib/color/apps.py +5 -0
  3. wbcore/contrib/color/enums.py +17 -0
  4. wbcore/contrib/color/factories.py +10 -0
  5. wbcore/contrib/color/fields.py +29 -0
  6. wbcore/contrib/color/forms.py +13 -0
  7. wbcore/contrib/color/models.py +62 -0
  8. wbcore/contrib/color/tests/conftest.py +10 -0
  9. wbcore/contrib/color/tests/test_color_models.py +61 -0
  10. wbcore/contrib/color/tests/test_fields.py +25 -0
  11. wbcore/contrib/documents/tests/conftest.py +30 -0
  12. wbcore/contrib/documents/tests/test_models.py +144 -0
  13. wbcore/contrib/example_app/tests/test_models/test_event.py +87 -0
  14. wbcore/contrib/example_app/tests/test_models/test_match.py +210 -0
  15. wbcore/contrib/example_app/tests/test_models/test_others.py +159 -0
  16. wbcore/contrib/example_app/tests/test_serializers/test_league_serializer.py +34 -0
  17. wbcore/contrib/example_app/tests/test_serializers/test_match_serializer.py +134 -0
  18. wbcore/contrib/example_app/tests/test_serializers/test_role_serializer.py +13 -0
  19. wbcore/contrib/example_app/tests/test_serializers/test_sport_serializer.py +14 -0
  20. wbcore/contrib/example_app/tests/test_serializers/test_stadium_serializer.py +14 -0
  21. wbcore/contrib/example_app/tests/test_serializers/test_team_result_serializer.py +30 -0
  22. wbcore/contrib/example_app/tests/test_serializers/test_team_serializer.py +70 -0
  23. wbcore/contrib/example_app/tests/test_viewsets/test_event_viewset.py +162 -0
  24. wbcore/contrib/example_app/tests/test_viewsets/test_league_viewset.py +84 -0
  25. wbcore/contrib/example_app/tests/test_viewsets/test_match_viewset.py +65 -0
  26. wbcore/contrib/example_app/tests/test_viewsets/test_person_viewset.py +166 -0
  27. wbcore/contrib/example_app/tests/test_viewsets/test_role_viewset.py +75 -0
  28. wbcore/contrib/example_app/tests/test_viewsets/test_sport_viewset.py +75 -0
  29. wbcore/contrib/example_app/tests/test_viewsets/test_stadium_viewset.py +75 -0
  30. wbcore/contrib/example_app/tests/test_viewsets/test_team_viewset.py +92 -0
  31. wbcore/contrib/example_app/tests/test_viewsets/test_teamresult_viewset.py +58 -0
  32. wbcore/contrib/example_app/tests/test_viewsets/test_utils_viewsets.py +124 -0
  33. wbcore/contrib/guardian/apps.py +6 -0
  34. wbcore/contrib/guardian/configurations.py +3 -0
  35. wbcore/contrib/guardian/filters.py +21 -0
  36. wbcore/contrib/guardian/tasks.py +10 -0
  37. wbcore/contrib/guardian/urls.py +12 -0
  38. wbcore/contrib/guardian/utils.py +124 -0
  39. wbcore/contrib/notifications/viewsets/configs/notification_types.py +27 -0
  40. wbcore/contrib/notifications/viewsets/configs/notifications.py +85 -0
  41. wbcore/contrib/workflow/tests/test_models/step/test_decision_step.py +79 -0
  42. wbcore/contrib/workflow/tests/test_models/step/test_email_step.py +45 -0
  43. wbcore/contrib/workflow/tests/test_models/step/test_finish_step.py +105 -0
  44. wbcore/contrib/workflow/tests/test_models/step/test_join_step.py +127 -0
  45. wbcore/contrib/workflow/tests/test_models/step/test_script_step.py +24 -0
  46. wbcore/contrib/workflow/tests/test_models/step/test_split_step.py +49 -0
  47. wbcore/contrib/workflow/tests/test_models/step/test_step.py +621 -0
  48. wbcore/contrib/workflow/tests/test_models/step/test_user_step.py +225 -0
  49. wbcore/contrib/workflow/tests/test_models/test_condition.py +103 -0
  50. wbcore/contrib/workflow/tests/test_models/test_data.py +134 -0
  51. wbcore/contrib/workflow/tests/test_models/test_process.py +98 -0
  52. wbcore/contrib/workflow/tests/test_models/test_transition.py +128 -0
  53. wbcore/contrib/workflow/tests/test_models/test_workflow.py +358 -0
  54. wbcore/templates/forms.py +0 -0
  55. wbcore/test/e2e_helpers_methods/e2e_checks.py +121 -0
  56. wbcore/test/e2e_helpers_methods/e2e_helper_methods.py +395 -0
  57. wbcore/tests/test_permissions/test_backend.py +29 -0
  58. {wbcore-2.2.3.dist-info → wbcore-2.2.5.dist-info}/METADATA +1 -1
  59. {wbcore-2.2.3.dist-info → wbcore-2.2.5.dist-info}/RECORD +60 -16
  60. wbcore/contrib/agenda/release_notes/1_0_0.md +0 -13
  61. wbcore/contrib/authentication/release_notes/1_0_0.md +0 -13
  62. wbcore/contrib/currency/release_notes/1_0_0.md +0 -13
  63. wbcore/contrib/directory/release_notes/1_0_0.md +0 -13
  64. wbcore/contrib/directory/release_notes/1_0_1.md +0 -13
  65. wbcore/contrib/documents/release_notes/1_0_0.md +0 -13
  66. wbcore/contrib/geography/release_notes/1_0_0.md +0 -13
  67. wbcore/contrib/io/release_notes/1_0_0.md +0 -13
  68. wbcore/contrib/notifications/release_notes/1_0_0.md +0 -13
  69. wbcore/contrib/tags/release_notes/1_0_0.md +0 -13
  70. wbcore/docs/orderable.md +0 -29
  71. wbcore/docs/reparent.md +0 -13
  72. wbcore/templates/reversion/compare_detail.html +0 -19
  73. {wbcore-2.2.3.dist-info → wbcore-2.2.5.dist-info}/WHEEL +0 -0
@@ -0,0 +1,28 @@
1
+ from django.conf import settings
2
+ from django.contrib import admin
3
+ from django_better_admin_arrayfield.models.fields import ArrayField
4
+ from wbcore.contrib.color.forms import DynamicArrayColorWidget
5
+ from wbcore.contrib.color.models import ColorGradient
6
+
7
+
8
+ @admin.register(ColorGradient)
9
+ class ColorGradientModelAdmin(admin.ModelAdmin):
10
+ class Media:
11
+ if settings.DEBUG:
12
+ js = [
13
+ "colorfield/jscolor/jscolor.js",
14
+ "colorfield/colorfield.js",
15
+ "js/django_better_admin_arrayfield.min.js",
16
+ ]
17
+ else:
18
+ js = [
19
+ "colorfield/jscolor/jscolor.min.js",
20
+ "colorfield/colorfield.js",
21
+ "js/django_better_admin_arrayfield.min.js",
22
+ ]
23
+ css = {"all": ("css/django_better_admin_arrayfield.min.css",)}
24
+
25
+ formfield_overrides = {
26
+ ArrayField: {"widget": DynamicArrayColorWidget},
27
+ }
28
+ list_display = ["title"]
@@ -0,0 +1,5 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class ColorAppConfig(AppConfig):
5
+ name = "wbcore.contrib.color"
@@ -0,0 +1,17 @@
1
+ from enum import Enum
2
+
3
+
4
+ class WBColor(Enum):
5
+ GREEN_LIGHT = "rgb(214, 229, 145)"
6
+ GREEN = "rgb(182, 223, 18)"
7
+ GREEN_DARK = "rgb(111, 181, 68)"
8
+ RED_LIGHT = "rgb(244, 132, 116)"
9
+ RED = "rgb(250, 84, 62)"
10
+ RED_DARK = "rgb(224, 57, 49)"
11
+ YELLOW_LIGHT = "rgb(247, 232, 113)"
12
+ YELLOW = "rgb(247, 207, 77)"
13
+ YELLOW_DARK = "rgb(247, 190, 47)"
14
+ GREY = "rgb(133, 144, 162)"
15
+ BLUE_LIGHT = "rgb(104, 198, 224)"
16
+ BLUE = "rgb(69, 112, 224)"
17
+ BLUE_DARK = "rgb(33, 26, 233)"
@@ -0,0 +1,10 @@
1
+ import factory
2
+ from wbcore.contrib.color.models import ColorGradient
3
+
4
+
5
+ class ColorGradientFactory(factory.django.DjangoModelFactory):
6
+ class Meta:
7
+ model = ColorGradient
8
+
9
+ title = factory.Sequence(lambda n: f"Color Gradient {n}")
10
+ colors = factory.List([factory.Faker("color") for _ in range(10)])
@@ -0,0 +1,29 @@
1
+ import re
2
+
3
+ from django.core.exceptions import ValidationError
4
+ from django.db.models import CharField
5
+
6
+
7
+ def validate_color(color_string):
8
+ """Hexadecimal validation."""
9
+ if color_string is None:
10
+ return color_string
11
+
12
+ if not re.search(r"^#(?:[0-9a-fA-F]{3}){1,2}$", color_string):
13
+ raise ValidationError("Please provide a valid hexadecimal color.")
14
+
15
+ return color_string
16
+
17
+
18
+ class ColorField(CharField):
19
+ description = "A field storing a hexadecimal color value"
20
+ default_validators = [validate_color]
21
+
22
+ def __init__(self, *args, **kwargs):
23
+ kwargs["max_length"] = 7
24
+ super().__init__(*args, **kwargs)
25
+
26
+ def deconstruct(self):
27
+ name, path, args, kwargs = super().deconstruct()
28
+ del kwargs["max_length"]
29
+ return name, path, args, kwargs
@@ -0,0 +1,13 @@
1
+ from django_better_admin_arrayfield.forms.widgets import DynamicArrayWidget
2
+
3
+
4
+ class DynamicArrayColorWidget(DynamicArrayWidget):
5
+ template_name = "wbcore/dynamic_color_array.html"
6
+
7
+ def get_context(self, name, value, attrs):
8
+ context = super().get_context(name, value, attrs)
9
+ context["default"] = "#ffffff"
10
+ return context
11
+
12
+ def __init__(self, *args, **kwargs):
13
+ super().__init__(*args, **kwargs)
@@ -0,0 +1,62 @@
1
+ from typing import Generator
2
+
3
+ from colorfield.fields import ColorField
4
+ from django.db import models
5
+ from django_better_admin_arrayfield.models.fields import ArrayField
6
+
7
+
8
+ class ColorGradient(models.Model):
9
+ """
10
+ Utility class for color gradient support in report
11
+ """
12
+
13
+ title = models.CharField(max_length=128, default="")
14
+ colors = ArrayField(ColorField(default="#FF0000"), default=list)
15
+
16
+ def __str__(self) -> str:
17
+ return self.title
18
+
19
+ def get_gradient(self, color: str | None = None, color_size: int = -1) -> Generator:
20
+ """
21
+ Return the consecutive colors (gradient) given a color based on the closest distance
22
+
23
+ Args:
24
+ color: a hex color. Optional.
25
+ color_size: The number of colors the list should return. Optional.
26
+
27
+ Returns:
28
+ A generator of colors bounds in size by the color_size parameters or the gradient defaults color palette size
29
+ """
30
+
31
+ def _hex2rgb(_color):
32
+ _color = _color.replace("#", "")
33
+ return tuple(int(_color[i : i + 2], 16) for i in (0, 2, 4))
34
+
35
+ if color_size < 0:
36
+ color_size = len(self.colors)
37
+
38
+ index = 0
39
+ if color:
40
+ color_rgb = _hex2rgb(color)
41
+ nearest_color = min(
42
+ self.colors, key=lambda subject: sum((s - q) ** 2 for s, q in zip(_hex2rgb(subject), color_rgb))
43
+ )
44
+
45
+ index = self.colors.index(nearest_color)
46
+ for i in range(color_size):
47
+ yield self.colors[(index + i) % len(self.colors)]
48
+
49
+
50
+ class TransparencyMixin:
51
+ def get_cell_formatting(self, request):
52
+ return [
53
+ {
54
+ "column": None,
55
+ "conditions": [
56
+ {
57
+ "condition": None,
58
+ "style": {"backgroundColor": f'rgba(255, 255, 255, {getattr(self, "TRANSPARENCY", 0.4)})'},
59
+ },
60
+ ],
61
+ }
62
+ ]
@@ -0,0 +1,10 @@
1
+ from django.apps import apps
2
+ from django.db.models.signals import pre_migrate
3
+ from pytest_factoryboy import register
4
+ from wbcore.contrib.color.factories import ColorGradientFactory
5
+ from wbcore.contrib.geography.tests.signals import app_pre_migration
6
+
7
+ register(ColorGradientFactory)
8
+
9
+
10
+ pre_migrate.connect(app_pre_migration, sender=apps.get_app_config("geography"))
@@ -0,0 +1,61 @@
1
+ import pytest
2
+ from wbcore.contrib.color.factories import ColorGradientFactory
3
+ from wbcore.contrib.color.models import ColorGradient
4
+
5
+ COLORS = ["#FFFFFF", "#000000", "#FF5733", "#3357FF", "#FF33A1", "#800080"]
6
+
7
+
8
+ class TestColorGradientModel:
9
+ @pytest.mark.django_db
10
+ def integration_test(self, mocker):
11
+ gradient = ColorGradientFactory()
12
+ assert ColorGradient.objects.filter(id=gradient.id).exists()
13
+
14
+ def test_str_method(self, mocker):
15
+ gradient = ColorGradient()
16
+ mocker.patch.object(gradient, "title", "My Gradient")
17
+ assert str(gradient) == "My Gradient"
18
+
19
+ def test_default_colors(self):
20
+ gradient = ColorGradient()
21
+ assert gradient.colors == []
22
+
23
+ def test_get_gradient(self, mocker):
24
+ gradient = ColorGradient()
25
+ assert list(gradient.get_gradient()) == []
26
+
27
+ def test_get_gradient_no_color_argument(self, mocker):
28
+ gradient = ColorGradient()
29
+ mocker.patch.object(gradient, "colors", COLORS)
30
+ palette = list(gradient.get_gradient(color=None, color_size=3))
31
+ assert len(palette) == 3
32
+
33
+ def test_get_gradient_with_non_existent_color(self, mocker):
34
+ gradient = ColorGradient()
35
+ mocker.patch.object(gradient, "colors", COLORS)
36
+ non_existent_color = "#123456"
37
+ palette = list(gradient.get_gradient(color=non_existent_color))
38
+ assert len(palette) == len(COLORS)
39
+ assert palette == ["#000000", "#FF5733", "#3357FF", "#FF33A1", "#800080", "#FFFFFF"]
40
+
41
+ @pytest.mark.parametrize("color_size", [20, 1, 0, -1])
42
+ def test_get_gradient_with_different_size(self, color_size, mocker):
43
+ gradient = ColorGradient()
44
+ mocker.patch.object(gradient, "colors", COLORS)
45
+ palette = list(gradient.get_gradient(color_size=color_size))
46
+ base_colors = gradient.colors
47
+ assert len(palette) == color_size if color_size > 0 else len(base_colors)
48
+ for index, color in enumerate(palette):
49
+ assert color == base_colors[index % len(base_colors)]
50
+
51
+ @pytest.mark.parametrize("color", COLORS)
52
+ def test_get_gradient_with_different_color(self, color, mocker):
53
+ gradient = ColorGradient()
54
+ mocker.patch.object(gradient, "colors", COLORS)
55
+ base_colors = gradient.colors
56
+
57
+ color_index = base_colors.index(color)
58
+ palette = list(gradient.get_gradient(color=color))
59
+
60
+ assert len(palette) == len(base_colors)
61
+ assert palette == base_colors[color_index:] + base_colors[:color_index]
@@ -0,0 +1,25 @@
1
+ import pytest
2
+ from django.core.exceptions import ValidationError
3
+ from wbcore.contrib.color.fields import validate_color
4
+
5
+
6
+ class TestColorield:
7
+ @pytest.mark.parametrize(
8
+ ("input", "valid"),
9
+ [
10
+ ("#1234567", False),
11
+ ("259868", False),
12
+ ("#25986", False),
13
+ ("", False),
14
+ ("#d225b0", True),
15
+ ("#4839a3", True),
16
+ ("#259868", True),
17
+ (None, True),
18
+ ],
19
+ )
20
+ def test_color_validator(self, input, valid):
21
+ if valid:
22
+ assert input == validate_color(input)
23
+ else:
24
+ with pytest.raises(ValidationError):
25
+ validate_color(input)
@@ -0,0 +1,30 @@
1
+ import pytest
2
+ from django.apps import apps
3
+ from django.db import connection
4
+ from django.db.models.signals import pre_migrate
5
+ from pytest_factoryboy import register
6
+ from wbcore.contrib.documents.factories import (
7
+ DocumentFactory,
8
+ DocumentTypeFactory,
9
+ ShareableLinkAccessFactory,
10
+ ShareableLinkFactory,
11
+ )
12
+ from wbcore.contrib.geography.tests.signals import app_pre_migration
13
+ from wbcore.tests.conftest import *
14
+
15
+ register(DocumentFactory)
16
+ register(DocumentTypeFactory)
17
+ register(ShareableLinkAccessFactory)
18
+ register(ShareableLinkFactory)
19
+
20
+
21
+ @pytest.fixture(
22
+ autouse=True, scope="session"
23
+ ) # Might want to find a way to registered default conftest logic automatically
24
+ def django_test_environment(django_test_environment):
25
+ from django.apps import apps
26
+
27
+ get_models = apps.get_models
28
+
29
+ for m in [m for m in get_models() if not m._meta.managed]:
30
+ m._meta.managed = True
@@ -0,0 +1,144 @@
1
+ import os
2
+ from datetime import timedelta
3
+ from unittest.mock import patch
4
+
5
+ import pytest
6
+ from django.core import mail
7
+ from django.core.validators import URLValidator
8
+ from django.utils import timezone
9
+ from faker import Faker
10
+ from slugify import slugify
11
+ from wbcore.contrib.documents.models.documents import (
12
+ Document,
13
+ send_email_as_task,
14
+ upload_to,
15
+ )
16
+
17
+ fake = Faker()
18
+
19
+
20
+ @pytest.mark.django_db
21
+ class TestDocumentModel:
22
+ def test_init(self, document):
23
+ assert document.id is not None
24
+
25
+ @pytest.mark.parametrize(
26
+ "filename",
27
+ [(fake.file_name())],
28
+ )
29
+ def test_upload_to(self, document, document_type_factory, filename):
30
+ parent_document_type = document_type_factory.create()
31
+ document_type = document_type_factory.create(parent=parent_document_type)
32
+ document.document_type = document_type
33
+ document.save()
34
+ assert (
35
+ upload_to(document, filename)
36
+ == f"dms/document/{slugify(parent_document_type.name)}/{slugify(document_type.name)}/{filename}"
37
+ )
38
+
39
+ @pytest.mark.parametrize(
40
+ "document__file",
41
+ [(fake.file_path())],
42
+ )
43
+ def test_filename(self, document):
44
+ assert document.filename == os.path.basename(document.file.name)
45
+
46
+ def test_linked_objects(self, document_factory):
47
+ document = document_factory.create()
48
+ link_obj = document_factory.create()
49
+ document.link(link_obj)
50
+ assert list(document.linked_objects)[0] == link_obj
51
+
52
+ @pytest.mark.parametrize(
53
+ "sharing_seconds_duration, one_time_link",
54
+ [(0, False), (fake.pyint(min_value=1), False), (0, True), (fake.pyint(min_value=1), True)],
55
+ )
56
+ def test_generate_shareable_link(self, document, sharing_seconds_duration, one_time_link):
57
+ document.generate_shareable_link(
58
+ sharing_seconds_duration=sharing_seconds_duration, one_time_link=one_time_link
59
+ )
60
+ assert (
61
+ document.shareable_links.filter(
62
+ valid_until__isnull=sharing_seconds_duration == 0, one_time_link=one_time_link
63
+ ).count()
64
+ == 1
65
+ )
66
+
67
+ @patch("wbcore.contrib.documents.models.documents.send_email_as_task.delay")
68
+ @pytest.mark.parametrize(
69
+ "to_emails",
70
+ [(fake.email())],
71
+ )
72
+ def test_send_email(self, mock_fct, document, to_emails):
73
+ document.send_email(to_emails)
74
+ assert mock_fct.call_count == 1
75
+
76
+ def test_get_for_object(self, document_factory):
77
+ document = document_factory.create()
78
+ link_obj = document_factory.create()
79
+ document.link(link_obj)
80
+ assert Document.get_for_object(link_obj).first() == document
81
+
82
+ @pytest.mark.parametrize(
83
+ "to_emails, as_link, subject, from_email, body",
84
+ [
85
+ (fake.email(), False, None, None, None),
86
+ (fake.email(), False, fake.paragraph(), fake.email(), fake.paragraph()),
87
+ (fake.email(), True, None, None, None),
88
+ ],
89
+ )
90
+ def test_send_email_as_task(self, document, to_emails, as_link, subject, from_email, body):
91
+ assert len(mail.outbox) == 0
92
+ send_email_as_task(document.id, to_emails, as_link=as_link, subject=subject, from_email=from_email, body=body)
93
+ assert len(mail.outbox) == 1
94
+ msg = mail.outbox[0]
95
+ if as_link:
96
+ assert document.shareable_links.count() == 1
97
+ assert len(msg.attachments) == 0
98
+ assert len(msg.alternatives) == 1
99
+ else:
100
+ assert document.shareable_links.count() == 0
101
+ assert len(msg.attachments) == 1
102
+ assert len(msg.alternatives) == 2
103
+ assert msg
104
+
105
+
106
+ @pytest.mark.django_db
107
+ class TestDocumentTypeModel:
108
+ def test_init(self, document_type):
109
+ assert document_type.id is not None
110
+
111
+ def test_str(self, document_type_factory):
112
+ document_type = document_type_factory.create(parent=document_type_factory.create())
113
+ document_type.save()
114
+ assert str(document_type) == document_type.compute_str()
115
+
116
+
117
+ @pytest.mark.django_db
118
+ class TestShareableLinkModel:
119
+ @pytest.mark.parametrize(
120
+ "shareable_link__manual_invalid, shareable_link__valid_until, shareable_link__one_time_link",
121
+ [(True, None, False), (False, timezone.now() - timedelta(seconds=60), False), (False, None, True)],
122
+ )
123
+ def test_unvalid_link(self, shareable_link):
124
+ if shareable_link.one_time_link:
125
+ assert shareable_link.is_valid()
126
+ shareable_link.access()
127
+ assert not shareable_link.is_valid()
128
+
129
+ def test_link(self, shareable_link):
130
+ URLValidator()(shareable_link.link)
131
+
132
+ def test_access(self, shareable_link):
133
+ assert shareable_link.accesses.count() == 0
134
+ shareable_link.access()
135
+ assert shareable_link.accesses.count() == 1
136
+
137
+ def test_init(self, shareable_link):
138
+ assert shareable_link.id is not None
139
+
140
+
141
+ @pytest.mark.django_db
142
+ class TestShareableLinkAccessModel:
143
+ def test_init(self, shareable_link_access):
144
+ assert shareable_link_access.id is not None
@@ -0,0 +1,87 @@
1
+ import pytest
2
+ from django.db import IntegrityError
3
+ from wbcore.contrib.example_app.factories import (
4
+ EventFactory,
5
+ EventTypeFactory,
6
+ PlayerFactory,
7
+ )
8
+
9
+
10
+ @pytest.mark.django_db
11
+ class TestEvent:
12
+ def test_str(self):
13
+ event = EventFactory(
14
+ minute=69,
15
+ person__first_name="Test",
16
+ person__last_name="Person",
17
+ event_type__name="Test Event Type",
18
+ match__sport__match_duration=90,
19
+ )
20
+ assert event.__str__() == "Test Event Type (69.) - Test Person"
21
+
22
+ @pytest.mark.parametrize(
23
+ "home, away, points, home_score, away_score", [(2, 1, 5, 7, 1), (0, 3, 2, 2, 3), (1, 1, 0, 1, 1)]
24
+ )
25
+ def test_save_home_scored(self, home, away, points, home_score, away_score):
26
+ person = PlayerFactory()
27
+ event = EventFactory(
28
+ person=person,
29
+ match__home=person.current_team,
30
+ match__score_home=home,
31
+ match__score_away=away,
32
+ event_type__points=points,
33
+ )
34
+ assert event.match.score_home == home_score
35
+ assert event.match.score_away == away_score
36
+
37
+ @pytest.mark.parametrize(
38
+ "home, away, points, home_score, away_score", [(41, 27, 4, 41, 31), (19, 0, 2, 19, 2), (22, 23, 0, 22, 23)]
39
+ )
40
+ def test_save_away_scored(self, home, away, points, home_score, away_score):
41
+ person = PlayerFactory()
42
+ event = EventFactory(
43
+ person=person,
44
+ match__away=person.current_team,
45
+ match__score_home=home,
46
+ match__score_away=away,
47
+ event_type__points=points,
48
+ )
49
+ assert event.match.score_home == home_score
50
+ assert event.match.score_away == away_score
51
+
52
+ def test_save_no_team(self):
53
+ person = PlayerFactory()
54
+ event = EventFactory(
55
+ person=person,
56
+ match__score_home=2,
57
+ match__score_away=1,
58
+ event_type__points=4,
59
+ )
60
+ assert event.match.score_home == 2
61
+ assert event.match.score_away == 1
62
+
63
+ def test_save_no_player(self):
64
+ event = EventFactory(
65
+ match__score_home=2,
66
+ match__score_away=1,
67
+ event_type__points=4,
68
+ )
69
+ assert event.match.score_home == 2
70
+ assert event.match.score_away == 1
71
+
72
+ def test_unique_constraint(self):
73
+ event = EventFactory()
74
+ with pytest.raises(IntegrityError):
75
+ EventFactory(person=event.person, minute=event.minute, event_type=event.event_type, match=event.match)
76
+
77
+
78
+ @pytest.mark.django_db
79
+ class TestEventType:
80
+ def test_str(self):
81
+ event_type = EventTypeFactory(name="Test Event Type")
82
+ assert event_type.__str__() == "Test Event Type"
83
+
84
+ def test_unique_constraint(self):
85
+ event_type = EventTypeFactory()
86
+ with pytest.raises(IntegrityError):
87
+ EventTypeFactory(name=event_type.name, sport=event_type.sport)