Skip to content

Filesystem integration tests - #249

Merged
amotl merged 7 commits into
mainfrom
filesystem-integration-tests
Jul 25, 2026
Merged

amotl merged 7 commits into
mainfrom
filesystem-integration-tests

Conversation

@amotl

@amotl amotl commented Jul 22, 2026

Copy link
Copy Markdown
Member

About

Add a few integration test cases that use remote filesystems.

References

@coderabbitai

coderabbitai Bot commented Jul 22, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@amotl, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 35 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 213ea5a1-b018-4950-b9f1-a6ee55aa9e8b

📥 Commits

Reviewing files that changed from the base of the PR and between 58d993c and c1d6109.

📒 Files selected for processing (9)
  • pyproject.toml
  • src/dlt_filesystem/source/impl/remote.py
  • src/dlt_filesystem/target/remote.py
  • src/dlt_filesystem/util/auth.py
  • src/omniload/source/docebo/adapter.py
  • src/omniload/source/github/adapter.py
  • tests/dlt_filesystem/gcs.py
  • tests/dlt_filesystem/test_source_incremental.py
  • tests/main/filesystem/test_remote_integration.py

Walkthrough

Remote GCS and Azure filesystem construction now normalizes credentials, supports Azure connection strings, and preserves destination compatibility. New emulator-backed integration tests validate S3, Azure, and GCS ingestion into DuckDB. Dependency constraints and logging output were also updated.

Changes

Remote filesystem support

Layer / File(s) Summary
Credential parsing and filesystem construction
src/dlt_filesystem/util/auth.py, src/dlt_filesystem/source/impl/remote.py, src/dlt_filesystem/target/remote.py, tests/dlt_filesystem/test_source_incremental.py
Azure auth supports connection strings and API versions; GCS and Azure sources construct filesystem clients from normalized kwargs, with updated Azure destination credential handling and mocks.
Emulator-backed ingestion validation
tests/dlt_filesystem/gcs.py, tests/main/filesystem/test_remote_integration.py
S3, Azure, and GCS emulators are provisioned for integration tests that ingest CSV data into DuckDB and verify 20 rows.
Dependency and logging maintenance
pyproject.toml, src/omniload/source/docebo/adapter.py, src/omniload/source/github/adapter.py
Dependency ranges and test extras were updated, debug output was removed, and GitHub overlap reporting now uses structured logging.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant IntegrationTest
  participant CloudEmulator
  participant run_ingest
  participant DuckDB
  IntegrationTest->>CloudEmulator: provision source asset
  IntegrationTest->>run_ingest: ingest emulator endpoint
  run_ingest->>CloudEmulator: read CSV asset
  run_ingest->>DuckDB: load records
  IntegrationTest->>DuckDB: verify 20 rows
Loading

Possibly related PRs

  • panodata/omniload#236: Related GCS and Azure filesystem construction and credential handling.
  • panodata/omniload#239: Related Azure connection-string parsing, endpoint handling, and incremental filesystem tests.

Suggested reviewers: hampsterx

Poem

I’m a rabbit with clouds in my queue,
GCS, Azure, and S3 hopped through.
Credentials now flow neat and bright,
DuckDB counts twenty rows just right.
Debug prints vanish—what a delight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 76.92% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title is concise and describes the main change: adding filesystem integration tests.
Description check ✅ Passed The description matches the PR's main purpose of adding integration tests for remote filesystems.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch filesystem-integration-tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@amotl
amotl force-pushed the filesystem-integration-tests branch from 2549e80 to f7e8af6 Compare July 23, 2026 21:25
@codecov

codecov Bot commented Jul 23, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.87234% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 55.71%. Comparing base (e46547f) to head (c1d6109).

Files with missing lines Patch % Lines
src/dlt_filesystem/source/impl/remote.py 96.77% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #249      +/-   ##
==========================================
+ Coverage   55.58%   55.71%   +0.12%     
==========================================
  Files         210      210              
  Lines        9871     9894      +23     
==========================================
+ Hits         5487     5512      +25     
+ Misses       4384     4382       -2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@amotl
amotl force-pushed the filesystem-integration-tests branch from f7e8af6 to 2697d90 Compare July 24, 2026 22:43
@amotl
amotl force-pushed the filesystem-integration-tests branch 2 times, most recently from 91a03c2 to 58d993c Compare July 25, 2026 16:24
@amotl amotl added the coderabbit-review Tell CodeRabbit to review the patch. label Jul 25, 2026
@amotl
amotl marked this pull request as ready for review July 25, 2026 16:33

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/dlt_filesystem/util/auth.py (1)

46-89: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

connection_string mode silently ignores conflicting credentials and the docstring wasn't updated.

The early return at Line 75-79 skips all subsequent parsing/validation once connection_string is supplied. If a caller also passes account_key, sas_token, or a partial service-principal triplet alongside connection_string, those values are silently dropped with no conflict error, unlike the existing account-key/SAS vs. service-principal conflict checks below. The docstring (lines 54-59) still only documents two auth modes and omits connection_string as a third mode.

🛡️ Proposed fix: validate that no other credential material is mixed with connection_string
     connection_string = one("connection_string")
     api_version = one("api_version")

     if connection_string is not None:
+        conflicting = [
+            k for k in ("account_name", "account_key", "sas_token", *AZURE_SERVICE_PRINCIPAL_FIELDS)
+            if one(k) is not None
+        ]
+        if conflicting:
+            raise ValueError(
+                "Conflicting Azure credentials: connection_string cannot be combined "
+                f"with {', '.join(conflicting)}."
+            )
         return AzureBlobAuth(
             connection_string=connection_string,
             api_version=api_version,
         )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dlt_filesystem/util/auth.py` around lines 46 - 89, Update the credential
parsing function around the connection_string early return to document
connection_string as a third auth mode and validate that no account_key,
sas_token, or service-principal fields are supplied alongside it. Raise the
existing conflict ValueError for any mixed credential material before returning
AzureBlobAuth; preserve the current connection_string-only behavior and
validation for the other auth modes.
src/dlt_filesystem/target/remote.py (1)

156-192: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Reject connection_string in the Azure destination before constructing credentials.

parse_azure_blob_auth() returns an AzureBlobAuth with only connection_string when that query param is supplied, leaving is_service_principal false and every account_name, account_key, sas_token, and account_host guard skipped. The code then returns a blank AzureCredentials(), while dlt’s Azure credential specs do not expose connection_string. Raise a clear unsupported-credential error for destination URIs using connection_string.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dlt_filesystem/target/remote.py` around lines 156 - 192, Update
credentials to detect when parse_azure_blob_auth returns a
connection_string-only authentication configuration and raise a clear
unsupported-credential error before constructing
AzureServicePrincipalCredentials or AzureCredentials. Preserve the existing
service-principal and account-key mappings for supported authentication fields.
🧹 Nitpick comments (6)
tests/main/filesystem/test_remote_integration.py (3)

41-54: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

"2025-11-05" API-version literal is duplicated between the monkeypatch and the test call.

The hardcoded version in adlfs_patch (Line 47) must stay in sync with the api_version=2025-11-05 query param in test_azure_source (Line 133) purely by convention; a shared module-level constant would prevent drift if one is updated without the other.

Also applies to: 128-133

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/main/filesystem/test_remote_integration.py` around lines 41 - 54,
Define a shared module-level constant for the Azure API version and reuse it in
both _create_aio_blob_service_client_from_connection_string_with_api_version and
the api_version query parameter in test_azure_source, removing the duplicated
literal while preserving the existing value.

98-106: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low value

SQL built via f-string flagged by both Ruff (S608) and OpenGrep.

table_name is only ever a hardcoded literal from within this same file today, so there's no attacker-controlled input, but the pattern is easy to copy into less-controlled code later. Consider quoting the identifier defensively.

🛡️ Proposed fix
 def duckdb_table_cardinality(db_path: Path, table_name: str) -> int:
     """Return number of records in database table."""
     import duckdb

     db = duckdb.connect(db_path)
-    result = db.execute(f"SELECT * FROM {table_name}").fetchall()
+    quoted = ".".join(f'"{part}"' for part in table_name.split("."))
+    result = db.execute(f"SELECT * FROM {quoted}").fetchall()
     count = len(result)
     db.close()
     return count
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/main/filesystem/test_remote_integration.py` around lines 98 - 106,
Update duckdb_table_cardinality to quote or safely escape the table_name
identifier before constructing the SQL query, avoiding direct interpolation of
the raw value while preserving the existing row-count behavior.

Source: Linters/SAST tools


62-71: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reading the fixture CSV via read_text() uses implicit/locale-dependent encoding.

Unlike the floci/gcsfakeserver fixtures which upload the file directly (upload_file/upload_from_filename), this fixture loads content via Path(...).read_text() without an explicit encoding=, which is platform/locale dependent and can differ from the other two fixtures' behavior across CI environments.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/main/filesystem/test_remote_integration.py` around lines 62 - 71,
Update the fixture upload in the test setup around BlobServiceClient and
cc.upload_blob to read create_replace.csv with an explicit UTF-8 encoding,
matching the deterministic file handling used by the other fixtures.
tests/dlt_filesystem/gcs.py (1)

30-46: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Doctest example references an undefined variable.

The example defines endpoint_url (Line 37) but then uses connection_string (Line 41) in client_options, which is never defined in this snippet. As per path instructions for test files ("Prefer flagging missing edge-case coverage over style nits"), this is a functional-correctness slip in the example rather than a style nit — running this as a doctest would raise NameError.

📝 Proposed fix
             >>> with GCSFakeServerContainer() as container:
             ...   endpoint_url = container.get_endpoint_url()
             ...   client = google.cloud.storage.Client(
             ...     credentials=AnonymousCredentials(),
             ...     project="test",
-            ...     client_options={"api_endpoint": connection_string},
+            ...     client_options={"api_endpoint": endpoint_url},
             ...   )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/dlt_filesystem/gcs.py` around lines 30 - 46, Update the doctest example
to pass the defined endpoint_url variable to google.cloud.storage.Client via
client_options instead of the undefined connection_string variable. Keep the
rest of the GCSFakeServerContainer setup and upload flow unchanged.

Source: Path instructions

src/dlt_filesystem/source/impl/remote.py (2)

67-83: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Merged query params can silently override control kwargs with un-typed strings.

Line 71 unconditionally merges every remaining URI query param into kwargs, after the caller's own **kwargs were already set. If a URI ever includes a query key that collides with an internal control kwarg (e.g. filesystem_incremental, column_types), the raw string value overwrites the caller-supplied typed value — and since any non-empty string (including "false") is truthy, filesystem_incremental=false in a URI would still enable incrementality. The existing if "token" not in kwargs guard shows the pattern was already considered for token; the same precedence should apply broadly.

♻️ Proposed fix: don't let query params clobber already-set control kwargs
-        # Merge params into fs kwargs.
-        kwargs.update({key: value[0] for key, value in params.items()})
+        # Merge params into fs kwargs, without overriding kwargs already
+        # supplied by the caller (e.g. filesystem_incremental, column_types).
+        for key, value in params.items():
+            kwargs.setdefault(key, value[0])
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dlt_filesystem/source/impl/remote.py` around lines 67 - 83, Update the
parameter merge in the remote filesystem initialization flow to preserve
caller-supplied kwargs: when converting remaining query parameters, only add
keys that are not already present in kwargs. Keep the existing token handling
and credential precedence intact, including typed control values such as
filesystem_incremental and column_types.

172-198: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Stale docstring: _azure_kwargs no longer builds a filesystem.

The docstring still says "Build an adlfs.AzureBlobFileSystem from resolved Azure auth params", but per the AI summary and the code the function now only returns a kwargs dict; construction happens separately via self.fs_class(**kwargs). Worth updating to avoid confusing future readers.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dlt_filesystem/source/impl/remote.py` around lines 172 - 198, The
docstring for _azure_kwargs inaccurately describes filesystem construction;
update it to state that the function builds and returns keyword arguments from
resolved Azure authentication parameters. Keep the existing parameter-forwarding
details and deferred-import explanation accurate, without implying that
AzureBlobFileSystem is instantiated here.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/dlt_filesystem/target/remote.py`:
- Around line 156-192: Update credentials to detect when parse_azure_blob_auth
returns a connection_string-only authentication configuration and raise a clear
unsupported-credential error before constructing
AzureServicePrincipalCredentials or AzureCredentials. Preserve the existing
service-principal and account-key mappings for supported authentication fields.

In `@src/dlt_filesystem/util/auth.py`:
- Around line 46-89: Update the credential parsing function around the
connection_string early return to document connection_string as a third auth
mode and validate that no account_key, sas_token, or service-principal fields
are supplied alongside it. Raise the existing conflict ValueError for any mixed
credential material before returning AzureBlobAuth; preserve the current
connection_string-only behavior and validation for the other auth modes.

---

Nitpick comments:
In `@src/dlt_filesystem/source/impl/remote.py`:
- Around line 67-83: Update the parameter merge in the remote filesystem
initialization flow to preserve caller-supplied kwargs: when converting
remaining query parameters, only add keys that are not already present in
kwargs. Keep the existing token handling and credential precedence intact,
including typed control values such as filesystem_incremental and column_types.
- Around line 172-198: The docstring for _azure_kwargs inaccurately describes
filesystem construction; update it to state that the function builds and returns
keyword arguments from resolved Azure authentication parameters. Keep the
existing parameter-forwarding details and deferred-import explanation accurate,
without implying that AzureBlobFileSystem is instantiated here.

In `@tests/dlt_filesystem/gcs.py`:
- Around line 30-46: Update the doctest example to pass the defined endpoint_url
variable to google.cloud.storage.Client via client_options instead of the
undefined connection_string variable. Keep the rest of the
GCSFakeServerContainer setup and upload flow unchanged.

In `@tests/main/filesystem/test_remote_integration.py`:
- Around line 41-54: Define a shared module-level constant for the Azure API
version and reuse it in both
_create_aio_blob_service_client_from_connection_string_with_api_version and the
api_version query parameter in test_azure_source, removing the duplicated
literal while preserving the existing value.
- Around line 98-106: Update duckdb_table_cardinality to quote or safely escape
the table_name identifier before constructing the SQL query, avoiding direct
interpolation of the raw value while preserving the existing row-count behavior.
- Around line 62-71: Update the fixture upload in the test setup around
BlobServiceClient and cc.upload_blob to read create_replace.csv with an explicit
UTF-8 encoding, matching the deterministic file handling used by the other
fixtures.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 236e0d25-f933-4073-b547-7d47a81cec84

📥 Commits

Reviewing files that changed from the base of the PR and between 759aea3 and 58d993c.

📒 Files selected for processing (9)
  • pyproject.toml
  • src/dlt_filesystem/source/impl/remote.py
  • src/dlt_filesystem/target/remote.py
  • src/dlt_filesystem/util/auth.py
  • src/omniload/source/docebo/adapter.py
  • src/omniload/source/github/adapter.py
  • tests/dlt_filesystem/gcs.py
  • tests/dlt_filesystem/test_source_incremental.py
  • tests/main/filesystem/test_remote_integration.py
💤 Files with no reviewable changes (1)
  • src/omniload/source/docebo/adapter.py

@amotl
amotl force-pushed the filesystem-integration-tests branch from 58d993c to c1d6109 Compare July 25, 2026 16:57
@amotl
amotl merged commit 7b24bb8 into main Jul 25, 2026
14 checks passed
@amotl
amotl deleted the filesystem-integration-tests branch July 25, 2026 17:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

coderabbit-review Tell CodeRabbit to review the patch.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant