Adapter Testing Guidelines
Relevant source files
The following files were used as context for generating this wiki page:
Adapter testing ensures that connectors accurately translate data between external systems and the Infrahub synchronization engine. These guidelines define the requirements for unit and integration tests to maintain sync safety and idempotency. Every new or modified adapter requires tests to be merged into the project.
Sources: dev/guides/testing-an-adapter.md:10-12, dev/constitution.md:122-123
Testing follows the principle of "Read-Only / Dry-Run by Default." Tests prioritize verifying data extraction and transformation before any mutation occurs.
Sources: dev/constitution.md:20-22
Core Testing Principles
The project mandates specific practices to ensure tests are reliable, fast, and secure.
- Mock Upstream Systems: Use
unittest.mock.MagicMockto stub upstream clients. Unit tests must pass without network access or live credentials. - Atomic Tests: Isolate one behavior per test. Use pytest parametrization for multiple configuration variants instead of loops.
- Dependency Protection: Use
pytest.importorskipfor optional SDK dependencies to prevent test collection failures in environments where the SDK is not installed. - Offline by Default: The default test suite (
uv run pytest -q) must stay offline. Mark live system tests as integration tests.
Sources: dev/guidelines/testing-adapters.md:11-20, dev/guidelines/testing-adapters.md:58-60, dev/guides/testing-an-adapter.md:38-40
Test Categories
| Category | Location | Markers | Purpose |
|---|---|---|---|
| Unit Tests | tests/adapters/ | None | Verify model_loader, conversion logic, and cursor methods using mocks. |
| Integration Tests | tests/integration/ | @pytest.mark.integration | Verify live API interactions. Opt-in via -m integration. |
| Sources: dev/guidelines/testing-adapters.md:81-86, dev/guides/testing-an-adapter.md:95-103 |
Testing the Conversion Path
The conversion path test ensures source records correctly transform into DiffSync models. You must verify that model_loader (or obj_to_diffsync) correctly maps fields and handles references.
Sources: dev/guidelines/testing-adapters.md:28-36
Conversion Flow Verification
The following diagram illustrates the sequence for testing the data conversion logic.
Required Coverage for Conversions:
- Field Mapping: Check that source fields map to the correct destination attribute names.
- Identity: Ensure
local_idand natural keyidentifiersare populated. - References: Verify that
referencefields resolve to the correct peerunique_id, including list-type references. - Transformation: Confirm Jinja2 transforms produce native Python types.
Sources: dev/guidelines/testing-adapters.md:30-36, dev/guides/testing-an-adapter.md:67-70
Testing the Incremental Contract
If an adapter implements incremental synchronization, you must test its cursor tier and change detection methods.
Sources: dev/guidelines/testing-adapters.md:40-42
- Cursor Tier: Assert that
cursor_tier_forreturns the expectedCursorTier(e.g.,TIMESTAMPorID) for mapped kinds andNONEfor unmapped kinds. - Change Filtering: Verify that
list_changed_sinceapplies the correct API filter (e.g.,last_updated__gte) and yields records in the expected shape. - Existing IDs: Test that
list_existing_idscorrectly yields currentunique_idstrings to facilitate deletion detection.
Sources: dev/guidelines/testing-adapters.md:43-52, dev/guides/testing-an-adapter.md:78-85
Error and Edge Case Coverage
Tests must exercise common failure modes encountered by connectors to prevent silent data loss or ambiguous states.
Sources: dev/constitution.md:37-41
Mandatory Edge Cases:
- Empty Sets: Handle cases where the API returns no records.
- Pagination: Ensure the adapter correctly iterates through multiple pages of results.
- Authentication: Verify that 401 (Unauthorized) and 403 (Forbidden) errors surface as clear exceptions.
- Timeouts: Ensure network timeouts result in explicit failures rather than hanging or skipping data.
- Unknown Models: Confirm that requesting an unmapped model name raises a
ValueErroror similar specific exception.
Sources: dev/guidelines/testing-adapters.md:67-73, dev/guides/testing-an-adapter.md:88-91
Implementation Procedure
Follow these steps to implement tests for a new adapter:
- Create Test Module: Add a file at
tests/adapters/test_<name>.py. - Mock Upstream SDK: Import the adapter and replace its client with a
MagicMock. - Define Fixtures: Create sample records and a
SyncConfigfixture with a validschema_mapping. - Implement Unit Tests: Add test functions for conversion, filtering, and cursors.
- Run Offline Suite: Execute
uv run pytest -q tests/adapters/test_<name>.py. - Verify Quality: Ensure
uv run invoke lintpasses for the new test files.
Sources: dev/guides/testing-an-adapter.md:28-36, dev/guides/testing-an-adapter.md:92-93, AGENTS.md:58-60
# Example: Mocking a client in a test fixture
from unittest.mock import MagicMock
from infrahub_sync.adapters.mysystem import MysystemAdapter
def test_loader_maps_fields(adapter, device_model):
# Mock the API return value
adapter.client.get.return_value = [{"name": "rtr1", "device_type": "qfx"}]
adapter.model_loader("InfraDevice", device_model)
# Assert result in store
obj = adapter.get(device_model, "rtr1")
assert obj.type == "qfx"
Sources: dev/guides/testing-an-adapter.md:44-55, dev/guides/testing-an-adapter.md:63-68
Summary
Adapter testing is a mandatory component of the development lifecycle designed to protect the integrity of infrastructure data. By strictly enforcing mocking, atomic testing, and comprehensive error handling, the project ensures that synchronization remains idempotent and safe across diverse external systems.
Sources: dev/constitution.md:12-16, dev/guidelines/testing-adapters.md:11-14