Task 003: add postgres schema and seed data
This commit is contained in:
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import unittest
|
||||
from importlib.util import find_spec
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[4]
|
||||
BACKEND_ROOT = REPO_ROOT / "apps" / "backend"
|
||||
sys.path.insert(0, str(BACKEND_ROOT))
|
||||
|
||||
|
||||
class PostgresAlembicMigrationIntegrationTest(unittest.TestCase):
|
||||
def test_alembic_upgrade_head_creates_schema_on_configured_postgres(self) -> None:
|
||||
dsn = os.environ.get("PIPELINE_TEST_DATABASE_DSN")
|
||||
if not dsn:
|
||||
self.skipTest("PIPELINE_TEST_DATABASE_DSN is not set")
|
||||
if not dsn.startswith(("postgresql://", "postgres://")):
|
||||
self.skipTest("Postgres DSN is required for Alembic migration test")
|
||||
if find_spec("alembic") is None:
|
||||
self.skipTest("alembic is not installed")
|
||||
|
||||
env = os.environ.copy()
|
||||
env["DATABASE_URL"] = dsn
|
||||
subprocess.run(
|
||||
[sys.executable, "-m", "alembic", "upgrade", "head"],
|
||||
cwd=REPO_ROOT,
|
||||
env=env,
|
||||
check=True,
|
||||
)
|
||||
|
||||
from src.domain.schema import CORE_TABLES
|
||||
from src.infrastructure.repositories import open_backend_repository
|
||||
|
||||
repository = open_backend_repository(dsn)
|
||||
self.assertTrue(set(CORE_TABLES).issubset(repository.schema.list_tables()))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,136 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from collections.abc import Iterator
|
||||
from contextlib import contextmanager
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
BACKEND_ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(BACKEND_ROOT))
|
||||
|
||||
|
||||
@contextmanager
|
||||
def repository_dsn() -> Iterator[str]:
|
||||
configured_dsn = os.environ.get("PIPELINE_TEST_DATABASE_DSN")
|
||||
if configured_dsn:
|
||||
yield configured_dsn
|
||||
return
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
yield f"sqlite:///{Path(tmp_dir) / 'pipeline-schema.db'}"
|
||||
|
||||
|
||||
class SchemaStorageContractsIntegrationTest(unittest.TestCase):
|
||||
def test_schema_contains_task_tables_and_operational_indexes(self) -> None:
|
||||
from src.domain.schema import CORE_TABLES
|
||||
from src.infrastructure.repositories import open_backend_repository
|
||||
|
||||
with repository_dsn() as dsn:
|
||||
repository = open_backend_repository(dsn)
|
||||
repository.setup()
|
||||
|
||||
self.assertTrue(set(CORE_TABLES).issubset(repository.schema.list_tables()))
|
||||
self.assertIn(
|
||||
"idx_target_sites_slug",
|
||||
repository.schema.list_indexes("target_sites"),
|
||||
)
|
||||
self.assertIn(
|
||||
"idx_articles_dashboard",
|
||||
repository.schema.list_indexes("articles"),
|
||||
)
|
||||
self.assertIn(
|
||||
"idx_agent_jobs_queue",
|
||||
repository.schema.list_indexes("agent_jobs"),
|
||||
)
|
||||
self.assertIn(
|
||||
"idx_workflow_events_timeline",
|
||||
repository.schema.list_indexes("workflow_events"),
|
||||
)
|
||||
|
||||
def test_required_domain_storage_fields_exist(self) -> None:
|
||||
from src.infrastructure.repositories import open_backend_repository
|
||||
|
||||
with repository_dsn() as dsn:
|
||||
repository = open_backend_repository(dsn)
|
||||
repository.setup()
|
||||
|
||||
self.assertTrue(
|
||||
{"status", "publishing_status"}.issubset(
|
||||
repository.schema.list_columns("articles")
|
||||
)
|
||||
)
|
||||
self.assertIn("status", repository.schema.list_columns("article_drafts"))
|
||||
self.assertTrue(
|
||||
{
|
||||
"created_by",
|
||||
"created_at",
|
||||
"diff",
|
||||
"rollback_target_version_id",
|
||||
"activated_at",
|
||||
"publishing_yaml",
|
||||
"transform_script",
|
||||
}.issubset(repository.schema.list_columns("script_config_versions"))
|
||||
)
|
||||
self.assertTrue(
|
||||
{
|
||||
"s3_prefix",
|
||||
"source_urls",
|
||||
"object_keys",
|
||||
"content_hashes",
|
||||
"artifact_types",
|
||||
"metadata_references",
|
||||
"artifacts",
|
||||
}.issubset(repository.schema.list_columns("research_run_manifests"))
|
||||
)
|
||||
self.assertTrue(
|
||||
{
|
||||
"repository_url",
|
||||
"branch",
|
||||
"commit_sha",
|
||||
"content_bundle_manifest",
|
||||
"status",
|
||||
}.issubset(repository.schema.list_columns("publish_commits"))
|
||||
)
|
||||
|
||||
def test_seed_data_is_idempotent_for_users_site_and_script_version(self) -> None:
|
||||
from src.application.seed_data import seed_reference_data
|
||||
from src.domain.schema import (
|
||||
SEEDED_ADMIN_EMAIL,
|
||||
SEEDED_EDITOR_EMAIL,
|
||||
SEEDED_TARGET_SITE_SLUG,
|
||||
)
|
||||
from src.infrastructure.repositories import open_backend_repository
|
||||
|
||||
with repository_dsn() as dsn:
|
||||
repository = open_backend_repository(dsn)
|
||||
repository.setup()
|
||||
|
||||
seed_reference_data(repository)
|
||||
seed_reference_data(repository)
|
||||
|
||||
site = repository.target_sites.get_by_slug(SEEDED_TARGET_SITE_SLUG)
|
||||
seeded_sites = [
|
||||
candidate
|
||||
for candidate in repository.target_sites.list()
|
||||
if candidate.slug == SEEDED_TARGET_SITE_SLUG
|
||||
]
|
||||
script_versions = repository.script_config_versions.list_for_site(site.id)
|
||||
|
||||
self.assertEqual(1, repository.users.count_by_email(SEEDED_ADMIN_EMAIL))
|
||||
self.assertEqual(1, repository.users.count_by_email(SEEDED_EDITOR_EMAIL))
|
||||
self.assertEqual([site.id], [candidate.id for candidate in seeded_sites])
|
||||
self.assertEqual(1, len(script_versions))
|
||||
self.assertEqual("ACTIVE", script_versions[0]["status"])
|
||||
self.assertEqual(
|
||||
str(site.active_script_config_version_id), str(script_versions[0]["id"])
|
||||
)
|
||||
self.assertIn("repository:", script_versions[0]["publishing_yaml"])
|
||||
self.assertIn("transformArticle", script_versions[0]["transform_script"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,74 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from collections.abc import Iterator
|
||||
from contextlib import contextmanager
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
BACKEND_ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(BACKEND_ROOT))
|
||||
|
||||
|
||||
SEEDED_SITE_SLUG = "b2b_saas_blog"
|
||||
|
||||
|
||||
@contextmanager
|
||||
def repository_dsn() -> Iterator[str]:
|
||||
configured_dsn = os.environ.get("PIPELINE_TEST_DATABASE_DSN")
|
||||
if configured_dsn:
|
||||
yield configured_dsn
|
||||
return
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
yield f"sqlite:///{Path(tmp_dir) / 'pipeline-seed-red.db'}"
|
||||
|
||||
|
||||
class SeededTargetSiteLookupIntegrationTest(unittest.TestCase):
|
||||
def test_seeded_git_next_target_site_lookup_by_slug_is_idempotent(self) -> None:
|
||||
from src.application.seed_data import seed_reference_data
|
||||
from src.infrastructure.repositories import open_backend_repository
|
||||
|
||||
with repository_dsn() as dsn:
|
||||
repository = open_backend_repository(dsn)
|
||||
repository.setup()
|
||||
|
||||
seed_reference_data(repository)
|
||||
seed_reference_data(repository)
|
||||
|
||||
site = repository.target_sites.get_by_slug(SEEDED_SITE_SLUG)
|
||||
seeded_sites = [
|
||||
candidate
|
||||
for candidate in repository.target_sites.list()
|
||||
if candidate.slug == SEEDED_SITE_SLUG
|
||||
]
|
||||
|
||||
from src.domain.contracts import TargetSiteConfig
|
||||
|
||||
self.assertIsInstance(site, TargetSiteConfig)
|
||||
self.assertEqual([site.id], [candidate.id for candidate in seeded_sites])
|
||||
self.assertEqual("B2B SaaS Blog", site.name)
|
||||
self.assertEqual("git_next", site.publishing_type)
|
||||
self.assertEqual("en", site.default_language)
|
||||
self.assertEqual(
|
||||
"git@github.com:example/site.git",
|
||||
site.publishing_rules.repository_url,
|
||||
)
|
||||
self.assertEqual("main", site.publishing_rules.production_branch)
|
||||
self.assertEqual("mdx", site.publishing_rules.content_format)
|
||||
self.assertEqual(
|
||||
"content/articles/{slug}.mdx",
|
||||
site.publishing_rules.content_path_template,
|
||||
)
|
||||
self.assertEqual(
|
||||
"public/articles/{slug}/{filename}",
|
||||
site.publishing_rules.asset_path_template,
|
||||
)
|
||||
self.assertIsNotNone(site.active_script_config_version_id)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user