Files
bracket/backend/tests/integration_tests/sql.py
dependabot[bot] 52c1d77c04 Update ruff requirement from 0.9.1 to 0.11.0 in /backend (#1155)
Updates the requirements on [ruff](https://github.com/astral-sh/ruff) to
permit the latest version.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/astral-sh/ruff/releases">ruff's
releases</a>.</em></p>
<blockquote>
<h2>0.11.0</h2>
<h2>Release Notes</h2>
<p>This is a follow-up to <a
href="https://github.com/astral-sh/ruff/releases/tag/0.10.0">release
0.10.0</a>. The <code>requires-python</code> inference changes were
unintentionally omitted from 0.10.0, and have been included here. This
release also includes stabilization of the preview behavior for
<code>PGH004</code>.</p>
<h3>Breaking changes</h3>
<ul>
<li>
<p><strong>Changes to how the Python version is inferred when a
<code>target-version</code> is not specified</strong> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/16319">#16319</a>)</p>
<p>In previous versions of Ruff, you could specify your Python version
with:</p>
<ul>
<li>The <code>target-version</code> option in a <code>ruff.toml</code>
file or the <code>[tool.ruff]</code> section of a pyproject.toml
file.</li>
<li>The <code>project.requires-python</code> field in a
<code>pyproject.toml</code> file with a <code>[tool.ruff]</code>
section.</li>
</ul>
<p>These options worked well in most cases, and are still recommended
for fine control of the Python version. However, because of the way Ruff
discovers config files, <code>pyproject.toml</code> files without a
<code>[tool.ruff]</code> section would be ignored, including the
<code>requires-python</code> setting. Ruff would then use the default
Python version (3.9 as of this writing) instead, which is surprising
when you've attempted to request another version.</p>
<p>In v0.10, config discovery has been updated to address this
issue:</p>
<ul>
<li>If Ruff finds a <code>ruff.toml</code> file without a
<code>target-version</code>, it will check
for a <code>pyproject.toml</code> file in the same directory and respect
its
<code>requires-python</code> version, even if it does not contain a
<code>[tool.ruff]</code>
section.</li>
<li>If Ruff finds a user-level configuration, the
<code>requires-python</code> field of the closest
<code>pyproject.toml</code> in a parent directory will take
precedence.</li>
<li>If there is no config file (<code>ruff.toml</code>or
<code>pyproject.toml</code> with a
<code>[tool.ruff]</code> section) in the directory of the file being
checked, Ruff will
search for the closest <code>pyproject.toml</code> in the parent
directories and use its
<code>requires-python</code> setting.</li>
</ul>
</li>
</ul>
<h3>Stabilization</h3>
<p>The following behaviors have been stabilized:</p>
<ul>
<li><a
href="https://docs.astral.sh/ruff/rules/blanket-noqa/"><code>blanket-noqa</code></a>
(<code>PGH004</code>): Also detect blanked file-level noqa comments (and
not just line level comments).</li>
</ul>
<h3>Preview features</h3>
<ul>
<li>[syntax-errors] Tuple unpacking in <code>for</code> statement
iterator clause before Python 3.9 (<a
href="https://redirect.github.com/astral-sh/ruff/pull/16558">#16558</a>)</li>
</ul>
<h2>Install ruff 0.11.0</h2>
<h3>Install prebuilt binaries via shell script</h3>
<pre lang="sh"><code>curl --proto '=https' --tlsv1.2 -LsSf
https://github.com/astral-sh/ruff/releases/download/0.11.0/ruff-installer.sh
| sh
</code></pre>
<h3>Install prebuilt binaries via powershell script</h3>
<pre lang="sh"><code>powershell -ExecutionPolicy ByPass -c &quot;irm
https://github.com/astral-sh/ruff/releases/download/0.11.0/ruff-installer.ps1
| iex&quot;
&lt;/tr&gt;&lt;/table&gt; 
</code></pre>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md">ruff's
changelog</a>.</em></p>
<blockquote>
<h2>0.11.0</h2>
<p>This is a follow-up to release 0.10.0. Because of a mistake in the
release process, the <code>requires-python</code> inference changes were
not included in that release. Ruff 0.11.0 now includes this change as
well as the stabilization of the preview behavior for
<code>PGH004</code>.</p>
<h3>Breaking changes</h3>
<ul>
<li>
<p><strong>Changes to how the Python version is inferred when a
<code>target-version</code> is not specified</strong> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/16319">#16319</a>)</p>
<p>In previous versions of Ruff, you could specify your Python version
with:</p>
<ul>
<li>The <code>target-version</code> option in a <code>ruff.toml</code>
file or the <code>[tool.ruff]</code> section of a pyproject.toml
file.</li>
<li>The <code>project.requires-python</code> field in a
<code>pyproject.toml</code> file with a <code>[tool.ruff]</code>
section.</li>
</ul>
<p>These options worked well in most cases, and are still recommended
for fine control of the Python version. However, because of the way Ruff
discovers config files, <code>pyproject.toml</code> files without a
<code>[tool.ruff]</code> section would be ignored, including the
<code>requires-python</code> setting. Ruff would then use the default
Python version (3.9 as of this writing) instead, which is surprising
when you've attempted to request another version.</p>
<p>In v0.10, config discovery has been updated to address this
issue:</p>
<ul>
<li>If Ruff finds a <code>ruff.toml</code> file without a
<code>target-version</code>, it will check
for a <code>pyproject.toml</code> file in the same directory and respect
its
<code>requires-python</code> version, even if it does not contain a
<code>[tool.ruff]</code>
section.</li>
<li>If Ruff finds a user-level configuration, the
<code>requires-python</code> field of the closest
<code>pyproject.toml</code> in a parent directory will take
precedence.</li>
<li>If there is no config file (<code>ruff.toml</code>or
<code>pyproject.toml</code> with a
<code>[tool.ruff]</code> section) in the directory of the file being
checked, Ruff will
search for the closest <code>pyproject.toml</code> in the parent
directories and use its
<code>requires-python</code> setting.</li>
</ul>
</li>
</ul>
<h3>Stabilization</h3>
<p>The following behaviors have been stabilized:</p>
<ul>
<li><a
href="https://docs.astral.sh/ruff/rules/blanket-noqa/"><code>blanket-noqa</code></a>
(<code>PGH004</code>): Also detect blanked file-level noqa comments (and
not just line level comments).</li>
</ul>
<h3>Preview features</h3>
<ul>
<li>[syntax-errors] Tuple unpacking in <code>for</code> statement
iterator clause before Python 3.9 (<a
href="https://redirect.github.com/astral-sh/ruff/pull/16558">#16558</a>)</li>
</ul>
<h2>0.10.0</h2>
<p>Check out the <a href="https://astral.sh/blog/ruff-v0.10.0">blog
post</a> for a migration guide and overview of the changes!</p>
<h3>Breaking changes</h3>
<p>See also, the &quot;Remapped rules&quot; section which may result in
disabled rules.</p>
<ul>
<li>
<p><strong>Changes to how the Python version is inferred when a
<code>target-version</code> is not specified</strong> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/16319">#16319</a>)</p>
<p>Because of a mistake in the release process, the
<code>requires-python</code> inference changes are not included in this
release and instead shipped as part of 0.11.0.
You can find a description of this change in the 0.11.0 section.</p>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="2cd25ef641"><code>2cd25ef</code></a>
Ruff 0.11.0 (<a
href="https://redirect.github.com/astral-sh/ruff/issues/16723">#16723</a>)</li>
<li><a
href="a22d206db2"><code>a22d206</code></a>
[red-knot] Preliminary tests for typing.Final (<a
href="https://redirect.github.com/astral-sh/ruff/issues/15917">#15917</a>)</li>
<li><a
href="270318c2e0"><code>270318c</code></a>
[red-knot] fix: improve type inference for binary ops on tuples (<a
href="https://redirect.github.com/astral-sh/ruff/issues/16725">#16725</a>)</li>
<li><a
href="d03b12e711"><code>d03b12e</code></a>
[red-knot] Assignments to attributes (<a
href="https://redirect.github.com/astral-sh/ruff/issues/16705">#16705</a>)</li>
<li><a
href="14c5ed5d7d"><code>14c5ed5</code></a>
[<code>pygrep-hooks</code>]: Detect file-level suppressions comments
without rul… (<a
href="https://redirect.github.com/astral-sh/ruff/issues/16720">#16720</a>)</li>
<li><a
href="595565015b"><code>5955650</code></a>
Fallback to requires-python in certain cases when target-version is not
found...</li>
<li><a
href="2382fe1f25"><code>2382fe1</code></a>
[syntax-errors] Tuple unpacking in <code>for</code> statement iterator
clause before Pyt...</li>
<li><a
href="27e9d1fe3e"><code>27e9d1f</code></a>
Ruff v0.10 Release (<a
href="https://redirect.github.com/astral-sh/ruff/issues/16708">#16708</a>)</li>
<li><a
href="acf35c55f8"><code>acf35c5</code></a>
Add new <code>noqa</code> specification to the docs (<a
href="https://redirect.github.com/astral-sh/ruff/issues/16703">#16703</a>)</li>
<li><a
href="b9b256209b"><code>b9b2562</code></a>
describe requires-python fallback in docs (<a
href="https://redirect.github.com/astral-sh/ruff/issues/16704">#16704</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/astral-sh/ruff/compare/0.9.1...0.11.0">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Erik Vroon <erik.vroon@channable.com>
2025-03-17 06:40:10 +00:00

201 lines
7.2 KiB
Python

from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from typing import cast
from sqlalchemy import Table
from bracket.database import database
from bracket.models.db.club import Club, ClubInsertable
from bracket.models.db.court import Court, CourtInsertable
from bracket.models.db.match import Match, MatchInsertable
from bracket.models.db.player import Player, PlayerInsertable
from bracket.models.db.player_x_team import PlayerXTeamInsertable
from bracket.models.db.ranking import Ranking, RankingInsertable
from bracket.models.db.round import Round, RoundInsertable
from bracket.models.db.stage import Stage, StageInsertable
from bracket.models.db.stage_item import StageItem, StageItemInsertable
from bracket.models.db.stage_item_inputs import (
StageItemInputBase,
StageItemInputEmpty,
StageItemInputFinal,
StageItemInputInsertable,
)
from bracket.models.db.team import Team, TeamInsertable
from bracket.models.db.tournament import Tournament, TournamentInsertable
from bracket.models.db.user import UserInDB, UserInsertable
from bracket.models.db.user_x_club import UserXClub, UserXClubInsertable, UserXClubRelation
from bracket.schema import (
clubs,
courts,
matches,
players,
players_x_teams,
rankings,
rounds,
stage_item_inputs,
stage_items,
stages,
teams,
tournaments,
users,
users_x_clubs,
)
from bracket.sql.teams import get_teams_by_id
from bracket.utils.db import insert_generic
from bracket.utils.dummy_records import DUMMY_CLUB, DUMMY_RANKING1, DUMMY_TOURNAMENT
from bracket.utils.id_types import TeamId
from bracket.utils.types import BaseModelT
from tests.integration_tests.mocks import get_mock_token, get_mock_user
from tests.integration_tests.models import AuthContext
async def assert_row_count_and_clear(table: Table, expected_rows: int) -> None:
# assert len(await database.fetch_all(query=table.select())) == expected_rows
await database.execute(query=table.delete())
@asynccontextmanager
async def inserted_generic(
data_model: BaseModelT, table: Table, return_type: type[BaseModelT]
) -> AsyncIterator[BaseModelT]:
last_record_id, row_inserted = await insert_generic(database, data_model, table, return_type)
try:
yield row_inserted
finally:
await database.execute(query=table.delete().where(table.c.id == last_record_id))
@asynccontextmanager
async def inserted_user(user: UserInsertable) -> AsyncIterator[UserInDB]:
async with inserted_generic(user, users, UserInDB) as row_inserted:
yield cast("UserInDB", row_inserted)
@asynccontextmanager
async def inserted_club(club: ClubInsertable) -> AsyncIterator[Club]:
async with inserted_generic(club, clubs, Club) as row_inserted:
yield cast("Club", row_inserted)
@asynccontextmanager
async def inserted_tournament(tournament: TournamentInsertable) -> AsyncIterator[Tournament]:
async with inserted_generic(tournament, tournaments, Tournament) as row_inserted:
yield cast("Tournament", row_inserted)
@asynccontextmanager
async def inserted_team(team: TeamInsertable) -> AsyncIterator[Team]:
async with inserted_generic(team, teams, Team) as row_inserted:
yield cast("Team", row_inserted)
@asynccontextmanager
async def inserted_court(court: CourtInsertable) -> AsyncIterator[Court]:
async with inserted_generic(court, courts, Court) as row_inserted:
yield cast("Court", row_inserted)
@asynccontextmanager
async def inserted_ranking(ranking: RankingInsertable) -> AsyncIterator[Ranking]:
async with inserted_generic(ranking, rankings, Ranking) as row_inserted:
yield cast("Ranking", row_inserted)
@asynccontextmanager
async def inserted_player(player: PlayerInsertable) -> AsyncIterator[Player]:
async with inserted_generic(player, players, Player) as row_inserted:
yield cast("Player", row_inserted)
@asynccontextmanager
async def inserted_player_in_team(
player: PlayerInsertable, team_id: TeamId
) -> AsyncIterator[Player]:
async with inserted_generic(player, players, Player) as row_inserted:
async with inserted_generic(
PlayerXTeamInsertable(player_id=cast("Player", row_inserted).id, team_id=team_id),
players_x_teams,
PlayerXTeamInsertable,
):
yield cast("Player", row_inserted)
@asynccontextmanager
async def inserted_stage(stage: StageInsertable) -> AsyncIterator[Stage]:
async with inserted_generic(stage, stages, Stage) as row_inserted:
yield cast("Stage", row_inserted)
@asynccontextmanager
async def inserted_stage_item(stage_item: StageItemInsertable) -> AsyncIterator[StageItem]:
async with inserted_generic(stage_item, stage_items, StageItem) as row_inserted:
yield StageItem(**row_inserted.model_dump())
@asynccontextmanager
async def inserted_stage_item_input(
stage_item_input: StageItemInputInsertable,
) -> AsyncIterator[StageItemInputFinal | StageItemInputEmpty]:
async with inserted_generic(
stage_item_input, stage_item_inputs, StageItemInputBase
) as row_inserted:
if stage_item_input.team_id is not None:
[team] = await get_teams_by_id(
{stage_item_input.team_id}, stage_item_input.tournament_id
)
yield StageItemInputFinal.model_validate(
row_inserted.model_dump() | {"team": team, "team_id": team.id}
)
else:
yield StageItemInputEmpty.model_validate(row_inserted.model_dump())
@asynccontextmanager
async def inserted_round(round_: RoundInsertable) -> AsyncIterator[Round]:
async with inserted_generic(round_, rounds, Round) as row_inserted:
yield cast("Round", row_inserted)
@asynccontextmanager
async def inserted_match(match: MatchInsertable) -> AsyncIterator[Match]:
async with inserted_generic(match, matches, Match) as row_inserted:
yield cast("Match", row_inserted)
@asynccontextmanager
async def inserted_user_x_club(user_x_club: UserXClubInsertable) -> AsyncIterator[UserXClub]:
async with inserted_generic(user_x_club, users_x_clubs, UserXClub) as row_inserted:
yield cast("UserXClub", row_inserted)
@asynccontextmanager
async def inserted_auth_context() -> AsyncIterator[AuthContext]:
mock_user = get_mock_user()
headers = {"Authorization": f"Bearer {get_mock_token(mock_user)}"}
async with (
inserted_user(mock_user) as user_inserted,
inserted_club(DUMMY_CLUB) as club_inserted,
inserted_tournament(
DUMMY_TOURNAMENT.model_copy(update={"club_id": club_inserted.id})
) as tournament_inserted,
inserted_ranking(
DUMMY_RANKING1.model_copy(update={"tournament_id": tournament_inserted.id})
) as ranking_inserted,
inserted_user_x_club(
UserXClubInsertable(
user_id=user_inserted.id,
club_id=club_inserted.id,
relation=UserXClubRelation.OWNER,
)
) as user_x_club_inserted,
):
yield AuthContext(
headers=headers,
user=user_inserted,
club=club_inserted,
tournament=tournament_inserted,
user_x_club=user_x_club_inserted,
ranking=ranking_inserted,
)