Skip to content

fix(fallback): Throw RuntimeException class on asset/json I/O failures.#111

Merged
terabytesoftw merged 1 commit intomainfrom
fix_mini_5
Jan 22, 2026
Merged

fix(fallback): Throw RuntimeException class on asset/json I/O failures.#111
terabytesoftw merged 1 commit intomainfrom
fix_mini_5

Conversation

@terabytesoftw
Copy link
Contributor

Q A
Is bugfix? ✔️
New feature?
Breaks BC?

@terabytesoftw terabytesoftw added the bug Something isn't working label Jan 22, 2026
@coderabbitai
Copy link

coderabbitai bot commented Jan 22, 2026

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes

    • Improved error handling for asset and JSON file operations to properly throw exceptions when file read, write, or remove operations fail.
  • Tests

    • Added test coverage for file operation failure scenarios to ensure proper exception handling.

✏️ Tip: You can customize this high-level summary in your review settings.

Walkthrough

This PR enhances error handling for file I/O operations in JSON and asset fallback classes by introducing RuntimeException throws on read/write/remove failures, alongside comprehensive test coverage using internal mocking infrastructure.

Changes

Cohort / File(s) Summary
Changelog
CHANGELOG.md
Added entry for bug #111 documenting RuntimeException behavior on asset/JSON I/O failures.
Source Error Handling
src/Fallback/AssetFallback.php, src/Json/JsonFile.php
Added RuntimeException imports and validation logic around file operations. AssetFallback now validates file_get_contents output on save, removes with try/catch on restore, and validates file_put_contents on restore. JsonFile validates file_get_contents in parseOriginalContent and throws RuntimeException on read failure.
Test Additions
tests/Fallback/AssetFallbackTest.php
Added three new test methods: testSaveThrowsWhenFileCannotBeRead, testRestoreThrowsWhenWriteFails, and testRestoreThrowsWhenRemoveFails. Tests verify RuntimeException is thrown with appropriate messages using MockerState to simulate file operation failures.
JSON Test Coverage
tests/Json/JsonFileTest.php
Added test verifying that getArrayKeys throws RuntimeException when file read fails, using mocked file_get_contents.
Test Infrastructure
tests/Support/InternalMockerExtension.php, tests/Support/internal-mocker-stubs.php
Updated Mocker constructor to accept explicit path arguments and expanded mock definitions. New stubs file provides function signature overrides for file_get_contents and file_put_contents to support failure simulation in tests.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 Hop, skip, and files now read with care,
Exceptions caught when things go wrong out there,
RuntimeExceptions thrown without delay,
When assets fail or JSON won't stay!
Mocks and stubs help tests explore the night,
Error handling done hopping-right!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.08% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: throwing RuntimeException on asset/json I/O failures, which is reflected throughout all modified files.
Description check ✅ Passed The description provides relevant information about the PR being a bugfix with no breaking changes, and relates directly to the changeset's goal of throwing RuntimeException on I/O failures.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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 and usage tips.

@codecov
Copy link

codecov bot commented Jan 22, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (583be98) to head (240c88d).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@             Coverage Diff             @@
##                main      #111   +/-   ##
===========================================
  Coverage     100.00%   100.00%           
- Complexity       343       347    +4     
===========================================
  Files             26        26           
  Lines            787       802   +15     
===========================================
+ Hits             787       802   +15     

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@tests/Fallback/AssetFallbackTest.php`:
- Line 98: The MockerState conditions for file I/O are using exact argument
arrays that don't match how AssetFallback calls file_get_contents and
file_put_contents; update the two MockerState::addCondition invocations (the one
referencing 'file_get_contents' and the one referencing 'file_put_contents') to
match actual signatures or use loose matchers: for file_get_contents use a
single-argument matcher corresponding to the path (e.g. match the first arg only
or a wildcard) and for file_put_contents match two arguments (path and data) or
use a matcher for the first two args instead of expecting flags/offset/null
values; adjust the conditions so they reflect AssetFallback's real calls to
file_get_contents($this->path) and file_put_contents($this->path,
$this->originalContent).
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 583be98 and 240c88d.

📒 Files selected for processing (7)
  • CHANGELOG.md
  • src/Fallback/AssetFallback.php
  • src/Json/JsonFile.php
  • tests/Fallback/AssetFallbackTest.php
  • tests/Json/JsonFileTest.php
  • tests/Support/InternalMockerExtension.php
  • tests/Support/internal-mocker-stubs.php
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: terabytesoftw
Repo: php-forge/support PR: 12
File: src/TestSupport.php:0-0
Timestamp: 2025-08-18T20:13:33.518Z
Learning: User terabytesoftw prefers clean trait-based designs over maintaining backward compatibility wrappers when refactoring utility classes in the php-forge/support project.
Learnt from: terabytesoftw
Repo: php-forge/actions PR: 65
File: .github/workflows/composer-require-checker.yml:80-83
Timestamp: 2025-09-28T15:12:48.345Z
Learning: The user terabytesoftw prefers using floating tags like v1 for third-party actions in GitHub workflows instead of pinning to specific commit SHAs, even when it's a security best practice to pin to immutable commits.
🧬 Code graph analysis (3)
tests/Fallback/AssetFallbackTest.php (1)
src/Exception/RuntimeException.php (1)
  • RuntimeException (21-23)
tests/Json/JsonFileTest.php (2)
src/Exception/RuntimeException.php (1)
  • RuntimeException (21-23)
src/Json/JsonFormatter.php (1)
  • getArrayKeys (34-39)
src/Json/JsonFile.php (1)
src/Exception/RuntimeException.php (1)
  • RuntimeException (21-23)
🪛 PHPMD (2.15.0)
tests/Fallback/AssetFallbackTest.php

98-98: Avoid using static access to class '\Xepozz\InternalMocker\MockerState' in method 'testSaveThrowsWhenFileCannotBeRead'. (undefined)

(StaticAccess)


163-163: Avoid using static access to class '\Xepozz\InternalMocker\MockerState' in method 'testRestoreThrowsWhenWriteFails'. (undefined)

(StaticAccess)


182-182: Missing class import via use statement (line '182', column '38'). (undefined)

(MissingImport)

tests/Json/JsonFileTest.php

94-94: Avoid using static access to class '\Xepozz\InternalMocker\MockerState' in method 'testGetArrayKeysThrowsWhenFileCannotBeRead'. (undefined)

(StaticAccess)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: phpunit / PHP 8.4-windows-latest
  • GitHub Check: phpunit / PHP 8.2-ubuntu-latest
🔇 Additional comments (13)
CHANGELOG.md (1)

14-14: LGTM!

The changelog entry accurately documents the bug fix and follows the established format.

tests/Json/JsonFileTest.php (2)

16-20: LGTM!

The new imports properly support the test for RuntimeException handling and mocking infrastructure.


87-102: LGTM!

The test correctly verifies RuntimeException is thrown when file reading fails:

  • Creates a valid file first to pass the exists() check
  • Mocks file_get_contents to return false for the specific path
  • Sets exception expectations before calling getArrayKeys()
  • Uses regex matcher to handle variable path in the message

The static analysis hint about MockerState::addCondition is a false positive—this is the intended API for the xepozz/internal-mocker library.

tests/Support/InternalMockerExtension.php (2)

58-77: LGTM!

The new mock registrations properly cover all file I/O functions used in JsonFile and AssetFallback classes, enabling the failure-path tests.


80-84: LGTM!

The explicit path configuration for the Mocker aligns with the updated constructor signature and correctly references the custom stubs file.

src/Json/JsonFile.php (2)

16-17: LGTM!

Import of the custom RuntimeException class is correct.


91-105: LGTM!

The guarded read flow properly handles both scenarios:

  • File exists but unreadable → throws RuntimeException with descriptive message
  • File doesn't exist → proceeds with empty content (graceful handling)

The implementation correctly uses inherited exists() and getPath() methods from Composer\Json\JsonFile.

tests/Support/internal-mocker-stubs.php (1)

1-21: LGTM!

The stub overrides correctly match PHP's function signatures for file_get_contents and file_put_contents, enabling precise argument matching in the mocked functions. The untyped $context parameters are intentional as they accept resource|null.

src/Fallback/AssetFallback.php (3)

19-19: LGTM!

Import of the custom RuntimeException class is correct.


40-53: LGTM!

The save() method now properly validates file read operations and throws a descriptive RuntimeException on failure, ensuring errors surface early rather than silently proceeding with invalid state.


55-80: LGTM!

The restore() method improvements are well-implemented:

  • Removal failures are caught and re-thrown with the original exception as cause (good for debugging)
  • Write failures are explicitly checked and surfaced

One consideration: if remove() succeeds but file_put_contents fails, the original file is lost. This is an acceptable trade-off since the exception clearly communicates the failure, allowing callers to handle it appropriately.

tests/Fallback/AssetFallbackTest.php (2)

19-22: LGTM — required test imports added.


171-199: Good coverage of remove-failure wrapping.
Asserts the wrapped exception and preserves the previous error—nice.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@terabytesoftw terabytesoftw merged commit ec42b52 into main Jan 22, 2026
24 checks passed
@terabytesoftw terabytesoftw deleted the fix_mini_5 branch January 22, 2026 11:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant