Skip to content

feat(standard-tests): read incremental streams from a far-future state - #1265

Merged
Anatolii Yatsuk (tolik0) merged 16 commits into
mainfrom
tolik0/cdk/future-state-read
Nov 5, 2026
Merged

Anatolii Yatsuk (tolik0) merged 16 commits into
mainfrom
tolik0/cdk/future-state-read

Conversation

@tolik0

@tolik0 Anatolii Yatsuk (tolik0) commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

What

Adds test_read_from_future_state to the source standard tests, with a Docker twin, test_docker_image_build_and_read_from_future_state. It replaces CAT's test_state_with_abnormally_large_values (airbytehq/airbyte-internal-issues#18407, P2.2a).

Every incremental entry of acceptance-test-config.yml with a future_state.future_state_path becomes a scenario of its own, with the test id '<config>' Test Scenario future state <state file stem>, for example 'config' Test Scenario future state abnormal_state. The test:

  • loads the state file. It must be a non-empty list of STREAM or GLOBAL state messages; a legacy {stream: state} mapping or an untyped message fails, naming the file and the message index.
  • reads the entry's incremental streams. These are the streams of its configured_catalog_path (or every discovered stream when it has none) that discover reports as supporting incremental sync, whatever sync_mode the catalog gives them. The read leaves out the entry's own empty_streams and its future_state.missing_streams, and reads each stream in incremental mode with the catalog's cursor field or its default one.
  • fails per stream when a stream has no state in the file, returns records, or emits no state; it also fails when the read fails, or when missing_streams leaves no incremental stream to read. Failure messages name the state file, streams and fields, never record or state values, because CI logs are public.
  • checks the discover and read output for the config's airbyte_secret values, as test_check does, before any assertion prints the connector's messages. The read's STATE messages are left out (design choices below). The Docker twin runs the same check before raising the connector's errors.
  • for a declarative source, checks every cursor value of the state file and of the emitted state against the cursor's declared format: the value must parse with its datetime_format alone. The source for this check is built with the read's state and catalog, as the read builds it, so a StateDelegatingStream is checked against its incremental cursor. A stream whose cursor declares no datetime format (an IncrementingCountCursor, for example) is named in a CursorFormatWarning. The check fails if the streams cannot be built.
  • fails a declarative source whose suite builds it without the read's state (a suite that derives from the generic SourceTestSuiteBase without overriding create_connector), instead of reporting records it read from the start.

Other changes:

  • Input state: run_test_job takes an input state for read and passes it with --state, both in process and in Docker.

  • Declarative sources: DeclarativeSourceTestSuite builds the source with the read's state and catalog, as source-declarative-manifest does. ConcurrentDeclarativeSource.read ignores its state argument.

  • Keys: future_state: {future_state_path, missing_streams, bypass_reason} is read in incremental entries. These fail collection:

    • a flat future_state_path;
    • future_state outside incremental;
    • cursor_format and any other unknown key.

    skip_comprehensive_incremental_tests and client_container_config are accepted and not read.

  • Bypasses: a bypass of incremental is reported as a skipped test. It does not satisfy the "no runnable scenario" collection guard, because ScenarioBypass.explains_no_scenarios is false for an incremental-only bypass.

  • Docker twin: test_docker_image_build_and_read_from_future_state is marked image_tests, so airbyte-cdk image test runs it. That is the command CI runs for a Python connector whose locked CDK predates the in-process standard tests. Like test_docker_image_build_and_read, it is off unless --read-from-streams is set, and it has no cursor format check. --read-scenarios default and all read every future-state entry; a list reads the entries of the configs it names, and an unknown name fails. --read-from-streams narrows the streams before the state coverage check, so the state only has to cover the streams read.

Design choices

  • The entry keeps only its own empty_streams. A stream that basic_read lists as empty is still read, because it must parse its state and emit state, as in CAT. Inheriting the basic_read empty streams left amazon-seller-partner, instagram and paypal-transaction with nothing to read.
  • Every stream that discover reports as supporting incremental is read, not only those the test catalog configures incremental. A catalog that reads a stream as full_refresh does not exempt the stream from honouring state. This is stricter than CAT, but in the dry run it changed only source-insightly (now read, and passing) and source-rss (now read, and failing on a pre-existing config error).
  • Strict from the start: there is no warn-only period. The state files that fail are fixed in airbyte before this is released (merge order below).
  • The cursor format check reads the state file, and parses instead of round-tripping. A CDK cursor parses a value in any of its cursor_datetime_formats and writes its state back in its own format, so the read passes and the emitted state is in format even when the file is not: only the file shows the problem. A value is in format when datetime_format alone parses it. A round trip (output_format(parse(value)) == value) flagged 31 connectors in the dry run on spellings the format accepts: Z for +0000, an integer for %s, three fraction digits for six. The emitted state gets the same parse check, so a stream that echoes its input state does not fail on a spelling its file passed with.
  • The read's STATE messages are not scanned for secrets. The platform stores state and never logs it, and the test prints no state. source-twilio partitions by account_sid, which its spec marks airbyte_secret and its records carry too, so scanning state failed it with nothing leaked. LOG, TRACE, CONNECTION_STATUS, RECORD and stderr are still scanned. find_leaked_secrets and assert_no_secrets_in_output take a skip_message_types argument for this; their other callers are unchanged.

Fleet dry run

The fleet is every connector in airbytehq/airbyte with a future_state_path: 83 live sources with 101 entries. Not counted: 11 connectors still on the legacy top-level tests: layout, which already fail collection the same way on main, and 4 archived connectors. It ran certified connectors first, then the rest. Each entry ran in process on its GSM test config:

  • manifest-only connectors on this branch;
  • Python connectors on their locked dependencies plus this branch's wheel, which is what a CDK bump does.

Excluded from the live runs:

  • 10 connectors with single-use refresh tokens, whose GSM secret must not be used. Their files were checked statically.
  • source-mysql, a Java connector that doesn't run these tests.

Airbyte master 31d1fa65bf6, this branch at 413c7268:

entries pass fail
master's state files 89 52 37
with airbytehq/airbyte#90877 and airbytehq/airbyte#90878 89 73 16

Failures on master's state files, by cause:

Connector Result Cause Read time Fixed in
source-freshdesk (certified) fail no state for ticket_activities 2.8 s airbytehq/airbyte#90877
source-shopify (certified) fail no state for deleted_products 21.3 s airbytehq/airbyte#90877
source-woocommerce (certified) fail no state for customers 1.7 s airbytehq/airbyte#90877
source-instagram (certified) fail state value not in the declared format; read fails parsing it 0.4 s airbytehq/airbyte#90877
source-google-search-console (certified) fail old state shape; 3 keyword report streams return records 144.9 s airbytehq/airbyte#90877
source-gcs config_glob (certified) fail newer bucket files missing from history 53.5 s airbytehq/airbyte#90877
source-notion (certified) fail catalog lists databases, no longer discovered 0.4 s airbytehq/airbyte#90877
source-avni, close-com, dixa, exchange-rates, freshservice, nytimes, plaid, youtube-analytics fail legacy mapping 0.2–15.2 s airbytehq/airbyte#90878
source-datascope, partnerstack fail flat future_state_path fails collection (also legacy mappings) collection airbytehq/airbyte#90878
source-ramp fail no state for vendors 3.6 s airbytehq/airbyte#90878
source-posthog (2 files) fail test project has no state; old-style file had a 2021 timestamp 163.8 s, 216.4 s airbytehq/airbyte#90878
source-railz fail old state shape; 12 streams return records 564.1 s airbytehq/airbyte#90878
source-microsoft-sharepoint fail newer site files missing from history 138.5 s airbytehq/airbyte#90878
source-greenhouse (static) fail 27 catalog streams without state — airbytehq/airbyte#90878
source-outreach (static) fail legacy mapping — airbytehq/airbyte#90878
source-trustpilot (static) fail untyped messages — airbytehq/airbyte#90878
source-appsflyer, braintree, cart, kyriba, rss fail legacy mapping (converted in #90878), plus a pre-existing failure (below) 0.1–2.4 s partly

These still fail with both airbyte PRs merged. Each fails test_basic_read with the same error on the same config, so the failures predate this change:

Connector Error
source-appsflyer, source-kyriba, source-zenloop 401
source-azure-table ConnectorStateManager.__init__() got an unexpected keyword argument 'stream_instance_map'
source-braintree pydantic v2: resolve_annotations removed
source-braze imports DatetimeBasedCursor, which the CDK removed
source-cart 'ConnectionError' object has no attribute 'headers'
source-iterable time data '' does not match format
source-marketo, source-surveymonkey config key missing (domain_url, access_token)
source-microsoft-onedrive pydantic v2: const removed
source-recharge manifest fails schema validation
source-rss empty URL in the test config
source-slack (certified) test token lacks the channels:write scope
source-trello 'DefaultStream' object has no attribute 'read_records'

source-partnerstack has no GSM secret, so it was not run. Its fixed file loads and covers its catalog.

Of these, azure-table, braintree, iterable, kyriba, marketo, microsoft-onedrive, recharge, rss, surveymonkey and zenloop lock a CDK older than the in-process standard tests, so CI does not run them in process: it runs the Docker read below.

Docker dry run

CI runs airbyte-cdk image test with --read-from-streams=default for a Python connector whose locked CDK predates the in-process standard tests. 10 such connectors have a future state. I ran that command with this branch's CLI (8861764d) on master's published image of each, on its GSM test config:

Connector Locked CDK master's state file with airbytehq/airbyte#90878
source-iterable, recharge, zenloop 3.9.6, 4.6.2, 1.3.0 pass pass
source-braintree, rss 1.8.0, 0.88.1 fail: legacy mapping pass
source-braintree also fixed: dispute_stream state written as 2222-01-01 00:00:00, but the cursor format is %Y-%m-%d
source-azure-table 0.80.0 fail pass: adds state for campaigns
source-marketo 2.3.0 fail: 2025 cursors return records pass: far-future cursors
source-surveymonkey 2.4.0 fail: survey_responses returns 9 records pass: 2050 cursors (the API answers 400 to a start after about 2069)
source-kyriba 0.80.0 fail: 401 at the token URL same; test_docker_image_build_and_read fails the same way
source-microsoft-onedrive 1.8.0 fail: discover "Error inferring schema from files" same; test_docker_image_build_and_read fails the same way

recharge's test_docker_image_build_and_read fails a schema check on master; its future-state read passes. source-zoho-crm now locks CDK 7.45.1, so it runs in process.

Bypassed entries. 13 connectors bypassed the CAT test as "This test does not make sense using Concurrent CDK", so they are outside the 89 entries above. The reason no longer holds for this test, which checks that no records come back and that every stream emits state. airbytehq/airbyte#90877 removes the 12 certified bypasses (13 entries) and fixes their files: 12 entries pass. source-tiktok-marketing's test credentials get a 403 that test_basic_read also gets. source-mixpanel keeps its bypass: it locks CDK 6.45.0 and its components do not load on CDK 7.

Catalogs that narrowed the read. An entry without a catalog reads every stream that discover reports as incremental. airbytehq/airbyte#90981 (stacked on #90877) removes the catalog from nine certified connectors' entries and adds state for the streams the catalogs left out: 69 more streams, about 23 s more CI time, all 9 entries pass. #90878 does the same for source-ramp. klaviyo, salesforce, orb, twitter, tiktok-marketing and snapchat-marketing keep their catalogs: the extra streams are slow, fail or are not readable on the test account.

Cursor format of the state files

The format check on the state files alone, without a read, for every manifest-only connector with a future state, at airbyte 365ca9b0dbd (#90981's head): 71 entries, 56 pass. Nine connectors have values in another format:

Connector Streams Values in the file
source-amazon-seller-partner 1 +00:00 offset, format has a literal Z
source-amplitude 1 ISO 8601, format is %Y%m%dT%H
source-jira 4 milliseconds the format does not have
source-klaviyo 2 space between date and time
source-linkedin-ads 3 a date, and two epoch-millisecond integers
source-mailchimp 6 +00:00 and Z offsets, format has microseconds
source-tiktok-marketing 24 date and time, format is a date; T separator in modify_time
source-twilio (2 entries) 6 T…Z, format is a date or uses a space
source-xero 14 +00:00 offset, format has none

The other six entries can't run in a dry run: gitlab, quickbooks and uptick rotate a single-use refresh token, so their GSM secrets were not used; partnerstack has no test secret; braze's components.py fails to import on CDK 7. The full run below found source-okta too, a Python connector that pins CDK 7.45.0.

airbytehq/airbyte#91059 rewrites those values in each cursor's own format. On its head (merged with master after #90981), the dry run passes all 66 runnable entries, and the future-state test passes for all ten connectors except tiktok-marketing, which fails on its 403 (below).

Full run at this head

All 82 connectors that can run, in process, at airbyte 8cb574c1352 (master with #91059's first commit, before #90981 merged), this branch at 2a69934b: 100 entries, 80 pass. The 20 failures:

  • 16 entries fail as in the pre-existing table above. iterable, marketo, rss, surveymonkey and zenloop now stop earlier, at the guard for a declarative source built without the read's state: their auto-generated suite builds it with the generic create_connector. They lock a CDK older than the in-process suite, so CI runs their Docker test instead.
  • source-slack's read now gets past the token-scope error: channel_messages returns 216 records, because the test workspace has channels the state file has no partition for, and the state has no global state to fall back on. slack already fails test_basic_read in CI (deps(source-slack): 🐙 update dependencies [2026-11-10] airbyte#88808), so this adds no new red check; its state file is a follow-up.
  • source-okta: format, fixed in #91059.
  • source-twilio (2 entries): account_sid in the emitted state partitions. Both pass at 0e09847f, which leaves STATE out of the secret scan.
  • source-tiktok-marketing: its 403 error trace prints the app secret, a real leak. Its test_basic_read already fails on the same 403.

CI cost (measured)

The test adds one read per future-state entry. With the airbyte fixes, the 73 passing entries take 515 s in total: median 1.9 s, p90 13.7 s, max 115.8 s (source-microsoft-sharepoint, which spends the time listing the test site's files). The next slowest are gcs config_glob (46.8 s), google-analytics-data-api (44.6 s) and zoho-crm (32.9 s). Stale state files made reads slow before the fix: railz took 564 s and posthog 164 s and 216 s. The 12 un-bypassed entries add 350 s, most of it snapchat-marketing's daily entry (271.3 s, now the slowest; it read 66 records per stream before its file was fixed).

Tests

unit_tests/test/test_future_state_read.py adds 80 test cases (59 test functions). They cover:

  • collection: own scenario, test ids, keys, bypasses;
  • run_test_job state;
  • load_future_state errors, which print no values;
  • catalog selection and coverage;
  • in-process pass and fail cases;
  • a declarative end-to-end read with requests_mock that makes 0 requests;
  • the cursor format check;
  • the Docker --state mount;
  • the Docker twin: its image_tests mark, scenario selection with --read-scenarios default, all, a list and an unknown name, and stream narrowing before the coverage check;
  • the cursor format check on GLOBAL and per-partition states, its source config, and a source whose streams fail to build;
  • a catalog that missing_streams empties;
  • a secret in the discover or read output, in process and in Docker, and a secret in a STATE message, which passes;
  • the cursor format of the state file: a value only cursor_datetime_formats parse, Z and integer spellings the format accepts, a StateDelegatingStream checked with the read's state, and an IncrementingCountCursor stream reported as unchecked;
  • a declarative source on the generic suite.

test_scenario_sections.py, test_scenario_keys.py and test_empty_scenarios.py cover the new section. The Docker section test stubs _run_docker_spec, so it passes when run alone, without a spec another test left in the session-wide cache.

Mutations

Each guard was reverted or flipped on a scratch copy, and the standard-test unit tests were run against it. All 31 mutations of the first commits fail at least one test:

Mutation First failing test
guard: incremental bypass explains empty test_collection_fails_when_no_scenario_and_no_bypass
explains_no_scenarios always true test_zero_scenarios_without_bypass_lists_why_each_part_produced_nothing
incremental entry collected without future state test_future_state_bypass_reason_is_a_bypass_of_the_incremental_section
future_state.bypass_reason ignored test_future_state_bypass_reason_is_a_bypass_of_the_incremental_section
future_state accepted outside incremental test_misplaced_future_state_keys_are_rejected[future_state_outside_incremental]
flat future_state_path message dropped test_misplaced_future_state_keys_are_rejected[flat_future_state_path]
legacy incremental keys rejected test_future_state_fields_are_read
incremental not mapped to a command test_incremental_entry_is_its_own_scenario_without_the_read_empty_streams
future-state reads merged with config test_incremental_entry_is_its_own_scenario_without_the_read_empty_streams
future-state read inherits the read empty_streams test_incremental_entry_is_its_own_scenario_without_the_read_empty_streams
run_test_job drops --state test_run_test_job_passes_the_input_state_to_read
run_test_job accepts state for other verbs test_run_test_job_rejects_state_outside_read
declarative source not rebuilt with state test_declarative_read_starts_from_the_future_state
legacy dict state accepted test_load_future_state_fails_naming_the_file_not_the_values[legacy_mapping]
empty state list accepted test_load_future_state_fails_naming_the_file_not_the_values[empty_list]
untyped/typeless messages accepted test_load_future_state_fails_naming_the_file_not_the_values[untyped_message]
missing_streams still read test_catalog_reads_every_incremental_stream_but_the_missing_ones
no-incremental-stream failure removed test_catalog_without_an_incremental_stream_fails
coverage check removed test_state_must_cover_every_stream_read
records not checked test_read_fails_per_stream_on_records_and_missing_state
missing state not checked test_read_fails_per_stream_on_records_and_missing_state
cursor format never wrong test_cursor_value_in_another_format_fails_without_printing_it[2999-01-01T00:00:00Z]
cursor format problems not reported test_declarative_read_fails_on_cursor_format_problems
in-process test drops cursor format problems test_declarative_read_fails_on_cursor_format_problems
docker read drops --state test_docker_read_passes_the_state_file
docker read on by default test_docker_read_is_off_by_default
in-process test skip gating removed test_source_suite_runs_each_test_for_its_sections[test_read_from_future_state-spec]
docker test skip gating removed test_docker_suite_runs_each_test_for_its_sections[test_docker_image_build_and_read_from_future_state-spec]
test ids without state stem test_incremental_entry_is_its_own_scenario_without_the_read_empty_streams
future-state read honours the declared full_refresh sync_mode test_catalog_reads_the_incremental_streams_whatever_the_declared_sync_mode
future-state read takes full-refresh-only streams test_catalog_reads_every_incremental_stream_but_the_missing_ones

The Docker run and the later fixes add 18 mutations. 17 fail at least one test:

Mutation First failing test
Docker twin without the image_tests mark test_docker_read_runs_in_image_test_like_the_docker_read
known scenarios are only read configs test_docker_read_reads_every_future_state_entry_by_default[read_scenarios2]
Docker twin skips the unknown-scenario check test_docker_read_fails_on_an_unknown_read_scenario
Docker coverage check before the --read-from-streams narrowing test_docker_read_needs_state_only_for_the_streams_it_reads
Docker narrowing removed test_docker_read_needs_state_only_for_the_streams_it_reads
--read-scenarios none ignored test_docker_read_is_off_with_read_scenarios_none
Docker coverage check dropped test_docker_read_fails_on_a_stream_without_state
cursor format streams built with config={} cursor format source-config test
no try around building the streams cursor format build-failure test
empty-catalog guard before the missing_streams filter test_catalog_whose_incremental_streams_are_all_missing_fails
no guard for a declarative source built without state test_generic_suite_fails_a_declarative_source_built_without_state
that guard on every declarative suite declarative read tests
no secret check on discover test_in_process_read_fails_on_a_secret_in_the_output[discover]
no secret check on read test_in_process_read_fails_on_a_secret_in_the_output[read]
cursor format GLOBAL branch removed GLOBAL state test
_NOT_CURSOR_VALUES emptied parent and partition values test
no recursion into nested states per-partition state test

The 18th, rebuilding the declarative source from the config passed to read instead of the injected config, survives because it is equivalent: ConcurrentDeclarativeSource.configure returns the injected, migrated config, so read already receives it. A components test covers the rebuild's behaviour.

The review fixes add 28 mutations. All fail at least one test:

Mutation A failing test
Docker: no secret check on discover test_docker_read_fails_when_the_connector_prints_a_secret[discover]
Docker: no secret check on read test_docker_read_fails_when_the_connector_prints_a_secret[read]
Docker: read raises before the secret check test_docker_read_fails_when_the_connector_prints_a_secret[read]
Docker: discover raises before the secret check test_docker_read_fails_when_the_connector_prints_a_secret[discover]
Docker: discover errors not raised test_docker_read_still_raises_the_connector_error_without_a_leak[discover]
Docker: read errors not raised test_docker_read_still_raises_the_connector_error_without_a_leak[read]
Docker: a failing spec not skipped test_docker_read_is_skipped_when_spec_fails
no state-file format check test_file_cursor_value_in_another_format_fails_without_printing_it
no emitted-state format check test_emitted_cursor_value_in_another_format_fails_without_printing_it
converter attribute renamed test_cursor_values_in_the_declared_format_pass
unchecked streams never reported test_a_stream_without_a_datetime_cursor_is_unchecked
GLOBAL states not walked test_cursor_format_is_checked_in_a_global_state
declarative suite builds the checked source without state test_declarative_read_checks_a_state_delegating_stream_with_the_read_state
the test checks the source built without state test_declarative_read_checks_a_state_delegating_stream_with_the_read_state
the test passes no state file to the check test_declarative_read_fails_on_a_file_value_the_cursor_parses_in_another_format
no CursorFormatWarning test_declarative_read_reports_cursor_format_problems_and_unchecked_streams
values checked by round trip test_value_that_the_declared_format_parses_passes
file parsed with the cursor_datetime_formats too test_declarative_read_fails_on_a_file_value_the_cursor_parses_in_another_format
emitted state checked by round trip test_value_that_the_declared_format_parses_passes
find_leaked_secrets ignores skip_message_types test_find_leaked_secrets_skips_the_message_types_it_is_given
assert_no_secrets_in_output drops it test_in_process_read_does_not_scan_state_messages_for_secrets
the suite's secret helper drops it test_in_process_read_does_not_scan_state_messages_for_secrets
in-process read passes no skip test_in_process_read_does_not_scan_state_messages_for_secrets
Docker read passes no skip test_docker_read_does_not_scan_state_messages_for_secrets
nothing skipped test_in_process_read_does_not_scan_state_messages_for_secrets
RECORD skipped too test_in_process_read_fails_on_a_secret_in_the_output[read]
TRACE skipped too test_docker_read_fails_when_the_connector_prints_a_secret[read]
section test: no _run_docker_spec stub, file run alone test_docker_suite_runs_each_test_for_its_sections[test_docker_image_build_and_read_from_future_state-incremental]

Merge order

  1. Merged: test(connectors): fix certified connectors' future states and stop bypassing the test airbyte#90877 (certified) and test(connectors): write community connectors' future states in the shape they emit airbyte#90878 (community), which fix the state files; test(connectors): read every incremental stream from the future state airbyte#90981, which removes the narrowing catalogs; chore(source-facebook-marketing): update CDK to 7.45 and use the generated standard tests airbyte#90974 (facebook-marketing) and test(source-shopify): read every stream in the generated basic_read test airbyte#90975 (shopify), which remove each connector's own standard-test suite, whose test_basic_read override would also run on the future-state scenario.
  2. test(connectors): write future-state cursor values in each cursor's datetime_format airbyte#91059 (draft): writes the future-state cursor values of ten connectors in each cursor's format. Without it, nine manifest-only connectors turn red on release.
  3. This PR, released as a minor version. The monorepo installs the airbyte-cdk CLI unpinned, so on release manifest-only connectors get the in-process test, and Python connectors whose locked CDK predates the in-process suite get the Docker test through the CI fallback. Other Python connectors get the in-process test when they bump the CDK.

CI on this PR

On 8861764d, Check: source-shopify failed on the missing deleted_products state that airbytehq/airbyte#90877 has since fixed, and Check: destination-motherduck failed on infrastructure (ConnectorSecretWithNoValidVersionsError), as on other open CDK PRs. Both re-run on the current head.

cdk-migrations.md has the entry under the unreleased section.

git merge-tree against the open CDK PRs that touch the same files, counting only conflicts that this branch adds:

🤖 Generated with Claude Code

Port CAT's test_state_with_abnormally_large_values. An incremental entry
with future_state.future_state_path becomes its own scenario; the new
test reads its incremental streams from that state and fails when a
stream returns records, emits no state, has no state in the file, or
(declarative) emits a cursor outside its declared format.
…e state

A read from a future state expects no records, so a stream without data
still shows whether the connector parses its state and emits state. The
future-state scenario keeps only its own empty_streams.
… future state

A test catalog that configures a stream full_refresh no longer exempts it from the
future-state read: every stream of the entry that discover reports as supporting
incremental sync is read in incremental mode.
@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown

👋 Greetings, Airbyte Team Member!

Here are some helpful tips and reminders for your convenience.

💡 Show Tips and Tricks

Testing This CDK Version

You can test this version of the CDK using the following:

# Run the CLI from this branch:
uvx 'git+https://github.com/airbytehq/airbyte-python-cdk.git@tolik0/cdk/future-state-read#egg=airbyte-python-cdk[dev]' --help

# Update a connector to use the CDK from this branch ref:
cd airbyte-integrations/connectors/source-example
poe use-cdk-branch tolik0/cdk/future-state-read

PR Slash Commands

Airbyte Maintainers can execute the following slash commands on your PR:

  • /autofix - Fixes most formatting and linting issues
  • /poetry-lock - Updates poetry.lock file
  • /test - Runs connector tests with the updated CDK
  • /prerelease - Triggers a prerelease publish with default arguments
  • /poe build - Regenerate git-committed build artifacts, such as the pydantic models which are generated from the manifest JSON schema in YAML.
  • /poe <command> - Runs any poe command in the CDK environment
📚 Show Repo Guidance

Helpful Resources

📝 Edit this welcome message.

@github-actions

github-actions Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

PyTest Results (Fast)

6 434 tests  +126   6 397 ✅ +111   6m 21s ⏱️ -24s
    1 suites ±  0      37 💤 + 15 
    1 files   ±  0       0 ❌ ±  0 

Results for commit 21e5dbb. ± Comparison against base commit 4f921fe.

This pull request removes 1 and adds 127 tests. Note that renamed tests count towards both.
unit_tests.test.test_scenario_sections ‑ test_unknown_section_is_rejected[incremental]
unit_tests.resources.source_pokeapi_w_components_py.integration_tests.test_airbyte_standards.TestSuiteSourcePokeAPI ‑ test_basic_read[bypassed-incremental]
unit_tests.resources.source_pokeapi_w_components_py.integration_tests.test_airbyte_standards.TestSuiteSourcePokeAPI ‑ test_check[bypassed-incremental]
unit_tests.resources.source_pokeapi_w_components_py.integration_tests.test_airbyte_standards.TestSuiteSourcePokeAPI ‑ test_config_matches_spec[bypassed-incremental]
unit_tests.resources.source_pokeapi_w_components_py.integration_tests.test_airbyte_standards.TestSuiteSourcePokeAPI ‑ test_discover[bypassed-incremental]
unit_tests.resources.source_pokeapi_w_components_py.integration_tests.test_airbyte_standards.TestSuiteSourcePokeAPI ‑ test_docker_image_build_and_check[bypassed-incremental]
unit_tests.resources.source_pokeapi_w_components_py.integration_tests.test_airbyte_standards.TestSuiteSourcePokeAPI ‑ test_docker_image_build_and_config_matches_spec[bypassed-incremental]
unit_tests.resources.source_pokeapi_w_components_py.integration_tests.test_airbyte_standards.TestSuiteSourcePokeAPI ‑ test_docker_image_build_and_discover[bypassed-incremental]
unit_tests.resources.source_pokeapi_w_components_py.integration_tests.test_airbyte_standards.TestSuiteSourcePokeAPI ‑ test_docker_image_build_and_read[bypassed-incremental]
unit_tests.resources.source_pokeapi_w_components_py.integration_tests.test_airbyte_standards.TestSuiteSourcePokeAPI ‑ test_docker_image_build_and_read_from_future_state['invalid_config' Test Scenario]
unit_tests.resources.source_pokeapi_w_components_py.integration_tests.test_airbyte_standards.TestSuiteSourcePokeAPI ‑ test_docker_image_build_and_read_from_future_state['valid_config' Test Scenario]
…

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

PyTest Results (Full)

6 437 tests  +126   6 401 ✅ +112   11m 17s ⏱️ - 1m 4s
    1 suites ±  0      36 💤 + 14 
    1 files   ±  0       0 ❌ ±  0 

Results for commit 21e5dbb. ± Comparison against base commit 4f921fe.

This pull request removes 1 and adds 127 tests. Note that renamed tests count towards both.
unit_tests.test.test_scenario_sections ‑ test_unknown_section_is_rejected[incremental]
unit_tests.resources.source_pokeapi_w_components_py.integration_tests.test_airbyte_standards.TestSuiteSourcePokeAPI ‑ test_basic_read[bypassed-incremental]
unit_tests.resources.source_pokeapi_w_components_py.integration_tests.test_airbyte_standards.TestSuiteSourcePokeAPI ‑ test_check[bypassed-incremental]
unit_tests.resources.source_pokeapi_w_components_py.integration_tests.test_airbyte_standards.TestSuiteSourcePokeAPI ‑ test_config_matches_spec[bypassed-incremental]
unit_tests.resources.source_pokeapi_w_components_py.integration_tests.test_airbyte_standards.TestSuiteSourcePokeAPI ‑ test_discover[bypassed-incremental]
unit_tests.resources.source_pokeapi_w_components_py.integration_tests.test_airbyte_standards.TestSuiteSourcePokeAPI ‑ test_docker_image_build_and_check[bypassed-incremental]
unit_tests.resources.source_pokeapi_w_components_py.integration_tests.test_airbyte_standards.TestSuiteSourcePokeAPI ‑ test_docker_image_build_and_config_matches_spec[bypassed-incremental]
unit_tests.resources.source_pokeapi_w_components_py.integration_tests.test_airbyte_standards.TestSuiteSourcePokeAPI ‑ test_docker_image_build_and_discover[bypassed-incremental]
unit_tests.resources.source_pokeapi_w_components_py.integration_tests.test_airbyte_standards.TestSuiteSourcePokeAPI ‑ test_docker_image_build_and_read[bypassed-incremental]
unit_tests.resources.source_pokeapi_w_components_py.integration_tests.test_airbyte_standards.TestSuiteSourcePokeAPI ‑ test_docker_image_build_and_read_from_future_state['invalid_config' Test Scenario]
unit_tests.resources.source_pokeapi_w_components_py.integration_tests.test_airbyte_standards.TestSuiteSourcePokeAPI ‑ test_docker_image_build_and_read_from_future_state['valid_config' Test Scenario]
…

♻️ This comment has been updated with latest results.

…k image test`

Mark test_docker_image_build_and_read_from_future_state with image_tests and the
docker skipif, as its sibling Docker read. Connector CI reads Python connectors
whose pinned CDK predates the in-process suite through `airbyte-cdk image test`,
which selects only image_tests, so the future-state read never ran there.

A --read-scenarios list is now checked against the read and future-state configs
in both Docker reads, so a typo fails and a name only one of them reads is not an
error. --read-from-streams narrows the catalog before the state file's coverage
is checked, so the state must cover only the streams read.
…config, and fail a future-state catalog that missing_streams empties

find_cursor_format_problems called streams(config={}), which a Python
declarative source's streams override may read; it now passes the source's own
config, and reports a failure to build the streams as one more problem (naming
the exception type only) instead of raising over the read's verdict.

build_future_state_catalog checked for an incremental stream before leaving out
future_state.missing_streams, so an entry listing every stream read an empty
catalog and passed. The check now runs on the streams left to read.
…ail a declarative source built without the read's state

test_read_from_future_state now passes the secret-leak check to its discover
and read, as test_basic_read does, so a leaked secret fails before an
assertion prints raw messages.

A suite that builds a ConcurrentDeclarativeSource with the inherited
create_connector builds it with no state, and the source reads only the state
it was built with. The test now fails naming the fix (DeclarativeSourceTestSuite
or a create_connector override) instead of reporting that the connector
ignored its state.
… Docker branches

Add tests for the GLOBAL state branch of the cursor format check, its
recursion into per-partition states, the parent_state and partition keys it
skips, a declarative source rebuilt with components.py, and the Docker
future-state read's --read-scenarios=none skip, empty stream-list skip and
coverage check.

Correct the comment on the rebuilt source: the read's config is the first
source's config after its migrations (ConcurrentDeclarativeSource.configure
returns it), not a config without the manifest.

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.

Approving. This changes only the test harness, so it isn't breaking and a minor release fits (same as 7.42.0). I ran the base and head collectors over every acceptance-test-config.yml on airbyte master. Only source-datascope and source-partnerstack newly fail collection, and #90878 fixes both. Existing scenarios and test IDs come out identical.

Release note: please hold the CDK release until airbytehq/airbyte#90877 and #90878 merge. Without them, those two manifest-only suites fail collection, and the unpinned CLI turns many future-state entries red.

Two non-blocking comments are inline.

…nst the declared format, with the source built from the read's state

The cursor format check of test_read_from_future_state now checks the state file as well as
the emitted state. A value is in the format when the cursor's datetime_format alone parses it,
so a file value that only one of the cursor's cursor_datetime_formats parses fails, although the
read passes. A value the format parses but spells differently passes on both sides.

The check builds its source with the read's catalog and state, as the read does, so a
StateDelegatingStream is checked against its incremental cursor. Streams whose cursor declares no
datetime format are named in a CursorFormatWarning.
… of the secret scan

The platform stores state and never logs it, and the test does not print it. A partition can
hold an airbyte_secret value that is also API data: source-twilio partitions by account_sid,
which its records carry too, so its future-state read failed the scan. Logs, traces,
connection statuses, records and stderr are still scanned. find_leaked_secrets and
assert_no_secrets_in_output take a skip_message_types argument for this; other callers are
unchanged.

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.

Round 2 at 6b4eade3: both round-1 comments are fixed (state-file format check, Docker secret scan). One test-only issue below. Separately, airbytehq/airbyte#91059 still needs to merge before release.

@tolik0
Anatolii Yatsuk (tolik0) marked this pull request as ready for review November 5, 2026 15:00
@tolik0
Anatolii Yatsuk (tolik0) requested a review from a team as a code owner November 5, 2026 15:00
@tolik0
Anatolii Yatsuk (tolik0) merged commit a0d630a into main Nov 5, 2026
28 of 29 checks passed
@tolik0
Anatolii Yatsuk (tolik0) deleted the tolik0/cdk/future-state-read branch November 5, 2026 15:02
Anatolii Yatsuk (tolik0) added a commit that referenced this pull request Nov 9, 2026
Keep both sides in `_spec_lint.py`: RECORD messages are skipped by default, and
`skip_message_types` (from #1265) skips more. #1265's read-leak test leaked through
a record, which is no longer searched, so it now leaks through an error trace.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants