Skip to content

Comments

BLDX-432: YAML-driven datasource fixtures for E2E testing#1040

Open
fyzanshaik-atlan wants to merge 9 commits intomainfrom
feat/datasource-fixtures
Open

BLDX-432: YAML-driven datasource fixtures for E2E testing#1040
fyzanshaik-atlan wants to merge 9 commits intomainfrom
feat/datasource-fixtures

Conversation

@fyzanshaik-atlan
Copy link
Contributor

@fyzanshaik-atlan fyzanshaik-atlan commented Feb 10, 2026

Summary

Declarative YAML-driven datasource fixture system for E2E tests. App developers declare a datasource.yaml with image, port, env, volumes, and credentials — SDK handles container provisioning, readiness checks, env var injection, and teardown.

Design

  • Generic credentials: ConnectionInfo holds host, port (from Docker), and a free-form credentials dict (from YAML). No hardcoded database fields — works for any connector type (databases, dashboards, pipelines).
  • Dynamic env vars: All credential keys are UPPERCASED and prefixed automatically. credentials: {api_token: x} with env_prefix: E2E_SUPERE2E_SUPER_API_TOKEN=x.
  • Progressive disclosure: YAML-only (80% of cases) → subclass ContainerizedFixture for custom readiness → full custom DataSourceFixture as escape hatch.
  • TCP readiness with recv(): Distinguishes real service readiness from Docker proxy false positives.

Module layout

application_sdk/test_utils/e2e/fixtures/
    base.py              # DataSourceFixture ABC + ConnectionInfo(host, port, credentials)
    schema.py            # Pydantic: ContainerizedDatasourceConfig, ReadinessConfig, VolumeConfig
    readiness.py         # check_tcp() with recv() verification
    containerized.py     # ContainerizedFixture — Docker lifecycle via testcontainers
    loader.py            # load_fixture_from_yaml() factory
    plugin.py            # pytest plugin (pytest11 entry point)
    __init__.py          # Public re-exports

Usage

# datasource.yaml
image: postgres:15.12
port: 5432
env:
  POSTGRES_USER: postgres
  POSTGRES_PASSWORD: postgres
  POSTGRES_DB: testdb
credentials:
  username: postgres
  password: postgres
  database: testdb
env_prefix: E2E_POSTGRES
readiness:
  strategy: tcp
  timeout: 120
  interval: 2
# conftest.py
def pytest_configure(config):
    config._datasource_yaml = "tests/e2e/datasource.yaml"

Test plan

  • 44 unit tests covering all modules
  • Pre-commit clean
  • Integration test with real Postgres container (atlan-postgres-app)

Add a fixtures package under test_utils/e2e/ that provides a reusable
abstraction for managing data source lifecycle in E2E tests. This moves
container orchestration (testcontainers), readiness checks, and env var
injection out of individual apps and into the SDK.

- DataSourceFixture ABC with setup/teardown/is_ready/get_env_vars contract
- ContainerizedDataSourceFixture for Docker-backed sources (postgres, mysql, etc.)
- HostedDataSourceFixture for non-containerizable sources (Snowflake, Redshift)
- Pytest plugin (pytest11 entry point) for session-scoped lifecycle management
- Optional testcontainers extra in pyproject.toml
- Unit tests for all fixture classes and the plugin
@github-actions
Copy link

github-actions bot commented Feb 10, 2026

📜 Docstring Coverage Report

RESULT: PASSED (minimum: 30.0%, actual: 77.9%)

Detailed Coverage Report
======= Coverage for /home/runner/work/application-sdk/application-sdk/ ========
----------------------------------- Summary ------------------------------------
| Name                                                                              | Total | Miss | Cover | Cover% |
|-----------------------------------------------------------------------------------|-------|------|-------|--------|
| application_sdk/__init__.py                                                       |     1 |    0 |     1 |   100% |
| application_sdk/constants.py                                                      |     2 |    0 |     2 |   100% |
| application_sdk/version.py                                                        |     1 |    0 |     1 |   100% |
| application_sdk/worker.py                                                         |     7 |    0 |     7 |   100% |
| application_sdk/activities/__init__.py                                            |    10 |    0 |    10 |   100% |
| application_sdk/activities/lock_management.py                                     |     3 |    0 |     3 |   100% |
| application_sdk/activities/common/__init__.py                                     |     1 |    1 |     0 |     0% |
| application_sdk/activities/common/models.py                                       |     3 |    1 |     2 |    67% |
| application_sdk/activities/common/sql_utils.py                                    |     6 |    1 |     5 |    83% |
| application_sdk/activities/common/utils.py                                        |     8 |    1 |     7 |    88% |
| application_sdk/activities/metadata_extraction/__init__.py                        |     1 |    1 |     0 |     0% |
| application_sdk/activities/metadata_extraction/base.py                            |     5 |    1 |     4 |    80% |
| application_sdk/activities/metadata_extraction/rest.py                            |     1 |    1 |     0 |     0% |
| application_sdk/activities/metadata_extraction/sql.py                             |    20 |    3 |    17 |    85% |
| application_sdk/activities/query_extraction/__init__.py                           |     1 |    1 |     0 |     0% |
| application_sdk/activities/query_extraction/sql.py                                |    13 |    1 |    12 |    92% |
| application_sdk/application/__init__.py                                           |    14 |    6 |     8 |    57% |
| application_sdk/application/metadata_extraction/sql.py                            |    10 |    3 |     7 |    70% |
| application_sdk/clients/__init__.py                                               |     4 |    0 |     4 |   100% |
| application_sdk/clients/atlan.py                                                  |     5 |    3 |     2 |    40% |
| application_sdk/clients/atlan_auth.py                                             |    10 |    0 |    10 |   100% |
| application_sdk/clients/base.py                                                   |     6 |    1 |     5 |    83% |
| application_sdk/clients/models.py                                                 |     3 |    0 |     3 |   100% |
| application_sdk/clients/redis.py                                                  |    27 |    0 |    27 |   100% |
| application_sdk/clients/sql.py                                                    |    23 |    0 |    23 |   100% |
| application_sdk/clients/temporal.py                                               |    14 |    1 |    13 |    93% |
| application_sdk/clients/utils.py                                                  |     2 |    1 |     1 |    50% |
| application_sdk/clients/workflow.py                                               |     9 |    2 |     7 |    78% |
| application_sdk/clients/azure/__init__.py                                         |     1 |    0 |     1 |   100% |
| application_sdk/clients/azure/auth.py                                             |     7 |    0 |     7 |   100% |
| application_sdk/clients/azure/client.py                                           |    13 |    0 |    13 |   100% |
| application_sdk/common/__init__.py                                                |     1 |    1 |     0 |     0% |
| application_sdk/common/aws_utils.py                                               |    10 |    1 |     9 |    90% |
| application_sdk/common/error_codes.py                                             |    14 |    2 |    12 |    86% |
| application_sdk/common/file_converter.py                                          |     9 |    5 |     4 |    44% |
| application_sdk/common/file_ops.py                                                |    16 |    1 |    15 |    94% |
| application_sdk/common/path.py                                                    |     2 |    1 |     1 |    50% |
| application_sdk/common/types.py                                                   |     2 |    1 |     1 |    50% |
| application_sdk/common/utils.py                                                   |    16 |    2 |    14 |    88% |
| application_sdk/decorators/__init__.py                                            |     1 |    1 |     0 |     0% |
| application_sdk/decorators/locks.py                                               |     3 |    2 |     1 |    33% |
| application_sdk/decorators/mcp_tool.py                                            |     3 |    1 |     2 |    67% |
| application_sdk/docgen/__init__.py                                                |     5 |    2 |     3 |    60% |
| application_sdk/docgen/exporters/__init__.py                                      |     1 |    1 |     0 |     0% |
| application_sdk/docgen/exporters/mkdocs.py                                        |     7 |    3 |     4 |    57% |
| application_sdk/docgen/models/__init__.py                                         |     1 |    1 |     0 |     0% |
| application_sdk/docgen/models/export/__init__.py                                  |     1 |    1 |     0 |     0% |
| application_sdk/docgen/models/export/page.py                                      |     2 |    1 |     1 |    50% |
| application_sdk/docgen/models/manifest/__init__.py                                |     2 |    1 |     1 |    50% |
| application_sdk/docgen/models/manifest/customer.py                                |     3 |    1 |     2 |    67% |
| application_sdk/docgen/models/manifest/internal.py                                |     2 |    1 |     1 |    50% |
| application_sdk/docgen/models/manifest/metadata.py                                |     2 |    1 |     1 |    50% |
| application_sdk/docgen/models/manifest/page.py                                    |     2 |    1 |     1 |    50% |
| application_sdk/docgen/models/manifest/section.py                                 |     2 |    1 |     1 |    50% |
| application_sdk/docgen/parsers/__init__.py                                        |     1 |    1 |     0 |     0% |
| application_sdk/docgen/parsers/directory.py                                       |    13 |    2 |    11 |    85% |
| application_sdk/docgen/parsers/manifest.py                                        |     6 |    1 |     5 |    83% |
| application_sdk/handlers/__init__.py                                              |     7 |    1 |     6 |    86% |
| application_sdk/handlers/base.py                                                  |     7 |    1 |     6 |    86% |
| application_sdk/handlers/sql.py                                                   |    19 |    6 |    13 |    68% |
| application_sdk/interceptors/__init__.py                                          |     1 |    1 |     0 |     0% |
| application_sdk/interceptors/cleanup.py                                           |     7 |    1 |     6 |    86% |
| application_sdk/interceptors/correlation_context.py                               |    13 |    0 |    13 |   100% |
| application_sdk/interceptors/events.py                                            |     9 |    1 |     8 |    89% |
| application_sdk/interceptors/lock.py                                              |    10 |    2 |     8 |    80% |
| application_sdk/interceptors/models.py                                            |    13 |    1 |    12 |    92% |
| application_sdk/io/__init__.py                                                    |    25 |    0 |    25 |   100% |
| application_sdk/io/json.py                                                        |    15 |    1 |    14 |    93% |
| application_sdk/io/parquet.py                                                     |    22 |    1 |    21 |    95% |
| application_sdk/io/utils.py                                                       |     8 |    1 |     7 |    88% |
| application_sdk/observability/__init__.py                                         |     1 |    1 |     0 |     0% |
| application_sdk/observability/context.py                                          |     1 |    0 |     1 |   100% |
| application_sdk/observability/logger_adaptor.py                                   |    30 |    2 |    28 |    93% |
| application_sdk/observability/metrics_adaptor.py                                  |    12 |    1 |    11 |    92% |
| application_sdk/observability/models.py                                           |     5 |    1 |     4 |    80% |
| application_sdk/observability/observability.py                                    |    24 |    1 |    23 |    96% |
| application_sdk/observability/segment_client.py                                   |    14 |    2 |    12 |    86% |
| application_sdk/observability/traces_adaptor.py                                   |    16 |    1 |    15 |    94% |
| application_sdk/observability/utils.py                                            |     4 |    1 |     3 |    75% |
| application_sdk/observability/decorators/observability_decorator.py               |     7 |    4 |     3 |    43% |
| application_sdk/server/__init__.py                                                |     4 |    0 |     4 |   100% |
| application_sdk/server/fastapi/__init__.py                                        |    24 |    5 |    19 |    79% |
| application_sdk/server/fastapi/models.py                                          |    30 |   27 |     3 |    10% |
| application_sdk/server/fastapi/utils.py                                           |     2 |    0 |     2 |   100% |
| application_sdk/server/fastapi/middleware/logmiddleware.py                        |     4 |    4 |     0 |     0% |
| application_sdk/server/fastapi/middleware/metrics.py                              |     3 |    3 |     0 |     0% |
| application_sdk/server/fastapi/routers/__init__.py                                |     1 |    1 |     0 |     0% |
| application_sdk/server/fastapi/routers/server.py                                  |     8 |    2 |     6 |    75% |
| application_sdk/server/mcp/__init__.py                                            |     1 |    1 |     0 |     0% |
| application_sdk/server/mcp/models.py                                              |     2 |    2 |     0 |     0% |
| application_sdk/server/mcp/server.py                                              |     5 |    0 |     5 |   100% |
| application_sdk/services/__init__.py                                              |     1 |    0 |     1 |   100% |
| application_sdk/services/_utils.py                                                |     2 |    1 |     1 |    50% |
| application_sdk/services/atlan_storage.py                                         |     5 |    0 |     5 |   100% |
| application_sdk/services/eventstore.py                                            |     5 |    0 |     5 |   100% |
| application_sdk/services/objectstore.py                                           |    16 |    0 |    16 |   100% |
| application_sdk/services/secretstore.py                                           |    14 |    0 |    14 |   100% |
| application_sdk/services/statestore.py                                            |     9 |    1 |     8 |    89% |
| application_sdk/test_utils/__init__.py                                            |     1 |    1 |     0 |     0% |
| application_sdk/test_utils/workflow_monitoring.py                                 |     3 |    0 |     3 |   100% |
| application_sdk/test_utils/e2e/__init__.py                                        |    14 |    2 |    12 |    86% |
| application_sdk/test_utils/e2e/base.py                                            |    16 |    2 |    14 |    88% |
| application_sdk/test_utils/e2e/client.py                                          |    10 |    2 |     8 |    80% |
| application_sdk/test_utils/e2e/conftest.py                                        |     1 |    1 |     0 |     0% |
| application_sdk/test_utils/e2e/utils.py                                           |     3 |    1 |     2 |    67% |
| application_sdk/test_utils/e2e/fixtures/__init__.py                               |     1 |    1 |     0 |     0% |
| application_sdk/test_utils/e2e/fixtures/base.py                                   |     8 |    1 |     7 |    88% |
| application_sdk/test_utils/e2e/fixtures/containerized.py                          |     8 |    1 |     7 |    88% |
| application_sdk/test_utils/e2e/fixtures/loader.py                                 |     2 |    0 |     2 |   100% |
| application_sdk/test_utils/e2e/fixtures/plugin.py                                 |     3 |    0 |     3 |   100% |
| application_sdk/test_utils/e2e/fixtures/readiness.py                              |     2 |    0 |     2 |   100% |
| application_sdk/test_utils/e2e/fixtures/schema.py                                 |     4 |    0 |     4 |   100% |
| application_sdk/test_utils/hypothesis/__init__.py                                 |     1 |    1 |     0 |     0% |
| application_sdk/test_utils/hypothesis/strategies/__init__.py                      |     1 |    1 |     0 |     0% |
| application_sdk/test_utils/hypothesis/strategies/sql_client.py                    |     1 |    1 |     0 |     0% |
| application_sdk/test_utils/hypothesis/strategies/temporal.py                      |     6 |    1 |     5 |    83% |
| application_sdk/test_utils/hypothesis/strategies/clients/__init__.py              |     1 |    1 |     0 |     0% |
| application_sdk/test_utils/hypothesis/strategies/clients/sql.py                   |     1 |    1 |     0 |     0% |
| application_sdk/test_utils/hypothesis/strategies/common/__init__.py               |     1 |    1 |     0 |     0% |
| application_sdk/test_utils/hypothesis/strategies/common/logger.py                 |     3 |    0 |     3 |   100% |
| application_sdk/test_utils/hypothesis/strategies/handlers/__init__.py             |     1 |    1 |     0 |     0% |
| application_sdk/test_utils/hypothesis/strategies/handlers/sql/__init__.py         |     1 |    1 |     0 |     0% |
| application_sdk/test_utils/hypothesis/strategies/handlers/sql/sql_metadata.py     |     1 |    1 |     0 |     0% |
| application_sdk/test_utils/hypothesis/strategies/handlers/sql/sql_preflight.py    |     1 |    1 |     0 |     0% |
| application_sdk/test_utils/hypothesis/strategies/inputs/__init__.py               |     1 |    1 |     0 |     0% |
| application_sdk/test_utils/hypothesis/strategies/inputs/json_input.py             |     1 |    1 |     0 |     0% |
| application_sdk/test_utils/hypothesis/strategies/inputs/parquet_input.py          |     1 |    1 |     0 |     0% |
| application_sdk/test_utils/hypothesis/strategies/outputs/__init__.py              |     1 |    1 |     0 |     0% |
| application_sdk/test_utils/hypothesis/strategies/outputs/json_output.py           |     2 |    1 |     1 |    50% |
| application_sdk/test_utils/hypothesis/strategies/outputs/statestore.py            |     3 |    1 |     2 |    67% |
| application_sdk/test_utils/hypothesis/strategies/server/__init__.py               |     1 |    1 |     0 |     0% |
| application_sdk/test_utils/hypothesis/strategies/server/fastapi/__init__.py       |     1 |    1 |     0 |     0% |
| application_sdk/test_utils/scale_data_generator/__init__.py                       |     1 |    1 |     0 |     0% |
| application_sdk/test_utils/scale_data_generator/config_loader.py                  |    10 |    4 |     6 |    60% |
| application_sdk/test_utils/scale_data_generator/data_generator.py                 |    10 |    3 |     7 |    70% |
| application_sdk/test_utils/scale_data_generator/driver.py                         |     3 |    3 |     0 |     0% |
| application_sdk/test_utils/scale_data_generator/output_handler/__init__.py        |     1 |    1 |     0 |     0% |
| application_sdk/test_utils/scale_data_generator/output_handler/base.py            |     7 |    3 |     4 |    57% |
| application_sdk/test_utils/scale_data_generator/output_handler/csv_handler.py     |     5 |    5 |     0 |     0% |
| application_sdk/test_utils/scale_data_generator/output_handler/json_handler.py    |     5 |    5 |     0 |     0% |
| application_sdk/test_utils/scale_data_generator/output_handler/parquet_handler.py |     6 |    6 |     0 |     0% |
| application_sdk/transformers/__init__.py                                          |     3 |    1 |     2 |    67% |
| application_sdk/transformers/atlas/__init__.py                                    |     6 |    1 |     5 |    83% |
| application_sdk/transformers/atlas/sql.py                                         |    25 |    4 |    21 |    84% |
| application_sdk/transformers/common/__init__.py                                   |     1 |    1 |     0 |     0% |
| application_sdk/transformers/common/utils.py                                      |     6 |    0 |     6 |   100% |
| application_sdk/transformers/query/__init__.py                                    |    11 |    2 |     9 |    82% |
| application_sdk/workflows/__init__.py                                             |     4 |    0 |     4 |   100% |
| application_sdk/workflows/metadata_extraction/__init__.py                         |     2 |    2 |     0 |     0% |
| application_sdk/workflows/metadata_extraction/sql.py                              |     8 |    0 |     8 |   100% |
| application_sdk/workflows/query_extraction/__init__.py                            |     2 |    2 |     0 |     0% |
| application_sdk/workflows/query_extraction/sql.py                                 |     4 |    0 |     4 |   100% |
| examples/application_custom_fastapi.py                                            |    14 |   14 |     0 |     0% |
| examples/application_fastapi.py                                                   |     9 |    9 |     0 |     0% |
| examples/application_hello_world.py                                               |     7 |    7 |     0 |     0% |
| examples/application_sql.py                                                       |     5 |    4 |     1 |    20% |
| examples/application_sql_miner.py                                                 |     5 |    4 |     1 |    20% |
| examples/application_sql_with_custom_pyatlan_transformer.py                       |    11 |    9 |     2 |    18% |
| examples/application_sql_with_custom_transformer.py                               |     9 |    8 |     1 |    11% |
| examples/run_examples.py                                                          |     2 |    1 |     1 |    50% |
| tests/__init__.py                                                                 |     1 |    1 |     0 |     0% |
| tests/conftest.py                                                                 |     4 |    0 |     4 |   100% |
| tests/unit/__init__.py                                                            |     1 |    1 |     0 |     0% |
| tests/unit/test_worker.py                                                         |    21 |    7 |    14 |    67% |
| tests/unit/activities/__init__.py                                                 |     1 |    1 |     0 |     0% |
| tests/unit/activities/test_activities.py                                          |    41 |    3 |    38 |    93% |
| tests/unit/activities/test_lock_management.py                                     |    12 |    0 |    12 |   100% |
| tests/unit/activities/common/__init__.py                                          |     1 |    1 |     0 |     0% |
| tests/unit/activities/common/test_sql_utils.py                                    |     4 |    1 |     3 |    75% |
| tests/unit/activities/common/test_utils.py                                        |    28 |   10 |    18 |    64% |
| tests/unit/activities/metadata_extraction/__init__.py                             |     1 |    1 |     0 |     0% |
| tests/unit/activities/metadata_extraction/test_sql.py                             |    56 |   38 |    18 |    32% |
| tests/unit/activities/query_extraction/__init__.py                                |     1 |    1 |     0 |     0% |
| tests/unit/application/__init__.py                                                |     1 |    1 |     0 |     0% |
| tests/unit/application/test_application.py                                        |    44 |    3 |    41 |    93% |
| tests/unit/application/metadata_extraction/test_sql.py                            |    36 |    6 |    30 |    83% |
| tests/unit/clients/__init__.py                                                    |     1 |    1 |     0 |     0% |
| tests/unit/clients/test_async_sql_client.py                                       |    15 |   14 |     1 |     7% |
| tests/unit/clients/test_atlan_auth.py                                             |    11 |    0 |    11 |   100% |
| tests/unit/clients/test_atlan_client.py                                           |     7 |    7 |     0 |     0% |
| tests/unit/clients/test_atlanauth.py                                              |    11 |    1 |    10 |    91% |
| tests/unit/clients/test_azure_auth.py                                             |    14 |    0 |    14 |   100% |
| tests/unit/clients/test_azure_client.py                                           |    19 |    0 |    19 |   100% |
| tests/unit/clients/test_base_client.py                                            |    23 |    1 |    22 |    96% |
| tests/unit/clients/test_redis_client.py                                           |    40 |    0 |    40 |   100% |
| tests/unit/clients/test_sql_client.py                                             |    28 |    6 |    22 |    79% |
| tests/unit/clients/test_temporal_client.py                                        |    20 |    4 |    16 |    80% |
| tests/unit/common/test_aws_utils.py                                               |    30 |    1 |    29 |    97% |
| tests/unit/common/test_credential_utils.py                                        |    30 |    1 |    29 |    97% |
| tests/unit/common/test_file_converter.py                                          |    29 |    0 |    29 |   100% |
| tests/unit/common/test_file_ops.py                                                |    21 |    0 |    21 |   100% |
| tests/unit/common/test_path.py                                                    |     6 |    0 |     6 |   100% |
| tests/unit/common/test_utils.py                                                   |    74 |    6 |    68 |    92% |
| tests/unit/common/test_utils_file_discovery.py                                    |    11 |    0 |    11 |   100% |
| tests/unit/decorators/__init__.py                                                 |     1 |    1 |     0 |     0% |
| tests/unit/decorators/test_mcp_tool.py                                            |    56 |    4 |    52 |    93% |
| tests/unit/docgen/parsers/test_directory_parser.py                                |    14 |    3 |    11 |    79% |
| tests/unit/docgen/parsers/test_manifest_parser.py                                 |    12 |   12 |     0 |     0% |
| tests/unit/handlers/__init__.py                                                   |     1 |    1 |     0 |     0% |
| tests/unit/handlers/test_base_handler.py                                          |    26 |    2 |    24 |    92% |
| tests/unit/handlers/sql/test_auth.py                                              |    10 |    4 |     6 |    60% |
| tests/unit/handlers/sql/test_check_schemas_and_databases.py                       |    14 |    4 |    10 |    71% |
| tests/unit/handlers/sql/test_extract_allowed_schemas.py                           |    11 |    3 |     8 |    73% |
| tests/unit/handlers/sql/test_metadata.py                                          |    27 |   10 |    17 |    63% |
| tests/unit/handlers/sql/test_preflight_check.py                                   |    16 |   15 |     1 |     6% |
| tests/unit/handlers/sql/test_prepare_metadata.py                                  |    14 |    4 |    10 |    71% |
| tests/unit/handlers/sql/test_tables_check.py                                      |     9 |    6 |     3 |    33% |
| tests/unit/handlers/sql/test_validate_filters.py                                  |    12 |    4 |     8 |    67% |
| tests/unit/interceptors/__init__.py                                               |     1 |    1 |     0 |     0% |
| tests/unit/interceptors/test_correlation_context.py                               |    37 |    0 |    37 |   100% |
| tests/unit/io/test_base_io.py                                                     |    29 |    4 |    25 |    86% |
| tests/unit/io/test_writer_data_integrity.py                                       |    12 |    5 |     7 |    58% |
| tests/unit/io/readers/test_json_reader.py                                         |    38 |   19 |    19 |    50% |
| tests/unit/io/readers/test_parquet_reader.py                                      |    60 |   38 |    22 |    37% |
| tests/unit/io/writers/test_json_writer.py                                         |     7 |    6 |     1 |    14% |
| tests/unit/io/writers/test_parquet_writer.py                                      |    57 |   10 |    47 |    82% |
| tests/unit/observability/__init__.py                                              |     1 |    1 |     0 |     0% |
| tests/unit/observability/test_logger_adaptor.py                                   |    35 |    2 |    33 |    94% |
| tests/unit/observability/test_metrics_adaptor.py                                  |    17 |    1 |    16 |    94% |
| tests/unit/observability/test_traces_adaptor.py                                   |    10 |    1 |     9 |    90% |
| tests/unit/server/__init__.py                                                     |     1 |    1 |     0 |     0% |
| tests/unit/server/fastapi/test_fastapi.py                                         |    64 |   27 |    37 |    58% |
| tests/unit/server/fastapi/routers/__init__.py                                     |     1 |    1 |     0 |     0% |
| tests/unit/server/fastapi/routers/server.py                                       |     1 |    1 |     0 |     0% |
| tests/unit/server/mcp/__init__.py                                                 |     1 |    1 |     0 |     0% |
| tests/unit/server/mcp/test_mcp_server.py                                          |    24 |    1 |    23 |    96% |
| tests/unit/services/test_atlan_storage.py                                         |    10 |    0 |    10 |   100% |
| tests/unit/services/test_eventstore.py                                            |    18 |    0 |    18 |   100% |
| tests/unit/services/test_objectstore.py                                           |    32 |    5 |    27 |    84% |
| tests/unit/services/test_statestore.py                                            |    14 |    0 |    14 |   100% |
| tests/unit/services/test_statestore_path_traversal.py                             |    23 |   17 |     6 |    26% |
| tests/unit/test_utils/__init__.py                                                 |     1 |    1 |     0 |     0% |
| tests/unit/test_utils/e2e/__init__.py                                             |     1 |    1 |     0 |     0% |
| tests/unit/test_utils/e2e/fixtures/__init__.py                                    |     1 |    1 |     0 |     0% |
| tests/unit/test_utils/e2e/fixtures/test_base.py                                   |    22 |   21 |     1 |     5% |
| tests/unit/test_utils/e2e/fixtures/test_containerized.py                          |    14 |   14 |     0 |     0% |
| tests/unit/test_utils/e2e/fixtures/test_loader.py                                 |     7 |    7 |     0 |     0% |
| tests/unit/test_utils/e2e/fixtures/test_plugin.py                                 |    14 |   13 |     1 |     7% |
| tests/unit/test_utils/e2e/fixtures/test_readiness.py                              |    11 |   10 |     1 |     9% |
| tests/unit/test_utils/e2e/fixtures/test_schema.py                                 |    11 |   11 |     0 |     0% |
| tests/unit/transformers/__init__.py                                               |     1 |    1 |     0 |     0% |
| tests/unit/transformers/atlas/__init__.py                                         |     1 |    1 |     0 |     0% |
| tests/unit/transformers/atlas/test_column.py                                      |    17 |    6 |    11 |    65% |
| tests/unit/transformers/atlas/test_database.py                                    |     8 |    6 |     2 |    25% |
| tests/unit/transformers/atlas/test_function.py                                    |     9 |    5 |     4 |    44% |
| tests/unit/transformers/atlas/test_procedure.py                                   |     7 |    6 |     1 |    14% |
| tests/unit/transformers/atlas/test_schema.py                                      |     8 |    6 |     2 |    25% |
| tests/unit/transformers/atlas/test_table.py                                       |    13 |    6 |     7 |    54% |
| tests/unit/transformers/query/test_sql_transformer.py                             |    16 |    4 |    12 |    75% |
| tests/unit/transformers/query/test_sql_transformer_output_validation.py           |     5 |    2 |     3 |    60% |
| tests/unit/workflows/metadata_extraction/test_sql_workflow.py                     |     9 |    4 |     5 |    56% |
| tests/unit/workflows/query_extraction/__init__.py                                 |     1 |    1 |     0 |     0% |
| tests/unit/workflows/query_extraction/test_sql.py                                 |     8 |    3 |     5 |    62% |
|-----------------------------------------------------------------------------------|-------|------|-------|--------|
| TOTAL                                                                             |  2596 |  737 |  1859 |  71.6% |
---------------- RESULT: PASSED (minimum: 30.0%, actual: 71.6%) ----------------

