tests.test_virustotal module

Tests for VirusTotal module.

tests.test_virustotal.patch_gh(**mock_kwargs)

The conftest patch_gh, bound to this module’s dispatch points.

class tests.test_virustotal.FakeClock[source]

Bases: object

A monotonic clock that only sleep moves, recording every sleep.

monotonic()[source]
Return type:

float

sleep(seconds)[source]
Return type:

None

tests.test_virustotal.clock(monkeypatch)[source]

Drive the module’s time calls through a FakeClock.

tests.test_virustotal.sample_records()[source]

Two scan records spanning two releases.

tests.test_virustotal.test_detection_stats_clean()[source]

Clean file: zero flagged out of total engines.

tests.test_virustotal.test_detection_stats_flagged()[source]

File with detections: malicious + suspicious count.

tests.test_virustotal.test_scan_record_roundtrip(sample_records)[source]

Records survive the CSV row they are committed as.

tests.test_virustotal.test_scan_record_key(sample_records)[source]

Identity is the (sha256, scanned) pair.

tests.test_virustotal.test_records_from_results_skips_pending()[source]

Results without detection stats produce no record.

tests.test_virustotal.test_records_from_results_stamps_today()[source]

The scan date defaults to today in YYYY-MM-DD form.

tests.test_virustotal.test_load_scan_records_missing_file(tmp_path)[source]

A missing history file loads as an empty list.

tests.test_virustotal.test_load_scan_records_refuses_a_lone_json_history(tmp_path)[source]

A history only stored as JSON is reported, never silently replaced.

tests.test_virustotal.test_load_scan_records_ignores_json_beside_the_csv(tmp_path, sample_records)[source]

Once the CSV exists, a stale JSON beside it is inert.

tests.test_virustotal.test_load_scan_records_malformed(tmp_path)[source]

A corrupt history file raises instead of being silently clobbered.

tests.test_virustotal.test_upsert_scan_records_creates_file(tmp_path, sample_records)[source]

The history file and its parent directories are created on demand.

tests.test_virustotal.test_upsert_scan_records_empty_creates_file(tmp_path)[source]

No new records still normalizes an empty history file into existence.

A header-only CSV is what later pipeline steps can rely on the presence of, and what read_csv distinguishes from a truncated file.

tests.test_virustotal.test_upsert_scan_records_idempotent(tmp_path, sample_records)[source]

Re-upserting the same records reports no change.

tests.test_virustotal.test_upsert_scan_records_replaces_same_day(tmp_path, sample_records)[source]

A record with the same (sha256, scanned) identity is replaced.

tests.test_virustotal.test_upsert_scan_records_sorted_by_release_version(tmp_path)[source]

Records are ordered by release version, one line each.

Sorting on the parsed version rather than on the tag string is what keeps v9 before v10, and one line per record is what keeps a release’s append readable in a diff.

tests.test_virustotal.test_pacer_spaces_calls_over_the_budget(clock)[source]

Each call reserves its share of the minute, an upload two shares.

tests.test_virustotal.test_uploads_and_polls_share_one_budget(tmp_path, clock)[source]

The first poll waits on the last upload, like any other call.

tests.test_virustotal.test_multipart_body_wraps_the_file_part()[source]

The body holds one file part, framed by the boundary the header names.

tests.test_virustotal.test_upload_file_posts_to_the_single_use_url(tmp_path)[source]

An upload reads a one-shot URL, then posts the file there with the key.

tests.test_virustotal.test_upload_file_waits_longer_than_an_api_read(tmp_path)[source]

The post of a file gets more time than the default timeout of a read.

urlopen sends a bytes body in one sendall call, which spends the socket timeout on the whole transfer: the default would fail a large binary on a slow link.

tests.test_virustotal.test_poll_detection_stats()[source]

Poll returns enriched results when analysis is complete.

tests.test_virustotal.test_poll_detection_stats_timeout(clock)[source]

Polling timeout produces results with None detection_stats.

tests.test_virustotal.test_poll_detection_stats_retries_an_unindexed_file(clock)[source]

A report VirusTotal has not indexed yet is polled again until the deadline.

tests.test_virustotal.test_poll_detection_stats_empty()[source]

Polling with no results returns empty list.

tests.test_virustotal.test_download_release_binaries_waits_for_the_asset_list(mock_sleep, tmp_path)[source]

The download starts once the release lists an asset, not before.

tests.test_virustotal.test_download_release_binaries_retries_a_failed_read(mock_sleep, tmp_path)[source]

A read that fails is retried like one that lists no asset.

tests.test_virustotal.test_download_release_binaries_downloads_after_the_last_empty_read(mock_sleep, tmp_path)[source]

An asset list that stays empty leaves the verdict to the download.

No wait follows the last read: the download runs at once, and its own failure is what reports a release that ships no binary.

tests.test_virustotal.test_scan_virustotal_rejects_an_unusable_target(args, message, tmp_path)[source]

Nothing is read or uploaded when the binaries cannot be located.

tests.test_virustotal.test_scan_virustotal_rejects_a_dependent_option_alone(args, message, tmp_path)[source]

An option that only refines another is refused without it.

tests.test_virustotal.test_scan_virustotal_uploads_nothing_without_binaries(tmp_path)[source]

A directory holding no .bin or .exe file ends the run before any upload.

tests.test_virustotal.test_scan_virustotal_reports_uploads_that_all_failed(tmp_path)[source]

No upload to poll ends the run, with the failure stated.

tests.test_virustotal.test_scan_virustotal_records_each_polled_verdict(tmp_path)[source]

Each verdict prints and lands in the history, once per day.

tests.test_virustotal.test_scan_virustotal_carries_the_history_from_its_branch(tmp_path)[source]

--carry-from restores the history before anything is scanned.

tests.test_virustotal.test_scan_virustotal_reports_a_failed_download(tmp_path)[source]

A release whose binaries cannot be fetched ends on a clean error.