Coverage for src/usls/ports/outbound.py: 100%

11 statements  

« prev     ^ index     » next       coverage.py v7.15.3, created at 2026-08-05 10:11 +0000

1"""Outbound ports — interfaces the application layer depends on. 

2 

3These are the contracts that secondary/driven adapters must satisfy. The 

4application layer only imports these protocols; it never references concrete 

5implementations. This is what makes the hexagonal boundary testable and 

6swap-able (e.g. real HTTP adapter vs. an in-memory stub). 

7""" 

8 

9from typing import Protocol 

10 

11from ..domain.models import Match, MatchDetails, NewsArticle, Player, Standing, Team 

12 

13 

14class USLSAPIPort(Protocol): 

15 """Contract for the upstream USL Super League data source (ESPN API).""" 

16 

17 async def get_teams(self) -> list[Team]: 

18 """Return all active USL Super League teams.""" 

19 ... 

20 

21 async def get_team(self, team_id: str) -> Team: 

22 """Return a single team by its ESPN team ID. 

23 

24 Raises: 

25 USLSNotFoundError: If no team with that ID exists. 

26 """ 

27 ... 

28 

29 async def get_scoreboard(self, date: str | None = None, end_date: str | None = None) -> list[Match]: 

30 """Return matches on the given date or date range, or today if date is None. 

31 

32 When ``end_date`` is provided, ``date`` is the start of the range and the 

33 upstream is queried with ``dates=START-END``. 

34 """ 

35 ... 

36 

37 async def get_news(self, limit: int) -> list[NewsArticle]: 

38 """Return up to ``limit`` recent USL Super League news articles.""" 

39 ... 

40 

41 async def get_roster(self, team_id: str) -> list[Player]: 

42 """Return the active roster for a team. 

43 

44 Raises: 

45 USLSNotFoundError: If no team with that ID exists. 

46 """ 

47 ... 

48 

49 async def get_match_details(self, match_id: str) -> MatchDetails: 

50 """Return detailed information for a single match. 

51 

52 Raises: 

53 USLSNotFoundError: If no match with that ID exists. 

54 """ 

55 ... 

56 

57 async def get_team_schedule(self, team_id: str) -> list[Match]: 

58 """Return all scheduled and completed matches for a team in the current season. 

59 

60 Raises: 

61 USLSNotFoundError: If no team with that ID exists. 

62 """ 

63 ... 

64 

65 async def get_standings(self) -> list[Standing]: 

66 """Return the current USL Super League standings ordered by points descending. 

67 

68 Each Standing carries its ``conference`` label ("Eastern Conference" / 

69 "Western Conference") so callers can group when needed. The list is 

70 flat across both conferences. 

71 """ 

72 ...