@github-actions
Copy link

github-actions bot commented Feb 10, 2026

📦 Trivy Vulnerability Scan Results

Schema Version Created At Artifact Type
2 2026-02-13T13:59:56.123602441Z . filesystem

Report Summary

Could not generate summary table (data length mismatch: 9 vs 8).

Scan Result Details

requirements.txt
uv.lock

@github-actions
Copy link

github-actions bot commented Feb 10, 2026

📦 Trivy Secret Scan Results

Schema Version Created At Artifact Type
2 2026-02-13T14:00:04.944528551Z . filesystem

Report Summary

Target Type Secrets . filesystem ✅ None found

Scan Result Details

✅ No secrets found during the scan for ..

@atlan-ci
Copy link
Collaborator

atlan-ci commented Feb 10, 2026

☂️ Python Coverage

current status: ✅

Overall Coverage

Lines Covered Coverage Threshold Status
7366 5312 72% 0% 🟢

New Files

No new covered files...

Modified Files

No covered modified files...

updated for commit: b8b73d9 by action🐍

@github-actions
Copy link

github-actions bot commented Feb 10, 2026

🛠 Full Test Coverage Report: https://k.atlan.dev/coverage/application-sdk/pr/1040

@fyzanshaik-atlan fyzanshaik-atlan added e2e-test run-examples Run examples on the Pull Request labels Feb 10, 2026
@github-actions
Copy link

github-actions bot commented Feb 10, 2026

🛠 Docs available at: https://k.atlan.dev/application-sdk/feat/datasource-fixtures

@github-actions
Copy link

github-actions bot commented Feb 10, 2026

📦 Example workflows test results

  • This workflow runs all the examples in the examples directory.

Example Status Time Taken
application_sql COMPLETED 🟢 8.44 seconds
application_sql_with_custom_transformer COMPLETED 🟢 8.45 seconds
application_sql_miner COMPLETED 🟢 43.26 seconds
application_hello_world COMPLETED 🟢 5.17 seconds

This is an automatically generated file. Please do not edit directly.
Operating system: macOS-latest

@github-actions
Copy link

github-actions bot commented Feb 10, 2026

📦 Example workflows test results

  • This workflow runs all the examples in the examples directory.

Example Status Time Taken
application_sql COMPLETED 🟢 13.64 seconds
application_sql_with_custom_transformer COMPLETED 🟢 8.60 seconds
application_sql_miner COMPLETED 🟢 53.98 seconds
application_hello_world COMPLETED 🟢 5.61 seconds

This is an automatically generated file. Please do not edit directly.
Operating system: windows-latest

@github-actions
Copy link

github-actions bot commented Feb 10, 2026

📦 Example workflows test results

  • This workflow runs all the examples in the examples directory.

