Skip to content

Add a HAR importer that turns a captured session into tests - #130

Merged
JE-Chen merged 5 commits into
mainfrom
dev
Jul 26, 2026
Merged

Add a HAR importer that turns a captured session into tests#130
JE-Chen merged 5 commits into
mainfrom
dev

Conversation

@JE-Chen

@JE-Chen JE-Chen commented Jul 25, 2026

Copy link
Copy Markdown
Member

What changed

Copying one request as cURL covers one call; a browser can save the whole session as HAR. This adds a tool that reads that export and generates a test suite from it, reusing everything the cURL importer already has.

HAR Import (new tool)

Open a .har file and every recorded call is listed with its method, path, status and media type. Page furniture — stylesheets, images, fonts — is filtered out by default, so what remains is the API traffic worth testing. Select the calls you want, or take everything listed, and generate one script for the whole set using the same targets as the cURL importer:

  • a pytest file with one test per call
  • an APITestka action list replaying the flow in capture order
  • a requests walkthrough, an APITestka Python script, or LoadDensity runs

Recorded headers, cookies, query parameters and bodies (raw, URL-encoded and multipart, including file uploads) map onto the existing CurlRequest, so every generator and tool already accepts them. HAR is JSON, so this needs nothing beyond the standard library, and nothing is ever replayed for you.

Details worth calling out

  • No silent loss in the load-test target. LoadDensity's shared task template iterates tasks.items(), which is keyed by HTTP method — merging several captured calls into one tasks dict would keep only the last GET. Each call therefore gets its own start_test run, with a comment saying why.
  • No test silently replacing another. Two recorded calls to the same endpoint would define the same pytest function twice; repeats are numbered (test_get_api_items_2).
  • Nothing sent twice. A HAR records cookies both structurally and in the Cookie header; the structured list wins and the duplicate header is dropped. HTTP/2 pseudo-headers (:method, :authority) are dropped too, since passing them on produces code that fails.
  • The two importers agree. A single selected request generates exactly what the cURL importer would, pinned by a test.

Supporting refactors (separate commits)

  • The header-collection rule (combine repeats, compare names case-insensitively) moves out of the curl parser into header_tools/header_merge, where both importers use it.
  • The script templates expose their per-request blocks — the call block, the pytest function with an optional name, the APITestka action as data — so a batch can be built from the same pieces as a single request. Generated text is unchanged.

Verification

  • ruff check . — clean
  • pytest test/test_utils — 824 passed (65 new, including a hypothesis property that parse_har on an arbitrary JSON document either returns entries or raises HarParseException, never a bare KeyError/TypeError from a mistyped HAR field)
  • test_language_parity.py — English/Chinese keys and placeholders in parity

JE-Chen added 3 commits July 25, 2026 16:00
The rule for collecting headers one at a time — combine a repeated name
the way a receiver combines field lines, compare names case-insensitively,
keep the spelling first seen — was private to the curl parser, and the
HAR importer will need exactly the same behaviour.

Move it to header_tools/header_merge, where generic header logic already
lives, and drop the content-type constant the curl parser no longer used.
Each target template was one function that emitted its import line, its
call and its trailing print together, so a caller writing several requests
into one script could not reuse any of it.

Split out the call block, the pytest function (with an optional name for
when several tests share a file) and the APITestka action as data, then
build the single-request templates from those pieces. The generated text
is unchanged.
Copying one request as cURL covers one call; a browser can save the whole
session as HAR. The new tool reads that export, lists each recorded call
with its method, path, status and media type, and hides page assets
(stylesheets, images, fonts) behind a filter so what is left is the API
traffic worth testing.

Selected calls are generated into one script for the same targets the
cURL importer offers: a pytest file with one test per call, an APITestka
action list replaying the flow in capture order, a requests walkthrough,
an APITestka script, or LoadDensity runs. Repeated endpoints get numbered
test names so no test silently replaces another, and the load-test target
emits one run per call because the shared task template holds a single
URL per HTTP method.

Recorded headers, cookies, query parameters and bodies (raw, URL-encoded
and multipart) map onto the existing CurlRequest, so every generator and
tool already accepts them. HTTP/2 pseudo-headers are dropped, and a
Cookie header duplicating the recorded cookie list is removed so nothing
is sent twice. A single selected request generates exactly what the cURL
importer would.
@codacy-production

codacy-production Bot commented Jul 25, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 129 complexity · 0 duplication

Metric Results
Complexity 129
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

JE-Chen added 2 commits July 25, 2026 16:39
The response analyzer kept its own copy of the token pattern so it could
spot a JWT inside a larger text. That scan belongs with the decoder, and
the header analyzer needs it too.

Expose find_tokens there, returning the distinct tokens in the order they
appear, and have the response analyzer use it.
Three findings still had to be copied between tools by hand. Each now
opens where it belongs, pre-filled:

- the cURL importer opens its parsed headers in the header analyzer
- the header analyzer opens a bearer token found in any header in the
  JWT decoder, decoded
- the response inspector opens a JSON body in the JSON format tool,
  already pretty-printed

Each button stays disabled until the analysis actually has something to
hand over, so it never opens an empty tab. The JSON format tool gained
the initial_json argument the hand-off needs, matching how the decoder,
status reference, URL builder and header analyzer are pre-filled.
@sonarqubecloud

Copy link
Copy Markdown

@JE-Chen
JE-Chen merged commit c67bde8 into main Jul 26, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant