forked from LiveCarta/ContentAutomation
Store posting results to a mongodb
This commit is contained in:
@@ -33,3 +33,9 @@ CONTENT_AUTOMATION_STORAGE__S3__REGION_NAME=
|
|||||||
CONTENT_AUTOMATION_STORAGE__S3__ENDPOINT_URL=
|
CONTENT_AUTOMATION_STORAGE__S3__ENDPOINT_URL=
|
||||||
CONTENT_AUTOMATION_STORAGE__S3__PUBLIC_URL_BASE=
|
CONTENT_AUTOMATION_STORAGE__S3__PUBLIC_URL_BASE=
|
||||||
CONTENT_AUTOMATION_STORAGE__S3__URL_EXPIRATION_SECONDS=3600
|
CONTENT_AUTOMATION_STORAGE__S3__URL_EXPIRATION_SECONDS=3600
|
||||||
|
|
||||||
|
# MongoDB publish persistence
|
||||||
|
CONTENT_AUTOMATION_MONGODB__ENABLED=false
|
||||||
|
CONTENT_AUTOMATION_MONGODB__CONNECTION_URI=
|
||||||
|
CONTENT_AUTOMATION_MONGODB__DATABASE_NAME=content_automation
|
||||||
|
CONTENT_AUTOMATION_MONGODB__COLLECTION_NAME=published_content
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ dependencies = [
|
|||||||
"boto3>=1.37.17",
|
"boto3>=1.37.17",
|
||||||
"google-api-python-client>=2.190.0",
|
"google-api-python-client>=2.190.0",
|
||||||
"google-auth-oauthlib>=1.2.4",
|
"google-auth-oauthlib>=1.2.4",
|
||||||
|
"pymongo>=4.15.2",
|
||||||
"pydantic>=2.12.5",
|
"pydantic>=2.12.5",
|
||||||
"pydantic-settings>=2.11.0",
|
"pydantic-settings>=2.11.0",
|
||||||
"pytest>=8.3.5",
|
"pytest>=8.3.5",
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from content_automation.adapters.publish_store.mongodb import MongoPublishedContentStore
|
||||||
|
|
||||||
|
__all__ = ["MongoPublishedContentStore"]
|
||||||
22
src/content_automation/adapters/publish_store/mongodb.py
Normal file
22
src/content_automation/adapters/publish_store/mongodb.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pymongo import MongoClient
|
||||||
|
|
||||||
|
from content_automation.interfaces import PublishedContentStore
|
||||||
|
from content_automation.models import PublishedContentRecord
|
||||||
|
|
||||||
|
|
||||||
|
class MongoPublishedContentStore(PublishedContentStore):
|
||||||
|
"""MongoDB-backed persistence for publish operation records."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
connection_uri: str,
|
||||||
|
database_name: str,
|
||||||
|
collection_name: str,
|
||||||
|
) -> None:
|
||||||
|
self._client = MongoClient(connection_uri)
|
||||||
|
self._collection = self._client[database_name][collection_name]
|
||||||
|
|
||||||
|
def save(self, record: PublishedContentRecord) -> None:
|
||||||
|
self._collection.insert_one(record.model_dump(mode="json"))
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Mapping
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from content_automation.adapters.storage.base import StorageAdapterBase
|
from content_automation.adapters.storage.base import StorageAdapterBase
|
||||||
from content_automation.interfaces import SocialNetworkAdapter
|
from content_automation.interfaces import PublishedContentStore, SocialNetworkAdapter
|
||||||
|
from content_automation.models import PublishedContentRecord
|
||||||
from content_automation.settings import AppSettings
|
from content_automation.settings import AppSettings
|
||||||
|
|
||||||
|
|
||||||
@@ -13,22 +17,60 @@ class PublishController:
|
|||||||
settings: AppSettings,
|
settings: AppSettings,
|
||||||
storage: StorageAdapterBase,
|
storage: StorageAdapterBase,
|
||||||
social_adapters: dict[str, SocialNetworkAdapter],
|
social_adapters: dict[str, SocialNetworkAdapter],
|
||||||
|
published_content_store: PublishedContentStore | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
self._settings = settings
|
self._settings = settings
|
||||||
self._storage = storage
|
self._storage = storage
|
||||||
self._social_adapters = social_adapters
|
self._social_adapters = social_adapters
|
||||||
|
self._published_content_store = published_content_store
|
||||||
|
|
||||||
def publish(self, relative_path: str, caption: str) -> dict[str, str]:
|
def publish(self, relative_path: str, caption: str) -> dict[str, Any]:
|
||||||
if not self._storage.exists(relative_path):
|
if not self._storage.exists(relative_path):
|
||||||
raise FileNotFoundError(
|
raise FileNotFoundError(
|
||||||
f"Media file is not available in storage: {relative_path}"
|
f"Media file is not available in storage: {relative_path}"
|
||||||
)
|
)
|
||||||
|
|
||||||
media_url = self._storage.get_public_url(relative_path)
|
media_url = self._storage.get_public_url(relative_path)
|
||||||
result: dict[str, str] = {}
|
result: dict[str, Any] = {}
|
||||||
for network in self._settings.target_social_networks:
|
for network in self._settings.target_social_networks:
|
||||||
adapter = self._social_adapters.get(network)
|
adapter = self._social_adapters.get(network)
|
||||||
if adapter is None:
|
if adapter is None:
|
||||||
raise ValueError(f"No adapter configured for network: {network}")
|
raise ValueError(f"No adapter configured for network: {network}")
|
||||||
result[network] = adapter.post_media(media_url=media_url, caption=caption)
|
result[network] = adapter.post_media(media_url=media_url, caption=caption)
|
||||||
|
|
||||||
|
if self._published_content_store is not None:
|
||||||
|
record = PublishedContentRecord(
|
||||||
|
relative_path=relative_path,
|
||||||
|
media_url=media_url,
|
||||||
|
caption=caption,
|
||||||
|
platform_responses=self._serialize_platform_responses(result),
|
||||||
|
)
|
||||||
|
self._published_content_store.save(record)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def _serialize_platform_responses(
|
||||||
|
self, responses: Mapping[str, Any]
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
serialized: dict[str, Any] = {}
|
||||||
|
for network, response in responses.items():
|
||||||
|
if network == "youtube":
|
||||||
|
serialized[network] = self._extract_youtube_video_id(response)
|
||||||
|
continue
|
||||||
|
serialized[network] = response
|
||||||
|
return serialized
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _extract_youtube_video_id(response: Any) -> str:
|
||||||
|
if isinstance(response, str):
|
||||||
|
return response
|
||||||
|
|
||||||
|
if isinstance(response, Mapping):
|
||||||
|
video_id = response.get("id")
|
||||||
|
if isinstance(video_id, str) and video_id:
|
||||||
|
return video_id
|
||||||
|
|
||||||
|
raise ValueError(
|
||||||
|
"Unsupported YouTube publish response: expected a string ID "
|
||||||
|
"or payload containing an 'id' field."
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from content_automation.adapters.publish_store.mongodb import MongoPublishedContentStore
|
||||||
from content_automation.adapters.social.instagram import InstagramAdapter
|
from content_automation.adapters.social.instagram import InstagramAdapter
|
||||||
from content_automation.adapters.social.youtube import YouTubeAdapter
|
from content_automation.adapters.social.youtube import YouTubeAdapter
|
||||||
from content_automation.adapters.storage.base import StorageAdapterBase
|
from content_automation.adapters.storage.base import StorageAdapterBase
|
||||||
from content_automation.adapters.storage.local import LocalFilesystemStorageAdapter
|
from content_automation.adapters.storage.local import LocalFilesystemStorageAdapter
|
||||||
from content_automation.adapters.storage.s3 import S3StorageAdapter
|
from content_automation.adapters.storage.s3 import S3StorageAdapter
|
||||||
from content_automation.interfaces import SocialNetworkAdapter
|
from content_automation.interfaces import PublishedContentStore, SocialNetworkAdapter
|
||||||
from content_automation.settings import AppSettings
|
from content_automation.settings import AppSettings
|
||||||
|
|
||||||
|
|
||||||
@@ -49,3 +50,22 @@ def build_social_adapters(settings: AppSettings) -> dict[str, SocialNetworkAdapt
|
|||||||
expiry=settings.youtube.expiry or None,
|
expiry=settings.youtube.expiry or None,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def build_published_content_store(
|
||||||
|
settings: AppSettings,
|
||||||
|
) -> PublishedContentStore | None:
|
||||||
|
if not settings.mongodb.enabled:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if not settings.mongodb.connection_uri:
|
||||||
|
raise ValueError(
|
||||||
|
"CONTENT_AUTOMATION_MONGODB__CONNECTION_URI must be set when "
|
||||||
|
"MongoDB persistence is enabled."
|
||||||
|
)
|
||||||
|
|
||||||
|
return MongoPublishedContentStore(
|
||||||
|
connection_uri=settings.mongodb.connection_uri,
|
||||||
|
database_name=settings.mongodb.database_name,
|
||||||
|
collection_name=settings.mongodb.collection_name,
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Protocol
|
from typing import Any, Protocol
|
||||||
|
|
||||||
|
from content_automation.models import PublishedContentRecord
|
||||||
|
|
||||||
|
|
||||||
class SocialNetworkAdapter(Protocol):
|
class SocialNetworkAdapter(Protocol):
|
||||||
@@ -8,5 +10,12 @@ class SocialNetworkAdapter(Protocol):
|
|||||||
|
|
||||||
name: str
|
name: str
|
||||||
|
|
||||||
def post_media(self, media_url: str, caption: str) -> str:
|
def post_media(self, media_url: str, caption: str) -> Any:
|
||||||
"""Publish media and return an external post identifier."""
|
"""Publish media and return a provider response payload."""
|
||||||
|
|
||||||
|
|
||||||
|
class PublishedContentStore(Protocol):
|
||||||
|
"""Contract for persisting publish operation metadata."""
|
||||||
|
|
||||||
|
def save(self, record: PublishedContentRecord) -> None:
|
||||||
|
"""Persist a published content record."""
|
||||||
|
|||||||
@@ -3,7 +3,11 @@ from __future__ import annotations
|
|||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
from content_automation.controller import PublishController
|
from content_automation.controller import PublishController
|
||||||
from content_automation.factories import build_social_adapters, build_storage_adapter
|
from content_automation.factories import (
|
||||||
|
build_published_content_store,
|
||||||
|
build_social_adapters,
|
||||||
|
build_storage_adapter,
|
||||||
|
)
|
||||||
from content_automation.settings import AppSettings
|
from content_automation.settings import AppSettings
|
||||||
|
|
||||||
|
|
||||||
@@ -23,8 +27,12 @@ def main() -> None:
|
|||||||
settings = AppSettings()
|
settings = AppSettings()
|
||||||
storage = build_storage_adapter(settings)
|
storage = build_storage_adapter(settings)
|
||||||
adapters = build_social_adapters(settings)
|
adapters = build_social_adapters(settings)
|
||||||
|
published_content_store = build_published_content_store(settings)
|
||||||
controller = PublishController(
|
controller = PublishController(
|
||||||
settings=settings, storage=storage, social_adapters=adapters
|
settings=settings,
|
||||||
|
storage=storage,
|
||||||
|
social_adapters=adapters,
|
||||||
|
published_content_store=published_content_store,
|
||||||
)
|
)
|
||||||
publish_results = controller.publish(
|
publish_results = controller.publish(
|
||||||
relative_path=args.relative_path, caption=args.caption
|
relative_path=args.relative_path, caption=args.caption
|
||||||
|
|||||||
16
src/content_automation/models.py
Normal file
16
src/content_automation/models.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from datetime import UTC, datetime
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
|
||||||
|
class PublishedContentRecord(BaseModel):
|
||||||
|
"""Stored metadata for a publish operation across social networks."""
|
||||||
|
|
||||||
|
relative_path: str
|
||||||
|
media_url: str
|
||||||
|
caption: str
|
||||||
|
platform_responses: dict[str, Any]
|
||||||
|
published_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
||||||
@@ -48,9 +48,16 @@ class AppSettings(BaseSettings):
|
|||||||
local: LocalSettings = Field(default_factory=LocalSettings)
|
local: LocalSettings = Field(default_factory=LocalSettings)
|
||||||
s3: S3Settings = Field(default_factory=S3Settings)
|
s3: S3Settings = Field(default_factory=S3Settings)
|
||||||
|
|
||||||
|
class MongoDbSettings(BaseModel):
|
||||||
|
enabled: bool = False
|
||||||
|
connection_uri: str = ""
|
||||||
|
database_name: str = "content_automation"
|
||||||
|
collection_name: str = "published_content"
|
||||||
|
|
||||||
target_social_networks: list[str] = Field(
|
target_social_networks: list[str] = Field(
|
||||||
default_factory=lambda: ["instagram", "youtube"]
|
default_factory=lambda: ["instagram", "youtube"]
|
||||||
)
|
)
|
||||||
instagram: InstagramSettings = Field(default_factory=InstagramSettings)
|
instagram: InstagramSettings = Field(default_factory=InstagramSettings)
|
||||||
youtube: YoutubeSettings = Field(default_factory=YoutubeSettings)
|
youtube: YoutubeSettings = Field(default_factory=YoutubeSettings)
|
||||||
storage: StorageSettings = Field(default_factory=StorageSettings)
|
storage: StorageSettings = Field(default_factory=StorageSettings)
|
||||||
|
mongodb: MongoDbSettings = Field(default_factory=MongoDbSettings)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import pytest
|
|||||||
|
|
||||||
from content_automation.adapters.storage.base import StorageAdapterBase
|
from content_automation.adapters.storage.base import StorageAdapterBase
|
||||||
from content_automation.controller import PublishController
|
from content_automation.controller import PublishController
|
||||||
|
from content_automation.models import PublishedContentRecord
|
||||||
from content_automation.settings import AppSettings
|
from content_automation.settings import AppSettings
|
||||||
|
|
||||||
|
|
||||||
@@ -29,6 +30,23 @@ class FakeAdapter:
|
|||||||
return f"{self.name}-post-id"
|
return f"{self.name}-post-id"
|
||||||
|
|
||||||
|
|
||||||
|
class FakeAdapterWithResponsePayload:
|
||||||
|
def __init__(self, payload: object) -> None:
|
||||||
|
self.name = "adapter-with-payload"
|
||||||
|
self._payload = payload
|
||||||
|
|
||||||
|
def post_media(self, media_url: str, caption: str) -> object:
|
||||||
|
return self._payload
|
||||||
|
|
||||||
|
|
||||||
|
class FakePublishedContentStore:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.records: list[PublishedContentRecord] = []
|
||||||
|
|
||||||
|
def save(self, record: PublishedContentRecord) -> None:
|
||||||
|
self.records.append(record)
|
||||||
|
|
||||||
|
|
||||||
def test_controller_publishes_to_all_configured_networks() -> None:
|
def test_controller_publishes_to_all_configured_networks() -> None:
|
||||||
settings = AppSettings.model_validate(
|
settings = AppSettings.model_validate(
|
||||||
{"target_social_networks": ["instagram", "youtube"]}
|
{"target_social_networks": ["instagram", "youtube"]}
|
||||||
@@ -47,6 +65,47 @@ def test_controller_publishes_to_all_configured_networks() -> None:
|
|||||||
assert result == {"instagram": "instagram-post-id", "youtube": "youtube-post-id"}
|
assert result == {"instagram": "instagram-post-id", "youtube": "youtube-post-id"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_controller_stores_published_content_with_youtube_id_only() -> None:
|
||||||
|
settings = AppSettings.model_validate(
|
||||||
|
{"target_social_networks": ["instagram", "youtube"]}
|
||||||
|
)
|
||||||
|
published_content_store = FakePublishedContentStore()
|
||||||
|
controller = PublishController(
|
||||||
|
settings=settings,
|
||||||
|
storage=FakeStorage(exists_result=True),
|
||||||
|
social_adapters={
|
||||||
|
"instagram": FakeAdapterWithResponsePayload(
|
||||||
|
{"id": "ig-123", "status": "ok"}
|
||||||
|
),
|
||||||
|
"youtube": FakeAdapterWithResponsePayload(
|
||||||
|
{
|
||||||
|
"id": "yt-456",
|
||||||
|
"kind": "youtube#video",
|
||||||
|
"snippet": {"title": "hello"},
|
||||||
|
}
|
||||||
|
),
|
||||||
|
},
|
||||||
|
published_content_store=published_content_store,
|
||||||
|
)
|
||||||
|
|
||||||
|
result = controller.publish(relative_path="video.mp4", caption="hello")
|
||||||
|
|
||||||
|
assert result["youtube"] == {
|
||||||
|
"id": "yt-456",
|
||||||
|
"kind": "youtube#video",
|
||||||
|
"snippet": {"title": "hello"},
|
||||||
|
}
|
||||||
|
assert len(published_content_store.records) == 1
|
||||||
|
saved_record = published_content_store.records[0]
|
||||||
|
assert saved_record.relative_path == "video.mp4"
|
||||||
|
assert saved_record.caption == "hello"
|
||||||
|
assert saved_record.platform_responses["instagram"] == {
|
||||||
|
"id": "ig-123",
|
||||||
|
"status": "ok",
|
||||||
|
}
|
||||||
|
assert saved_record.platform_responses["youtube"] == "yt-456"
|
||||||
|
|
||||||
|
|
||||||
def test_controller_raises_when_file_missing() -> None:
|
def test_controller_raises_when_file_missing() -> None:
|
||||||
settings = AppSettings.model_validate({"target_social_networks": ["youtube"]})
|
settings = AppSettings.model_validate({"target_social_networks": ["youtube"]})
|
||||||
controller = PublishController(
|
controller = PublishController(
|
||||||
|
|||||||
115
tests/test_publish_store.py
Normal file
115
tests/test_publish_store.py
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from content_automation.adapters.publish_store import MongoPublishedContentStore
|
||||||
|
from content_automation.factories import build_published_content_store
|
||||||
|
from content_automation.models import PublishedContentRecord
|
||||||
|
from content_automation.settings import AppSettings
|
||||||
|
|
||||||
|
|
||||||
|
class FakeCollection:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.documents: list[dict[str, object]] = []
|
||||||
|
|
||||||
|
def insert_one(self, document: dict[str, object]) -> None:
|
||||||
|
self.documents.append(document)
|
||||||
|
|
||||||
|
|
||||||
|
class FakeDatabase:
|
||||||
|
def __init__(self, collection: FakeCollection) -> None:
|
||||||
|
self._collection = collection
|
||||||
|
|
||||||
|
def __getitem__(self, collection_name: str) -> FakeCollection:
|
||||||
|
return self._collection
|
||||||
|
|
||||||
|
|
||||||
|
class FakeMongoClient:
|
||||||
|
def __init__(self, connection_uri: str) -> None:
|
||||||
|
self.connection_uri = connection_uri
|
||||||
|
self.collection = FakeCollection()
|
||||||
|
|
||||||
|
def __getitem__(self, database_name: str) -> FakeDatabase:
|
||||||
|
return FakeDatabase(self.collection)
|
||||||
|
|
||||||
|
|
||||||
|
def test_mongo_published_content_store_saves_record(monkeypatch) -> None:
|
||||||
|
fake_client = FakeMongoClient("mongodb://example:27017")
|
||||||
|
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"content_automation.adapters.publish_store.mongodb.MongoClient",
|
||||||
|
lambda connection_uri: fake_client,
|
||||||
|
)
|
||||||
|
|
||||||
|
store = MongoPublishedContentStore(
|
||||||
|
connection_uri="mongodb://example:27017",
|
||||||
|
database_name="content_automation",
|
||||||
|
collection_name="published_content",
|
||||||
|
)
|
||||||
|
|
||||||
|
store.save(
|
||||||
|
PublishedContentRecord(
|
||||||
|
relative_path="media/video.mp4",
|
||||||
|
media_url="https://cdn.example.com/media/video.mp4",
|
||||||
|
caption="caption",
|
||||||
|
platform_responses={
|
||||||
|
"instagram": {"id": "ig-1"},
|
||||||
|
"youtube": "yt-1",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(fake_client.collection.documents) == 1
|
||||||
|
assert fake_client.collection.documents[0]["relative_path"] == "media/video.mp4"
|
||||||
|
assert fake_client.collection.documents[0]["platform_responses"] == {
|
||||||
|
"instagram": {"id": "ig-1"},
|
||||||
|
"youtube": "yt-1",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_build_published_content_store_returns_none_when_disabled() -> None:
|
||||||
|
settings = AppSettings.model_validate({"mongodb": {"enabled": False}})
|
||||||
|
|
||||||
|
store = build_published_content_store(settings)
|
||||||
|
|
||||||
|
assert store is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_build_published_content_store_requires_connection_uri() -> None:
|
||||||
|
settings = AppSettings.model_validate({"mongodb": {"enabled": True}})
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
build_published_content_store(settings)
|
||||||
|
|
||||||
|
|
||||||
|
def test_build_published_content_store_returns_mongo_store(monkeypatch) -> None:
|
||||||
|
class FakeMongoStore:
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
connection_uri: str,
|
||||||
|
database_name: str,
|
||||||
|
collection_name: str,
|
||||||
|
) -> None:
|
||||||
|
self.connection_uri = connection_uri
|
||||||
|
self.database_name = database_name
|
||||||
|
self.collection_name = collection_name
|
||||||
|
|
||||||
|
monkeypatch.setattr("content_automation.factories.MongoPublishedContentStore", FakeMongoStore)
|
||||||
|
|
||||||
|
settings = AppSettings.model_validate(
|
||||||
|
{
|
||||||
|
"mongodb": {
|
||||||
|
"enabled": True,
|
||||||
|
"connection_uri": "mongodb://localhost:27017",
|
||||||
|
"database_name": "content_automation",
|
||||||
|
"collection_name": "published_content",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
store = build_published_content_store(settings)
|
||||||
|
|
||||||
|
assert isinstance(store, FakeMongoStore)
|
||||||
|
assert store.connection_uri == "mongodb://localhost:27017"
|
||||||
|
assert store.database_name == "content_automation"
|
||||||
|
assert store.collection_name == "published_content"
|
||||||
@@ -9,6 +9,15 @@ def test_settings_parse_nested_env(monkeypatch) -> None:
|
|||||||
monkeypatch.setenv("CONTENT_AUTOMATION_YOUTUBE__USE_RESUMABLE_UPLOAD", "true")
|
monkeypatch.setenv("CONTENT_AUTOMATION_YOUTUBE__USE_RESUMABLE_UPLOAD", "true")
|
||||||
monkeypatch.setenv("CONTENT_AUTOMATION_STORAGE__BACKEND", "s3")
|
monkeypatch.setenv("CONTENT_AUTOMATION_STORAGE__BACKEND", "s3")
|
||||||
monkeypatch.setenv("CONTENT_AUTOMATION_STORAGE__S3__BUCKET_NAME", "bucket-a")
|
monkeypatch.setenv("CONTENT_AUTOMATION_STORAGE__S3__BUCKET_NAME", "bucket-a")
|
||||||
|
monkeypatch.setenv("CONTENT_AUTOMATION_MONGODB__ENABLED", "true")
|
||||||
|
monkeypatch.setenv(
|
||||||
|
"CONTENT_AUTOMATION_MONGODB__CONNECTION_URI",
|
||||||
|
"mongodb://localhost:27017",
|
||||||
|
)
|
||||||
|
monkeypatch.setenv(
|
||||||
|
"CONTENT_AUTOMATION_MONGODB__DATABASE_NAME",
|
||||||
|
"content-automation",
|
||||||
|
)
|
||||||
|
|
||||||
settings = AppSettings()
|
settings = AppSettings()
|
||||||
|
|
||||||
@@ -17,3 +26,6 @@ def test_settings_parse_nested_env(monkeypatch) -> None:
|
|||||||
assert settings.youtube.use_resumable_upload is True
|
assert settings.youtube.use_resumable_upload is True
|
||||||
assert settings.storage.backend == "s3"
|
assert settings.storage.backend == "s3"
|
||||||
assert settings.storage.s3.bucket_name == "bucket-a"
|
assert settings.storage.s3.bucket_name == "bucket-a"
|
||||||
|
assert settings.mongodb.enabled is True
|
||||||
|
assert settings.mongodb.connection_uri == "mongodb://localhost:27017"
|
||||||
|
assert settings.mongodb.database_name == "content-automation"
|
||||||
|
|||||||
Reference in New Issue
Block a user