Add clip pinning test coverage
This commit is contained in:
@@ -56,6 +56,7 @@ class AudioClip(Base):
|
||||
start_ms: Mapped[int] = mapped_column(Integer)
|
||||
end_ms: Mapped[int] = mapped_column(Integer)
|
||||
sort_order: Mapped[int] = mapped_column(Integer, default=0, index=True)
|
||||
hidden: Mapped[bool] = mapped_column(Boolean, default=False, index=True)
|
||||
normalization_status: Mapped[str] = mapped_column(String(32), default="pending")
|
||||
normalized_path: Mapped[str | None] = mapped_column(String(512))
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow)
|
||||
|
||||
@@ -65,7 +65,10 @@ def list_assignments(
|
||||
_: UserSession = Depends(require_session),
|
||||
db: Session = Depends(get_db),
|
||||
) -> list[GameAssignmentResponse]:
|
||||
query = select(GameAssignment).join(GameAssignment.clip).where(GameAssignment.external_game_id == external_game_id)
|
||||
query = select(GameAssignment).join(GameAssignment.clip).where(
|
||||
GameAssignment.external_game_id == external_game_id,
|
||||
AudioClip.hidden.is_(False),
|
||||
)
|
||||
if external_player_id:
|
||||
query = query.where(GameAssignment.external_player_id == external_player_id)
|
||||
assignments = db.scalars(query.order_by(AudioClip.sort_order.asc(), GameAssignment.updated_at.desc())).all()
|
||||
@@ -82,6 +85,8 @@ def create_assignment(
|
||||
clip = db.get(AudioClip, payload.clip_id)
|
||||
if clip is None or clip.normalization_status != "ready":
|
||||
raise HTTPException(status_code=422, detail="Clip is not ready")
|
||||
if clip.hidden:
|
||||
raise HTTPException(status_code=404, detail="Clip not found")
|
||||
if clip.asset.external_team_id != payload.external_team_id:
|
||||
raise HTTPException(status_code=422, detail="Clip does not belong to this team")
|
||||
if clip.asset.owner_external_player_id != payload.external_player_id:
|
||||
@@ -142,7 +147,7 @@ def prepare_game(
|
||||
assignments = db.scalars(
|
||||
select(GameAssignment)
|
||||
.join(GameAssignment.clip)
|
||||
.where(GameAssignment.external_game_id == external_game_id)
|
||||
.where(GameAssignment.external_game_id == external_game_id, AudioClip.hidden.is_(False))
|
||||
.order_by(AudioClip.sort_order.asc(), GameAssignment.updated_at.desc())
|
||||
).all()
|
||||
external_team_id = assignments[0].external_team_id if assignments else ""
|
||||
@@ -192,11 +197,15 @@ def trigger_playback(
|
||||
assignment = db.get(GameAssignment, payload.assignment_id)
|
||||
if assignment is None or assignment.external_game_id != external_game_id:
|
||||
raise HTTPException(status_code=404, detail="Pin not found")
|
||||
if assignment.clip.hidden:
|
||||
raise HTTPException(status_code=404, detail="Pin not found")
|
||||
playback.current_assignment_id = assignment.id
|
||||
else:
|
||||
clip = db.get(AudioClip, payload.clip_id)
|
||||
if clip is None or clip.asset.external_team_id != playback.external_team_id:
|
||||
raise HTTPException(status_code=404, detail="Clip not found")
|
||||
if clip.hidden:
|
||||
raise HTTPException(status_code=404, detail="Clip not found")
|
||||
if payload.external_player_id and clip.asset.owner_external_player_id != payload.external_player_id:
|
||||
raise HTTPException(status_code=403, detail="Clip does not belong to that player")
|
||||
playback.current_assignment_id = None
|
||||
|
||||
@@ -41,6 +41,7 @@ def clip_to_response(clip: AudioClip) -> AudioClipResponse:
|
||||
start_ms=clip.start_ms,
|
||||
end_ms=clip.end_ms,
|
||||
sort_order=clip.sort_order,
|
||||
hidden=clip.hidden,
|
||||
normalization_status=clip.normalization_status,
|
||||
normalized_url=normalized_url,
|
||||
waveform_duration_ms=waveform["duration_ms"] if waveform else None,
|
||||
@@ -340,6 +341,8 @@ def update_clip(
|
||||
clip.end_ms = payload.end_ms
|
||||
if payload.sort_order is not None:
|
||||
clip.sort_order = payload.sort_order
|
||||
if payload.hidden is not None:
|
||||
clip.hidden = payload.hidden
|
||||
db.commit()
|
||||
db.refresh(clip)
|
||||
return clip_to_response(clip)
|
||||
@@ -376,6 +379,7 @@ def delete_clip(
|
||||
def list_clips(
|
||||
external_team_id: str,
|
||||
owner_external_player_id: str | None = None,
|
||||
include_hidden: bool = False,
|
||||
_: UserSession = Depends(require_session),
|
||||
db: Session = Depends(get_db),
|
||||
) -> list[AudioClipResponse]:
|
||||
@@ -387,6 +391,8 @@ def list_clips(
|
||||
)
|
||||
if owner_external_player_id:
|
||||
query = query.where(AudioAsset.owner_external_player_id == owner_external_player_id)
|
||||
if not include_hidden:
|
||||
query = query.where(AudioClip.hidden.is_(False))
|
||||
clips = db.scalars(query).all()
|
||||
return [clip_to_response(clip) for clip in clips]
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ class AudioClipUpdate(BaseModel):
|
||||
start_ms: int = Field(ge=0)
|
||||
end_ms: int = Field(gt=0)
|
||||
sort_order: int | None = None
|
||||
hidden: bool | None = None
|
||||
|
||||
|
||||
class AudioClipResponse(BaseModel):
|
||||
@@ -80,6 +81,7 @@ class AudioClipResponse(BaseModel):
|
||||
start_ms: int
|
||||
end_ms: int
|
||||
sort_order: int
|
||||
hidden: bool
|
||||
normalization_status: str
|
||||
normalized_url: str | None
|
||||
waveform_duration_ms: int | None = None
|
||||
|
||||
@@ -346,6 +346,104 @@ def test_player_can_reorder_clips_in_their_library() -> None:
|
||||
assert [item["sort_order"] for item in clips.json()] == [0, 1]
|
||||
|
||||
|
||||
def test_hidden_clips_are_removed_from_gameday_views_but_remain_pinnable() -> None:
|
||||
login = client.post("/auth/admin/login", json={"username": "admin", "password": "admin"})
|
||||
assert login.status_code == 200
|
||||
|
||||
db = SessionLocal()
|
||||
session = UserSession(
|
||||
session_token="player-hidden-session",
|
||||
provider="teamsnap",
|
||||
external_team_id="team-hidden",
|
||||
external_player_id="player-hidden",
|
||||
)
|
||||
db.add(session)
|
||||
db.flush()
|
||||
asset = AudioAsset(
|
||||
external_team_id="team-hidden",
|
||||
owner_external_player_id="player-hidden",
|
||||
uploaded_by_session_id=session.id,
|
||||
title="Hidden song",
|
||||
original_filename="hidden.mp3",
|
||||
mime_type="audio/mpeg",
|
||||
size_bytes=123,
|
||||
storage_path="uploads/hidden.mp3",
|
||||
)
|
||||
db.add(asset)
|
||||
db.flush()
|
||||
clip = AudioClip(
|
||||
asset_id=asset.id,
|
||||
label="Hidden clip",
|
||||
start_ms=0,
|
||||
end_ms=10000,
|
||||
normalization_status="ready",
|
||||
normalized_path="clips/hidden.mp3",
|
||||
)
|
||||
db.add(clip)
|
||||
db.commit()
|
||||
db.refresh(clip)
|
||||
db.close()
|
||||
|
||||
client.cookies.set(settings.session_cookie_name, "player-hidden-session")
|
||||
|
||||
pin_response = client.post(
|
||||
"/games/game-hidden/assignments",
|
||||
json={
|
||||
"external_team_id": "team-hidden",
|
||||
"external_player_id": "player-hidden",
|
||||
"clip_id": clip.id,
|
||||
"batting_slot": 1,
|
||||
"status": "ready",
|
||||
},
|
||||
)
|
||||
assert pin_response.status_code == 200
|
||||
|
||||
visible_before_hide = client.get(
|
||||
"/media/clips",
|
||||
params={"external_team_id": "team-hidden", "owner_external_player_id": "player-hidden"},
|
||||
)
|
||||
assert visible_before_hide.status_code == 200
|
||||
assert [item["id"] for item in visible_before_hide.json()] == [clip.id]
|
||||
assert visible_before_hide.json()[0]["hidden"] is False
|
||||
|
||||
hide_response = client.patch(
|
||||
f"/media/clips/{clip.id}",
|
||||
params={"owner_external_player_id": "player-hidden"},
|
||||
json={"start_ms": 0, "end_ms": 10000, "hidden": True},
|
||||
)
|
||||
assert hide_response.status_code == 200
|
||||
assert hide_response.json()["hidden"] is True
|
||||
|
||||
visible_after_hide = client.get(
|
||||
"/media/clips",
|
||||
params={"external_team_id": "team-hidden", "owner_external_player_id": "player-hidden"},
|
||||
)
|
||||
assert visible_after_hide.status_code == 200
|
||||
assert visible_after_hide.json() == []
|
||||
|
||||
hidden_in_library = client.get(
|
||||
"/media/clips",
|
||||
params={
|
||||
"external_team_id": "team-hidden",
|
||||
"owner_external_player_id": "player-hidden",
|
||||
"include_hidden": True,
|
||||
},
|
||||
)
|
||||
assert hidden_in_library.status_code == 200
|
||||
assert [item["id"] for item in hidden_in_library.json()] == [clip.id]
|
||||
assert hidden_in_library.json()[0]["hidden"] is True
|
||||
|
||||
game_assignments = client.get("/games/game-hidden/assignments")
|
||||
prep = client.get("/games/game-hidden/prep")
|
||||
pins = client.get("/games/pins", params={"external_player_id": "player-hidden"})
|
||||
assert game_assignments.status_code == 200
|
||||
assert prep.status_code == 200
|
||||
assert pins.status_code == 200
|
||||
assert game_assignments.json() == []
|
||||
assert prep.json()["assignments"] == []
|
||||
assert [item["clip_id"] for item in pins.json()] == [clip.id]
|
||||
|
||||
|
||||
def test_clip_updates_can_use_player_scoped_authorization() -> None:
|
||||
uploader_session = UserSession(session_token="uploader-session", provider="teamsnap")
|
||||
editor_session = UserSession(session_token="editor-session", provider="teamsnap")
|
||||
|
||||
Reference in New Issue
Block a user