Skip to content

fix: resolve three defects found by UI test audit - #1

Closed
voidvec wants to merge 9 commits into
masterfrom
fix/three-defects-from-ui-audit
Closed

voidvec wants to merge 9 commits into
masterfrom
fix/three-defects-from-ui-audit

Conversation

@voidvec

@voidvec voidvec commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes three defects discovered during comprehensive UI interaction testing:

1. Abort empty stub -> real implementation

onAbortTask() was completely empty. Now:

  • m_currentTaskId tracks the running request via NetworkReply::task()->id
  • onAbortTask() calls stopRequest(taskId) and clears the id
  • onResponse() clears taskId on normal completion

2. Auth credential env var expansion

applyAuthHeader() now resolves {{var}} placeholders in auth credentials (Basic username/password, Bearer token, ApiKey key/value) before writing the Authorization header.

3. Silent auth credential fallback -> user warning

When a non-None auth type is selected but required credentials are empty, onSendRequest() now shows a QMessageBox::warning so the user knows the request will be sent without authentication.

Test changes

  • testAbortRunningRequest: real abort flow with mock server (was QSKIP)
  • testApplyAuthHeader: added {{token}} env expansion case
  • testAuthEmptyCredentialFallback: added warning dialog auto-dismiss case

Test results

100% tests passed, 0 tests failed out of 7
Total Test time = 29.16 sec

voidvec added 9 commits August 4, 2026 19:09
…nse extras

- Add AuthConfig with Basic/Bearer/ApiKey(in Header or Query) support
- Extend RequestContext with BodyType(raw/json/xml/form-urlencoded/binary/form-data),
  binaryBody and queryParams; fluent Builder methods for each
- Enhance ResponseResult with cookies, json() and contentType()
- Apply auth/body/query params in NetworkRequest pipeline
- GUI: auth settings dialog (incl. ApiKey), binary body page, response Cookies tab,
  route UI through RequestContextBuilder
- Tests: AuthTests/BodyTypeTests suites + GUI buildRequestContext/auth/cookies cases
- Fix greedy hex-escape bugs in test fixtures ('\x00DEF' -> C7744, '\x00data' -> 0x00DA)
…y, cleanup and test.bat

- Add NetworkRequestTool::applyBodySyntaxHighlighting to colorize raw JSON/XML
  request bodies (reuses JsonSyntaxHighlighter/XmlSyntaxHighlighter); clears the
  4 TODO placeholders in onBodyTypeChanged/onRawTypeChanged
- Add RequestContextBuilder::bodyFormData(files, kvPairs) so BodyType::FormData
  has a symmetric builder; add testBodyFormDataBuilder (BuilderTests)
- Section F cleanup: merge duplicate cmb_body_type slot onBodyTypeComboChanged
  into onBodyTypeChanged (also fixes cmb_raw_type left enabled after binary), and
  remove the duplicate table_headers cellChanged lambda in the constructor
- Add scripts/test.bat (Windows counterpart of test_local.sh): Qt/VS auto-detect,
  configure + build + ctest, with /clean /nobuild /debug /help and ctest exit code
  propagated
…/ Postman v2.1)

New files:
- include/authconfig.h: OAuth2Config with 4 grant types + bearer token header
- include/environment.h, source/environment.cpp: env var substitution
  ({{var}} in url/headers/body/queryParams/auth fields)
- include/oauth2tokencache.h, source/oauth2tokencache.cpp: Q_GLOBAL_STATIC
  singleton token cache with expiry
- include/collectionmodel.h, source/collectionmodel.cpp: in-memory Collection
  tree (folders + requests) with JSON serde, findById, add/remove
- include/postmanconverter.h, source/postmanconverter.cpp: Postman v2.1
  bidirectional converter (auth round-trip, nested folders, request mapping)
- test/test_environment.*, test/test_collection.*, test/test_postman.*,
  test/main_collection.cpp: BuilderTests env cases + CollectionTests

Modified:
- include/requestcontext.h: BodyType::FormData + bodyFormData() builder
- source/CMakeLists.txt, test/CMakeLists.txt: added new sources
- scripts/test.bat: Windows test runner (walk-up repo root, goto VS detect,
  no EnableDelayedExpansion, echo brackets avoided)

Fixes across 4 compile rounds: struct order, member/method name collision,
Q_GLOBAL_STATIC access, QJsonArray::reserve, Qt5.15 QString::replace API,
missing QJsonArray/QJsonObject includes, Postman import empty-id collision.

All 7 test suites pass.
Pipeline (B.2):
- source/networkrequest.cpp: wire applyEnvironment() into setRequestContext()
  after m_upContext = move and before m_url = QUrl, so {{var}} placeholders
  are resolved before the URL/headers/body/query/auth are finalized.

EnvironmentStore (B.3):
- include/environmentstore.h, source/environmentstore.cpp: multi-environment
  manager with JSON persistence ({active, environments[{name, variables}]}).
  NETWORK_EXPORT, no UI dependency.
- source/CMakeLists.txt: add environmentstore.cpp + .h

GUI (B.4):
- networkrequesttool.h: EnvironmentStore m_envStore, QComboBox/PushButton
  members, onEnvironmentChanged/onManageEnvironments slots, helpers.
- networkrequesttool.cpp:
  - initializeUI(): inject cmb_environment + btn_manage_env into request toolbar
  - buildRequestContext(): append .environment(m_envStore.activeVariables())
  - loadEnvironments(): load from environments.json, create default 'Dev' on
    first run (host=localhost, port=8080)
  - onManageEnvironments(): full dialog with QListWidget env picker +
    QTableWidget variable editor + Add/Remove + JSON persistence

UiTests:
- test_qtrequester.h/.cpp: test_envDropdownSwitch verifies combo exists,
  No Environment yields empty map, Dev selection populates activeVariables,
  buildRequestContext carries the env map.

All 7 test suites green.
R1 — applyAuthConfig OAuth2 case:
- source/networkrequest.cpp: fold AuthType::OAuth2 into Basic/Bearer group
  so the fetched access_token is written to the Authorization header.

Refactor start() -> performSend():
- source/networkcommonrequest.h: add #include <functional>, private methods
  performSend() and fetchOAuth2Token(onReady).
- source/networkcommonrequest.cpp: extract send block (L98-243) into
  performSend(). start() calls fetchOAuth2Token for OAuth2, else directly
  calls performSend().

fetchOAuth2Token async token retrieval:
- Cache key via OAuth2TokenCache::makeKey(). Synchronous fast path on cache
  hit (find() + isValid()). Miss -> POST to tokenUrl via thread-affine NAM
  (NetworkRequestManager::acquireThreadNam()). Parse JSON {access_token,
  refresh_token, expires_in}, store to OAuth2TokenCache::instance().store().
  Errors reported via setError() + emit response(ToFailedResult()).

401 auto-refresh:
- m_bOAuthRefreshed flag moved from NetworkCommonRequest to base class
  NetworkRequest (source/networkrequest.h). Reset in setRequestContext().
- handleFailure() in base class gains OAuth2 401 branch: after tryRetry(),
  before redirect. On 401 + OAuth2 + !refreshed + refreshToken available:
  set refreshed=true, swap grant to RefreshToken, cleanup + restart().
  Second 401 skips refresh (guard), emits failure.

GUI OAuth2 settings (A.4):
- RequestSettings gains 8 OAuth2 fields (grantType/clientId/clientSecret/
  scopes/tokenUrl/username/password/refreshToken).
- onSettingsClicked(): add 'OAuth2' to authTypeCombo, add OAuth2 controls
  (grant combo + 7 QLineEdits), show/hide by grant type (Password shows
  user/pass, Refresh Token shows refresh field).
- buildAuthConfig(): new OAuth2 branch builds OAuth2Config from settings
  and returns AuthConfig::oauth2(cfg).

All 7 test suites green.
GUI-only enhancement — inject a compact toolbar into the response body tab:

Search (QLineEdit + prev/next buttons):
- onResponseSearchChanged: re-search on every keystroke
- doResponseSearch: QTextDocument::find() to collect all matches,
  highlight with QTextEdit::ExtraSelection (yellow for all, orange for
  current hit)
- navigateSearchHit(): wrap-around prev/next with scroll-to-hit

Copy: QApplication::clipboard()->setText(response body)
Save: QFileDialog::getSaveFileName + QFile::write(response body)
Pretty/Raw toggle: re-format JSON with QJsonDocument::Indented (pretty)
  or show raw m_lastResponseBody. Non-JSON responses ignore the toggle.

onResponse() now stores m_lastResponseBody + m_isResponseJson.
Response toolbar injected at index 1 in verticalLayout_response_body
(after m_labelResponseInfo, mirroring existing injection pattern).

All 7 test suites green.
R2 — auth serialize/deserialize:
- saveToDisk(): serialize all auth fields (type/Basic/Bearer/ApiKey/OAuth2)
  alongside method/url/body/headers so authenticated requests survive
  save/load and Postman round-trips.
- loadRequestFromJson(): new reusable method extracts form population from
  loadFromDisk; restores auth fields into m_settings. loadFromDisk() is
  now a thin file-I/O wrapper calling loadRequestFromJson.

D.1 — loadFromDisk refactor:
- Dead loadFromDisk() refactored into loadRequestFromJson(const QJsonObject&),
  reusable by collection-click (D.3) and Postman import (E.2).
- loadFromDisk() = open file + parse JSON + call loadRequestFromJson.

D.2-D.4 — Collection tree panel:
- buildCollectionPanel(): inject QTreeWidget + toolbar (+Coll/+Folder/+Req/
  Import/Export) into left panel layout above history list.
- loadCollection()/saveCollection(): persist to collection.json via
  Collection::load/save.
- populateCollectionTree(): rebuild tree from Collection model (folders
  have folder icon, requests have file icon).
- onNewCollection(): create a named collection.
- onAddCollectionFolder(): add folder under selected node or root.
- onAddCollectionRequest(): snapshot current form (url/method/body/headers/
  auth) as a collection request node.
- onCollectionItemClicked(): load request into form via loadRequestFromJson.
- onImportPostman(): QFileDialog -> PostmanConverter::fromPostmanV21.
- onExportPostman(): PostmanConverter::toPostmanV21 -> QFileDialog write.

All 7 test suites green.
Tests:
- HttpTestServer: add /oauth/token endpoint supporting client_credentials,
  password, and refresh_token grants with proper validation.
- AuthTests: 5 new OAuth2 integration tests (client_credentials, cache hit,
  password grant, refresh_token, tokenUrl-missing rejection).
- UiTests: testSaveAndLoadDiskWithAuth (R2 auth serialisation round-trip
  for Bearer + OAuth2 fields), testResponseSearch (search highlighting +
  prev/next navigation).

Bug fix:
- include/requestcontext.h: add missing RequestContextBuilder::authOAuth2()
  method (A.2 from original plan, missed during M0).

Docs:
- Changelog: v2.3.0 entry covering all M0-M4 features.
- README.zh-CN.md: add OAuth2, environments, response toolbar, collections,
  Postman v2.1 to feature list.
- AGENTS.md: update directory map (16 public headers, 7 test suites,
  39+ source files).

All 7 test suites green.
1. Fix abort empty stub: onAbortTask() now stores taskId from NetworkReply
   and calls stopRequest(); onResponse() clears taskId on completion.

2. Fix auth env var expansion: applyAuthHeader() now resolves {{var}}
   placeholders in Basic/Bearer/ApiKey credentials before writing headers.

3. Fix silent auth fallback: onSendRequest() now warns via QMessageBox when
   a non-None auth type is selected but required credentials are empty.

Tests updated:
- testAbortRunningRequest: real abort flow with mock server (was QSKIP)
- testApplyAuthHeader: added {{token}} env expansion case
- testAuthEmptyCredentialFallback: added warning dialog auto-dismiss case
@voidvec voidvec closed this Aug 6, 2026
@voidvec
voidvec deleted the fix/three-defects-from-ui-audit branch August 6, 2026 04:30
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