tests.test_unsubscribe module¶
Tests for repomatic.github.unsubscribe.
- tests.test_unsubscribe.patch_gh(**mock_kwargs)¶
The conftest
patch_gh, bound to this module’s dispatch points.
- tests.test_unsubscribe.test_compute_cutoff(fixed_now, months, expected)[source]¶
Subtracting months clamps the day and rolls the year over correctly.
- tests.test_unsubscribe.test_format_link(number, html_url, expected)[source]¶
A link renders only with both a number and a URL, else the repo name.
- tests.test_unsubscribe.test_render_detail_table_empty()[source]¶
No rows renders to an empty string, not a headerless table.
- tests.test_unsubscribe.test_render_detail_table_exact()[source]¶
Rows render as a headed markdown table with one line each.
- tests.test_unsubscribe.test_render_detail_table_humanizes_timestamp()[source]¶
A real timestamp renders a relative string, not the ‘-’ placeholder.
- tests.test_unsubscribe.test_render_report_phase1_dry_run_summary()[source]¶
Dry-run phase 1 counts candidate rows and marks the mode.
- tests.test_unsubscribe.test_render_report_phase1_live_summary()[source]¶
Live phase 1 reports unsubscribed and failed counts.
- tests.test_unsubscribe.test_render_report_backlog_warning(threads_deferred, expect_warning)[source]¶
The warning fires on work the cap deferred, and on nothing else.
Inspection is not capped, so no thread goes uninspected: what a run leaves behind is eligible threads it declined to act on. That is a remainder a higher cap really does clear, which is what the warning advises.
- tests.test_unsubscribe.test_render_report_backlog_warning_links_the_workflow()[source]¶
The manual-run advice links the workflow whose run carries the report.
- tests.test_unsubscribe.test_backlog_warning_breaks_only_between_sentences()[source]¶
A job summary renders every newline as a line break.
Wrapping the prose to a source column therefore leaks those wraps into the rendered warning, which is what made this one read as randomly broken. Each line has to end a sentence instead.
- tests.test_unsubscribe.test_unsubscribe_workflow_constant_names_a_bundled_workflow()[source]¶
The link is only worth rendering while repomatic ships that workflow.
- tests.test_unsubscribe.test_render_report_phase2_skipped()[source]¶
A skipped phase 2 renders its reason inside a warning admonition.
- tests.test_unsubscribe.test_render_report_phase2_content()[source]¶
An active phase 2 renders its summary, search table, and query.
- tests.test_unsubscribe.test_render_report_empty_result()[source]¶
A default result renders both phases plus the attribution footer.
- tests.test_unsubscribe.test_fetch_notification_threads_sorts_oldest_first()[source]¶
The pool comes back oldest first, whatever order the API used.
The order is imposed here, not inherited: the endpoint calls itself sorted by most recent update, and a probe against 1637 real threads found the response ordered by neither
updated_atnor id. The arrival order below is deliberately none of those.
- tests.test_unsubscribe.test_fetch_notification_threads_skips_malformed_lines()[source]¶
Blank and unparsable lines are dropped without failing the fetch.
- tests.test_unsubscribe.test_fetch_notification_threads_gh_failure_returns_empty()[source]¶
A gh failure degrades to an empty pool.
- tests.test_unsubscribe.test_fetch_notification_threads_query_args()[source]¶
The fetch requests both subject types, paginated, gated on the cutoff.
- tests.test_unsubscribe.test_fetch_notification_threads_sends_the_cutoff_in_utc()[source]¶
A cutoff from another zone must not stamp its wall clock with a
Z.The suffix is a claim about the instant, so formatting without converting first would shift the window by the offset and silently fetch the wrong threads.
- tests.test_unsubscribe.test_get_thread_details_success()[source]¶
A well-formed response is parsed and returned verbatim.
- tests.test_unsubscribe.test_get_thread_details_inaccessible_returns_none()[source]¶
A gh failure (inaccessible subject) yields None.
- tests.test_unsubscribe.test_get_thread_details_malformed_returns_none()[source]¶
A non-JSON response yields None instead of raising.
- tests.test_unsubscribe.test_unsubscribe_rest_thread_success()[source]¶
A successful unsubscribe deletes the subscription then marks it read.
- tests.test_unsubscribe.test_unsubscribe_rest_thread_delete_failure()[source]¶
A failed DELETE returns False and never attempts the PATCH.
- tests.test_unsubscribe.test_unsubscribe_rest_thread_patch_failure()[source]¶
A failed PATCH (after a successful DELETE) returns False.
- tests.test_unsubscribe.test_graphql_unsubscribe_success()[source]¶
A successful mutation passes the node id and query, returning True.
- tests.test_unsubscribe.test_get_authenticated_username_strips()[source]¶
The login is trimmed of trailing whitespace.
- tests.test_unsubscribe.test_get_authenticated_username_propagates_error()[source]¶
A gh failure propagates so the caller can skip phase 2.
- tests.test_unsubscribe.test_iter_closed_items_delegates_to_paginator()[source]¶
The search wraps the shared paginator, bounded by GitHub’s own ceiling.
- tests.test_unsubscribe.test_validate_notifications_token_exact_scope(caplog)[source]¶
A token scoped to exactly ‘notifications’ passes without a warning.
- tests.test_unsubscribe.test_validate_notifications_token_extra_scopes_warns(caplog)[source]¶
Extra scopes on the token log a least-privilege warning.
- tests.test_unsubscribe.test_validate_notifications_token_propagates()[source]¶
A validation failure propagates to the CLI entry point.
- tests.test_unsubscribe.test_phase1_live_unsubscribes_closed_stale()[source]¶
A closed, stale thread is unsubscribed via DELETE + PATCH in live mode.
- tests.test_unsubscribe.test_phase1_dry_run_records_candidate_without_mutations()[source]¶
Dry-run records a candidate row but issues no DELETE or PATCH.
- tests.test_unsubscribe.test_phase1_skip_scenarios(detail, counter)[source]¶
Open, recent, unknown-state, and unparsable threads are each skipped.
- tests.test_unsubscribe.test_phase1_inaccessible_subject_skipped()[source]¶
An inaccessible subject (no details) counts as unknown, not a failure.
- tests.test_unsubscribe.test_phase1_delete_failure_records_failed()[source]¶
A failed DELETE records a FAILED row and skips the PATCH.
- tests.test_unsubscribe.test_phase1_cap_defers_but_reports_total()[source]¶
The cap bounds the unsubscribes, yet the full total is reported.
- tests.test_unsubscribe.test_phase1_fetch_failure_skips_phase()[source]¶
A fetch failure leaves phase 1 empty while phase 2 still runs.
- tests.test_unsubscribe.test_phase2_filters_non_subscribed_items()[source]¶
Only items with viewerSubscription SUBSCRIBED are acted on.
- tests.test_unsubscribe.test_phase2_skips_items_active_since_cutoff()[source]¶
Fresh or timestamp-less items are skipped despite the search filter.
The search query’s day-granular
updated:<filter is served by GitHub’s search index, which can lag; the client-side re-check mirrors phase 1.
- tests.test_unsubscribe.test_phase2_dry_run_no_mutation_calls()[source]¶
Dry-run records a candidate row but issues no mutation.
- tests.test_unsubscribe.test_phase2_live_unsubscribes_subscribed()[source]¶
Live mode issues the mutation for a subscribed item.
- tests.test_unsubscribe.test_phase2_mutation_failure_records_failed()[source]¶
A failed mutation records a FAILED row.
- tests.test_unsubscribe.test_phase2_username_failure_skips_search()[source]¶
A username lookup failure skips phase 2 before any search.
- tests.test_unsubscribe.test_phase2_search_failure_skips()[source]¶
A GraphQL search failure marks phase 2 skipped with a reason.
- tests.test_unsubscribe.test_phase2_search_query_built()[source]¶
The search query embeds the username and the computed cutoff date.
- tests.test_unsubscribe.test_batch_subject_details_maps_graphql_onto_the_rest_vocabulary()[source]¶
One round trip answers many subjects, in the spelling the phase reads.
REST calls a merged pull request
closed; GraphQL distinguishesMERGED. The phase compares one vocabulary, so the batch collapses the two.
- tests.test_unsubscribe.test_fetch_subject_details_falls_back_to_one_call_each()[source]¶
A failed batch falls back to one REST call per subject, losing no result.