| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117 |
- from __future__ import annotations
- from pathlib import Path
- ROOT = Path(__file__).resolve().parents[2]
- def valid_graph():
- return {
- "classes": [{"uid": "customer", "name": "Customer"}],
- "properties": [
- {"uid": "customer-id", "name": "customerId", "class_uid": "customer", "required": True, "cardinality": "1", "data_element_uid": "element-1"}
- ],
- "relations": [],
- "constraints": [],
- "domain_links": [
- {"domain_uid": "sales", "role": "owner"},
- {"domain_uid": "service", "role": "contributor"},
- ],
- "element_mappings": [{"property_uid": "customer-id", "data_element_uid": "element-1"}],
- }
- def test_v60_v62_acceptance_multi_source_policy_evidence_and_redaction():
- from app.core.data_research.artifacts import redact_excerpt
- from app.core.data_research.extractors.registry import default_registry
- from app.core.data_research.file_policy import FilePolicy
- registry = default_registry()
- assert registry.resolve("text/sql", "schema.sql").parser_version.startswith("sql")
- assert registry.resolve("text/csv", "elements.csv").parser_version.startswith("csv")
- assert registry.resolve("application/pdf", "standard.pdf").parser_version.startswith("pdf")
- assert registry.resolve(
- "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "standard.docx"
- ).parser_version.startswith("docx")
- assert set(FilePolicy.MEDIA_TYPES) == {".sql", ".csv", ".xls", ".xlsx", ".docx", ".pdf", ".png", ".jpg", ".jpeg"}
- redacted = redact_excerpt("phone=13800138000 token=top-secret")
- assert "138****8000" in redacted and "top-secret" not in redacted
- def test_v61_v64_acceptance_element_ontology_dynamic_publish_rollback_and_knowledge():
- from app.core.data_research.data_elements import DataElementService
- from app.core.data_research.ontology.change_sets import ChangeSetService
- from app.core.data_research.ontology.knowledge_sync import OntologyKnowledgeSync
- from app.core.data_research.ontology.publication import OntologyPublicationService
- from app.core.data_research.ontology.repository import MemoryOntologyRepository
- from app.core.data_research.ontology.suggestions import OntologySuggestionEngine
- class Elements:
- def __init__(self): self.records = {}
- def get(self, uid): return self.records.get(uid)
- def get_by_code(self, code): return next((x for x in self.records.values() if x.code == code), None)
- def save(self, record): self.records[record.uid] = record; return record
- element_events = []
- elements = DataElementService(Elements(), uid_factory=lambda: "element-1", outbox_enqueue=lambda **event: element_events.append(event))
- element = elements.create_draft(
- {"code": "CUSTOMER_ID", "name_zh": "客户编号", "name_en": "customer_id", "definition": "客户唯一编号", "logical_type": "string", "evidence_uids": ["evidence-1"], "business_domain_uids": ["sales", "service"]},
- "editor-1",
- )
- elements.transition(element.uid, "in_review", expected_version=1, actor_uid="editor-1")
- published_element = elements.transition(element.uid, "published", expected_version=2, actor_uid="admin-1")
- assert published_element.current_version == 3 and element_events[0]["event_type"] == "data_element.version_published"
- ids = iter(("ontology-1", "version-1", "version-2"))
- repository = MemoryOntologyRepository(uid_factory=ids.__next__)
- ontology = repository.create(code="CUSTOMER", name="客户本体", owner_uid="admin-1", domain_links=valid_graph()["domain_links"])
- assert {link.domain_uid for link in ontology.domain_links} == {"sales", "service"}
- version = repository.save_draft(ontology.uid, valid_graph(), expected_revision=0, actor_uid="editor-1")
- ontology_events = []
- publication = OntologyPublicationService(repository, outbox_enqueue=lambda **event: ontology_events.append(event))
- published = publication.publish(version.uid, idempotency_key="publish-1", actor_uid="admin-1")
- rollback = publication.create_rollback(ontology.uid, target_version_uid=published.uid, expected_revision=1, actor_uid="admin-1")
- assert published.status == "published" and rollback.version == 2
- suggestions = OntologySuggestionEngine().generate(
- domains=[{"uid": "sales", "name": "销售"}],
- data_elements=[{"uid": "element-1", "name": "客户编号", "status": "published"}],
- foreign_keys=[],
- )
- changes = ChangeSetService(uid_factory=lambda: "change-1")
- change_set = changes.create(ontology.uid, suggestions, actor_uid="editor-1")
- reviewed = changes.decide(change_set, [{"suggestion_uid": suggestions[0].uid, "decision": "accept", "reason": "已核验证据"}], actor_uid="reviewer-1")
- applied = changes.apply(valid_graph(), reviewed)
- assert len(applied["classes"]) == 2
- class KnowledgeRepo:
- def __init__(self): self.docs = {}; self.chunks = {}
- def upsert_document(self, doc):
- key = (doc["object_uid"], doc["object_version"]); changed = key not in self.docs; self.docs[key] = doc; return changed
- def replace_chunks(self, key, chunks): self.chunks[key] = chunks
- synced = OntologyKnowledgeSync(KnowledgeRepo()).sync({"uid": ontology.uid, "name": ontology.name, "version": published.version, "status": "published", "graph_document": valid_graph()})
- assert synced["document"]["object_version"] == 1
- def test_v65_acceptance_rbac_reconciliation_and_release_parity():
- from app.core.system.permissions import permission_for_request
- assert permission_for_request("/api/development/v1/ontologies/o/publish", "POST") == ("ontologies:publish",)
- assert permission_for_request("/api/development/v1/sources/files", "POST") == ("ingestion:run",)
- assert permission_for_request("/api/development/v1/evidence/e", "GET") == ("governance:read",)
- for relative in (
- "core/data_research/ontology/publication.py",
- "core/data_research/operations.py",
- "api/data_development/routes.py",
- ):
- assert (ROOT / "deployment/app" / relative).read_bytes() == (ROOT / "app" / relative).read_bytes()
- overview = (ROOT / "docs/architecture/ARCHITECTURE_OVERVIEW.md").read_text(encoding="utf-8")
- data_model = (ROOT / "docs/architecture/DATA_MODEL.md").read_text(encoding="utf-8")
- roadmap = (ROOT / "docs/architecture/NEXT_ITERATION_ROADMAP.md").read_text(encoding="utf-8")
- assert "V60-V65 数据研发" in overview
- assert "ontology_versions" in data_model
- assert "V65" in roadmap and "已完成" in roadmap
|