Repository navigation
Conversation
D40 records the maintainer's rule: the same code gives every test the same outcome on any machine, date, and run order; tests read no wall clock, reach no network, use no unseeded randomness, and bound every wait; they assert on structured results, never on a document's wording; hard inputs come from software the project builds; and acceptance names a test, a check, or a command, never a text search or a review. - The plan, CLAUDE.md, docs/testing.md, and CONTRIBUTING.md state D40. - Steps 3b-2a-2d, 3b-2a-3, 3b-2b-1, and 3b-2b-2 accept on software: the step's tests failing on the parent's code and passing on the head, or two suite runs agreeing. - scripts/check_junit.py --same-as fails unless a second JUnit report holds the same tests with the same outcome each.
tests/db/test_schema.py - Remove _tampered and _redefined and every test that rebuilt a copy of the head template (changed definitions, a dropped index, probe indexes and triggers, literal whitespace, table options, changed views, objects outside the comparison, changed plan rules, a dropped foreign-key index) and the create_all checker's written module. - Head against SQLAlchemy's metadata, table by table: columns, foreign keys, created index SQL (CreateIndex), definition items (CreateTable), named CHECKs, flags, no trigger. - Databases the migrations built at 33f8570f6540, 94a52e4f1d57, and 2d2ba76b177c: the readings differ only in the table 2d2ba76b177c lists, in its added column, index, and foreign key; 94a52e4f1d57's changed CHECK text; view_differences and objects_outside_the_comparison against the views frozen through 94a52e4f1d57, expected from DROPPED_VIEWS, REPLACED_VIEWS, and VIEWS; nullable columns and UNIQUE sets across 33f8570f6540 and 94a52e4f1d57. - Scratch databases of valid SQL for the readers: CHECK text, partial and expression indexes, a trigger and a view with whitespace in a literal, STRICT and WITHOUT ROWID flags, a nullable composite primary-key column, and a column with two foreign keys. - Exact counts from the models: sqlite_master rows at head, delete actions, references to github_users, foreign keys examined for an index; create_all_uses counts the files it reads (checked with os.walk) and the tokenizer's names and strings. - CHECK failures assert sqlite_errorname SQLITE_CONSTRAINT_CHECK and the constraint's name. tests/db/schema_support.py - frozen_views takes an optional revision to stop at (default head); foreign_key_columns lists the foreign keys unindexed_foreign_keys examines. tests/db/test_migrations.py - Remove the test-only revisions holding an orphan row, module-level code, a failure, COMMIT, END, ROLLBACK, BEGIN, refused statements, ATTACH, or a batch alter under views; the connection put inside a transaction; the copies changed for the definition reading; the revision checker's written files. - current, check, stamp, and upgrade over a file of seeded rows, and the chain from 94a52e4f1d57 over seeded rows, each with the rows of every table with a foreign key. - An interrupt at a set point after the second revision: the file stays at the first schema. - env.py's own authorizer, captured through a recording connection factory and called at each revision of the real chain and after the commit, with every answer exact. - A failed commit and the write lock on the real chain from 94a52e4f1d57, the timeout from the URL, sqlite_errorname SQLITE_BUSY, and the statements run. - The tag-table refusal over valid tag rows; the step 3a upgrade's views as frozen; table counts for the CHANGED_TABLES tests; legacy_alter_table in the connection probe; the refused tracked-file connection is closed; revision_source counts imports and VIEWS against the tokenizer and the loaded modules. - Settings read no .env from the checkout; the alembic command runs outside it. tests/db/test_tracked_database.py - The frozen-content check fails on databases the migrations wrote (head, first schema); the printed digests equal the files' SHA-256. - git and pre-commit run with an explicit environment (own HOME, no global or system config); the scripts with PATH only. tests/db/test_views.py - Remove the row id projected under a consumer name and the single-column index copies; exact row counts for the views compared. tests/db/test_types.py - The column default reads a fixed clock; rejected values assert the driver error's class. tests/db/test_application_engine.py, tests/test_db_engine.py - Settings read no .env from the checkout; foreign-key failures assert sqlite_errorname; the connect listener's failure is an interrupt at its first statement. tests/test_data_dictionary.py - differences and lock_differences over the dictionary and schema lock of 21a4267 and 77663a7 and databases at 33f8570f6540, 94a52e4f1d57, and 2d2ba76b177c; every frozen view's digest; the scratch database renamed. tests/test_check_query_lock.py - Removed and deleted entries, pairs, and versions come from the lock files of c1f474b, 6fedb05, 21a4267, and 77663a7; the history's own changes pass; edits with no real producer are held on the passing side. - The CI step runs on those files; the base checker is shown by a pull request that deletes its copy. - git, the checker, and the step run with an explicit environment and fixed commit dates. tests/fixtures/history - The lock files and data dictionaries above, copied unchanged from git show, listed in README.md.
tests/sync/test_engine.py - A database error mid-batch, a close that fails, a failed close after step 7, and an interrupted close whose own commit fails now come from another connection holding a read open (SQLite's SHARED lock) through the busy timeout, not from triggers or patched writer calls. A handle_error listener records each error's sqlite_errorname (SQLITE_BUSY) and, where the run goes on to close, ends the read first. The engine's connections wait 100 ms instead of 30 s (db.engine.SQLITE_BUSY_TIMEOUT_MS, a short_busy_timeout fixture). - The run ending after step 7 prunes a real untracked repository; the interrupt is raised at the prune's DELETE, and the fatal case fails the prune's commit on the held read. Both keep the halt reason, and the prune deletes nothing. - The AccountMissing test that patched write_pull_request is replaced by a passing-side test: commit links whose account was sighted by the item, stored earlier, sighted in the batch, found by a lookup, and answered NOT_FOUND, with the linked pr_commits rows compared to the linked commits counted from the scenario model. - The RuntimeError test that patched apply_in_place_updates is removed; the same except arm is held by the recorded 401 and by the database error above. The 401 test checks the error's class field. - A database not at head is a file built by the migrations to the revision before head (MIGRATION_REVISIONS[-2]), not an edited alembic_version. tests/sync/engine_support.py - World.expects_account_missing is removed with the patched test; the world fixture's AccountMissing check has no exemption. tests/sync/test_runs.py - The user_version test is removed; the head check's passing side reads alembic_version and PRAGMA user_version on its own connection and compares them with alembic's script head and the newest key of tests/schema_versions.json. - The claim's head refusal uses a file at the revision before head. - The held-lock claim asserts sqlite_errorname SQLITE_BUSY and the connection's busy_timeout instead of elapsed time; the claim-in- progress test commits on an event set at the claim's BEGIN IMMEDIATE instead of sleeping, and asserts busy_timeout instead of elapsed time. - The refused stale reclaim drops its message substring; blocking is asserted. - git runs with HOME and GIT_CEILING_DIRECTORIES at tmp_path, no global or system config, no inherited GIT_* variables, fixed author and committer dates, and an environment built from PATH and fixed values. tests/sync/test_writer.py - The unchanged-default-branch test records the statements the write sends and asserts none updates pull_requests, over valid rows; the changing side asserts the one update it sends. - Each ItemConflict refusal also asserts the error's number field. - "The controls" in a docstring is reworded. tests/sync/test_snapshot.py - The digest's positive control is removed; test_writes_nothing compares every byte of a file whose size equals page_count * page_size and whose pull requests the reader read. - The half-read cursor control is removed; the read count comes from the statements recorded as they reach the database, one per table, and the probe's refusing side is another connection's open read. tests/sync/test_connection.py - The CHECK-violating insert is replaced by a BEGIN IMMEDIATE that fails on another connection's write lock; lock errors assert sqlite_errorname SQLITE_BUSY instead of message text. - The body's own KeyError no longer says "planted". tests/test_cli_sync.py - A database not at head is a file at the revision before head. - The replay fixture sets GITHUB_SHA and unsets GITHUB_EVENT_NAME, so no git runs and the trigger is fixed; the first sync and the option test assert the trigger and code version, and the run's counters are asserted in sync_runs. - Child processes get an environment of PATH and fixed values (HOME is the test's directory, GITHUB_SHA set); the child setup hook is renamed from plant to setup. tests/test_cli_app.py - The traceback test no longer adds a failing command to the app; it asserts the console script loads the root app and that app's pretty_exceptions_show_locals is False. - The exit-code test apps are renamed from planted to commands. - Installed-script children get PATH and HOME (the test's directory) only.
tests/test_check_log_placeholders.py: remove the written-source cases (flagged calls, accepted calls, per-site listing). The run over src/ asserts exit 0, no message found, and the files scanned equal an os.walk count of .py/.pyi files. The empty-directory refusal asserts exit 1 and empty stdout instead of stderr wording. Subprocess env is PATH only. tests/test_count_suppressions.py: remove the written spellings, packages, refused/accepted files, the chmod 000 file, and the non-file entries. src/ at the hook caps: exit 0, empty stderr, files scanned equal to os.walk, counts equal to a second reading with tokenize. Caps at and one below each count over src/: exit 0, then exit 1 listing exactly the sites the second reading finds. scripts/count_suppressions.py, whose docstring holds directive text, is counted as its comments alone. refusal() returns None for every regular file under src/ (os.walk list). identify's read of a file stubbed to raise PermissionError: refusal() reports it and main() exits 1 naming each non-Python file; unstubbed, exit 0. Empty directory: exit 1, empty stdout. tests/test_check_any_aliases.py: remove the written alias, non-alias, and stub cases. Add the run over src/: exit 0, no alias, files scanned equal to os.walk. Empty directory: exit 1, empty stdout. tests/test_suite_rules.py: remove the written-shape test; the scan over tests/ asserts its file count equals os.walk's. tests/test_import_boundaries.py: remove the written spellings, text, and package copy. Both passing sides assert the file count from os.walk and that the (file, line) of every import the scan read equals a tokenize reading of the same files. source/'s githubkit imports and sync/'s SQLAlchemy and db/ imports are found at exactly the statements a text match finds. tests/test_server_side_cursors.py: remove the written calls; the src/ scan asserts its file count equals os.walk's. tests/test_declared_dependencies.py: remove the written package and the distributions installed on disk. src/ against pyproject.toml at ea7d12e (tests/fixtures/history, before httpx was declared) reports exactly each file importing a dependency declared since, by text match and packaging's requirement names. The environment's top-level map, stubbed in memory with sqlalchemy installed by an undeclared sibling, by declared ones, or by none, gives one finding per sqlalchemy importer, none, and one with no provider. undeclared_imports takes an optional owners map. The src/ pass asserts the files scanned equal os.walk's. tests/test_wheel_smoke.py: remove the package copy without tracking.toml. Runs use -I as CI does, an explicit env, and a temporary working directory. The packaged count equals tomllib's count of the source file; refusals assert exit 1, empty stderr, and the value named. An override naming a missing file is refused before any read. tests/test_check_junit.py: reports are pytest's own, or built in memory from one with its counters changed. Assertions are exit codes, the counters the gate prints, the number of problems, and empty stdout for an unreadable report or a refused allowance. Nested pytest gets its own pytest.ini and an explicit env without plugin autoload. tests/fixtures/history/: ea7d12e's pyproject.toml, unchanged, and a README naming its source commit.
tests/source/recorder.py: record() and record_first_sync() date units from the run's clock
(SystemClock by default) instead of datetime.now. PrivateRepository carries full_name;
RecordingFailed carries the failed items, the failed lookups, and the first sync's report.
tests/source/test_recorder.py: every record() and first sync runs on a FakeClock and asserts
recorded_at from it. Refusals assert structured fields: PrivateRepository.full_name,
(number, CompareRefused, 404) for a failed item, ServerError causes for every looked-up id,
and the first sync report's exit code, status, error and reason classes, and failure numbers.
main() runs over the real recorder and the replay: an earlier recording at one clock, a later
one at another, compared by file bytes. A scenario no longer listed leaves no file; a private
resolve leaves the earlier recording; --only rate-limits replaces only the readings;
--only documents drops a scenario's GraphQL units, keeps compare pages and readings, and adds
the compare pages an earlier recording lacked. The patched MAX_FILE_BYTES is replaced by the
largest recorded compare page with its first commit's message lengthened past the limit, for
main() and --only first-sync. The '{}' and 'stale' placeholder files are replaced by recorder
output.
tests/source/test_recordings.py: the edited batch and continuation documents and the replay
refusals of edited bindings are replaced. Refusing side: units the recorder wrote at Step 2
(c1f474b), committed unchanged under tests/fixtures/history/, are refused by the replay when
the real source requests them (batch alias, comments continuation, resolve). Passing side:
batch stamps at every size 1..PR_BATCH_SIZE equal the whole one-alias document, wrapper and
rateLimit included; continuation stamps equal the one-alias document; every batch size and
every continuation request size the hydrator sends passes the binding checks, with the
aliases and variables counted; the compare requests for #8858 find exactly the recorded
compare units. The no-unit refusal asserts the whole problem list.
tests/source/test_scenario.py: the edited Resolve, index, and batch documents and the
endCursor rewritten to a cursor never served are replaced: the model's stamps equal the
current documents' for every kind and continued connection in the recordings' layout; a
missing repository is answered null for the current index and batch documents; the
repository-gone tests count the requests answered; each cursor the source sends is one the
model served, one continuation per further page. The unanswered() test sends a request the
replay cannot answer instead of appending to problems.
tests/source/test_hydrate.py: an assignee listed twice is the same node again (first page
and overflow); the github_id comparison is tested on its passing side over one page and two.
PagingStalled asserts its fields instead of its message.
tests/source/test_resolve_index_users.py: IndexOrderViolation asserts full_name, connection,
number (from the recorded index page), and problem.
tests/source/test_transport.py: the TypeError and ValueError raised from inside a client and
the incompatible client are replaced by a check that the rate-limit reader names are methods
of the installed githubkit; the JWT client failure asserts its RequestError cause; the system
clock's jitter runs on a seeded random.Random.
tests/test_logging.py: no sleep; level selection reads the console handler's lines; file,
stdlib, and bound-context tests assert loguru records' level, message, and extra.
tests/test_config.py: the docs/development.md and .env.example wording test is replaced by
Settings reading a .env copied from .env.example; get_settings runs in a temporary working
directory with its cache cleared.
tests/test_tracking_config.py: the loader's refusals assert the cause type.
…nv test - cli/sync.py measured the report's seconds with time.monotonic(), a wall clock in every code path a CLI sync test runs. It now reads the clock the run reads (common.clock()), and a test advances the fake clock seven seconds during the resolve and finds "7 s" in the report. - The unreadable .env test made the file mode 000, which a root user can still read. The OS's refusal is now stubbed at the dotenv read (PermissionError), whatever uid runs the tests; the not-UTF-8 case stays a real file through the installed command. - D39's inputs name a timing setting a test shortens (SQLite's busy timeout), which changes how long a wait lasts and no outcome.
The module-level .env test sat inside TestInstalledEntryPoint, so the class's last four methods (six test ids) became functions nested in it, which pytest never collects. The test now follows the class.
tests/determinism.py, installed for the session by the conftest, refuses a network connection other than a Unix socket, every host-name lookup, and SystemClock's now, sleep, and unseeded random source. A refusal raises NotDeterministic (a BaseException) and is recorded; a test that reached one fails after it runs even where the code caught it. tests/test_suite_rules.py reads source: no test file reads the wall clock, sleeps on it, or draws unseeded random numbers; the package reads them only in SystemClock and db.types.utc_now; and no test function is defined inside another function. scripts/check_junit.py --same-as is tested on both sides over reports pytest wrote. The plan's 3b-2a-2d scope names the one src change (the sync report's timing), and CLAUDE.md, the testing guide, and CONTRIBUTING.md describe what enforces D40.
- tests/determinism.py sets no_proxy to *, so the source built without a recorded transport is refused at GitHub's host whatever proxy the environment or the macOS settings name; it refuses every host-name lookup the socket module offers, and connect_ex reports its own name. The conftest installs it at configure time and checks the record after every test and at the end of the session, without clearing it first. - Tests for connect_ex, each lookup, getfqdn, the Unix-socket pass-through of connect and connect_ex, and the source under four proxy settings. - The source scan registers `import os.path` as os, counts Random(None) as unseeded, and reads annotations that hold a call or Annotated metadata; its docstring names what it reads and what is out of scope. - check_junit --same-as reads a test pytest reports in two <testcase> elements (a failure, then a teardown error) as one outcome, error+failed, instead of refusing the report. - docs/testing.md no longer describes the replaced tests.
…it readers and an assignee databaseId under two node ids test_transport.py: a ValueError raised inside the HTTP client by anything other than githubkit's rate-limit readers is a ClientFailure with no wait. The input is githubkit's own OAuth token refresh, run inside client.send, parsing a 200 from github.com/login/oauth/access_token whose body is the endpoint's form-encoded default format: response.json() raises JSONDecodeError. The test asserts the ClientFailure, its RequestError cause, the JSONDecodeError's msg, doc, and pos, no sleep, and that the token request was the only request sent. test_hydrate.py: databaseId 175728472 under node ids BOT_kgDOCnlnWA and MDM6Qm90MTc1NzI4NDcy among #15771's assignees, on one page and across an overflow page, fails the item with DuplicateEntry(15771, "assignedActors", 175728472); the node-id comparison passes both entries.
…tatement cache SQLAlchemy invalidates the migration connection when an interrupt ends a statement. env.py's handler then wrote Connection.info, which revalidates the connection and raised PendingRollbackError in place of the interrupt. The handler now writes the authorizer's mapping, held before the upgrade runs, so the original exception propagates and the failed state is still recorded. - test_an_interrupt_after_the_second_revision_applies_none: the KeyboardInterrupt raised at the set point is the exception the upgrade ends with, the pool invalidated the connection once for it, and the file keeps its revision and contents. - test_a_revision_that_refuses_leaves_env_py_only_its_rollback: after the real chain's refusal of a repositories row, the authorizer allows ROLLBACK and refuses BEGIN and COMMIT. - test_the_driver_keeps_no_prepared_statements: the driver receives cached_statements=0 whether or not the caller's URL carries ?cached_statements=128.
- The token-refresh test's non-JSON answer is something between the client and GitHub (a captive portal or a proxy's block page) answering 200 with HTML; githubkit asks GitHub for JSON, so GitHub's own form-encoded format is not what that request gets. - The refusing-revision test asserts SQLAlchemy never invalidated the connection, the premise its docstring states. - The plan's 3b-2a-2d scope names the env.py change, and the prepared-statement evidence row says what the test holds.
D39 bans mutation testing (changing the code under test or a check to see whether a test notices), false content planted in the project's real files or documents, and scoring a reviewer on seeded flaws. Its first wording also banned ordinary negative tests; the clarified wording expects them: a test builds its own invalid or failing input and feeds it to the unchanged code, and every check is tested on both sides. - The rule in the plan's D39 row, its Step 3 restatement, CLAUDE.md, docs/testing.md, and CONTRIBUTING.md; D40's tail in each copy. - Step 3b-2a-2d keeps the negative tests on main, beside the tests it adds over real revisions and history files. - The REST sampler's one mismatch per field, verify's stored-row checks on a copy with a row deleted, and snapshot's refusals on a changed copy return to their steps.
The negative tests main had for the source and lock checkers, the wheel smoke check, the suppression counter, the import boundaries, the server-side cursor scan, the suite rules, the data dictionary's lock, the engine's connect listener, and the settings default, back as they were, beside the tests this step added. - test_check_any_aliases, test_check_log_placeholders: _run_on_source writes the sample module and runs the check over it with head's _run. - test_wheel_smoke: _run_package runs the check with -s and PYTHONPATH, since -I ignores PYTHONPATH, for the copy without tracking.toml. - test_check_query_lock: V3 follows V2, which it extends. - Not restored: test_config's check of the documented DATABASE_URL lines (a document's wording, D40); two query-lock cases that test_the_verdict_does_not_depend_on_how_the_checker_runs holds.
The negative tests main had for the replay's refusals of edited query documents and requests, the recordings' stamps, the scenario model's refusals, the transport's client defects, the hydrator's assignee listed twice with two databaseIds, the writer's connection on a constraint failure, the engine with the writer raising, the run claim on another user_version, and the snapshot digest and lock probe, back as they were. - test_recordings: _first_refused reads the first problem the replay records, as main's _refused returned it. - The engine's world fixture and World.expects_account_missing let the test that fakes the writer's AccountMissing refusal leave that failure. - Not restored: two assignee tests that test_an_assignee_github_id_under_two_node_ids holds with real ids.
The negative tests main had for env.py (a revision that fails, one that leaves a foreign-key violation, one that ends the transaction, attaches a database, or runs an action the authorizer does not list, a batch alter under a view, a connection already inside a transaction, a file holding a violation), the revision-rule checker, the schema comparison and plan rules on changed copies, the frozen file's changed copy, and the participation views' growth with single-column indexes, back as they were, beside the tests this step added over real revisions. - _batch_alter_revision takes main's form again, which also builds the revision without the view statements. - create_all_uses returns the uses and the files read; the restored caller reads the uses. - The participation test builds its copies with the single-column indexes, then measures them with head's growth.
Each spelling the determinism scan refuses (the wall clock through datetime, date, and time; sleeps; unseeded random, secrets, os.urandom, and uuid4; aliases, from-imports, a module as a value, and a call in an annotation) is reported with its line, and the spellings it allows (tzset, a seeded Random, an annotation, sleep(0), uuid5, a datetime built from its fields, another object's now) are not. The collection scan reports a test function or class nested in a function, and not one at module or class level.
env.py held the authorizer's state mapping before the upgrade, so after SQLAlchemy invalidated the connection and a revision called rollback(), the next statement reconnected on a second driver connection whose own mapping still read "open". env.py's ROLLBACK of that connection was then refused, and the upgrade ended with DatabaseError "not authorized" in place of the revision's error; a revision that went on instead returned with nothing committed and the file behind. Once the upgrade's connection is open, a connect listener now closes and refuses any other, so the statement fails with StatementError wrapping MigrationRefused, and a second connection a revision asks the engine for is refused as well. Tests (tests/db/test_migrations.py): - test_no_statement_reconnects_after_the_connection_is_invalidated (the revision goes on / raises) and test_a_revision_gets_no_second_connection fail at f83a06a and pass here. - The interrupt test also runs SystemExit(3), and test_an_error_read_as_a_disconnect_applies_none covers the disconnect route the env.py comment names. - TestRevisionSources.test_flags_each_rule_on_planted_files runs revision_source, the checker the real revision files go through, and asserts its exact problems and counts; the copy revision_source_problems is removed. - The authorizer tests assert DatabaseError and SQLITE_AUTH instead of the message text. - test_aborts_while_a_tag_table_holds_rows has main's inputs back, including the pr_user_tags row whose tag is missing; the valid-file case is test_aborts_while_a_pull_request_is_tagged_in_a_valid_file. tests/db/test_schema.py's module docstring names the changed copies of the head template among the schemas its readings tell apart.
…ones (D39) Main's bodies return under main's names, each with the input the test built in its own database or a stub that raises: - test_engine: a trigger refusing #2's insert mid-batch, a stub prune that raises RuntimeError or KeyboardInterrupt, an alembic_version edited to 'older', a trigger refusing the close, and a trigger refusing the aborted close. - test_runs: PRAGMA user_version = 1 at the claim. - test_writer: a wrong targets_default_branch an unchanged default branch leaves in place. Exact results where main's asserted part of one: the run's error string, and the HeadCheckError message for the edited revision and user_version. The lock-based and real-revision bodies keep their cases under new names: test_an_item_commit_that_fails_on_a_lock_is_fatal_and_the_next_run_resumes, test_a_run_ending_at_the_prune_s_delete_keeps_step_7_s_halt_reason, test_a_database_at_the_previous_revision_starts_no_run, test_a_close_whose_commit_fails_on_a_lock_leaves_the_row_running_and_exits_1, test_an_interrupted_close_whose_commit_fails_on_a_lock_leaves_the_row_running (the lock case only; main's test holds the second-interrupt case), test_the_claim_refuses_a_database_at_the_previous_revision, and test_an_unchanged_default_branch_sends_no_update_of_pull_requests. test_snapshot: test_writes_nothing and its positive control share one helper, which reads the snapshot, runs the control's UPDATE when given, and commits; both compare the file's bytes, and the control asserts the rows it changed. The reader's statements are 7, one on each of the 7 tables named. The half-consumed-read probe and the constraint failure assert sqlite_errorname (SQLITE_BUSY, SQLITE_CONSTRAINT_NOTNULL) and the cause's class instead of message text.
- test_cli_app: test_a_traceback_shows_no_local_variables runs a test-only command that raises with a credential-like local through the root app in a child interpreter again, and asserts the local is not printed. A second test does the same for a command in the github group, whose own Typer keeps typer's default of showing locals. The entry-point and setting check is kept as test_the_console_script_calls_the_root_app_which_hides_local_variables. - test_cli_app: the refused .env read is run with a token set and a recording source, and asserts no settings were cached and no source was opened; a second test reads the .env and asserts the source is opened once with the token and the settings are cached. - test_cli_sync: test_a_database_not_at_head_exits_1 edits its own database's alembic_version to an unknown revision again, asserting the exact refusal; the real-revision case is kept as test_a_database_at_the_revision_before_head_exits_1.
…s; record the history copies' blob ids Main's tests whose input the test built (edited lock entries, a relaxed checker copy in the pull request, written databases, installed sample distributions, non-directive comments) are back under main's names; the bodies that read tests/fixtures/history keep their own names: - test_check_query_lock: test_a_removed_entry_fails, TestSchemaLock's test_a_removed_entry_fails, test_a_pair_removed_from_an_object_entry_fails, test_both_locks_are_checked_by_default, test_lock_selects_one, test_the_step_fails_a_pair_removed_from_a_schema_entry, and test_the_step_runs_the_base_checker_not_the_pull_requests. The history-based refusals assert the exact problem lines. - test_count_suppressions: NOT_SUPPRESSIONS and test_directive_text_outside_a_directive_comment_is_not_counted; the docstring case is test_the_scripts_own_docstring_is_not_counted. The src cap test runs noqa only and refuses an empty site list. - test_data_dictionary: the in-memory test_differences_are_reported_both_ways. - test_declared_dependencies: test_a_name_several_distributions_install_needs_all_of_them_declared over distributions written to a temporary site directory. The empty-input refusals of the alias, placeholder, and suppression checkers assert their one stderr line. The import-boundary scans assert their site counts (5 and 20). test_config keeps TestDocumentedDatabaseUrl's node id and drops the identical TestDefaultDatabaseUrl test. tests/fixtures/history/README.md records each copy's git blob id, and tests/test_history_fixtures.py recomputes it for every tracked copy.
The after-test refusal check: a pytest run over sample tests under the conftest's hooks and fixtures, each catching a refusal at collection, in a test, in a module fixture's setup or teardown, or in a session fixture's teardown, gives the exact outcome of each test in its JUnit report. Run order: tests/conftest.py takes --reverse-order, which reverses the collected items after every other plugin has ordered them. CI runs the suite a second time with it, under PYTHONHASHSEED 2 (the first run sets 1), and compares the two JUnit reports with check_junit.py --max-skipped 0 --same-as. A sample order-dependent pair shows the option and the comparison on both sides. The guard: - db.types.utc_now reads the wall clock through _wall_clock, which tests/determinism.py refuses for the session; replacing it leaves the datetime name UtcDateTime's type check reads alone. test_types replaces the seam and writes an explicit timestamp beside the default; the two test_db_engine tests that wrote UserTag without created_at pass one. - SystemClock's now and sleep are refused at the names source/clock.py reads (datetime, asyncio), so a test gives SystemClock a fixed datetime or a recording sleep and checks its bodies. - A host-name lookup called with keyword arguments, and any method of SystemClock._random, are refused and recorded. The scans: the determinism scan also reads alembic/, its file count checked against MIGRATION_REVISIONS, and treats a starred argument to Random as unseeded; the collection scan also reports a test function or class inside a class whose name does not start with Test. Sample source covers star imports of each modelled module and annotations holding a call. Every scan refuses an empty file list. test_unreadable_report_fails calls check_junit.main in process and asserts it returns 1 and the exact stderr line; the Unix-socket test asserts connect's None and connect_ex's 0 exactly.
…ad partial ones - The head-check refusals in the engine and run tests compare the whole message, the only result HeadCheckError gives. - The interrupted close records the database errors each ending meets: none for a second interrupt, the trigger's refusal for the other. - The first-sync failure states the run's error, GraphQL points, REST calls, and the repository's reason in full. - The restored query-lock bodies compare each problem line the checker prints, built from its message templates. - The history fixtures: a repository the test builds shows only tracked files are compared. - The guard: the other clock readings are refused, and asyncio answers every name but sleep; the scan reads scripts/; an upgrade from the first schema opens one driver connection. - tests/pytest_runs.py: the one pytest runner and check_junit loader the two test modules shared as copies. - ruff skips tests/fixtures/history, whose pyproject.toml is a copy.
- D40 in its four copies: no result a test asserts comes from the wall clock; a timeout that bounds a wait and a log record's time are not results. What determinism.py refuses is named: connections, lookups, the real clocks, the unseeded random source. - Step 3b-2a-2d: its scope (the CI run in reverse order, db.types' clock seam, env.py's one connection, ruff's exclusion), what it keeps and why six tests are not kept, and its acceptance as CI checks it. - Step 3b-2b-2: verify's createdAt filter is tested on both sides. - docs/testing.md: what enforces D40, the layout, the two-run command as CI runs it, the CI job; docs/development.md: the CI job; docs/database.md: an upgrade runs on one driver connection.
- alembic/env.py: a refused second connection is recorded, and an upgrade whose revision caught the refusal and went on fails before the commit; the file keeps its revision and contents. - CI: the two-run comparison also runs after a failing reversed run, so each test whose outcome differs is named. - D40 keeps the maintainer's wording in its four copies; what the guard does not reach (a socket's sendto and bind, child processes, the clocks behind a timeout, a log record's time) is listed as its limits in docs/testing.md. - Exact results: the step-7 run's error and the aborted row's exit code; the run that blocks the claim after an interrupted close; the scripts the scan read. - The plan: the six tests not kept and the recorder rewrites that keep main's names; the child-count checks of verify and Step 4a on a copy with a row deleted; the createdAt case's filteredCount; the report seconds under a wall-clock step. docs/testing.md and docs/development.md name tests/fixtures/history.
…evision helpers
tests/support.py holds the helpers several test packages repeated:
- files_under(root, suffixes): the files os.walk lists under a directory, by suffix (None for
every file), sorted. It replaces the counters in test_check_any_aliases, test_check_log_
placeholders, test_declared_dependencies, test_import_boundaries, test_server_side_cursors,
test_suite_rules, and db/test_schema, and the walk in test_count_suppressions' _walk, which
keeps its file-as-root case and regular-file filter. db/test_schema keeps its name-ending
match, which rglob("*.py") also applies.
- sqlite_error_name(error): the result code of the sqlite3 error under SQLAlchemy's DBAPIError,
for db/test_migrations, sync/test_connection (through __cause__), and db/test_schema's
failed_check, which keeps its IntegrityError and CHECK-name assertions.
- git_environment(home, **extra): PATH, a home of the test's own, and no global or system
configuration, plus the variables each caller names: fixed dates in test_check_query_lock,
the ceiling directory and fixed dates in sync/test_runs' git_env (which still sets them in
the process environment), and nothing more in db/test_tracked_database and
test_history_fixtures.
- listening(name, listener, target): moved from db/test_migrations; it replaces the
event.listen/event.remove pairs in sync/test_engine (database_errors and five tests; the
three whose finally also called reader.release() keep it there, after the listener's
removal) and sync/test_writer's _sent_during.
STEP3A, STEP3B1, and LAST_RUN_ID are defined once, in tests/db/alembic_support.py; sync/test_runs
uses STEP3A for its older revision. tests/conftest.py's session-scoped revision_template builds
the read-only database at a revision once per session; the module-scoped revisions fixtures of
db/test_schema and test_data_dictionary read the three revisions from it.
The collected node ids are unchanged.
alembic/env.py is main's again. Its fix (an upgrade's exception ends it as itself, and an upgrade runs on one driver connection) moves, with its six tests and its paragraph in docs/database.md, to a change of its own; this step changes nothing under alembic/.
|
Head D39 as clarifiedD39 bans three things: mutation testing (changing the code under test or a check, on disk or in memory, to see whether a test notices); false content planted in the project's real files or documents to see whether a check or reviewer catches it; and scoring a reviewer or agent on seeded flaws. Negative tests are expected: a test builds its own invalid or failing input (a sample test that fails, an invalid configuration, an edited copy in its temporary directory, a test-only revision that raises, a trigger in its own database, a stub that raises, a recorded response changed in memory) and feeds it to the unchanged code; every check is tested on both sides. The first wording also banned negative tests, and the description's table lists many as replaced or removed under it. They are kept. Tests from
|
Test on main, not at head |
Reason |
|---|---|
test_cli_app.py::TestInstalledEntryPoint::test_an_unreadable_env_file_exits_1_without_a_traceback |
Its mode-000 case passes or fails with the uid (D40). test_an_env_file_that_is_not_utf8_exits_1_without_a_traceback and test_an_env_file_the_os_refuses_to_read_exits_1 hold both cases. |
test_hydrate.py::test_an_assignee_github_id_listed_twice_under_two_node_ids, test_an_assignee_github_id_listed_again_on_a_later_page |
test_an_assignee_github_id_under_two_node_ids[first-page, overflow]: a recorded unit changed in memory to list one bot's two GitHub node ids. |
test_check_query_lock.py::test_the_checker_runs_isolated_from_site_packages, test_the_verdict_does_not_depend_on_the_working_directory |
test_the_verdict_does_not_depend_on_how_the_checker_runs, over real lock files. |
test_config.py::TestDocumentedDatabaseUrl::test_names_the_local_database |
It checks the wording of .env.example and docs/development.md (D40). |
What D40's enforcement is at head
tests/determinism.py, installed from collection on, refuses a connection other than to a Unix socket, each host-name lookup function thesocketmodule offers, the package's two real clocks (source/clock.py's readings andasyncio.sleep, anddb.types.utc_now, which reads the time through_wall_clock), and every draw fromSystemClock's unseeded random source;no_proxyis*. Refusals are recorded and checked after every test and at the end of the session; each refusal and that check's failing side have tests.tests/test_suite_rules.py: no test file reads the wall clock, sleeps on it, or draws unseeded random numbers; the package reads them only in its two real clocks;alembic/andscripts/read none; no test is defined inside a function or a non-Testclass. Each scan is tested on sample source holding each spelling it refuses and over every file it reads.scripts/check_junit.py --same-ascompares two runs test by test; a test pytest reports twice (a failure, then a teardown error) has the outcomeerror+failed.- CI's tests job runs the suite a second time in reverse order (
--reverse-order) under anotherPYTHONHASHSEEDand compares the two reports with--same-as, also after a failing reversed run. - Limits, stated in
docs/testing.md: a socket'ssendtoandbind, processes a test starts, the clocks behind a timeout, and a log record's time are not refused at run time.
Other changes since the description
- Code:
cli/sync.pytimes its report on the run's clock;db/types.pyreads the time through_wall_clock. Nothing underalembic/changes: the env.py fix (an upgrade's exception ends it as itself, on one driver connection) is alembic/env.py: an upgrade's exception ends it as itself, on one connection #31. tests/fixtures/history/README.mdrows record each copy's git blob id, andtests/test_history_fixtures.pychecks them. Shared test helpers live intests/support.py,tests/pytest_runs.py,tests/db/alembic_support.py(revision ids), andtests/conftest.py(revision_template).pyproject.toml: ruff skipstests/fixtures/history/.
Evidence at c4e793f8906573970a35c7f1946c3aafb6a9f276
PYTHONHASHSEED=1 pytestandPYTHONHASHSEED=2 pytest --reverse-order: 2314 tests, 0 failed, 0 errored, 0 skipped each;scripts/check_junit.py first.xml --max-skipped 0 --same-as second.xmlexits 0.pre-commit run --all-files: every hook passed.- CI on
c4e793f8906573970a35c7f1946c3aafb6a9f276was queued when this comment was written; its result is on this pull request's checks.
Not run: a live GitHub request; a real SIGINT to ghactivity sync from a shell.
…-planted-inputs # Conflicts: # tests/db/test_migrations.py
ChrisHuie
left a comment
There was a problem hiding this comment.
Reviewed ee231d2e540fd8eaeb6dd96ff2d951355911388d. One P2 is attached inline with a self-contained reproduction, scope limits, and outcome-based acceptance criteria.
| @pytest.fixture(autouse=True, scope="session") | ||
| def nothing_refused_after_the_last_test() -> Iterator[None]: | ||
| """The refusals recorded after the last test's check, in fixtures torn down after it.""" | ||
| yield | ||
| assert determinism.drain() == [] |
There was a problem hiding this comment.
[P2] Check recorded refusals after all session fixture teardown
At ee231d2e540fd8eaeb6dd96ff2d951355911388d, this final check is itself a session-autouse fixture. Another session-autouse fixture can initialize before it and therefore finalize afterward. If that cleanup catches NotDeterministic, the operation is blocked and its refusal is recorded after the last drain, but pytest exits 0 and the JUnit gate accepts the run. That contradicts the documented check through session teardown.
The existing test_a_refusal_the_code_caught_fails_the_next_check requests an ordinary session fixture from the last test. That fixture starts after this guard, so its finalizer runs before the check and does not expose this ordering.
Executable reproduction against unchanged checkout code
From the repository root with the locked dev environment installed, run:
uv run --no-sync python - <<'PY'
import os, subprocess, sys, tempfile
from pathlib import Path
repo = Path.cwd()
fixture = """import pytest
from github_activity_db.source.clock import SystemClock
from tests import determinism
@pytest.fixture(scope="session", autouse=True)
def cleanup_fixture():
yield
try:
SystemClock().now()
except determinism.NotDeterministic:
pass
assert [(r.what, r.detail) for r in determinism.REFUSED] == [
("wall clock", "source.clock: datetime.now(UTC)")
]
"""
with tempfile.TemporaryDirectory() as tmp:
folder = Path(tmp)
(folder / "fixture_plugin.py").write_text(fixture)
(folder / "test_sample.py").write_text("def test_ok():\n assert True\n")
env = {
"PATH": os.environ.get("PATH", ""),
"PYTHONPATH": os.pathsep.join([str(repo / "src"), str(repo)]),
"PYTEST_DISABLE_PLUGIN_AUTOLOAD": "1",
"PYTHONDONTWRITEBYTECODE": "1",
}
results = []
for name, plugins, reverse, seed in [
("forward", ["fixture_plugin", "tests.conftest"], False, "1"),
("reverse", ["fixture_plugin", "tests.conftest"], True, "2"),
("control", ["tests.conftest", "fixture_plugin"], False, "1"),
]:
cmd = [sys.executable, "-m", "pytest", "-q", "-p", "no:cacheprovider",
"-c", str(repo / "pyproject.toml"), "--rootdir", str(folder),
"--confcutdir", str(folder), f"--junitxml={name}.xml"]
for plugin in [*plugins, "pytest_asyncio.plugin", "pytest_cov.plugin"]:
cmd += ["-p", plugin]
if reverse:
cmd += ["--reverse-order"]
run = subprocess.run(cmd + [str(folder / "test_sample.py")], cwd=folder,
env={**env, "PYTHONHASHSEED": seed},
capture_output=True, text=True, timeout=60)
gate = subprocess.run(
[sys.executable, str(repo / "scripts/check_junit.py"),
str(folder / f"{name}.xml"), "--max-skipped", "0"],
capture_output=True, text=True, timeout=60)
results.append((name, run.returncode, gate.returncode))
compare = subprocess.run(
[sys.executable, str(repo / "scripts/check_junit.py"),
str(folder / "forward.xml"), "--max-skipped", "0",
"--same-as", str(folder / "reverse.xml")],
capture_output=True, text=True, timeout=60)
print("name, pytest exit, JUnit gate exit:", results)
print("forward/reverse comparison exit:", compare.returncode)
PYObserved on Python 3.12.10 and 3.13.0 with pytest 9.0.2:
name, pytest exit, JUnit gate exit: [('forward', 0, 0), ('reverse', 0, 0), ('control', 1, 1)]
forward/reverse comparison exit: 0
This loads the complete project conftest and the actual project pytest configuration, including its strict settings and warning filters. It writes only a temporary sample project. The fixture's assertion proves the real refusal is recorded; it never clears the guard's state. The control changes only plugin registration order. The one-test reversed run demonstrates comparator acceptance, not a claim that reversing test order changes the verdict. Thus neither the source-rule checks nor a second run/comparison provide a backstop for this lifecycle gap.
Scope and nuance: the guarded operation still cannot execute. Ordinary except Exception does not swallow this BaseException; the case needs a direct catch of NotDeterministic, a broader catch, or equivalent cleanup handling. No current application test was shown escaping detection. This is a defect in the new guard's promised verification result, not evidence that the existing 2,322-test suite is presently nondeterministic. Separate controls also confirmed the same mechanism for async cleanup, a joined worker thread and a socket-connection refusal.
Acceptance criteria:
- Check recorded refusals after the covered session-fixture cleanup, while keeping the guard active during that cleanup. Renaming this fixture or adding dependencies to selected fixtures does not establish a final-check guarantee.
- Add a subprocess regression for a session-autouse cleanup that catches the real refusal. Require a failing pytest/CI result in both plugin registration orders, including the forward/reverse runs; the otherwise-identical clean input must still pass without a pending refusal.
- Preserve ordinary per-test and collection checks, existing failures, and restoration of the patched boundaries.
- Identify the recorded refusal in the failure diagnostic. A valid session-hook fix may fail pytest after JUnit has already been serialized: pytest's nonzero exit fails the existing workflow, so retroactively rewriting XML to invent a failed testcase is not required.
The reproduction above intentionally asserts the presence of the refusal during the fixture's cleanup. The regression's final oracle should be the subprocess/session result after cleanup, so it can verify a repaired implementation.
Ran: the exact reproduction above on Python 3.12 and 3.13; full-plugin and strict-config lifecycle controls; the actual JUnit and comparison commands. The earlier four complete runs at this unchanged head each passed 2,322 tests.
Not run: a modified implementation or proposed fix; an exhaustive matrix of arbitrary third-party plugin behavior.
… undone The final check was a session-scoped fixture, so a session fixture set up before it (a plugin's, a root conftest's) was torn down after it, and a refusal caught there left pytest exiting 0. The check now runs in tests/conftest.py's pytest_unconfigure, after the guard is undone: each refusal is written to stderr and an exit status of 0 becomes 1. run_pytest gives its sample processes a fixed hash seed and makes the directories of the files it writes.
|
Head Since the last head comment (
|
| Test | Input | Asserts |
|---|---|---|
test_a_refusal_caught_after_the_last_tests_check_fails_the_run (6 cases) |
the real clock's refusal caught in a session fixture of a plugin registered before the guard and after it (each in order and last to first), in a plugin's pytest_unconfigure that runs before the guard's, and in a root conftest's session fixture with the guard in a directory's conftest; each beside a twin that reads a FakeClock |
refusing twin: exit 1, every test passed, the refusal parsed from stderr, and every stderr line a refusal; clean twin: exit 0, nothing on stderr; both: the 11 guarded names patched at session finish and none after, and no refusal left recorded |
test_a_refusal_the_code_caught_fails_the_next_check |
the existing sample, now loading the guard with -p tests.conftest |
test_e_last::test_last passed (it was an error), exit 1, its refusal on stderr |
test_a_sample_run_hashes_with_a_fixed_seed |
a sample test reading sys.flags.hash_randomization |
0: tests/pytest_runs.py now sets PYTHONHASHSEED=0 for the pytest processes it starts (D40) |
Against ee231d2's tests/conftest.py the 7 guard tests fail, each on an exit-code or outcome difference; against its tests/pytest_runs.py the hash-seed test fails.
Ran, at 0177681: the full suite with PYTHONHASHSEED=1 and with --reverse-order under PYTHONHASHSEED=2 on Python 3.13.0 (2329 passed each; check_junit.py --max-skipped 0 --same-as exit 0; stderr empty); pre-commit; CI (lint and types, tests on Python 3.12 and 3.13, built wheel: all passed).
Not run: the full suite on Python 3.12 outside CI; pytest-xdist.
Redesign plan Step 3b-2a-2d: tests without planted inputs, deterministic (D39, D40).
What changes
D39: no planted inputs. Every test that changed a file or a copy of one, or wrote a revision, a file, a call, a connection, or a decoy holding a flaw to see whether a check catches it, is replaced. Earlier schemas come from real revisions (
upgrade(path, "<revision>")); earlier repository files are copied unchanged intotests/fixtures/history/<sha>/(index in its README); GitHub-side inputs are recorded units, or stubs built from them in memory in a shape GitHub or the environment can send. The table at the end lists each test replaced, removed, or renamed, the rule it held, and the test that holds it now.D40: the same result every time. The plan's D40 row,
CLAUDE.md,CONTRIBUTING.md, anddocs/testing.mdstate the rule. Enforcement:tests/determinism.py, installed bytests/conftest.pyat configure time, refuses in the pytest process a connection other than a Unix socket, every host-name lookup thesocketmodule offers, andSystemClock.now,SystemClock.sleep, and its unseeded random source. It setsno_proxy=*, so a client reading proxies from the environment or the macOS settings is refused at GitHub's own host on any machine. A refusal raisesNotDeterministic(aBaseException) and is recorded; the record is checked after every test and at the end of the session, so a test fails even where the code under test caught the exception. Each refusal and the Unix-socket pass-through has a test, as does the source built with githubkit's own transport under four proxy settings.tests/test_suite_rules.pyreads source: no test file reads the wall clock, sleeps on it, or draws unseeded random numbers; the package reads them only in its two real clocks (SystemClock,db.types.utc_now), asserted as the exact list of four reads; and no test function is defined inside another function, where pytest never collects it. Each scan counts the files it read against a second walk of the directory.scripts/check_junit.py --same-as OTHER.xmlfails on each test whose outcome differs between two reports or that only one holds. A test pytest reports in two<testcase>elements (a failure, then a teardown error) has the outcomeerror+failed. Tested on both sides over reports pytest wrote.Code changes
src/github_activity_db/cli/sync.py: the report's seconds come from the run's clock, nottime.monotonic.tests/test_cli_sync.py::test_the_report_times_the_run_on_its_clockadvances the fake clock 7 s during the resolve and asserts the report line.alembic/env.py: the upgrade holds the connection'sinfomapping before it runs and records a failure there. An interrupt inside a statement, which SQLAlchemy answers by invalidating the connection, now propagates as theKeyboardInterruptitself; before, readingconnection.infoon the invalidated connection raisedPendingRollbackErrorin its place. Every refusal env.py made is unchanged.Tests no longer collected, restored. Four functions of
tests/test_cli_app.py::TestInstalledEntryPoint(six test ids) had become functions nested in a module-level test; they are collected again, and the collection rule above holds it.Evidence at head
5f5b0e39691e61dbbb6d89cfd9e4d7a6e44b1100python scripts/check_junit.py first.xml --max-skipped 0 --same-as second.xmlexits 0.uv run pre-commit run --all-files: every hook passed.c37d9a8'ssrc/listscli/sync.py:108: time.monotonicandcli/sync.py:118: time.monotonicbeside the four real-clock reads; at head it lists only the four.8f721e0'stests/test_cli_app.pylists the four nested functions; at head, none.test_the_report_times_the_run_on_its_clockfails on the parent'scli/sync.py.test_an_interrupt_after_the_second_revision_applies_nonefails on the parent'senv.pywithPendingRollbackError: Can't reconnect until invalid transaction is rolled back, and passes at head.HTTPS_PROXY=http://192.0.2.10:3128, the parent's network-refusal test fails (its refusal is the proxy'sconnect); head's passes underHTTPS_PROXY,https_proxy,ALL_PROXY, and none.check_junit.py --same-asexits 1 withappears twice; head's reads it and reports1 failed,1 errored, and no difference.Outside this step
These stay as they are; each limits what a test can assert, not what it runs:
match=on the message:ItemConflict(only.number),HeadCheckError,MigrationRefused,CannotCheck,DictionaryError,ScenarioError,replay.problems(strings), the source layer'sUnexpectedResponse(beyond status),Forbidden,RepositoryShapeError,ResponseShapeError,LookupShapeError,ServerError, thetracking.tomlvalidators, and the prose refusals ofscripts/wheel_smoke.py,check_db_journal.py,check_frozen_db.py, andcheck_junit.py'sproblems().SyncReport.errorandRepositoryReport.reasonhold'<class>: <message>'text.db.types.utc_nowhas no injectable clock;tests/db/test_types.pyreplaces the module'sdatetimename to reach a fixed instant.uv.lock, so githubkit's App auth flow raisesRuntimeErrorbefore it parses anything; its malformed-keyValueErrorcannot be produced.logging.InterceptHandlerattributes an intercepted stdlib record tologging.callHandlers, not its caller;test_intercept_stdlib_loggingasserts level and message only.scripts/check_log_placeholders.pyandcheck_any_aliases.pyreport files scanned, not the calls or assignments compared.db.engine.SQLITE_BUSY_TIMEOUT_MSto 100 ms (D39's timing input);SyncOptionshas no busy-timeout option.env.py.Tests replaced, removed, or renamed
tests/db/test_migrations.pytests/db/test_migrations.pytests/db/test_migrations.pytests/db/test_migrations.pytests/db/test_migrations.py?cached_statements=128) is held byTestTransactionControl::test_the_driver_keeps_no_prepared_statements: the driver receivescached_statements=0on its one connection.tests/db/test_migrations.pytests/db/test_migrations.pytests/db/test_migrations.pytests/db/test_migrations.pytests/db/test_migrations.pytests/db/test_schema.pytests/db/test_schema.pytests/db/test_schema.pytests/db/test_schema.pytests/db/test_schema.pytests/db/test_schema.pytests/db/test_schema.pytests/db/test_schema.pytests/db/test_schema.pytests/db/test_schema.pytests/db/test_schema.pythrough(default head).tests/db/test_schema.pytests/db/test_schema.pyrevisionsholding databases built by upgrade(path, rev) at 33f8570f6540, 94a52e4f1d57 and 2d2ba76b177c (read-only).tests/db/test_schema.pytests/db/test_tracked_database.pytests/db/test_views.pytests/source/test_hydrate.pytest_an_assignee_github_id_under_two_node_ids[first-page, overflow], databaseId 175728472 under node idsBOT_kgDOCnlnWAandMDM6Qm90MTc1NzI4NDcy(gap 103), DuplicateEntry (15771, assignedActors, 175728472).tests/source/test_hydrate.pytests/source/test_recorder.pytests/source/test_recorder.pytests/source/test_recorder.pytests/source/test_recorder.pytests/source/test_recorder.pytests/source/test_recorder.pytests/source/test_recorder.pytests/source/test_recorder.pytests/source/test_recordings.pytests/source/test_recordings.pytests/source/test_recordings.pytests/source/test_recordings.pytests/source/test_recordings.pytests/source/test_recordings.pytests/source/test_scenario.pytests/source/test_scenario.pytests/source/test_scenario.pytests/source/test_transport.pyTestCredentials::test_a_token_refresh_githubkit_cannot_parse_is_fatal: githubkit's own OAuth token refresh, answered 200 with an HTML page by something between the client and GitHub, ends in ClientFailure from RequestError from JSONDecodeError, with no wait.tests/source/test_transport.pytests/sync/engine_support.pytests/sync/test_connection.pytests/sync/test_connection.pytests/sync/test_engine.pytests/sync/test_engine.pytests/sync/test_engine.pytests/sync/test_engine.pytests/sync/test_engine.pytests/sync/test_engine.pytests/sync/test_engine.pytests/sync/test_runs.pytests/sync/test_runs.pytests/sync/test_snapshot.pytests/sync/test_snapshot.pytests/sync/test_writer.pytests/sync/test_writer.pytests/test_check_any_aliases.pytests/test_check_any_aliases.pytests/test_check_any_aliases.pytests/test_check_any_aliases.pytests/test_check_junit.py# noqa: S314; tests/ is outside the src/ cap.tests/test_check_log_placeholders.pytests/test_check_log_placeholders.pytests/test_check_log_placeholders.pytests/test_check_log_placeholders.pytests/test_check_query_lock.pytests/test_check_query_lock.pytests/test_check_query_lock.pytests/test_check_query_lock.pytests/test_check_query_lock.pytests/test_check_query_lock.pytests/test_cli_app.pytests/test_cli_app.pyTestInstalledEntryPoint::test_an_env_file_that_is_not_utf8_exits_1_without_a_traceback(a real non-UTF-8 .env through the installed entry point) andtest_an_env_file_the_os_refuses_to_read_exits_1(pydantic-settings' read of .env answers PermissionError, whatever uid runs the tests; exit 1, SystemExit).tests/test_cli_app.pytests/test_cli_sync.pytests/test_cli_sync.pytests/test_config.pytests/test_count_suppressions.pytests/test_count_suppressions.pytests/test_count_suppressions.pytests/test_count_suppressions.pytests/test_count_suppressions.py# noqaand# type: ignore(checked with tokenize). It asserts exit 0, scanned 1, and counts equal to the second reading of its comment tokens. It uses the default caps and computed counts instead of caps 0 and a literal (0, 0).tests/test_count_suppressions.pytests/test_count_suppressions.pytests/test_count_suppressions.pytests/test_count_suppressions.pytests/test_count_suppressions.pytests/test_data_dictionary.pytests/test_data_dictionary.pytests/test_data_dictionary.pytests/test_data_dictionary.pytests/test_db_engine.pytests/test_declared_dependencies.pytests/test_declared_dependencies.pytests/test_declared_dependencies.pytests/test_import_boundaries.pyfrom ..dbimports has no real input and is passing-side only.tests/test_import_boundaries.pytests/test_import_boundaries.pyimport githubkit, the importlib/import forms, name= and class-body imports have no real input.tests/test_import_boundaries.py^\s*(from|import) githubkit\bover the same text. At base that is 5 statements and 14 findings, including 2 inside TYPE_CHECKING blocks.tests/test_server_side_cursors.pytests/test_server_side_cursors.pytests/test_server_side_cursors.pytests/test_suite_rules.pytests/test_suite_rules.pytests/test_wheel_smoke.pytests/test_wheel_smoke.pytests/test_wheel_smoke.pyRan: the full suite twice on CPython 3.13 at
5f5b0e39691e61dbbb6d89cfd9e4d7a6e44b1100and the--same-ascomparison; pre-commit over all files; the parent-commit runs listed above.Not run: CI's Python 3.12 job (CI runs it on this PR); a live GitHub request; a real SIGINT to
alembic upgradefrom a shell; the macOS system-proxy path (read from source:no_proxyin the environment short-circuits it).