test_ingestion_models.py 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. from __future__ import annotations
  2. from sqlalchemy import CheckConstraint, UniqueConstraint
  3. def test_ingestion_models_expose_stable_control_plane_tables():
  4. from app.models.data_research import (
  5. EvidenceFragment,
  6. ExtractionCandidate,
  7. IngestionJob,
  8. IngestionSource,
  9. SourceArtifact,
  10. )
  11. assert IngestionSource.__tablename__ == "ingestion_sources"
  12. assert IngestionJob.__tablename__ == "ingestion_jobs"
  13. assert SourceArtifact.__tablename__ == "source_artifacts"
  14. assert EvidenceFragment.__tablename__ == "evidence_fragments"
  15. assert ExtractionCandidate.__tablename__ == "extraction_candidates"
  16. assert IngestionJob.__table__.c.source_uid.foreign_keys
  17. assert IngestionJob.__table__.c.artifact_uid.foreign_keys
  18. assert EvidenceFragment.__table__.c.job_uid.foreign_keys
  19. assert ExtractionCandidate.__table__.c.job_uid.foreign_keys
  20. def test_ingestion_job_has_idempotency_and_status_constraints():
  21. from app.models.data_research import IngestionJob
  22. constraints = list(IngestionJob.__table__.constraints)
  23. unique_columns = {
  24. tuple(column.name for column in constraint.columns)
  25. for constraint in constraints
  26. if isinstance(constraint, UniqueConstraint)
  27. }
  28. check_sql = " ".join(
  29. str(constraint.sqltext)
  30. for constraint in constraints
  31. if isinstance(constraint, CheckConstraint)
  32. )
  33. assert ("idempotency_key",) in unique_columns
  34. for status in (
  35. "created",
  36. "queued",
  37. "extracting",
  38. "normalizing",
  39. "matching",
  40. "awaiting_review",
  41. "published",
  42. "partial",
  43. "failed",
  44. "cancelled",
  45. ):
  46. assert status in check_sql
  47. def test_model_serialization_redacts_control_plane_secrets():
  48. from app.models.data_research import IngestionSource, SourceArtifact
  49. source = IngestionSource(
  50. uid="00000000-0000-0000-0000-000000000001",
  51. source_type="database",
  52. name="orders",
  53. config={"secret_ref": "vault://orders", "schema": "public"},
  54. permission_scope={"roles": ["editor"]},
  55. status="active",
  56. )
  57. artifact = SourceArtifact(
  58. uid="00000000-0000-0000-0000-000000000002",
  59. source_uid=source.uid,
  60. filename="orders.csv",
  61. media_type="text/csv",
  62. size_bytes=12,
  63. content_hash="a" * 64,
  64. storage_ref="minio://private/orders.csv?token=secret",
  65. parser_version="csv-v1",
  66. )
  67. assert "config" not in source.to_dict()
  68. assert "vault://orders" not in str(source.to_dict())
  69. assert "storage_ref" not in artifact.to_dict()
  70. assert "token=secret" not in str(artifact.to_dict())
  71. assert artifact.to_dict()["content_hash"] == "a" * 64