import pytest from tht.config import ( ConfigError, PgvectorDirectConfig, PostgresDwhConfig, ThothRestDwhConfig, ThothVectorHttpConfig, workspace_id_for_config, load_config, ) from tht.adapters.evidence import FilesystemEvidenceSource, HttpManifestEvidenceSource from tht.adapters.factory import build_evidence_sources def test_direct_vector_passwords_load_from_file_references(monkeypatch, tmp_path): reader = tmp_path / "reader" writer = tmp_path / "writer" reader.write_text("reader-secret") writer.write_text("writer-secret") monkeypatch.setenv("READER_FILE", str(reader)) monkeypatch.setenv("WRITER_FILE", str(writer)) workspace = tmp_path / "workspace.yaml" workspace.write_text(""" dwh: type: postgres_direct connection: {database: d, schema: public, user: u, password: p} vectors: type: pgvector_direct reader: {database: d, schema: vectors, user: r, password_file: '${READER_FILE}'} writer: {database: d, schema: vectors, user: w, password_file: '${WRITER_FILE}'} """) config = load_config(workspace) assert config.vectors.reader.password == "reader-secret" assert config.vectors.writer.password == "writer-secret" def test_direct_vector_secret_file_rejects_whitespace(tmp_path): secret = tmp_path / "reader" secret.write_text("bad secret") workspace = tmp_path / "workspace.yaml" workspace.write_text(f""" dwh: type: postgres_direct connection: {{database: d, schema: public, user: u, password: p}} vectors: type: pgvector_direct reader: {{database: d, schema: vectors, user: r, password_file: {secret}}} """) with pytest.raises(ConfigError, match="secret file"): load_config(workspace) def test_loads_discriminated_dwh_and_vector_resources(tmp_path): workspace = tmp_path / "workspace.yaml" workspace.write_text( """ dwh: type: thoth_rest database: database: analytics schema: mart endpoint: base_url: https://dwh.example.test/ api_key: dwh-reader vectors: type: thoth_vector_http reader: base_url: https://vectors.example.test/ api_key: vector-reader writer: base_url: https://vectors.example.test/ api_key: vector-writer roots: artifacts: build/artifacts indexes: build/indexes sessions: build/sessions """ ) cfg = load_config(workspace) assert isinstance(cfg.dwh, ThothRestDwhConfig) assert cfg.dwh.database.db_schema == "mart" assert isinstance(cfg.vectors, ThothVectorHttpConfig) assert cfg.vectors.writer.api_key == "vector-writer" assert cfg.roots.sessions.as_posix() == "build/sessions" def test_runtime_handoff_preserves_canonical_identity_and_durable_roots(monkeypatch, tmp_path): data_root = tmp_path / "data" runtime_root = data_root / "sessions" / "psd-clinical" workspace = tmp_path / "runtime-random-uuid.yaml" workspace.write_text(f""" runtime_identity: workspace_id: psd-clinical workspace_revision: {'a' * 40} source_identity: workspace://psd-clinical dwh: type: postgres_direct connection: {{database: analytics, schema: mart, user: reader, password: secret}} vectors: type: pgvector_direct connection: {{database: analytics, schema: vectors, user: vector, password: secret}} roots: sessions: {runtime_root / 'sessions'} artifacts: {runtime_root / 'artifacts'} indexes: {runtime_root / 'indexes'} embeddings: {{base_url: http://embedding.invalid, model: embed, dim: 768}} """) monkeypatch.setenv("THT_DATA_ROOT", str(data_root)) cfg = load_config(workspace) assert cfg._workspace_id == "psd-clinical" assert cfg._workspace_revision == "a" * 40 assert cfg._config_source == "workspace://psd-clinical" assert cfg.paths.sessions == runtime_root / "sessions" assert cfg.paths.artifacts == runtime_root / "artifacts" assert cfg.paths.indexes == runtime_root / "indexes" def test_runtime_identity_is_authoritative_over_runtime_filename(tmp_path): workspace = tmp_path / "runtime-random-uuid.yaml" workspace.write_text(f""" runtime_identity: workspace_id: psd-clinical workspace_revision: {'a' * 40} dwh: type: postgres_direct connection: {{database: analytics, schema: mart, user: reader, password: secret}} roots: {{sessions: sessions, artifacts: artifacts, indexes: indexes}} """) cfg = load_config(workspace) assert workspace_id_for_config(cfg, workspace) == "psd-clinical" def test_runtime_identity_rejects_a_source_for_another_workspace(tmp_path): workspace = tmp_path / "runtime-random-uuid.yaml" workspace.write_text(f""" runtime_identity: workspace_id: psd-clinical workspace_revision: {'a' * 40} source_identity: workspace://another-workspace dwh: type: postgres_direct connection: {{database: analytics, schema: mart, user: reader, password: secret}} roots: {{sessions: sessions, artifacts: artifacts, indexes: indexes}} """) with pytest.raises(ConfigError, match="source_identity"): load_config(workspace) def test_load_config_rejects_executable_yaml_tags_without_running_them(tmp_path): marker = tmp_path / "must-not-exist" workspace = tmp_path / "workspace.yaml" workspace.write_text(f"dwh: !command touch {marker}\n") with pytest.raises(ConfigError, match="YAML|configurazione"): load_config(workspace) assert not marker.exists() def test_loads_direct_discriminated_resources(tmp_path): workspace = tmp_path / "workspace.yaml" workspace.write_text( """ dwh: type: postgres_direct connection: &database host: db database: analytics schema: mart user: reader password: secret vectors: type: pgvector_direct connection: <<: *database schema: vectors """ ) cfg = load_config(workspace) assert isinstance(cfg.dwh, PostgresDwhConfig) assert cfg.database.transport == "direct" assert isinstance(cfg.vectors, PgvectorDirectConfig) assert cfg.vector_db.db_schema == "vectors" def test_loads_writer_only_http_vector_resource(tmp_path): workspace = tmp_path / "workspace.yaml" workspace.write_text( """ dwh: type: thoth_rest database: {database: analytics, schema: mart} endpoint: {base_url: https://dwh.test/, api_key: reader} vectors: type: thoth_vector_http writer: {base_url: https://vectors.test/, api_key: writer} embeddings: {base_url: http://ollama:11434, dim: 768} """ ) cfg = load_config(workspace) assert cfg.vectors.reader is None assert cfg.vectors.writer.api_key == "writer" def test_builds_typed_evidence_sources_and_keeps_legacy_compatible(tmp_path): common = """ dwh: type: postgres_direct connection: {database: d, schema: public, user: u, password: p} """ modern = tmp_path / "modern.yaml" modern.write_text(common + f""" evidence: sources: - type: filesystem root: {tmp_path} max_bytes: 123 - type: http urls: ['https://example.test/doc.md?token=transport-only'] """) cfg = load_config(modern) assert "transport-only" not in repr(cfg.evidence) assert "transport-only" not in cfg.evidence.model_dump_json() assert cfg.evidence.sources[1].allow_private_hosts is False sources = build_evidence_sources(cfg) assert isinstance(sources[0], FilesystemEvidenceSource) assert isinstance(sources[1], HttpManifestEvidenceSource) assert "transport-only" not in repr(sources[1]) legacy = tmp_path / "legacy.yaml" (tmp_path / "curated").mkdir() legacy.write_text(common + f""" evidence: source_root: {tmp_path} evidence_dir: curated """) legacy_source = build_evidence_sources(load_config(legacy))[0] assert isinstance(legacy_source, FilesystemEvidenceSource) assert legacy_source.root == (tmp_path / "curated").resolve()