============================= test session starts ============================== created: 2/2 workers 2 workers [808 items] ss........................s...........s................................. [ 8%] ........................................................................ [ 17%] .....................FF.....................F........................... [ 26%] ........s.....s......................................................... [ 35%] ........................................................................ [ 44%] ........................................................................ [ 53%] ....................................................................F... [ 62%] .........F.............................................................. [ 71%] ...............................F........................................ [ 80%] ........................................................................ [ 89%] ...........F................sss...s..s..sss...........F................. [ 98%] ...........sss.. [100%] =================================== FAILURES =================================== ________________ UnitAPITestCase.test_create_unit_with_results _________________ [gw1] linux -- Python 3.13.2 /opt/hostedtoolcache/Python/3.13.2/x64/bin/python self = def test_create_unit_with_results(self): headers = self._auth_header("jollyroger", "secret") payload = { "competition": self.sql_comp.id, "event_id": "11", "heat": 1, "round": 1, "day": 1, "results": [ {"bib": "101", "performance": "16:30.00", "place": 1}, {"bib": "102", "performance": "17:00.00", "place": 2}, ], } resp = self.client.post("/api/units/", json.dumps(payload), content_type="application/json", **headers) self.assertEqual(resp.status_code, 201) data = resp.json() self.assertEqual(len(data["results"]), 2) self.assertEqual(data["results"][0]["bib"], "101") self.assertEqual(data["results"][0]["performance"], "16:30.00") from urllib.parse import urlparse > sql_unit = SQLUnit.objects.get(pk=urlparse(data["id"]).path.rstrip("/").rpartition("/")[2]) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ project/drapi/tests/test_unit_api.py:153: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /opt/hostedtoolcache/Python/3.13.2/x64/lib/python3.13/site-packages/django/db/models/manager.py:87: in manager_method return getattr(self.get_queryset(), name)(*args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = , , , , , , , , ]> args = (), kwargs = {'pk': '2'} def get(self, *args, **kwargs): """ Perform the query and return a single object matching the given keyword arguments. """ if self.query.combinator and (args or kwargs): raise NotSupportedError( "Calling QuerySet.get(...) with filters after %s() is not " "supported." % self.query.combinator ) clone = self._chain() if self.query.combinator else self.filter(*args, **kwargs) if self.query.can_filter() and not self.query.distinct_fields: clone = clone.order_by() limit = None if ( not clone.query.select_for_update or connections[clone.db].features.supports_select_for_update_with_limit ): limit = MAX_GET_RESULTS clone.query.set_limits(high=limit) num = len(clone) if num == 1: return clone._result_cache[0] if not num: > raise self.model.DoesNotExist( "%s matching query does not exist." % self.model._meta.object_name ) E project.reference.models.Unit.DoesNotExist: Unit matching query does not exist. /opt/hostedtoolcache/Python/3.13.2/x64/lib/python3.13/site-packages/django/db/models/query.py:633: DoesNotExist ________________ UnitAPITestCase.test_director_can_create_unit _________________ [gw1] linux -- Python 3.13.2 /opt/hostedtoolcache/Python/3.13.2/x64/bin/python self = def test_director_can_create_unit(self): headers = self._auth_header("dave", "secret") payload = { "competition": self.sql_comp.id, "event_id": "11", "heat": 1, "round": 1, } resp = self.client.post("/api/units/", json.dumps(payload), content_type="application/json", **headers) self.assertEqual(resp.status_code, 201) data = resp.json() from urllib.parse import urlparse > sql_unit = SQLUnit.objects.get(pk=urlparse(data["id"]).path.rstrip("/").rpartition("/")[2]) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ project/drapi/tests/test_unit_api.py:213: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /opt/hostedtoolcache/Python/3.13.2/x64/lib/python3.13/site-packages/django/db/models/manager.py:87: in manager_method return getattr(self.get_queryset(), name)(*args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = , , , , , , , , ]> args = (), kwargs = {'pk': '2'} def get(self, *args, **kwargs): """ Perform the query and return a single object matching the given keyword arguments. """ if self.query.combinator and (args or kwargs): raise NotSupportedError( "Calling QuerySet.get(...) with filters after %s() is not " "supported." % self.query.combinator ) clone = self._chain() if self.query.combinator else self.filter(*args, **kwargs) if self.query.can_filter() and not self.query.distinct_fields: clone = clone.order_by() limit = None if ( not clone.query.select_for_update or connections[clone.db].features.supports_select_for_update_with_limit ): limit = MAX_GET_RESULTS clone.query.set_limits(high=limit) num = len(clone) if num == 1: return clone._result_cache[0] if not num: > raise self.model.DoesNotExist( "%s matching query does not exist." % self.model._meta.object_name ) E project.reference.models.Unit.DoesNotExist: Unit matching query does not exist. /opt/hostedtoolcache/Python/3.13.2/x64/lib/python3.13/site-packages/django/db/models/query.py:633: DoesNotExist _________ UnitAPITestCase.test_superuser_can_delete_unit_with_results __________ [gw1] linux -- Python 3.13.2 /opt/hostedtoolcache/Python/3.13.2/x64/bin/python self = [, , , , , , , , , , ] q_objs = (), query = {'pk': ''} def get(self, *q_objs, **query): """Retrieve the matching object raising :class:`~mongoengine.queryset.MultipleObjectsReturned` or `DocumentName.MultipleObjectsReturned` exception if multiple results and :class:`~mongoengine.queryset.DoesNotExist` or `DocumentName.DoesNotExist` if no results are found. """ queryset = self.clone() queryset = queryset.order_by().limit(2) queryset = queryset.filter(*q_objs, **query) try: > result = next(queryset) ^^^^^^^^^^^^^^ /opt/hostedtoolcache/Python/3.13.2/x64/lib/python3.13/site-packages/mongoengine/queryset/base.py:274: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /opt/hostedtoolcache/Python/3.13.2/x64/lib/python3.13/site-packages/mongoengine/queryset/base.py:1646: in __next__ raw_doc = next(self._cursor) ^^^^^^^^^^^^^^^^^^ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def next(self) -> _DocumentType: """Advance the cursor.""" if self.__empty: raise StopIteration if len(self.__data) or self._refresh(): return self.__data.popleft() else: > raise StopIteration E StopIteration /opt/hostedtoolcache/Python/3.13.2/x64/lib/python3.13/site-packages/pymongo/cursor.py:1246: StopIteration During handling of the above exception, another exception occurred: self = def test_superuser_can_delete_unit_with_results(self): """Superusers can still delete a unit that already has results.""" from urllib.parse import urlparse headers = self._auth_header("jollyroger", "secret") ev = self.comp.get_event("5") # Create a fresh unit so we don't delete the test fixture payload = { "competition": self.sql_comp.id, "event_id": "5", "heat": 99, "round": 1, } resp = self.client.post("/api/units/", json.dumps(payload), content_type="application/json", **headers) self.assertEqual(resp.status_code, 201) data = resp.json() print(data) new_sql_pk = urlparse(data["id"]).path.rstrip("/").rpartition("/")[2] print(new_sql_pk) new_sql_unit = SQLUnit.objects.get(id=new_sql_pk) > new_mongo_unit = MongoUnit.objects.get(pk=new_sql_unit.mongo_unit_id) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ project/drapi/tests/test_unit_api.py:397: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = [, , , , , , , , , , ] q_objs = (), query = {'pk': ''} def get(self, *q_objs, **query): """Retrieve the matching object raising :class:`~mongoengine.queryset.MultipleObjectsReturned` or `DocumentName.MultipleObjectsReturned` exception if multiple results and :class:`~mongoengine.queryset.DoesNotExist` or `DocumentName.DoesNotExist` if no results are found. """ queryset = self.clone() queryset = queryset.order_by().limit(2) queryset = queryset.filter(*q_objs, **query) try: result = next(queryset) except StopIteration: msg = "%s matching query does not exist." % queryset._document._class_name > raise queryset._document.DoesNotExist(msg) E project.reference.mongo.unit.DoesNotExist: Unit matching query does not exist. /opt/hostedtoolcache/Python/3.13.2/x64/lib/python3.13/site-packages/mongoengine/queryset/base.py:277: DoesNotExist ______ EventSpecAPITestCase.test_superuser_can_delete_event_with_results _______ [gw0] linux -- Python 3.13.2 /opt/hostedtoolcache/Python/3.13.2/x64/bin/python self = def test_superuser_can_delete_event_with_results(self): """Superusers can still delete an event even when a unit under it has results.""" headers = self._auth_header("jollyroger", "secret") if not EventSpec.objects.filter(competition=self.sql_comp, event_id="99").count(): # Create a fresh event so we don't delete the test fixture payload = { "competition": self.sql_comp.id, "event_id": "99", "event_code": "1500", "age_groups": "SEN", "genders": "M", "category": "SM", "name": "Delete Me Event", } resp = self.client.post("/api/events/", json.dumps(payload), content_type="application/json", **headers) self.assertEqual(resp.status_code, 201) data = resp.json() sql_event = EventSpec.objects.get(competition=self.sql_comp, event_id="99") > mongo_event = MongoEventSpec.objects.get(competition=self.comp.id, event_id="99") ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ project/drapi/tests/test_eventspec_api.py:657: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = [, , , , , , , , , , , , ] q_objs = () query = {'competition': 'd2226400-a5bf-49df-902e-5aa0f8ec5aa1', 'event_id': '99'} def get(self, *q_objs, **query): """Retrieve the matching object raising :class:`~mongoengine.queryset.MultipleObjectsReturned` or `DocumentName.MultipleObjectsReturned` exception if multiple results and :class:`~mongoengine.queryset.DoesNotExist` or `DocumentName.DoesNotExist` if no results are found. """ queryset = self.clone() queryset = queryset.order_by().limit(2) queryset = queryset.filter(*q_objs, **query) try: result = next(queryset) except StopIteration: msg = "%s matching query does not exist." % queryset._document._class_name raise queryset._document.DoesNotExist(msg) try: # Check if there is another match next(queryset) except StopIteration: return result # If we were able to retrieve the 2nd doc, raise the MultipleObjectsReturned exception. > raise queryset._document.MultipleObjectsReturned( "2 or more items returned, instead of 1" ) E project.reference.mongo.eventspec.MultipleObjectsReturned: 2 or more items returned, instead of 1 /opt/hostedtoolcache/Python/3.13.2/x64/lib/python3.13/site-packages/mongoengine/queryset/base.py:286: MultipleObjectsReturned ____________ EventSpecAPITestCase.test_update_rounds_creates_units _____________ [gw0] linux -- Python 3.13.2 /opt/hostedtoolcache/Python/3.13.2/x64/bin/python self = def test_update_rounds_creates_units(self): """ Changing rounds via PUT should create/remove Units accordingly. Event '1' starts with rounds='2,1' (2 heats + 1 final). Changing to '3,2,1' should create 6 units total. """ headers = self._auth_header("jollyroger", "secret") sql_event = EventSpec.objects.get(competition=self.sql_comp, event_id="1") initial_units = Unit.objects.filter(event=sql_event).count() # The existing comp has the 100m with rounds='2,1' so should have 3 units payload = { "competition": self.sql_comp.id, "event_id": "1", "event_code": "100", "age_groups": "SEN,U20,U18", "genders": "M", "category": "SM", "limit": 24, "name": "100m with more rounds", "rounds": "3,2,1", } resp = self.client.put(f"/api/events/{sql_event.pk}/", json.dumps(payload), content_type="application/json", **headers) self.assertEqual(resp.status_code, 200) # Verify Mongo rounds updated mongo_event = MongoEventSpec.objects.get( competition=self.comp.id, event_id="1" ) self.assertEqual(mongo_event.rounds, "3,2,1") # these bg tasks normally get run in the background, but we need to run them manually here to verify the Unit changes ensure_units_and_timetable.now( self.sql_comp.id, [str(mongo_event.id)], ) # Verify units updated updated_units = MongoUnit.objects.filter(event=mongo_event) for u in updated_units: u.sql_sync() self.assertEqual(updated_units.count(), 6) # 3+2+1 updated_units = Unit.objects.filter(event=sql_event) > self.assertEqual(updated_units.count(), 6) # 3+2+1 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E AssertionError: 4 != 6 project/drapi/tests/test_eventspec_api.py:475: AssertionError ___________ ScottishAthleticsSyncTests.test_sync_sa_status_from_data ___________ [gw1] linux -- Python 3.13.2 /opt/hostedtoolcache/Python/3.13.2/x64/bin/python self = def test_sync_sa_status_from_data(self): """ Verify Person.sync_sa_status_from_data updates role, identifier and clubs """ sa_role = self.person.has_role_with(self.sa_org, create_if_missing={'role_type': 'REGISTERED'}) self.person.sync_sa_status_from_data(self.sample_feed_data) sa_role.refresh_from_db() self.assertEqual(sa_role.identifier, "JS006670") self.assertEqual(sa_role.role_status, "OK") self.assertEqual(sa_role.date_to, date(2027, 1, 18)) # Newburgh should be primary self.assertTrue(Role.objects.filter(person=self.person, organisation=self.newburgh, ordering=1).exists()) # Aberdeen should be secondary > self.assertTrue(Role.objects.filter(person=self.person, organisation=self.aberdeen, ordering=None).exists()) E AssertionError: False is not true project/reference/tests/test_sa_sync.py:65: AssertionError _______________________ EventTests.test_event_split_form _______________________ [gw0] linux -- Python 3.13.2 /opt/hostedtoolcache/Python/3.13.2/x64/bin/python self = def test_event_split_form(self): self.reset() comp = self.ema_comp F01 = comp.get_event('F01') form_data = { # id, suffix, gender, age_groups 'events': [[None, 'A', 'M', 'V35,V40'], [None, 'B', 'F', 'V60']] } valid_event_split_form = EventSplitForm(form_data, instance=comp, event=F01) > self.assertEqual(valid_event_split_form.is_valid(), True, msg='EventSplitForm is_valid should return True with correct data') ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ project/comp/tests/test_events.py:437: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /opt/hostedtoolcache/Python/3.13.2/x64/lib/python3.13/site-packages/django/forms/forms.py:206: in is_valid return self.is_bound and not self.errors ^^^^^^^^^^^ /opt/hostedtoolcache/Python/3.13.2/x64/lib/python3.13/site-packages/django/forms/forms.py:201: in errors self.full_clean() /opt/hostedtoolcache/Python/3.13.2/x64/lib/python3.13/site-packages/django/forms/forms.py:337: in full_clean self._clean_fields() /opt/hostedtoolcache/Python/3.13.2/x64/lib/python3.13/site-packages/django/forms/forms.py:347: in _clean_fields value = getattr(self, "clean_%s" % name)() ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def clean_events(self): cd = self.cleaned_data['events'] event_id = self.event.event_id male_split_params = '' female_split_params = '' choosen_suffixes = [] all_age_groups = [] for n, ev in enumerate(cd, start=1): suffix = ev[1] gender = ev[2] age_groups = ev[3] age_groups.replace(" ", "") if suffix in choosen_suffixes: # duplicate raise forms.ValidationError(_("suffixes must be unique '%(value)s' at row %(row)s") % {'value': suffix, 'row': n}) choosen_suffixes.append(suffix) for ag in age_groups.split(','): all_age_groups.append(ag) self._check_age_group(ag, n) if gender == 'M': male_split_params += f'{event_id}{suffix}: {age_groups};' elif gender == 'F': female_split_params += f'{event_id}{suffix}: {age_groups};' try: if male_split_params: self.event.validate_split_children_spec(male_split_params) except Exception as e: raise forms.ValidationError(_("Invalid event splitting data '%(value)s'") % {'value': male_split_params}) try: if female_split_params: self.event.validate_split_children_spec(female_split_params) except Exception as e: raise forms.ValidationError(_("Invalid event splitting data '%(value)s'") % {'value': female_split_params}) # check that all competitors would be split out. all_competitors_for_event = self.event.get_competitors() > competitors_after_split = all_competitors.filter(age_group__in=all_age_groups) ^^^^^^^^^^^^^^^ E NameError: name 'all_competitors' is not defined project/comp/forms.py:2269: NameError ______________ CompE2ETests.test_record_length_and_height_events _______________ [gw1] linux -- Python 3.13.2 /opt/hostedtoolcache/Python/3.13.2/x64/bin/python self = def test_record_length_and_height_events(self): """E2E test for recording results on length (LJ) and height (HJ) events.""" self.login() # ============================================== # PART 1: Long Jump (Length Event with Wind) # ============================================== lj_url = f"{self.live_server_url}{comp_reverse('comp-unit-record', self.sql_comp, event_id='3', round='1', heat='1')}" print(f"LJ recording URL: {lj_url}", flush=True) self.selenium.get(lj_url) time.sleep(3) print(f"Current URL: {self.selenium.current_url}", flush=True) print(f"Page title: {self.selenium.title}", flush=True) try: body = self.selenium.find_element(By.TAG_NAME, "body") print(f"Body text: {body.text[:1500]}", flush=True) except Exception as e: print(f"Failed to get body: {e}", flush=True) self.selenium.save_screenshot('/tmp/lj_page.png') self.accept_confirm() time.sleep(1) > self.wait.until(lambda _: len(self._length_starter_rows()) > 0) project/comp/tests/test_comp_e2e.py:87: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = method = . at 0x7f3c076f7d80> message = '' def until(self, method: Callable[[D], Union[Literal[False], T]], message: str = "") -> T: """Wait until the method returns a value that is not False. Calls the method provided with the driver as an argument until the return value does not evaluate to ``False``. Parameters: ----------- method: callable(WebDriver) - A callable object that takes a WebDriver instance as an argument. message: str - Optional message for :exc:`TimeoutException` Return: ------- object: T - The result of the last call to `method` Raises: ------- TimeoutException - If 'method' does not return a truthy value within the WebDriverWait object's timeout Example: -------- >>> from selenium.webdriver.common.by import By >>> from selenium.webdriver.support.ui import WebDriverWait >>> from selenium.webdriver.support import expected_conditions as EC # Wait until an element is visible on the page >>> wait = WebDriverWait(driver, 10) >>> element = wait.until(EC.visibility_of_element_located((By.ID, "exampleId"))) >>> print(element.text) """ screen = None stacktrace = None end_time = time.monotonic() + self._timeout while True: try: value = method(self._driver) if value: return value except self._ignored_exceptions as exc: screen = getattr(exc, "screen", None) stacktrace = getattr(exc, "stacktrace", None) if time.monotonic() > end_time: break time.sleep(self._poll) > raise TimeoutException(message, screen, stacktrace) E selenium.common.exceptions.TimeoutException: Message: /opt/hostedtoolcache/Python/3.13.2/x64/lib/python3.13/site-packages/selenium/webdriver/support/wait.py:146: TimeoutException =========================== short test summary info ============================ FAILED project/drapi/tests/test_unit_api.py::UnitAPITestCase::test_create_unit_with_results - project.reference.models.Unit.DoesNotExist: Unit matching query does not exist. FAILED project/drapi/tests/test_unit_api.py::UnitAPITestCase::test_director_can_create_unit - project.reference.models.Unit.DoesNotExist: Unit matching query does not exist. FAILED project/drapi/tests/test_unit_api.py::UnitAPITestCase::test_superuser_can_delete_unit_with_results - project.reference.mongo.unit.DoesNotExist: Unit matching query does not exist. FAILED project/drapi/tests/test_eventspec_api.py::EventSpecAPITestCase::test_superuser_can_delete_event_with_results - project.reference.mongo.eventspec.MultipleObjectsReturned: 2 or more items returned, instead of 1 FAILED project/drapi/tests/test_eventspec_api.py::EventSpecAPITestCase::test_update_rounds_creates_units - AssertionError: 4 != 6 FAILED project/reference/tests/test_sa_sync.py::ScottishAthleticsSyncTests::test_sync_sa_status_from_data - AssertionError: False is not true FAILED project/comp/tests/test_events.py::EventTests::test_event_split_form - NameError: name 'all_competitors' is not defined FAILED project/comp/tests/test_comp_e2e.py::CompE2ETests::test_record_length_and_height_events - selenium.common.exceptions.TimeoutException: Message: ==== 8 failed, 783 passed, 17 skipped, 18242 warnings in 709.38s (0:11:49) =====