Example Status Time Taken
application_sql COMPLETED 🟢 8.28 seconds
application_sql_with_custom_transformer COMPLETED 🟢 8.81 seconds
application_sql_miner COMPLETED 🟢 58.39 seconds
application_hello_world COMPLETED 🟢 5.31 seconds

This is an automatically generated file. Please do not edit directly.
Operating system: ubuntu-22.04

Replace the class-based ContainerizedDataSourceFixture and
HostedDataSourceFixture with a declarative YAML-driven approach.
App developers now declare a datasource.yaml instead of writing
Python fixture subclasses.

New modules:
- schema.py: Pydantic models for YAML config validation
- readiness.py: TCP readiness check for container startup
- containerized.py: ContainerizedFixture using testcontainers GenericContainer
- loader.py: Factory function to parse YAML and return fixture
- plugin.py: Simplified pytest plugin activated via config._datasource_yaml

Removes HostedFixture entirely — hosted databases use existing
CI secret injection + expandvars pattern without SDK involvement.
@fyzanshaik-atlan fyzanshaik-atlan changed the title BLDX-432: Add DataSourceFixture abstraction for E2E testing feat(test_utils): YAML-driven datasource fixtures for E2E testing Feb 11, 2026
@fyzanshaik-atlan fyzanshaik-atlan changed the title feat(test_utils): YAML-driven datasource fixtures for E2E testing BLDX-432: YAML-driven datasource fixtures for E2E testing Feb 11, 2026
Use tmp_path fixture instead of hardcoded Unix paths so tests
pass on Windows where Path.resolve() prepends the drive letter.
…e positives

