Repository navigation
Remove, merge, and rewrite duplicate tests in the asyncio and Spark tests #1245
Description
Summary
Part of #1239. Files: aio/test_connection.py, aio/test_cursor.py, aio/test_result_set.py, aio/test_util.py, aio/arrow/test_cursor.py, aio/polars/test_cursor.py, aio/s3fs/test_cursor.py, aio/spark/test_cursor.py, spark/test_async_cursor.py, spark/test_spark_cursor.py, spark/test_common.py (aio/test_common.py is all KEEP). At master 59169c5 with the cross-check corrections: 224 test functions: 102 KEEP, 65 REMOVE, 29 MERGE, 28 REWRITE. One REMOVE (aio/test_util.py::test_async_retry_api_call_stop_when) is deleted by #1238 and is not listed, so the tables hold 64 REMOVE. Saved per run: about 85 Athena queries (about 30 of them Iceberg DDL/DML from the executemany_table fixture, and 2 UNLOADs), 4 Spark sessions and about 8 calculations (with one S3 Parquet write and one Glue table), and 1 live Glue request.
Paths are relative to tests/pyathena/. [original] marks a test that existed at 21cbf05 (v3.15.0).
Order
- After Break result sets after failed or cancelled fetches and drop the cross-thread guards #1238. It deletes
aio/test_util.py::test_async_retry_api_call_stop_whenand thestop_whenparameter, and the KEEP testsaio/test_result_set.py::TestAthenaAioResultSet::test_fetch_counts_rows_taken_before_error,test_fetch_with_close_during_request,test_fetch_with_close_during_retries. It addsaio/test_result_set.py::TestAthenaAioResultSet::test_fetch_after_failed_fetch,test_fetch_after_cancelled_fetch,aio/test_common.py::TestWithAsyncFetch::test_cancelled_fetch_detaches_result_setand fouraio/test_util.py::test_run_in_thread_or_close*tests. Rows inaio/test_result_set.py,aio/test_util.pyand the newaio/test_common.pytest are applied on top of it. - With or after the top-level sub-issue, whose MERGE/REWRITE targets are the covers here:
aio/test_connection.py::test_empty_work_group_disables_environment_fallback→test_connection.py::test_environment_fallback(its MERGE keeps theConnectionwork_group=""case).- The aio keyword-only and callback tests merge into the rewritten
test_connection.py::test_connect_keyword_only,test_cursor_constructor_keyword_only,test_cursor_execute_keyword_onlyandtest_cursor_accepts_connection_callbacks. aio/test_cursor.py::TestAioCursor::test_binary_parameter→test_cursor.py::TestCursor::test_binary_parameter(REWRITE to one query).- The aio arraysize tests merge into
test_connection.py::TestConnection::test_cursor_arraysize*. The top-level sub-issue keepsTestConnection::test_cursor_arraysize_over_page_sizefor this merge, and the merged test must keep theAioCursor/AioDictCursorcases, becauseAioCursorhas its ownarraysizesetter (pyathena/aio/cursor.py:96) and this sub-issue removesTestAioCursor::test_invalid_arraysize.
- The pandas sub-issue depends on rows here: its
aio/pandas/test_cursor.py::TestAioPandasCursor::test_invalid_arraysizeremoval needs the arraysize MERGE in section 1 (which addsAioPandasCursorto the syncUNCAPPED_CURSORS), and itstest_cancel_initialremoval needs the newaio/test_common.py::TestWithAsyncFetch::test_cancel_without_queryfrom section 2. - Within this sub-issue: the
test_cancel_initialcopies are removed only aftertest_cancel_without_queryexists;TestAioCursor::test_executemanyandtest_executemany_fetchland with thetest_executemany_rowcountREWRITE that keeps[update];spark/test_common.py::TestSparkBaseCursor::test_wait_for_idle_session_raises_on_failure_statelands with the REWRITE oftest_init_terminates_new_session_in_failure_state, which keeps the three states, the exception type and the termination.
Findings
1. aio/test_connection.py
| test | change | criteria | reason | covered by |
|---|---|---|---|---|
test_empty_work_group_disables_environment_fallback |
REMOVE | 2 | aio_connect/AioConnection.create forward kwargs to Connection.__init__, where the environment fallback lives. |
test_connection.py::test_environment_fallback (Connection, work_group="") |
test_connect_keyword_only, test_connection_constructor_keyword_only |
REMOVE | 2, 6 | aio_connect(**kwargs), create(cls, **kwargs) and AioConnection.__init__(self, **kwargs) are keyword-only by their signatures and forward to Connection. |
test_connection.py::test_connect_keyword_only (rewritten) |
test_cursor_constructor_keyword_only, test_cursor_execute_keyword_only, test_cursor_unknown_constructor_keyword |
MERGE | 2 | Line-for-line copies of the sync tests that need no await; add the 7 aio classes to the sync CURSOR_CLASSES. |
into test_connection.py::test_cursor_constructor_keyword_only, test_cursor_execute_keyword_only (rewritten), test_cursor_unknown_constructor_keyword |
test_cursor_accepts_connection_callbacks |
MERGE | 1, 2 | Copy of the sync test that asserts the private _on_start_query_execution/_on_poll; merge into the sync parametrization and close the cursor explicitly. |
into test_connection.py::test_cursor_accepts_connection_callbacks (rewritten) |
TestAioConnection::test_cursor_arguments_override_cursor_kwargs |
REMOVE | 2 | Connection.cursor() is inherited unchanged; the sync test checks the same merge with more keys. |
test_connection.py::TestConnection::test_cursor_arguments_override_cursor_kwargs |
TestAioConnection::test_cursor_arraysize, test_cursor_arraysize_over_page_size, test_cursor_arraysize_uncapped, test_cursor_arraysize_not_positive |
MERGE | 2 | Same bodies as the sync tests; add AioCursor/AioDictCursor to the sync PAGED_CURSORS and AioArrowCursor, AioPandasCursor, AioPolarsCursor, AioS3FSCursor to the sync UNCAPPED_CURSORS. |
into test_connection.py::TestConnection::test_cursor_arraysize, test_cursor_arraysize_over_page_size, test_cursor_arraysize_uncapped, test_cursor_arraysize_not_positive |
TestAioConnection::test_internal_cursor_leaves_out_default_cursor_options |
REMOVE | 1, 2 | The private Connection._internal_cursor() is inherited; only the class names differ from the sync copy. |
test_connection.py::TestConnection::test_internal_cursor_leaves_out_default_cursor_options |
2. aio/test_cursor.py
| test | change | criteria | reason | covered by |
|---|---|---|---|---|
TestAioCursor::test_binary_parameter (7 cases) |
REMOVE | 2, 4 | Binary formatting is the shared formatter, and the sync test has the same 7 cases (7 queries). | test_cursor.py::TestCursor::test_binary_parameter (rewritten to one query) |
TestAioCursor::test_async_iterator |
REMOVE | 2, 4 | __anext__ is WithAsyncFetch's and is tested offline for AioCursor. |
aio/test_common.py::TestWithAsyncFetch::test_async_iteration[AioCursor], TestAioCursor::test_fetchone |
TestAioCursor::test_execute_returns_self |
MERGE | 4 | One extra query for assert result is cursor. |
into TestAioCursor::test_fetchone |
TestAioCursor::test_execute_internal_options_passthrough |
REWRITE | 1 | Patches the private _build_start_query_execution_request/_find_previous_query_id; drive execute() with a mocked client and assert the StartQueryExecution request (WorkGroup, OutputLocation, ResultReuseConfiguration) and the ListQueryExecutions WorkGroup/MaxResults. |
— |
TestAioCursor::test_execute_qmark_parameters_skip_cache |
REWRITE | 1 | The #941 regression for aio code, asserted through patched private methods; call execute("SELECT ?", [...], paramstyle="qmark", cache_size=10) with a mocked client and assert no list_query_executions call. |
— |
TestAioCursor::test_execute_kill_on_interrupt_timeout |
REMOVE | 2 | A wait_for timeout is a task cancellation, whose path and task.cancelled() the cover asserts; the TimeoutError is asyncio's. |
TestAioCursor::test_execute_kill_on_interrupt |
TestAioCursor::test_execute_timeout_while_starting |
REMOVE | 2, 3 | The same start-phase cancellation through wait_for, with sleep(0.1) against a 0.05 s timeout. |
TestAioCursor::test_execute_cancelled_while_starting |
TestAioCursor::test_execute_cancelled_again_while_start_is_retried |
REWRITE | 3, 5 | Proves "no retry" by asyncio.sleep(0.2) and calls == 1; wait for the start worker to finish (an event set when retry_api_call returns) before asserting calls == 1. |
— |
TestAioCursor::test_cache_size_different_schema, test_cache_size_different_catalog, test_cache_search_prefers_latest_execution |
REMOVE | 1, 2 | They exercise _match_previous_query, shared code (pyathena/common.py:1122, called from pyathena/aio/common.py:357); identical sync copies exist. |
test_cursor.py::TestCursor::test_cache_size_different_schema, test_cache_size_different_catalog, test_cache_search_prefers_latest_execution |
TestAioCursor::test_cache_search_stops_at_expired_execution, test_cache_search_reads_pages_up_to_cache_size |
REWRITE | 1 | The page loop is aio code, but the tests patch the private _list_query_executions and match log text; stub list_query_executions/batch_get_query_execution and assert the number and MaxResults of the ListQueryExecutions requests. |
— |
TestAioCursor::test_no_result_set_raises |
MERGE | 2, 4 | The offline test covers the other 4 aio cursors; add AioCursor and AioDictCursor (own fetch overrides) to its parametrization. |
into aio/test_common.py::TestWithAsyncFetch::test_fetch_without_result_set |
TestAioCursor::test_description_initial |
REMOVE | 2, 6 | The description property is the shared WithResultSet's. |
test_cursor.py::TestCursor::test_description_initial [original] |
TestAioCursor::test_query_id |
REMOVE | 2, 4 | Checks the UUID shape of Athena's ID; query_id after execute is asserted by test_fetchone. |
TestAioCursor::test_fetchone, test_cursor.py::TestCursor::test_query_id [original] |
TestAioCursor::test_query_execution_initial |
REMOVE | 2, 6 | 24 shared WithResultSet properties that return None before execute. |
test_cursor.py::TestCursor::test_query_execution_initial [original] |
TestAioCursor::test_cancel_initial |
MERGE | 2, 4 | cancel() without a query is the shared WithAsyncFetch.cancel; write one offline test over _ALL_CURSOR_CLASSES that raises ProgrammingError. |
into the new aio/test_common.py::TestWithAsyncFetch::test_cancel_without_query |
TestAioCursor::test_executemany |
REMOVE | 2, 4 | Rowcount summing is asserted by test_executemany_rowcount[update], and the quoting of jim o'rourke is the shared formatter (6 queries). |
TestAioCursor::test_executemany_rowcount[update], test_cursor.py::TestCursor::test_executemany [original] |
TestAioCursor::test_executemany_rowcount[insert], [delete] |
REWRITE | 2, 4 | Keep only [update]; the per-statement counts come from the shared _process_update_count (about 20 queries). |
test_cursor.py::TestCursor::test_iceberg_table [original], arrow/test_cursor.py::TestArrowCursor::test_iceberg_table [original] |
TestAioCursor::test_executemany_failure[0] |
REWRITE | 2, 4 | Both cases reach the same except branch; keep [1], which also shows earlier executions are not rolled back (about 7 queries). |
TestAioCursor::test_executemany_failure[1], test_cursor.py::TestCursor::test_executemany_failure[0] |
TestAioCursor::test_executemany_parameter_iteration_failure |
MERGE | 2, 4 | The offline test drives the same failure; have its fake execute set query_id and assert rowcount == -1 and the retained query_id. |
into aio/test_common.py::TestWithAsyncFetch::test_executemany_failure_closes_result_set_off_event_loop |
TestAioCursor::test_executemany_fetch |
REMOVE | 2, 4 | After executemany the result set is None, and fetch without one raises; both are asserted offline. |
aio/test_common.py::TestWithAsyncFetch::test_executemany_closes_previous_result_set_off_event_loop, TestAioCursor::test_executemany_rowcount[update] |
TestAioCursor::test_context_manager |
REMOVE | 2, 4 | Same as test_aio_connect (default cursor class under async with). |
TestAioCursor::test_aio_connect |
TestAioCursor::test_open_close |
REMOVE | 2, 6 | Every fixture opens and closes a connection. | TestAioCursor::test_aio_connect |
TestAioCursor::test_arraysize_default |
REMOVE | 6 | Asserts a class constant. | — |
TestAioCursor::test_invalid_arraysize |
REMOVE | 2 | The constructor goes through the same AioCursor.arraysize setter. |
test_connection.py::TestConnection::test_cursor_arraysize_over_page_size[AioCursor], test_cursor_arraysize_not_positive[AioCursor] (after the section 1 merge) |
TestAioCursor::test_glue_request_runs_off_the_event_loop |
REWRITE | 1, 4 | Asserts the internal call order ["request", "client"] against live Glue; keep only "the Glue request does not run on the event loop thread", offline with a stubbed Glue client. |
— |
TestAioCursor::test_list_databases, test_get_table_metadata, test_list_table_metadata |
MERGE | 2, 4 | test_throttled_metadata_reads_glue makes the same three calls; assert "default"/ENV.schema in the databases, name == "one_row", and one_row in the listing on its expected. |
into TestAioCursor::test_throttled_metadata_reads_glue |
TestAioDictCursor::test_fetchmany |
REMOVE | 2, 4 | Dict rows across pages are tested offline, aio paging by TestAioCursor::test_fetchmany. |
aio/test_result_set.py::TestAthenaAioResultSet::test_fetchmany_across_pages[AthenaAioDictResultSet], TestAioCursor::test_fetchmany |
TestAioDictCursor::test_fetchall |
REMOVE | 2, 4 | The dict shape is test_fetchone's, the 10,000-row paging TestAioCursor::test_fetchall's (2 queries). |
TestAioDictCursor::test_fetchone, TestAioCursor::test_fetchall |
TestAioDictCursor::test_dict_type_custom_result_set |
MERGE | 4 | Use the custom subclass as the OrderedDict cursor in test_dict_type; one query covers both. |
into TestAioDictCursor::test_dict_type |
3. aio/test_result_set.py
| test | change | criteria | reason | covered by |
|---|---|---|---|---|
TestAthenaAioResultSet::test_get_query_results_after_close |
REMOVE | 1, 7 | Calls the private _async_get_query_results and matches message text; fetch after close is tested through the public API. |
TestAthenaAioResultSet::test_fetch_after_close |
4. aio/test_util.py
#1238 deletes test_async_retry_api_call_stop_when; it is not listed.
| test | change | criteria | reason | covered by |
|---|---|---|---|---|
test_async_retry_api_call_cancelled[backoff], test_create_in_thread_cancelled_stops_retries[backoff] |
REWRITE | 2, 3 | Drop the backoff case, which uses asyncio.sleep(0.1) to "let the backoff begin" and passes if it cancels during the request; keep [request]. |
test_util.py::test_retry_api_call_stopped_by_event[backoff] |
test_async_retry_api_call_not_stoppable |
REMOVE | 2 | The same scenario (cancel during the first attempt, stoppable=False, 3 attempts) at the cursor level. |
aio/test_cursor.py::TestAioCursor::test_cancel_cancelled_while_retrying |
test_async_retry_api_call_retries |
REMOVE | 2, 6 | Retrying until success without a cancellation is retry_api_call's basic behavior. |
test_util.py::test_retry_metadata_errors (cases with 2 calls), aio/test_cursor.py::TestAioCursor::test_execute_cancelled_while_start_is_retried, test_async_retry_api_call_not_stoppable_in_stopped_scope |
5. aio/arrow/test_cursor.py
| test | change | criteria | reason | covered by |
|---|---|---|---|---|
TestAioArrowCursor::test_binary_null_vs_empty |
REMOVE | 2, 4 | Parsing is the sync AthenaArrowResultSet's; the query is identical. |
arrow/test_cursor.py::TestArrowCursor::test_binary_null_vs_empty |
TestAioArrowCursor::test_fetchmany |
REMOVE | 2, 4 | The wrapper forwards fetchmany(size) to a thread (tested offline); slicing is the sync result set's. |
aio/test_common.py::TestWithAsyncFetch::test_fetch_runs_result_set_fetch, arrow/test_cursor.py::TestArrowCursor::test_fetchmany [original] |
TestAioArrowCursor::test_fetchall |
REMOVE | 2, 4 | One to_thread call; test_fetchone proves the aio execute builds a working result set (2 queries, one 10,000-row read). |
TestAioArrowCursor::test_fetchone, arrow/test_cursor.py::TestArrowCursor::test_fetchall |
TestAioArrowCursor::test_execute_returns_self |
MERGE | 4 | Fold assert await cursor.execute(...) is cursor into test_fetchone. |
into TestAioArrowCursor::test_fetchone |
TestAioArrowCursor::test_as_polars |
MERGE | 4 | Same one_row query as test_as_arrow; call both on one result. |
into TestAioArrowCursor::test_as_arrow |
TestAioArrowCursor::test_as_arrow_unload |
MERGE | 4 | Same UNLOAD query as test_fetchone_unload; call as_arrow() there. |
into TestAioArrowCursor::test_fetchone_unload |
TestAioArrowCursor::test_no_result_set_raises |
MERGE | 2, 4 | The fetch part duplicates the offline test; move the as_arrow() assertion there. |
into aio/test_common.py::TestWithAsyncFetch::test_fetch_without_result_set |
TestAioArrowCursor::test_cancel_initial |
MERGE | 2, 4 | Shared WithAsyncFetch.cancel. |
into the new aio/test_common.py::TestWithAsyncFetch::test_cancel_without_query |
TestAioArrowCursor::test_context_manager |
REMOVE | 2, 4 | __aexit__ is shared and tested offline; every fixture uses async with. |
aio/test_common.py::TestWithAsyncFetch::test_context_exit_closes_off_event_loop |
TestAioArrowCursor::test_arraysize_default |
REMOVE | 6 | Asserts the result-set class constant. | — |
TestAioArrowCursor::test_invalid_arraysize |
REMOVE | 2 | The same setter as the constructor path. | test_connection.py::TestConnection::test_cursor_arraysize_not_positive[AioArrowCursor] (after the section 1 merge) |
TestAioArrowCursor::test_description |
REMOVE | 2, 4 | The description comes from the shared metadata handling of the sync result set. | aio/test_cursor.py::TestAioCursor::test_description, test_cursor.py::TestCursor::test_description |
TestAioArrowCursor::test_description_initial |
REMOVE | 2, 6 | Shared WithResultSet.description. |
test_cursor.py::TestCursor::test_description_initial [original] |
TestAioArrowCursor::test_executemany_fetch |
REMOVE | 2, 4 | The shared WithAsyncFetch.executemany resets the result set, and fetch without one raises; both are tested offline. |
aio/test_common.py::TestWithAsyncFetch::test_executemany_closes_previous_result_set_off_event_loop, test_fetch_without_result_set |
6. aio/polars/test_cursor.py
| test | change | criteria | reason | covered by |
|---|---|---|---|---|
TestAioPolarsCursor::test_fetchmany |
REMOVE | 2, 4 | The wrapper forwards fetchmany(size) to a thread; slicing is the sync result set's. |
aio/test_common.py::TestWithAsyncFetch::test_fetch_runs_result_set_fetch, polars/test_cursor.py::TestPolarsCursor::test_fetchmany |
TestAioPolarsCursor::test_fetchall |
REMOVE | 2, 4 | One to_thread call (2 queries, one 10,000-row read). |
TestAioPolarsCursor::test_fetchone, polars/test_cursor.py::TestPolarsCursor::test_fetchall |
TestAioPolarsCursor::test_execute_returns_self |
MERGE | 4 | Fold the is cursor assertion into test_fetchone. |
into TestAioPolarsCursor::test_fetchone |
TestAioPolarsCursor::test_as_arrow |
MERGE | 4 | Same one_row query as test_as_polars; call both on one result. |
into TestAioPolarsCursor::test_as_polars |
TestAioPolarsCursor::test_as_polars_unload |
MERGE | 4 | Same UNLOAD query as test_fetchone_unload; call as_polars() there. |
into TestAioPolarsCursor::test_fetchone_unload |
TestAioPolarsCursor::test_no_result_set_raises |
MERGE | 2, 4 | Move the as_polars() assertion into the offline test. |
into aio/test_common.py::TestWithAsyncFetch::test_fetch_without_result_set |
TestAioPolarsCursor::test_cancel_initial |
MERGE | 2, 4 | Shared WithAsyncFetch.cancel. |
into the new aio/test_common.py::TestWithAsyncFetch::test_cancel_without_query |
TestAioPolarsCursor::test_context_manager |
REMOVE | 2, 4 | Shared __aexit__, tested offline. |
aio/test_common.py::TestWithAsyncFetch::test_context_exit_closes_off_event_loop |
TestAioPolarsCursor::test_arraysize_default |
REMOVE | 6 | Asserts the result-set class constant. | — |
TestAioPolarsCursor::test_invalid_arraysize |
REMOVE | 2 | The same setter as the constructor path. | test_connection.py::TestConnection::test_cursor_arraysize_not_positive[AioPolarsCursor] (after the section 1 merge) |
TestAioPolarsCursor::test_description |
REMOVE | 2, 4 | Shared metadata handling of the sync result set. | aio/test_cursor.py::TestAioCursor::test_description, test_cursor.py::TestCursor::test_description |
TestAioPolarsCursor::test_description_initial |
REMOVE | 2, 6 | Shared WithResultSet.description. |
test_cursor.py::TestCursor::test_description_initial [original] |
TestAioPolarsCursor::test_executemany_fetch |
REMOVE | 2, 4 | Shared WithAsyncFetch.executemany, tested offline. |
aio/test_common.py::TestWithAsyncFetch::test_executemany_closes_previous_result_set_off_event_loop, test_fetch_without_result_set |
7. aio/s3fs/test_cursor.py
| test | change | criteria | reason | covered by |
|---|---|---|---|---|
TestAioS3FSCursor::test_null_vs_empty_string (2 cases) |
REMOVE | 2, 4 | Reader behavior is the sync result set's; the cursor option reaching the result set is asserted offline (2 queries). | s3fs/test_cursor.py::TestS3FSCursor::test_null_vs_empty_string, TestAioS3FSCursor::test_read_options |
TestAioS3FSCursor::test_fetchmany |
REMOVE | 2, 4 | The wrapper forwards fetchmany(size) to a thread; slicing is the sync result set's. |
aio/test_common.py::TestWithAsyncFetch::test_fetch_runs_result_set_fetch, s3fs/test_cursor.py::TestS3FSCursor::test_fetchmany |
TestAioS3FSCursor::test_fetchall |
REMOVE | 2, 4 | One to_thread call (2 queries, one 10,000-row read). |
TestAioS3FSCursor::test_fetchone, s3fs/test_cursor.py::TestS3FSCursor::test_fetchall |
TestAioS3FSCursor::test_execute_returns_self |
MERGE | 4 | Fold the is cursor assertion into test_fetchone. |
into TestAioS3FSCursor::test_fetchone |
TestAioS3FSCursor::test_cancel_initial |
MERGE | 2, 4 | Shared WithAsyncFetch.cancel. |
into the new aio/test_common.py::TestWithAsyncFetch::test_cancel_without_query |
TestAioS3FSCursor::test_context_manager |
REMOVE | 2, 4 | Shared __aexit__, tested offline. |
aio/test_common.py::TestWithAsyncFetch::test_context_exit_closes_off_event_loop |
TestAioS3FSCursor::test_async_iterator |
REMOVE | 2, 4 | Shared __anext__. |
aio/test_common.py::TestWithAsyncFetch::test_async_iteration[AioS3FSCursor] |
TestAioS3FSCursor::test_arraysize_default |
REMOVE | 6 | Asserts the result-set class constant. | — |
TestAioS3FSCursor::test_invalid_arraysize |
REMOVE | 2 | The same setter as the constructor path. | test_connection.py::TestConnection::test_cursor_arraysize_not_positive[AioS3FSCursor] (after the section 1 merge) |
TestAioS3FSCursor::test_description |
REMOVE | 2, 4 | Shared metadata handling of the sync result set. | aio/test_cursor.py::TestAioCursor::test_description, test_cursor.py::TestCursor::test_description |
TestAioS3FSCursor::test_description_initial |
REMOVE | 2, 6 | Shared WithResultSet.description. |
test_cursor.py::TestCursor::test_description_initial [original] |
TestAioS3FSCursor::test_executemany_fetch |
REMOVE | 2, 4 | Shared WithAsyncFetch.executemany, tested offline. |
aio/test_common.py::TestWithAsyncFetch::test_executemany_closes_previous_result_set_off_event_loop, test_fetch_without_result_set |
8. aio/spark/test_cursor.py
| test | change | criteria | reason | covered by |
|---|---|---|---|---|
TestAioSparkCursor::test_spark_dataframe |
REWRITE | 2, 4 | Run one short calculation with description= and assert the cursor properties and get_std_out(); the CSV read, groupBy and saveAsTable exercise Spark, and they write the same ENV.schema.spark_group_by table and path as the sync test. |
spark/test_spark_cursor.py::TestSparkCursor::test_spark_dataframe [original] |
TestAioSparkCursor::test_spark_sql |
REMOVE | 2, 4 | Engine behavior already in the sync test; its other job, dropping the table that test_spark_dataframe writes, ends with that rewrite (1 session, 2 calculations). |
spark/test_spark_cursor.py::TestSparkCursor::test_spark_sql [original] |
TestAioSparkCursor::test_session_ownership |
REMOVE | 2, 4 | The ownership logic is the shared SparkBaseCursor.__init__, and aio close() per ownership case is tested offline (1 session, 2 calculations). |
spark/test_common.py::TestSparkBaseCursor::test_close_terminates_session_by_ownership[AioSparkCursor-*], spark/test_spark_cursor.py::TestSparkCursor::test_session_ownership |
TestAioSparkCursor::test_execute_timeout_while_starting |
REMOVE | 2, 3 | A wait_for timeout is the same task cancellation, with sleep(0.1) against a 0.05 s timeout. |
TestAioSparkCursor::test_execute_cancelled_while_starting |
TestAioSparkCursor::test_execute_accepts_none_parameters (3 cases) |
REWRITE | 1 | Asserts the exact kwargs of the private _calculate; assert one StartCalculationExecution on a mocked client and that calculation_id and the state are set. |
— |
TestAioSparkCursor::test_executemany |
REWRITE | 4 | Starts a Spark session to see NotSupportedError from a method that always raises; build the cursor with __new__ as the neighbouring offline tests do and assert the same error. |
— |
TestAioSparkCursor::test_context_manager |
REMOVE | 2, 4 | __aexit__ is await self.close(), and close is tested offline (1 session, 1 calculation). |
spark/test_common.py::TestSparkBaseCursor::test_close_terminates_session_by_ownership[AioSparkCursor-*] |
9. spark/test_async_cursor.py
| test | change | criteria | reason | covered by |
|---|---|---|---|---|
TestAsyncSparkCursor::test_session_ownership |
REMOVE | 2, 4 | AsyncSparkCursor.close() delegates the termination to SparkBaseCursor.close(); the matrix is tested offline (1 session, 2 calculations). |
spark/test_common.py::TestSparkBaseCursor::test_close_terminates_session_by_ownership[AsyncSparkCursor-*], spark/test_spark_cursor.py::TestSparkCursor::test_session_ownership |
TestAsyncSparkCursor::test_close_retries_termination_after_failure |
REMOVE | 1, 2 | Patches the private _terminate_session; the same contract is tested with a mocked client. |
spark/test_common.py::TestSparkBaseCursor::test_close_retries_failed_termination[AsyncSparkCursor] |
TestAsyncSparkCursor::test_execute_accepts_none_parameters (3 cases) |
REWRITE | 1 | Asserts the exact args of the private _calculate; assert the returned (query_id, future) and the submitted calculation instead. |
— |
10. spark/test_spark_cursor.py
| test | change | criteria | reason | covered by |
|---|---|---|---|---|
TestSparkCursor::test_execute_accepts_none_parameters (3 cases) |
REWRITE | 1 | Asserts the exact args of the private _calculate; assert that the calculation ran (calculation_id and the state set). |
— |
11. spark/test_common.py
| test | change | criteria | reason | covered by |
|---|---|---|---|---|
TestSparkBaseCursor::test_wait_for_idle_session_raises_on_failure_state (3 cases) |
REMOVE | 1, 2 | Calls the private _wait_for_idle_session with a patched _get_session_status; the same 3 states are asserted through construction. |
TestSparkBaseCursor::test_init_terminates_new_session_in_failure_state (rewritten) |
TestSparkBaseCursor::test_wait_for_idle_session_raises_without_reason |
REMOVE | 1, 7 | Checks only the message format without a reason, with an anchored regex. | — |
TestSparkBaseCursor::test_wait_for_idle_session_waits_until_idle |
REWRITE | 1 | Asserts get_status.call_count == 3 and sleep.call_count == 2; drive _init_cursor with the statuses CREATING, BUSY, IDLE and assert that the cursor is created and no termination is sent. |
— |
TestSparkBaseCursor::test_exists_session_raises_on_failure_state |
REWRITE | 1 | Calls the private _exists_session and asserts the exact get_session args; construct with session_id="supplied-session" and a TERMINATED status and assert OperationalError and no termination. |
— |
TestSparkBaseCursor::test_calculate_reuses_generated_token_on_retry, test_calculate_generates_token_per_call, test_calculate_keeps_caller_token, test_calculate_interrupted_while_starting_failure, test_calculate_second_interrupt_while_starting, test_calculate_interrupted_before_request_is_sent |
REWRITE | 2 | Drop the AsyncSparkCursor case; it inherits SparkBaseCursor._calculate and _start_execution unchanged, and the tests call _calculate on the caller thread (about 20 duplicate cases). |
the SparkCursor cases of the same tests |
TestSparkBaseCursor::test_calculate_interrupted_while_starting (2 classes × 2 states) |
MERGE | 1, 2 | Calls the private _calculate; parametrize the public execute() version over final_state. |
into TestSparkBaseCursor::test_execute_interrupted_while_starting |
TestSparkBaseCursor::test_calculate_interrupt_without_kill_on_interrupt (2 cases) |
REMOVE | 1, 2 | Asserts that threading.Thread is not called; the kill_on_interrupt=False branch is the shared BaseCursor._start_execution, tested with the same patch for the query cursor. |
test_cursor.py::TestCursor::test_execute_interrupted_while_starting_without_kill_on_interrupt |
TestSparkBaseCursor::test_init_starts_session (3 cases) |
REMOVE | 2, 6 | Asserts that the StartSession ID is stored, which every default-init ownership case depends on. | TestSparkBaseCursor::test_close_terminates_session_by_ownership[*-None-None] |
TestSparkBaseCursor::test_init_terminates_new_session_that_does_not_become_idle, test_init_does_not_terminate_supplied_session, test_init_does_not_start_session_when_s3_client_fails |
REWRITE | 2 | Drop the AioSparkCursor case; it has no __init__ and the same bases as SparkCursor. Keep SparkCursor and AsyncSparkCursor. |
the SparkCursor cases of the same tests |
TestSparkBaseCursor::test_init_terminates_new_session_in_failure_state (3 × 3) |
REWRITE | 1, 2 | Drop the AioSparkCursor case, loosen the anchored message regex and drop get_session_status.assert_called_once_with; assert the exception type and the termination for each of the 3 states. |
— |
TestSparkBaseCursor::test_init_keeps_original_error_when_cleanup_fails (3 cases) |
REWRITE | 1, 2 | Drop the AioSparkCursor case and the caplog text assertion; assert exc_info.value is error. |
— |
TestSparkBaseCursor::test_async_close_shuts_down_executor_without_terminating_session |
REMOVE | 2 | "Supplied session not terminated" is a case of the ownership matrix, and the executor shutdown is asserted by the close tests. | TestSparkBaseCursor::test_close_terminates_session_by_ownership[AsyncSparkCursor-supplied-session-None], spark/test_async_cursor.py::TestAsyncSparkCursor::test_close_no_wait_shuts_down_executor |
Notes
- Borderline KEEPs:
aio/test_common.py(all 10): parametrized over all aio SQL cursors although the behavior is inherited fromWithAsyncFetch; the cases guard each class's MRO and its ownexecute().aio/test_cursor.py::TestAioCursor::test_execute_with_options,test_arraysize: the only checks that the aioexecute()passesoptionsandarraysizeto its result set (1 query each).aio/test_cursor.py::TestAioCursor::test_executemany_cancellation(about 5 queries): the only check ofquery_idandcancel()after a cancelled executemany.aio/test_cursor.py::TestAioCursor::test_cancel_cancelled_while_retrying: calls the private_cancel; the only check of the unstoppableStopQueryExecutionretries.aio/test_util.py::test_create_in_thread_cancelled_after_construction: patchespyathena.aio.util.asyncio.waitto reach the race; the close-in-new-thread path has no other test.aio/test_result_set.py::TestAthenaAioResultSet::test_fetch_with_close_during_retries[backoff]uses a 0.1 sthreading.Timer; Break result sets after failed or cancelled fetches and drop the cross-thread guards #1238 deletes it.aio/{arrow,polars,s3fs}/test_cursor.py::*::test_read_options,aio/test_connection.py::test_cursor_execute_supported_keywords: patch private_execute/_polland assert constructor kwargs; the override merge is each cursor's own code and has no offline observable effect.aio/spark/test_cursor.py::TestAioSparkCursor::test_cancel(1 session, 2 long calculations): the only live check of aiocancel().spark/test_spark_cursor.py::TestSparkCursor::test_cleanup_in_stopped_scope,test_spark_on_poll_invoked_each_iteration: patch private methods; the only checks of the unstoppable Spark cleanup (Retries continue after a result set is closed or an asyncio API call is cancelled #1202) and of the Sparkon_pollloop.spark/test_common.py::TestSparkBaseCursor::test_calculate_interrupted_before_request_is_sent: patchespyathena.common.threading.Thread; the only test of the "abandon before the helper starts" branch ofBaseCursor._start_execution.spark/test_async_cursor.py::TestAsyncSparkCursor::test_close_wait_shuts_down_executor,test_close_no_wait_shuts_down_executor: theshutdown.assert_called_once_with(wait=...)and_terminate_session.assert_called_once_with()assertions can be dropped; the futures andsubmitafter close carry the contract.- The
[original]spark/test_spark_cursor.py::TestSparkCursor::test_spark_dataframe/test_spark_sqland their aio copies write and drop the sameENV.schema.spark_group_bytable and S3 path; the section 8 rewrite removes the overlap.
- Unused after the change:
aio/test_cursor.py: importsre,BINARY,Binary,ProgrammingError,AthenaResultSet,_aio_connect;callandpatchonce the cache-search tests stub the client.aio/test_connection.py:RecordingAioCursor; the emptyTestAioConnection; importspyathena,Parameter,signature,DefaultTypeConverter,ProgrammingError;PAGED_CURSORS/UNCAPPED_CURSORS(inline them intoCURSOR_CLASSES).aio/test_result_set.py:ProgrammingErrorimport.aio/test_util.py:ClientErrorimport; thestop_inparameter and"backoff"branch of_cancel_once_started.aio/arrow/test_cursor.py,aio/polars/test_cursor.py,aio/s3fs/test_cursor.py: imports of the sync result-set class,ProgrammingError,ENV,_aio_connect.aio/spark/test_cursor.py: imports_aio_connect,AthenaSessionStatus,wait_for_spark_session_state;ENVif the rewrittentest_spark_dataframeno longer reads the CSV.spark/test_async_cursor.py: importsAthenaSessionStatus,wait_for_spark_session_state.spark/test_common.py: helpers_cursor()and_session_status();SYNC_SPARK_CURSOR_CLASSES(replace withSparkCursor);loggingonce thecaplogassertion is dropped.
- Each row's cover is re-checked before the row is applied. A row whose cover does not hold becomes MERGE or KEEP.
Activity
Remove and rewrite unnecessary aio and Spark cursor tests
Remove and rewrite unnecessary aio and Spark cursor tests
Merge pull request #1265 from pyathena-dev/test/1244-aio-pandas-rows