1
0

Implemented content upload app with tests and pre-commit hooks

This commit is contained in:
2026-03-13 11:30:28 +00:00
commit 342d39d457
33 changed files with 2740 additions and 0 deletions

View File

@@ -0,0 +1,208 @@
from __future__ import annotations
import json
from datetime import UTC, datetime, timedelta
from pathlib import Path
from content_automation.adapters.social.youtube import (
YouTubeAdapter,
YouTubeDataApiClient,
YouTubeSnippet,
YouTubeStatus,
YouTubeVideoInsertPayload,
)
class FakeInsertRequest:
def __init__(self, response: dict[str, object]) -> None:
self._response = response
def execute(self) -> dict[str, object]:
return self._response
class FakeResumableRequest:
def __init__(self) -> None:
self._calls = 0
def next_chunk(self):
self._calls += 1
if self._calls == 1:
return object(), None
return None, {"id": "video-123"}
class FakeVideosResource:
def __init__(self, request) -> None:
self._request = request
def insert(self, part: str, body: dict, media_body=None):
return self._request
class FakeService:
def __init__(self, request) -> None:
self._videos = FakeVideosResource(request)
def videos(self) -> FakeVideosResource:
return self._videos
def test_resumable_upload_happy_path(monkeypatch, tmp_path: Path) -> None:
media_file = tmp_path / "clip.mp4"
media_file.write_bytes(b"abcdef")
monkeypatch.setattr(
"content_automation.adapters.social.youtube.build",
lambda *args, **kwargs: FakeService(FakeResumableRequest()),
)
monkeypatch.setattr(
"content_automation.adapters.social.youtube.MediaFileUpload",
lambda *args, **kwargs: object(),
)
adapter = YouTubeAdapter(
access_token="token",
use_resumable_upload=True,
resumable_chunk_size=3,
)
post_id = adapter.post_media(media_url=media_file.as_uri(), caption="caption")
assert post_id == "video-123"
def test_regular_upload_happy_path(monkeypatch, tmp_path: Path) -> None:
media_file = tmp_path / "clip.mp4"
media_file.write_bytes(b"abcdef")
monkeypatch.setattr(
"content_automation.adapters.social.youtube.build",
lambda *args, **kwargs: FakeService(
FakeInsertRequest({"id": "video-regular-123"})
),
)
monkeypatch.setattr(
"content_automation.adapters.social.youtube.MediaFileUpload",
lambda *args, **kwargs: object(),
)
adapter = YouTubeAdapter(
access_token="token",
use_resumable_upload=False,
)
post_id = adapter.post_media(media_url=media_file.as_uri(), caption="caption")
assert post_id == "video-regular-123"
def test_insert_video_happy_path_for_non_local_url(monkeypatch) -> None:
monkeypatch.setattr(
"content_automation.adapters.social.youtube.build",
lambda *args, **kwargs: FakeService(
FakeInsertRequest({"id": "video-insert-123"})
),
)
adapter = YouTubeAdapter(access_token="token")
post_id = adapter.post_media(
media_url="https://cdn.example.com/path/to/video.mp4", caption="caption"
)
assert post_id == "video-insert-123"
def test_client_refreshes_expired_token_before_request(monkeypatch) -> None:
refreshed_tokens: list[str] = []
def fake_refresh(self, request) -> None:
self.token = "new-token"
self.expiry = datetime.now(UTC).replace(tzinfo=None) + timedelta(minutes=30)
refreshed_tokens.append(self.token)
monkeypatch.setattr(
"content_automation.adapters.social.youtube.Credentials.refresh",
fake_refresh,
)
monkeypatch.setattr(
"content_automation.adapters.social.youtube.build",
lambda *args, **kwargs: FakeService(
FakeInsertRequest({"id": "video-refreshed"})
),
)
client = YouTubeDataApiClient(
access_token="expired-token",
category_id="22",
privacy_status="public",
refresh_token="refresh-token",
client_id="client-id",
client_secret="client-secret",
expiry="2024-01-01T00:00:00Z",
)
payload = YouTubeVideoInsertPayload(
snippet=YouTubeSnippet(
title="title",
description="description",
categoryId="22",
),
status=YouTubeStatus(privacyStatus="public"),
sourceUrl="https://cdn.example.com/video.mp4",
)
response = client.insert_video(part="snippet,status", payload=payload)
assert response["id"] == "video-refreshed"
assert refreshed_tokens == ["new-token"]
def test_obtain_credentials_from_client_secret_file(
monkeypatch, tmp_path: Path
) -> None:
captured: dict[str, object] = {}
class FakeCredentials:
def to_json(self) -> str:
return json.dumps(
{
"token": "token-123",
"refresh_token": "refresh-123",
"token_uri": "https://oauth2.googleapis.com/token",
"client_id": "client-123",
"client_secret": "secret-123",
"scopes": ["https://www.googleapis.com/auth/youtube.upload"],
}
)
class FakeFlow:
def run_local_server(self):
return FakeCredentials()
def fake_from_client_secrets_file(client_secret_file: str, scopes: list[str]):
captured["client_secret_file"] = client_secret_file
captured["scopes"] = scopes
return FakeFlow()
monkeypatch.setattr(
"content_automation.adapters.social.youtube.InstalledAppFlow.from_client_secrets_file",
fake_from_client_secrets_file,
)
client_secret_path = tmp_path / "client_secret.json"
token_output_path = tmp_path / "youtube_credentials.json"
credentials_payload = YouTubeAdapter.obtain_credentials_from_client_secret_file(
client_secret_file_path=client_secret_path,
scopes=["https://www.googleapis.com/auth/youtube.upload"],
token_output_path=token_output_path,
)
assert captured["client_secret_file"] == str(client_secret_path)
assert captured["scopes"] == ["https://www.googleapis.com/auth/youtube.upload"]
assert credentials_payload["token"] == "token-123"
assert token_output_path.exists()
assert (
json.loads(token_output_path.read_text(encoding="utf-8"))["token"]
== "token-123"
)