TCP connect() alone can succeed when Docker's port proxy accepts the
connection before the actual service is listening inside the container.
The check now does a 1-byte recv() after connect to verify the service
is actually holding the connection open.
…tials

Replace hardcoded username/password/database/extra fields in ConnectionInfo
with a single credentials dict pass-through from YAML. Env vars are now
generated dynamically for all credential keys, uppercased automatically.

This makes the fixture system work for any connector type (databases,
dashboards, pipelines) without assuming a fixed credential shape.
@fyzanshaik-atlan fyzanshaik-atlan added the e2e-testcontainers Run E2E testcontainers tests on Linux + Windows label Feb 12, 2026
@snykgituser
Copy link

snykgituser commented Feb 13, 2026

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues
Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@fyzanshaik-atlan fyzanshaik-atlan removed the e2e-testcontainers Run E2E testcontainers tests on Linux + Windows label Feb 13, 2026
@fyzanshaik-atlan
Copy link
Contributor Author

@greptile review

@greptile-apps
Copy link

greptile-apps bot commented Feb 16, 2026

Greptile Summary

This PR introduces a declarative YAML-driven datasource fixture system for E2E testing, enabling app developers to provision containerized databases/services without writing Python code.

Key changes:

  • Generic ConnectionInfo holds host, port, and free-form credentials dict (supports any connector type)
  • Dynamic env var generation: credential keys auto-UPPERCASED with configurable prefix
  • TCP readiness check uses recv() to distinguish real service readiness from Docker proxy false positives
  • Pytest plugin auto-loads via pytest11 entry point and manages fixture lifecycle (setup/teardown)
  • Progressive disclosure: YAML-only for simple cases, subclass ContainerizedFixture for custom readiness, or implement full DataSourceFixture ABC

Issues found:

  • Missing exception handling in plugin.py:33-38 could prevent cleanup if setup fails
  • Missing exception handling in containerized.py:74-78 could leak container resources if teardown fails

Test coverage:

  • 44 comprehensive unit tests across all modules with mocking
  • Integration test with real Postgres container pending (mentioned in test plan)

Confidence Score: 4/5

  • Safe to merge after addressing exception handling issues in plugin and containerized fixture
  • Well-designed architecture with comprehensive unit tests (44 tests), but two critical exception handling gaps could cause resource leaks. Once fixed, the implementation follows clean abstractions and project standards.
  • Pay close attention to application_sdk/test_utils/e2e/fixtures/plugin.py and application_sdk/test_utils/e2e/fixtures/containerized.py for exception handling fixes

Important Files Changed

Filename Overview
application_sdk/test_utils/e2e/fixtures/base.py Defines ConnectionInfo and DataSourceFixture ABC with clean abstractions; wait_until_ready properly captures last error
application_sdk/test_utils/e2e/fixtures/schema.py Pydantic models for YAML config with proper defaults and field descriptions; clean schema design
application_sdk/test_utils/e2e/fixtures/readiness.py TCP readiness check with recv() to distinguish Docker proxy from real service; handles all connection states correctly
application_sdk/test_utils/e2e/fixtures/containerized.py Container lifecycle management with volume mounting and env var generation; missing exception handling in teardown
application_sdk/test_utils/e2e/fixtures/loader.py YAML loader factory with proper error handling for missing files and validation errors
application_sdk/test_utils/e2e/fixtures/plugin.py Pytest plugin for fixture lifecycle; missing exception handling for setup failures could prevent cleanup

Flowchart

flowchart TD
    A[App's pytest_configure] -->|Sets _datasource_yaml| B[pytest_sessionstart hook]
    B --> C[load_fixture_from_yaml]
    C --> D[Parse YAML with Pydantic]
    D --> E[Create ContainerizedFixture]
    E --> F[fixture.setup]
    F --> G[Start Docker container]
    G --> H[Extract host:port]
    H --> I[Create ConnectionInfo]
    I --> J[wait_until_ready]
    J --> K{check_tcp with recv}
    K -->|Ready| L[Inject env vars to os.environ]
    K -->|Not Ready| M[Retry with interval]
    M --> K
    L --> N[Store connection_info on config]
    N --> O[Run tests]
    O --> P[pytest_sessionfinish hook]
    P --> Q[fixture.teardown]
    Q --> R[Stop container]
    R --> S[Clean up resources]
Loading

Last reviewed commit: b8b73d9

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

19 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +33 to +38
_active_fixture = load_fixture_from_yaml(yaml_path)
connection_info = _active_fixture.setup()

for key, value in _active_fixture.get_env_vars().items():
os.environ[key] = value

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing exception handling for fixture setup failures. If setup() or get_env_vars() raises an exception, the test session will fail to start but _active_fixture will still be set, preventing proper cleanup in pytest_sessionfinish().

Suggested change
_active_fixture = load_fixture_from_yaml(yaml_path)
connection_info = _active_fixture.setup()
for key, value in _active_fixture.get_env_vars().items():
os.environ[key] = value
try:
_active_fixture = load_fixture_from_yaml(yaml_path)
connection_info = _active_fixture.setup()
for key, value in _active_fixture.get_env_vars().items():
os.environ[key] = value
session.config._datasource_connection_info = connection_info # type: ignore[attr-defined]
except Exception:
if _active_fixture is not None:
_active_fixture.teardown()
_active_fixture = None
raise

Comment on lines +74 to +78
def teardown(self) -> None:
"""Stop and remove the container."""
if self._container is not None:
self._container.stop()
self._container = None
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing exception handling during container teardown. If stop() fails, container resources may leak and _container will remain set.

Suggested change
def teardown(self) -> None:
"""Stop and remove the container."""
if self._container is not None:
self._container.stop()
self._container = None
def teardown(self) -> None:
"""Stop and remove the container."""
if self._container is not None:
try:
self._container.stop()
except Exception:
pass # Best-effort cleanup
finally:
self._container = None
self._connection_info = None

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

e2e-test run-examples Run examples on the Pull Request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants