Skip to content

chore(release): Merge v2.0.0 back into develop - #33

Merged
soulevilx merged 3 commits into
developfrom
master
Jul 21, 2026
Merged

chore(release): Merge v2.0.0 back into develop#33
soulevilx merged 3 commits into
developfrom
master

Conversation

@soulevilx

@soulevilx soulevilx commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Release merge-back\n\nSynchronizes the published v2.0.0 release from master back into develop.\n\n- Release: https://github.com/jooservices/client/releases/tag/v2.0.0\n- Source release PR: #32\n- Tag: v2.0.0\n\nAll release validation, publication, and Packagist update jobs succeeded.

Summary by CodeRabbit

  • New Features
    • Added JSON request, multipart upload, redirect, proxy, and response-inspection helpers.
    • Added builder-native HTTP fakes with queued responses, request recording, assertions, retries, and deterministic timing.
    • Added configurable native MongoDB logging with schema versioning, bounded timeouts, and sanitized event documents.
    • Added cURL export with sensitive-header redaction.
  • Bug Fixes
    • Improved timeout detection for Guzzle connection errors.
  • Documentation
    • Updated installation, migration, API, MongoDB, testing, and release documentation for version 2.0.

dependabot Bot and others added 3 commits July 21, 2026 02:24
Bumps [guzzlehttp/guzzle](https://github.com/guzzle/guzzle) from 7.15.1 to 8.0.0.
- [Release notes](https://github.com/guzzle/guzzle/releases)
- [Changelog](https://github.com/guzzle/guzzle/blob/8.0/CHANGELOG.md)
- [Commits](guzzle/guzzle@7.15.1...8.0.0)

---
updated-dependencies:
- dependency-name: guzzlehttp/guzzle
  dependency-version: 8.0.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This release updates the package to Guzzle 8 and native MongoDB logging, adds JSON/upload and response helpers, introduces builder-native HTTP fakes, refactors middleware handling, updates CI with MongoDB services, and expands documentation and test coverage.

Changes

2.0 runtime and release alignment

Layer / File(s) Summary
Dependencies, CI, container, and documentation
.github/workflows/*, composer.json, Dockerfile, README.md, UPGRADE-2.0.md, docs/*, CHANGELOG.md
Guzzle 8, native MongoDB, ext-mongodb, MongoDB-backed CI validation, non-root containers, and 2.0 documentation are introduced.
HTTP and response APIs
src/Contracts/*, src/Client/HttpClient.php, src/Response/ResponseWrapper.php, src/Support/*
JSON and multipart helpers, response body/header/status utilities, DTO fallbacks, cURL export, and strict Guzzle option validation are added.
MongoDB logging
src/Logging/*, src/Client/ClientBuilder.php, tests/Feature/Logging/*, tests/Unit/Logging/*
MongoDB logging uses validated configuration, native collection writers, BSON timestamps, schema versions, explicit writers, and swallowed write failures.
Middleware and resilience
src/Middleware/MiddlewarePipeline.php, src/Adapters/Guzzle/*, src/Support/NullSleeper.php
Middleware wrapping and response validation are centralized, timeout detection uses exception messages, and fake clients use no-op sleeping.
HTTP fake toolkit
src/Testing/*, src/Client/ClientBuilder.php
Shared fake queues, response factories, recorded requests, redacted cURL/array output, request assertions, registry state, and teardown cleanup are added.
Validation coverage
tests/Unit/*, tests/TestCase.php, phpstan*.neon
Tests cover the new HTTP, MongoDB, middleware, response, option-shaping, fake-client, and static-analysis behavior.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description notes the merge-back goal, but it skips the required template sections for testing, risk, docs impact, and checklist. Rewrite the PR description using the repository template and fill in Summary, What changed, Why, How tested, Documentation impact, Risk / rollback, and Checklist.
Docstring Coverage ⚠️ Warning Docstring coverage is 44.44% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the release merge-back into develop for v2.0.0.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch master

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov-commenter

codecov-commenter commented Jul 21, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.72%. Comparing base (bb4e6e7) to head (4a4f217).
⚠️ Report is 4 commits behind head on develop.

Additional details and impacted files
@@              Coverage Diff              @@
##             develop      #33      +/-   ##
=============================================
+ Coverage      98.32%   98.72%   +0.40%     
- Complexity       729      940     +211     
=============================================
  Files             61       71      +10     
  Lines           1732     2198     +466     
=============================================
+ Hits            1703     2170     +467     
+ Misses            29       28       -1     
Flag Coverage Δ
unittests 98.72% <100.00%> (+0.40%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@soulevilx
soulevilx merged commit 65159bf into develop Jul 21, 2026
29 of 30 checks passed
@soulevilx
soulevilx deleted the master branch July 21, 2026 22:04

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 16

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
docs/00-architecture/04-modules-and-domains.md (1)

380-391: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Document schema_version in the MongoDB event structure.

The supplied MongoDbLogger implementation writes schema_version alongside logged_at, but the structure listed here omits it. Add the field so consumers can implement the documented schema contract correctly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/00-architecture/04-modules-and-domains.md` around lines 380 - 391,
Update the MongoDB event “Document Structure” list to include the schema_version
field alongside logged_at, matching the fields written by MongoDbLogger and
documenting its schema-contract value.
docs/00-architecture/05-data-flow.md (1)

587-593: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Update the remaining MongoDB configuration fields.

After Line [587] changes storage to native MongoDB\Collection documents, Lines [590-593] still describe a connection name. Document uri, database, and collection (or an injected native collection) instead, matching the 2.0 API.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/00-architecture/05-data-flow.md` around lines 587 - 593, Update the
MongoDB Configuration section to remove the connection-name field and document
uri, database, and collection, including support for an injected native
MongoDB.Collection, consistent with the 2.0 API. Keep the existing body
truncation limits and header redaction rules entries.
tests/Unit/Adapters/GuzzleHttpClientAdapterTest.php (2)

88-94: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

TransferException takes no RequestInterface argument here
GuzzleHttp\Exception\TransferException uses the default RuntimeException constructor, so passing $request as the second arg will throw a TypeError before the adapter is exercised.

🐛 Proposed fix
-            new \GuzzleHttp\Exception\TransferException('Transfer error', $request),
+            new \GuzzleHttp\Exception\TransferException('Transfer error'),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Unit/Adapters/GuzzleHttpClientAdapterTest.php` around lines 88 - 94,
Update test_throws_ClientException_on_non_bad_guzzle_exception to construct
TransferException with only its supported message argument, removing the
$request parameter; keep the request for the adapter invocation so the test
reaches and verifies the intended exception-handling behavior.

1-1: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Drop the RequestInterface argument here and in tests/Unit/Adapters/GuzzleHttpClientAdapterAsyncTest.php. GuzzleHttp\Exception\TransferException uses the base exception constructor, so the second argument is treated as the exception code; passing $request triggers a TypeError before the ClientException path is exercised.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Unit/Adapters/GuzzleHttpClientAdapterTest.php` at line 1, Remove the
RequestInterface argument from TransferException construction in
GuzzleHttpClientAdapterTest and GuzzleHttpClientAdapterAsyncTest, leaving only
arguments supported by the base exception constructor so the ClientException
path is exercised without a TypeError.
tests/Unit/Adapters/GuzzleHttpClientAdapterAsyncTest.php (1)

77-90: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Pass only the message to TransferException The second argument is the exception code, so this throws a TypeError before the async wrapper path runs. Change it to new \GuzzleHttp\Exception\TransferException('Transfer error').

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Unit/Adapters/GuzzleHttpClientAdapterAsyncTest.php` around lines 77 -
90, Update test_wraps_generic_guzzle_exception_in_async_promise to construct
TransferException with only the error message, removing the Request argument so
the async rejection reaches the intended ClientException wrapper path.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@composer.json`:
- Around line 39-40: Move the ext-mongodb entry from the suggest section into
Composer's require section, preserving its existing constraint and removing the
duplicate suggestion entry. Leave the other dependency declarations unchanged.

In `@docs/00-architecture/01-project-overview.md`:
- Around line 15-17: Update the **Name** field in the project overview to use
the canonical product name “JOOservices Client”, while retaining
“jooservices/client” only for the Composer package identifier.

In `@docs/00-architecture/04-modules-and-domains.md`:
- Around line 393-395: Update the logging contract near the confirmed MongoDB
logger behavior to avoid guaranteeing persistence when MongoDbLogger::log()
swallows writer failures. Replace the “one document is written for each logger
call” wording with an accurate statement that one write is attempted or that a
document is written only after successful persistence, while retaining the
sanitization, immutability, timeout, and failure-isolation guarantees.

In `@docs/01-getting-started/basic-concepts.md`:
- Line 210: Update the surrounding MongoDB documentation to remove legacy claims
that MongoDB-Laravel is a dependency or that Laravel application integration is
implemented. Ensure the 2.0+ guidance states native mongodb/mongodb:^2.0 usage,
the required ext-mongodb extension, and the absence of Laravel application
integration.

In `@docs/05-maintenance/BACKLOG-POST-2.0.md`:
- Line 41: Update the user-facing architecture page 04-modules-and-domains.md to
remove or correct outdated “Features NOT Implemented” entries for middleware
capabilities already present in the codebase and changelog, including rate
limiting, signing, metrics, and tracing. Keep the M7 backlog entry only for any
remaining cleanup after the page is corrected.

In `@README.md`:
- Around line 87-99: Update both fake-client examples in README.md to include
imports for ClientBuilder, RetryConfig, and Tests\TestCase so each snippet is
self-contained and works when copied independently.
- Around line 68-70: Update the README JSON client example to configure a base
URI via ClientBuilder::withBaseUri before calling postJson('/users', ...), or
change the request to an absolute URL so the example resolves correctly.
- Around line 213-220: Update the README examples around ClientBuilder and
MongoDbLogConfig to read getenv('MONGODB_URI') once, validate that it returns a
string before use, and reuse the validated URI in both configuration paths.
Preserve the existing example behavior while preventing false from being passed
to withMongoUriLogging() or MongoDbLogConfig::__construct().

In `@src/Client/ClientBuilder.php`:
- Around line 510-512: Preserve caller-defined resilience middleware order by
recording the insertion position when configuring rate limiting in
ClientBuilder’s rate-limit method (src/Client/ClientBuilder.php:510-512) and
retry in its retry method (src/Client/ClientBuilder.php:552-554), rather than
only storing configuration. Update the middleware materialization logic at
src/Client/ClientBuilder.php:816-830 to insert each deferred entry at its
recorded position, while retaining client-specific retry and resilience handling
where it intentionally differs from DTO behavior.

In `@src/Support/CurlExporter.php`:
- Around line 51-54: Replace the hand-rolled apostrophe escaping in
CurlExporter::quote() with PHP’s escapeshellarg() so values containing single
quotes produce valid shell arguments. Add a regression test covering a value
such as “it's” and verify the exported curl command preserves it correctly.

In `@src/Testing/InteractsWithHttpClient.php`:
- Around line 19-23: Update the tearDown method to wrap parent::tearDown() in a
finally block so ClientBuilder::clearFake() always executes, including when
parent teardown throws.

In `@tests/Feature/Logging/MongoDbLoggingTest.php`:
- Around line 124-126: Update the assertions in the MongoDB logging test to
correlate both checks to the same test run: retrieve one document using the
unique $marker and assert its response_payload equals 'YYYYYYYYYY'. Avoid
separate collection-wide queries that could match unrelated stale documents.

In `@tests/Unit/Client/ClientBuilderExtendedTest.php`:
- Around line 141-151: Update
test_withSleeper_overrides_the_default_sleeper_even_when_not_faked to exercise
the retry path: configure a mocked handler to return 503 followed by 200,
execute a request through the built client, and assert the injected
SleeperInterface mock receives exactly one sleep call. Keep the test focused on
verifying withSleeper() is honored during retry handling.

In `@tests/Unit/Logging/MongoDbLoggerFactoriesTest.php`:
- Around line 27-65: The unit tests in
tests/Unit/Logging/MongoDbLoggerFactoriesTest.php lines 27-65 and
tests/Unit/Client/ClientBuilderMongoLoggingTest.php lines 19-54 must stop
invoking real MongoDB writes against 127.0.0.1:1. Replace the MongoDB writer in
the affected factory and client-builder tests with a writer double while
preserving assertions for configuration wiring and swallowed write failures;
leave live insertOne coverage to integration tests.

In `@tests/Unit/Response/ResponseWrapperTest.php`:
- Around line 148-155: Update tests/Unit/Response/ResponseWrapperTest.php lines
148-155 to seek the response body to a nonzero offset before calling body(),
then assert the underlying stream tell() remains unchanged. Update
tests/Unit/Testing/RecordedRequestTest.php lines 34-40 similarly: seek the
request body before capture() and assert its tell() position is preserved.

In `@tests/Unit/Support/NullSleeperTest.php`:
- Around line 23-32: Remove the hrtime-based elapsedMs measurement and
assertLessThan wall-clock check from
test_sleep_returns_immediately_regardless_of_duration in NullSleeperTest. Keep
the NullSleeper::sleep(500) invocation so the interface behavior remains covered
by the existing invocation test.

---

Outside diff comments:
In `@docs/00-architecture/04-modules-and-domains.md`:
- Around line 380-391: Update the MongoDB event “Document Structure” list to
include the schema_version field alongside logged_at, matching the fields
written by MongoDbLogger and documenting its schema-contract value.

In `@docs/00-architecture/05-data-flow.md`:
- Around line 587-593: Update the MongoDB Configuration section to remove the
connection-name field and document uri, database, and collection, including
support for an injected native MongoDB.Collection, consistent with the 2.0 API.
Keep the existing body truncation limits and header redaction rules entries.

In `@tests/Unit/Adapters/GuzzleHttpClientAdapterAsyncTest.php`:
- Around line 77-90: Update test_wraps_generic_guzzle_exception_in_async_promise
to construct TransferException with only the error message, removing the Request
argument so the async rejection reaches the intended ClientException wrapper
path.

In `@tests/Unit/Adapters/GuzzleHttpClientAdapterTest.php`:
- Around line 88-94: Update
test_throws_ClientException_on_non_bad_guzzle_exception to construct
TransferException with only its supported message argument, removing the
$request parameter; keep the request for the adapter invocation so the test
reaches and verifies the intended exception-handling behavior.
- Line 1: Remove the RequestInterface argument from TransferException
construction in GuzzleHttpClientAdapterTest and
GuzzleHttpClientAdapterAsyncTest, leaving only arguments supported by the base
exception constructor so the ClientException path is exercised without a
TypeError.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 99d5d9d7-39fb-48ec-9c5e-8dfd13b01482

📥 Commits

Reviewing files that changed from the base of the PR and between bb4e6e7 and 4a4f217.

⛔ Files ignored due to path filters (1)
  • composer.lock is excluded by !**/*.lock
📒 Files selected for processing (75)
  • .github/workflows/ci.yml
  • .github/workflows/release.yml
  • AGENTS.md
  • CHANGELOG.md
  • Dockerfile
  • README.md
  • UPGRADE-2.0.md
  • ai/skills/http-logging.md
  • ai/skills/mongodb-config.md
  • composer.json
  • docs/00-architecture/01-project-overview.md
  • docs/00-architecture/03-tech-stack.md
  • docs/00-architecture/04-modules-and-domains.md
  • docs/00-architecture/05-data-flow.md
  • docs/00-architecture/business-context-and-goals.md
  • docs/01-getting-started/basic-concepts.md
  • docs/01-getting-started/installation.md
  • docs/02-user-guide/api-reference.md
  • docs/04-development/confidence-levels.md
  • docs/04-development/documentation-standards.md
  • docs/04-development/glossary.md
  • docs/04-development/templates-and-writing-rules.md
  • docs/05-maintenance/BACKLOG-POST-2.0.md
  • docs/05-maintenance/PR1-REVIEW-VERIFICATION.md
  • docs/05-maintenance/README.md
  • phpstan-tests-baseline.neon
  • phpstan.neon
  • src/Adapters/Guzzle/GuzzleHttpClientAdapter.php
  • src/Client/ClientBuilder.php
  • src/Client/HttpClient.php
  • src/Contracts/AsyncHttpClientInterface.php
  • src/Contracts/HttpClientInterface.php
  • src/Contracts/ResponseWrapperInterface.php
  • src/Contracts/WanIpProviderInterface.php
  • src/Logging/MongoDbLogConfig.php
  • src/Logging/MongoDbLogger.php
  • src/Middleware/MiddlewarePipeline.php
  • src/Models/Mongo/ClientRequestLog.php
  • src/Response/ResponseWrapper.php
  • src/Support/CurlExporter.php
  • src/Support/GuzzleOptionShape.php
  • src/Support/NullSleeper.php
  • src/Testing/AssertionException.php
  • src/Testing/FakeHttpClient.php
  • src/Testing/HttpFake.php
  • src/Testing/HttpFakeRegistry.php
  • src/Testing/InteractsWithHttpClient.php
  • src/Testing/RecordedRequest.php
  • src/Testing/TestResponse.php
  • src/Testing/TestResponseSequence.php
  • tests/Feature/Logging/MongoDbLoggingTest.php
  • tests/TestCase.php
  • tests/Unit/Adapters/GuzzleHttpClientAdapterAsyncTest.php
  • tests/Unit/Adapters/GuzzleHttpClientAdapterTest.php
  • tests/Unit/Client/ClientBuilderExtendedTest.php
  • tests/Unit/Client/ClientBuilderFakeTest.php
  • tests/Unit/Client/ClientBuilderMongoLoggingTest.php
  • tests/Unit/Client/HttpClientAsyncTest.php
  • tests/Unit/Client/HttpClientJsonAndUploadTest.php
  • tests/Unit/Logging/MongoDbLogConfigTest.php
  • tests/Unit/Logging/MongoDbLoggerDefaultWriterTest.php
  • tests/Unit/Logging/MongoDbLoggerFactoriesTest.php
  • tests/Unit/Logging/MongoDbLoggerTest.php
  • tests/Unit/Middleware/MiddlewarePipelineCoverageTest.php
  • tests/Unit/Response/ResponseWrapperTest.php
  • tests/Unit/Response/ResponseWrapperToDtoTest.php
  • tests/Unit/Support/CurlExporterTest.php
  • tests/Unit/Support/GuzzleOptionShapeTest.php
  • tests/Unit/Support/NullSleeperTest.php
  • tests/Unit/Testing/HttpFakeRegistryTest.php
  • tests/Unit/Testing/HttpFakeTest.php
  • tests/Unit/Testing/InteractsWithHttpClientTest.php
  • tests/Unit/Testing/RecordedRequestTest.php
  • tests/Unit/Testing/TestResponseSequenceTest.php
  • tests/Unit/Testing/TestResponseTest.php
💤 Files with no reviewable changes (3)
  • docs/05-maintenance/PR1-REVIEW-VERIFICATION.md
  • src/Models/Mongo/ClientRequestLog.php
  • docs/04-development/confidence-levels.md

Comment thread composer.json
Comment on lines +39 to +40
"jooservices/dto": "Optional DTO helpers for ResponseWrapper::toDto()",
"ext-mongodb": "Required to persist logs with MongoDbLogger (PECL mongodb); mongodb/mongodb hard-requires it at composer install time"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== composer.json ==\n'
cat -n composer.json | sed -n '1,140p'

printf '\n== Search for MongoDbLogger and mongodb usage ==\n'
rg -n "MongoDbLogger|ext-mongodb|mongodb/mongodb|MongoDB" .

Repository: jooservices/client

Length of output: 24612


Move ext-mongodb into require.

mongodb/mongodb already makes the native extension mandatory at install time, so listing it under suggest is misleading. Keep the install-time dependency surface explicit by requiring ext-mongodb and removing it from suggest.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@composer.json` around lines 39 - 40, Move the ext-mongodb entry from the
suggest section into Composer's require section, preserving its existing
constraint and removing the duplicate suggestion entry. Leave the other
dependency declarations unchanged.

Comment on lines 15 to +17
**Name**: jooservices/client
**Type**: PHP Library (Composer package)
**Version**: 1.3.0
**Release target**: 2.0.0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use the canonical product name for the repository identity.

Line [15] should say JOOservices Client; reserve jooservices/client for the Composer package identifier.

As per coding guidelines, use the canonical product name JOOservices Client; use jooservices/client only as the Composer identifier.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/00-architecture/01-project-overview.md` around lines 15 - 17, Update the
**Name** field in the project overview to use the canonical product name
“JOOservices Client”, while retaining “jooservices/client” only for the Composer
package identifier.

Source: Coding guidelines

Comment on lines 393 to +395
**Status**: ✅ Confirmed (implementation exists)

**Unknown**: Production-readiness and intended use case
**Contract**: One sanitized immutable PSR-3 event document is written for each logger call. Driver timeouts are bounded and write failures are swallowed so logging cannot break transport.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Do not promise a write when writer failures are swallowed.

Because MongoDbLogger::log() catches writer exceptions, “one document is written for each logger call” is not guaranteed. Change this to “one write is attempted” or “one document is written on successful persistence.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/00-architecture/04-modules-and-domains.md` around lines 393 - 395,
Update the logging contract near the confirmed MongoDB logger behavior to avoid
guaranteeing persistence when MongoDbLogger::log() swallows writer failures.
Replace the “one document is written for each logger call” wording with an
accurate statement that one write is attempted or that a document is written
only after successful persistence, while retaining the sanitization,
immutability, timeout, and failure-isolation guarantees.

- MongoDB-Laravel (adds Laravel coupling) ⚠️

**Risk (Medium)**: `mongodb/laravel-mongodb` dependency suggests Laravel coupling despite library being framework-agnostic.
**Compatibility note**: Mongo logging uses native `mongodb/mongodb`. Its `ext-mongodb` requirement applies at Composer install time to all consumers.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove the stale Laravel MongoDB claims from this section.

The surrounding text still says MongoDB-Laravel is a dependency and that Laravel integration is implemented, which contradicts the native-driver-only 2.0 contract and this compatibility note. Remove or rewrite those legacy statements.

As per coding guidelines, for 2.0 and later documentation must describe native mongodb/mongodb:^2.0, required ext-mongodb, and the absence of Laravel application integration.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/01-getting-started/basic-concepts.md` at line 210, Update the
surrounding MongoDB documentation to remove legacy claims that MongoDB-Laravel
is a dependency or that Laravel application integration is implemented. Ensure
the 2.0+ guidance states native mongodb/mongodb:^2.0 usage, the required
ext-mongodb extension, and the absence of Laravel application integration.

Source: Coding guidelines


| ID | Item | Notes |
|----|------|-------|
| M7 | Refresh stale “Features NOT Implemented” | `docs/00-architecture/04-modules-and-domains.md` still lists rate limit, signing, metrics, tracing, etc. as missing — outdated vs codebase/CHANGELOG. |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the stale architecture reference instead of deferring it.

This entry confirms that docs/00-architecture/04-modules-and-domains.md still tells users that implemented middleware is missing. Update that user-facing page now; retain M7 only for any residual cleanup.

As per coding guidelines, stale references should be fixed immediately rather than left as placeholder guidance.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/05-maintenance/BACKLOG-POST-2.0.md` at line 41, Update the user-facing
architecture page 04-modules-and-domains.md to remove or correct outdated
“Features NOT Implemented” entries for middleware capabilities already present
in the codebase and changelog, including rate limiting, signing, metrics, and
tracing. Keep the M7 backlog entry only for any remaining cleanup after the page
is corrected.

Source: Coding guidelines

Comment on lines +124 to +126
$collection = $mongo->selectCollection('jooservices_client', 'client_request_logs');
self::assertNotNull($collection->findOne(['message' => ['$regex' => $marker]]));
self::assertNotNull($collection->findOne(['response_payload' => 'YYYYYYYYYY']));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Correlate the persisted assertions to one test run.

The marker query and payload query can match separate stale documents. Use a marker-specific collection or a single correlated query/document before asserting the payload.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Feature/Logging/MongoDbLoggingTest.php` around lines 124 - 126, Update
the assertions in the MongoDB logging test to correlate both checks to the same
test run: retrieve one document using the unique $marker and assert its
response_payload equals 'YYYYYYYYYY'. Avoid separate collection-wide queries
that could match unrelated stale documents.

Comment on lines +141 to +151
public function test_withSleeper_overrides_the_default_sleeper_even_when_not_faked(): void
{
$sleeper = Mockery::mock(\JOOservices\Client\Contracts\SleeperInterface::class);
$sleeper->shouldReceive('sleep')->never();

ClientBuilder::create()
->withSleeper($sleeper)
->withRetry(new RetryConfig())
->build();

$this->addToAssertionCount(1);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the relevant test file and related symbols
git ls-files 'tests/Unit/Client/ClientBuilderExtendedTest.php' 'src/**/*.php' | sed -n '1,120p'

echo '--- outline: tests/Unit/Client/ClientBuilderExtendedTest.php ---'
ast-grep outline tests/Unit/Client/ClientBuilderExtendedTest.php --view expanded || true

echo '--- search for withSleeper / retry / sleeper usage ---'
rg -n "withSleeper|SleeperInterface|sleep\(|RetryConfig|withRetry|MockHandler|HandlerStack|withOption\('handler'" tests src -g '!vendor' -g '!node_modules'

Repository: jooservices/client

Length of output: 37632


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- ClientBuilderExtendedTest excerpt ---'
sed -n '135,160p' tests/Unit/Client/ClientBuilderExtendedTest.php

echo '--- ClientBuilder sleeper/retry wiring ---'
sed -n '280,340p' src/Client/ClientBuilder.php
sed -n '540,610p' src/Client/ClientBuilder.php
sed -n '730,790p' src/Client/ClientBuilder.php

echo '--- RetryMiddleware ---'
sed -n '1,140p' src/Middleware/RetryMiddleware.php

echo '--- existing retry/sleeper tests ---'
sed -n '1,220p' tests/Feature/Resilience/RetryTest.php
sed -n '240,340p' tests/Unit/Middleware/RetryMiddlewareTest.php

Repository: jooservices/client

Length of output: 15833


Exercise the retry path here. build() never calls sleep(), so this still passes if withSleeper() is ignored. Send a 503 then 200 through a mocked handler and assert the injected sleeper is called once.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Unit/Client/ClientBuilderExtendedTest.php` around lines 141 - 151,
Update test_withSleeper_overrides_the_default_sleeper_even_when_not_faked to
exercise the retry path: configure a mocked handler to return 503 followed by
200, execute a request through the built client, and assert the injected
SleeperInterface mock receives exactly one sleep call. Keep the test focused on
verifying withSleeper() is honored during retry handling.

Source: Coding guidelines

Comment on lines +27 to +65
public function test_fromUri_writer_swallows_failures_when_mongo_is_unreachable(): void
{
$logger = MongoDbLogger::fromUri('mongodb://127.0.0.1:1', 'logs');

$logger->info('unavailable writer is swallowed, not thrown');
$this->addToAssertionCount(1);
}

public function test_fromConfig_wires_the_configs_database_and_collection(): void
{
$config = new MongoDbLogConfig('mongodb://127.0.0.1:1', 'logs', 'custom_collection');
$logger = MongoDbLogger::fromConfig($config);

self::assertSame('logs', $logger->getConnection());
self::assertSame('custom_collection', $logger->getCollection());

// The writer targets a live Mongo instance and cannot be swapped via fromConfig();
// this only proves an unreachable Mongo does not raise out of log().
$logger->info('unreachable fromConfig writer is swallowed, not thrown');
$this->addToAssertionCount(1);
}

public function test_fromCollection_derives_connection_and_collection_from_the_mongo_collection(): void
{
$collection = (new MongoClient('mongodb://127.0.0.1:1'))->selectCollection('logs_db', 'events');
$logger = MongoDbLogger::fromCollection($collection);

self::assertSame('logs_db', $logger->getConnection());
self::assertSame('events', $logger->getCollection());
}

public function test_fromCollection_default_writer_calls_insertOne_and_swallows_its_failure(): void
{
$collection = (new MongoClient('mongodb://127.0.0.1:1'))->selectCollection('logs_db', 'events');
$logger = MongoDbLogger::fromCollection($collection);

$logger->info('unreachable collection insert is swallowed, not thrown');
$this->addToAssertionCount(1);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

For mongodb/mongodb 2.0, what is MongoDB\Client's default serverSelectionTimeoutMS, and does Collection::insertOne wait for server selection before throwing when no server is reachable?

💡 Result:

For the MongoDB PHP driver, the default value for serverSelectionTimeoutMS is 30,000 milliseconds (30 seconds) [1][2][3]. Regarding the behavior of Collection::insertOne when no server is reachable: yes, it does wait for server selection to complete before throwing an exception [4][3]. When an operation is performed, the driver initiates a server selection process to identify a suitable server (e.g., a primary for write operations) [1][5]. The driver will block and attempt to find a reachable server until the serverSelectionTimeoutMS limit is reached [4][3]. If no suitable server is found within that time, the driver throws a MongoDB\Driver\Exception\ConnectionTimeoutException (or a similar exception indicating that server selection has expired) [4]. Note that by default, the driver may also be configured with serverSelectionTryOnce set to true, which instructs the driver to perform a single scan of the deployment; if that initial scan fails to find a server, the driver may raise an error without exhausting the full timeout period unless explicitly configured otherwise [2][4][3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

git ls-files 'tests/Unit/Logging/MongoDbLoggerFactoriesTest.php' \
             'tests/Unit/Client/ClientBuilderMongoLoggingTest.php' \
             'src' | sed -n '1,200p'

Repository: jooservices/client

Length of output: 4092


🏁 Script executed:

#!/bin/bash
set -euo pipefail

ast-grep outline tests/Unit/Logging/MongoDbLoggerFactoriesTest.php --view expanded
printf '\n---\n'
ast-grep outline tests/Unit/Client/ClientBuilderMongoLoggingTest.php --view expanded
printf '\n---\n'
fd -a 'MongoDbLogger.php|ClientBuilder.php|MongoDbLogConfig.php' src tests

Repository: jooservices/client

Length of output: 1362


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '--- MongoDbLogger.php ---\n'
sed -n '1,240p' src/Logging/MongoDbLogger.php
printf '\n--- ClientBuilder.php ---\n'
sed -n '1,260p' src/Client/ClientBuilder.php
printf '\n--- MongoDbLogConfig.php ---\n'
sed -n '1,200p' src/Logging/MongoDbLogConfig.php
printf '\n--- tests/Unit/Logging/MongoDbLoggerFactoriesTest.php ---\n'
sed -n '1,220p' tests/Unit/Logging/MongoDbLoggerFactoriesTest.php
printf '\n--- tests/Unit/Client/ClientBuilderMongoLoggingTest.php ---\n'
sed -n '1,220p' tests/Unit/Client/ClientBuilderMongoLoggingTest.php

Repository: jooservices/client

Length of output: 247


🏁 Script executed:

#!/bin/bash
set -euo pipefail

php -v >/dev/null 2>&1 && echo "php-present" || echo "php-missing"
composer --version >/dev/null 2>&1 && echo "composer-present" || echo "composer-missing"

Repository: jooservices/client

Length of output: 185


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path

for path in [
    Path("src/Logging/MongoDbLogger.php"),
    Path("src/Client/ClientBuilder.php"),
    Path("src/Logging/MongoDbLogConfig.php"),
    Path("tests/Unit/Logging/MongoDbLoggerFactoriesTest.php"),
    Path("tests/Unit/Client/ClientBuilderMongoLoggingTest.php"),
]:
    print(f"\n### {path} ###")
    text = path.read_text()
    print(text[:12000])
PY

Repository: jooservices/client

Length of output: 31086


Keep MongoDB writes out of the unit suite.

tests/Unit/Logging/MongoDbLoggerFactoriesTest.php#L27-L65 and tests/Unit/Client/ClientBuilderMongoLoggingTest.php#L19-L54 still exercise the real MongoDB insertOne() path against 127.0.0.1:1. Those calls wait on server selection before the logger swallows the failure, so the unit suite can stall for the driver timeout and become flaky. Use a writer double for unit coverage; keep the live MongoDB path in integration tests.

📍 Affects 2 files
  • tests/Unit/Logging/MongoDbLoggerFactoriesTest.php#L27-L65 (this comment)
  • tests/Unit/Client/ClientBuilderMongoLoggingTest.php#L19-L54
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Unit/Logging/MongoDbLoggerFactoriesTest.php` around lines 27 - 65, The
unit tests in tests/Unit/Logging/MongoDbLoggerFactoriesTest.php lines 27-65 and
tests/Unit/Client/ClientBuilderMongoLoggingTest.php lines 19-54 must stop
invoking real MongoDB writes against 127.0.0.1:1. Replace the MongoDB writer in
the affected factory and client-builder tests with a writer double while
preserving assertions for configuration wiring and swallowed write failures;
leave live insertOne coverage to integration tests.

Comment on lines +148 to +155
public function test_body_preserves_the_stream_position_for_seekable_bodies(): void
{
$wrapper = new ResponseWrapper(new Response(200, [], 'abcdef'));

$wrapper->body();
$wrapper->body();

self::assertSame('abcdef', $wrapper->body());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert the original stream cursor directly.

Both tests pass without proving cursor restoration: repeated reads still work if each implementation rewinds before reading, and casting a Guzzle stream to string can seek independently.

  • tests/Unit/Response/ResponseWrapperTest.php#L148-L155: seek the response body to a nonzero offset before body(), then assert tell() remains at that offset.
  • tests/Unit/Testing/RecordedRequestTest.php#L34-L40: seek the request body to a nonzero offset before capture(), then assert tell() is unchanged.
📍 Affects 2 files
  • tests/Unit/Response/ResponseWrapperTest.php#L148-L155 (this comment)
  • tests/Unit/Testing/RecordedRequestTest.php#L34-L40
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Unit/Response/ResponseWrapperTest.php` around lines 148 - 155, Update
tests/Unit/Response/ResponseWrapperTest.php lines 148-155 to seek the response
body to a nonzero offset before calling body(), then assert the underlying
stream tell() remains unchanged. Update
tests/Unit/Testing/RecordedRequestTest.php lines 34-40 similarly: seek the
request body before capture() and assert its tell() position is preserved.

Comment on lines +23 to +32
public function test_sleep_returns_immediately_regardless_of_duration(): void
{
$sleeper = new NullSleeper();

$start = hrtime(true);
$sleeper->sleep(500);
$elapsedMs = (hrtime(true) - $start) / 1_000_000;

self::assertLessThan(50, $elapsedMs);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Remove the wall-clock assertion.

A paused or overloaded CI worker can exceed 50 ms even when NullSleeper::sleep() is a no-op, making this test flaky. The interface invocation test already covers the observable contract.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Unit/Support/NullSleeperTest.php` around lines 23 - 32, Remove the
hrtime-based elapsedMs measurement and assertLessThan wall-clock check from
test_sleep_returns_immediately_regardless_of_duration in NullSleeperTest. Keep
the NullSleeper::sleep(500) invocation so the interface behavior remains covered
by the existing invocation test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants