feat(trogon-std): add CreateDirAll and OpenAppendFile fs traits#8
Conversation
PR SummaryMedium Risk Overview Implements both traits for Written by Cursor Bugbot for commit 5d49d27. This will update automatically on new commits. Configure here. |
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (6)
WalkthroughThis pull request introduces two new public trait abstractions to the trogon-std filesystem module: Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
rsworkspace/crates/trogon-std/src/fs/mem.rs (2)
260-278: Add a test coveringwas_opened.
was_openedand the side-effect ofopen_appendrecording the path are entirely untested. The two existing tests only covercreate_dir_all.🧪 Suggested test
+ #[test] + fn test_memfs_open_append_records_path() { + let fs = MemFs::new(); + let path = Path::new("/logs/app.log"); + + assert!(!fs.was_opened(path)); + fs.open_append(path).unwrap(); + assert!(fs.was_opened(path)); + assert!(!fs.was_opened(Path::new("/logs/other.log"))); + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@rsworkspace/crates/trogon-std/src/fs/mem.rs` around lines 260 - 278, Add a unit test in the same mem.rs test module that constructs a MemFs, calls open_append on a file path (e.g., Path::new("/foo/bar.txt")), then asserts that MemFs::was_opened(&path) returns true and that subsequent calls (e.g., opening again or create_dir_all) behave idempotently; specifically exercise MemFs::open_append and MemFs::was_opened to verify the side-effect of recording opened paths is implemented. Use the existing test style (creating MemFs with MemFs::new and Path::new) and include negative assertions for paths not opened to ensure correctness.
108-116:MemFs::open_appendreturns a disconnectedVec<u8>— written content is unobservable.Every call returns a fresh, owned
Vec<u8>.MemFsholds no reference to it, so any bytes the code-under-test writes (e.g.,writer.write_all(b"...")) are permanently lost.was_openedcan only assert that the file was opened, not what was written. For a test double covering the full append contract, callers have no way to verify written content.Consider replacing
Writer = Vec<u8>with a shared-buffer newtype:♻️ Suggested approach: shared observable buffer
+#[cfg(any(test, feature = "test-support"))] +#[derive(Clone, Default)] +pub struct SharedBuffer(std::sync::Arc<std::sync::Mutex<Vec<u8>>>); + +#[cfg(any(test, feature = "test-support"))] +impl SharedBuffer { + pub fn into_bytes(self) -> Vec<u8> { + self.0.lock().unwrap().clone() + } +} + +#[cfg(any(test, feature = "test-support"))] +impl io::Write for SharedBuffer { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + self.0.lock().unwrap().write(buf) + } + fn flush(&mut self) -> io::Result<()> { Ok(()) } +} #[cfg(any(test, feature = "test-support"))] impl OpenAppendFile for MemFs { - type Writer = Vec<u8>; + type Writer = SharedBuffer; fn open_append(&self, path: &Path) -> io::Result<Self::Writer> { + let buf = SharedBuffer::default(); self.opened_files.borrow_mut().insert(path.to_path_buf()); - Ok(Vec::new()) + // optionally also store buf.clone() keyed by path for post-call inspection + Ok(buf) } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@rsworkspace/crates/trogon-std/src/fs/mem.rs` around lines 108 - 116, The test double currently implements OpenAppendFile for MemFs with Writer = Vec<u8> and open_append returning a fresh Vec that MemFs never stores, so written bytes are lost; change Writer to a shared buffer type (e.g., Rc<RefCell<Vec<u8>>> or Arc<Mutex<Vec<u8>>>) and have open_append create or retrieve a shared buffer stored inside MemFs (e.g., keyed by path in the opened_files or a new map field), return that shared buffer as the Writer, and provide a way for tests to inspect the buffer contents; update the OpenAppendFile impl, the open_append method, and any tests to use the new shared-buffer Writer so writes are observable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@rsworkspace/crates/trogon-std/src/fs/mem.rs`:
- Around line 260-278: Add a unit test in the same mem.rs test module that
constructs a MemFs, calls open_append on a file path (e.g.,
Path::new("/foo/bar.txt")), then asserts that MemFs::was_opened(&path) returns
true and that subsequent calls (e.g., opening again or create_dir_all) behave
idempotently; specifically exercise MemFs::open_append and MemFs::was_opened to
verify the side-effect of recording opened paths is implemented. Use the
existing test style (creating MemFs with MemFs::new and Path::new) and include
negative assertions for paths not opened to ensure correctness.
- Around line 108-116: The test double currently implements OpenAppendFile for
MemFs with Writer = Vec<u8> and open_append returning a fresh Vec that MemFs
never stores, so written bytes are lost; change Writer to a shared buffer type
(e.g., Rc<RefCell<Vec<u8>>> or Arc<Mutex<Vec<u8>>>) and have open_append create
or retrieve a shared buffer stored inside MemFs (e.g., keyed by path in the
opened_files or a new map field), return that shared buffer as the Writer, and
provide a way for tests to inspect the buffer contents; update the
OpenAppendFile impl, the open_append method, and any tests to use the new
shared-buffer Writer so writes are observable.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
rsworkspace/crates/trogon-std/src/fs/create_dir_all.rsrsworkspace/crates/trogon-std/src/fs/mem.rsrsworkspace/crates/trogon-std/src/fs/mod.rsrsworkspace/crates/trogon-std/src/fs/open_append_file.rsrsworkspace/crates/trogon-std/src/fs/system.rs
6b5e53e to
434aaf5
Compare
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
434aaf5 to
5d49d27
Compare
- backoff_shift_saturates_at_31_for_high_attempt_counts: verifies the
formula (attempts-1).min(31) saturates the shift count at 31 for all
attempt values >= 32, preventing u32 overflow in Duration arithmetic
- e2e_empty_query_string_appended_as_bare_question_mark: documents that
a URI with trailing '?' (empty query) causes uri.query() = Some(""),
which the proxy formats as "?" and appends to the upstream URL
Gaps #1 (double publish failure) and #8 (consumer stream error) are not
testable without introducing a trait abstraction over async_nats::Client.
- backoff_shift_saturates_at_31_for_high_attempt_counts: verifies the
formula (attempts-1).min(31) saturates the shift count at 31 for all
attempt values >= 32, preventing u32 overflow in Duration arithmetic
- e2e_empty_query_string_appended_as_bare_question_mark: documents that
a URI with trailing '?' (empty query) causes uri.query() = Some(""),
which the proxy formats as "?" and appends to the upstream URL
Gaps #1 (double publish failure) and #8 (consumer stream error) are not
testable without introducing a trait abstraction over async_nats::Client.
- backoff_shift_saturates_at_31_for_high_attempt_counts: verifies the
formula (attempts-1).min(31) saturates the shift count at 31 for all
attempt values >= 32, preventing u32 overflow in Duration arithmetic
- e2e_empty_query_string_appended_as_bare_question_mark: documents that
a URI with trailing '?' (empty query) causes uri.query() = Some(""),
which the proxy formats as "?" and appends to the upstream URL
Gaps #1 (double publish failure) and #8 (consumer stream error) are not
testable without introducing a trait abstraction over async_nats::Client.
- backoff_shift_saturates_at_31_for_high_attempt_counts: verifies the
formula (attempts-1).min(31) saturates the shift count at 31 for all
attempt values >= 32, preventing u32 overflow in Duration arithmetic
- e2e_empty_query_string_appended_as_bare_question_mark: documents that
a URI with trailing '?' (empty query) causes uri.query() = Some(""),
which the proxy formats as "?" and appends to the upstream URL
Gaps #1 (double publish failure) and #8 (consumer stream error) are not
testable without introducing a trait abstraction over async_nats::Client.
- backoff_shift_saturates_at_31_for_high_attempt_counts: verifies the
formula (attempts-1).min(31) saturates the shift count at 31 for all
attempt values >= 32, preventing u32 overflow in Duration arithmetic
- e2e_empty_query_string_appended_as_bare_question_mark: documents that
a URI with trailing '?' (empty query) causes uri.query() = Some(""),
which the proxy formats as "?" and appends to the upstream URL
Gaps #1 (double publish failure) and #8 (consumer stream error) are not
testable without introducing a trait abstraction over async_nats::Client.
- backoff_shift_saturates_at_31_for_high_attempt_counts: verifies the
formula (attempts-1).min(31) saturates the shift count at 31 for all
attempt values >= 32, preventing u32 overflow in Duration arithmetic
- e2e_empty_query_string_appended_as_bare_question_mark: documents that
a URI with trailing '?' (empty query) causes uri.query() = Some(""),
which the proxy formats as "?" and appends to the upstream URL
Gaps #1 (double publish failure) and #8 (consumer stream error) are not
testable without introducing a trait abstraction over async_nats::Client.
- backoff_shift_saturates_at_31_for_high_attempt_counts: verifies the
formula (attempts-1).min(31) saturates the shift count at 31 for all
attempt values >= 32, preventing u32 overflow in Duration arithmetic
- e2e_empty_query_string_appended_as_bare_question_mark: documents that
a URI with trailing '?' (empty query) causes uri.query() = Some(""),
which the proxy formats as "?" and appends to the upstream URL
Gaps #1 (double publish failure) and #8 (consumer stream error) are not
testable without introducing a trait abstraction over async_nats::Client.
- backoff_shift_saturates_at_31_for_high_attempt_counts: verifies the
formula (attempts-1).min(31) saturates the shift count at 31 for all
attempt values >= 32, preventing u32 overflow in Duration arithmetic
- e2e_empty_query_string_appended_as_bare_question_mark: documents that
a URI with trailing '?' (empty query) causes uri.query() = Some(""),
which the proxy formats as "?" and appends to the upstream URL
Gaps #1 (double publish failure) and #8 (consumer stream error) are not
testable without introducing a trait abstraction over async_nats::Client.
…op_reason → Failed #7 — split max_tokens out of the catch-all stop_reason arm (keeps PermanentFailed) and change the true `other` arm to write `Failed` instead of PermanentFailed. A future Anthropic stop_reason that is transient would previously be discarded forever; now NATS redelivery can retry it up to the stream's delivery limit. #8 — add `recovery_count: u32` (#[serde(default)]) to AgentPromise. Each time startup recovery re-claims a stale Running promise the field is incremented. At MAX_RECOVERY_ATTEMPTS (5) the promise is marked PermanentFailed and the run is skipped, breaking the infinite cycle caused by deterministic crashes that prevent try_mark_permanent_failed_fresh from writing. Failed promises (NATS redelivery path) are not counted. Tests added: max_tokens_stop_reason_marks_promise_permanent_failed, unknown_stop_reason_marks_promise_failed_not_permanent, prepare_agent_over_recovery_limit_marks_permanent_failed_and_returns_none, prepare_agent_running_reclaim_increments_recovery_count.
- backoff_shift_saturates_at_31_for_high_attempt_counts: verifies the
formula (attempts-1).min(31) saturates the shift count at 31 for all
attempt values >= 32, preventing u32 overflow in Duration arithmetic
- e2e_empty_query_string_appended_as_bare_question_mark: documents that
a URI with trailing '?' (empty query) causes uri.query() = Some(""),
which the proxy formats as "?" and appends to the upstream URL
Gaps #1 (double publish failure) and #8 (consumer stream error) are not
testable without introducing a trait abstraction over async_nats::Client.
- backoff_shift_saturates_at_31_for_high_attempt_counts: verifies the
formula (attempts-1).min(31) saturates the shift count at 31 for all
attempt values >= 32, preventing u32 overflow in Duration arithmetic
- e2e_empty_query_string_appended_as_bare_question_mark: documents that
a URI with trailing '?' (empty query) causes uri.query() = Some(""),
which the proxy formats as "?" and appends to the upstream URL
Gaps #1 (double publish failure) and #8 (consumer stream error) are not
testable without introducing a trait abstraction over async_nats::Client.
…auth callout Task #8 needs proof that real server authorization requests reach the dispatcher and mint caller ACLs before operator rollout; wire decoding was aligned with how nats-server actually signs and omits JWT fields. Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
…test Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
Summary
CreateDirAlltrait for recursive directory creationOpenAppendFiletrait for opening files in append mode with an associatedWritertypeSystemFs(delegates tostd::fs) andMemFs(in-memory test double)Test plan
MemFs::create_dir_all(creates ancestor dirs, idempotent)cargo testpasses introgon-std