Skip to content

Commit 48bb213

Browse files
committed
Milestone 3 - Step 2
codetracer-python-recorder/src/session/bootstrap/filesystem.rs: codetracer-python-recorder/src/session/bootstrap/filters.rs: codetracer-python-recorder/src/session/bootstrap/metadata.rs: codetracer-python-recorder/src/session/bootstrap.rs: design-docs/codetracer-architecture-refactor-implementation-plan.status.md: Signed-off-by: Tzanko Matev <[email protected]>
1 parent f9db586 commit 48bb213

File tree

5 files changed

+112
-73
lines changed

5 files changed

+112
-73
lines changed

codetracer-python-recorder/src/session/bootstrap.rs

Lines changed: 0 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -94,73 +94,9 @@ impl TraceSessionBootstrap {
9494
mod tests {
9595
use super::*;
9696
use metadata::tests::{with_sys_argv, ProgramArgs};
97-
use recorder_errors::ErrorCode;
9897
use std::path::PathBuf;
9998
use tempfile::tempdir;
10099

101-
#[test]
102-
fn ensure_trace_directory_creates_missing_dir() {
103-
let tmp = tempdir().expect("tempdir");
104-
let target = tmp.path().join("trace-out");
105-
ensure_trace_directory(&target).expect("create directory");
106-
assert!(target.is_dir());
107-
}
108-
109-
#[test]
110-
fn ensure_trace_directory_rejects_file_path() {
111-
let tmp = tempdir().expect("tempdir");
112-
let file_path = tmp.path().join("trace.bin");
113-
std::fs::write(&file_path, b"stub").expect("write stub file");
114-
let err = ensure_trace_directory(&file_path).expect_err("should reject file path");
115-
assert_eq!(err.code, ErrorCode::TraceDirectoryConflict);
116-
}
117-
118-
#[test]
119-
fn resolve_trace_format_accepts_supported_aliases() {
120-
assert!(matches!(
121-
resolve_trace_format("json").expect("json format"),
122-
TraceEventsFileFormat::Json
123-
));
124-
assert!(matches!(
125-
resolve_trace_format("BiNaRy").expect("binary alias"),
126-
TraceEventsFileFormat::BinaryV0
127-
));
128-
}
129-
130-
#[test]
131-
fn resolve_trace_format_rejects_unknown_values() {
132-
let err = resolve_trace_format("yaml").expect_err("should reject yaml");
133-
assert_eq!(err.code, ErrorCode::UnsupportedFormat);
134-
assert!(err.message().contains("unsupported trace format"));
135-
}
136-
137-
#[test]
138-
fn collect_program_metadata_reads_sys_argv() {
139-
Python::with_gil(|py| {
140-
let metadata = with_sys_argv(
141-
py,
142-
ProgramArgs::new(["/tmp/prog.py", "--flag", "value"]),
143-
|| collect_program_metadata(py),
144-
)
145-
.expect("metadata");
146-
assert_eq!(metadata.program, "/tmp/prog.py");
147-
assert_eq!(
148-
metadata.args,
149-
vec!["--flag".to_string(), "value".to_string()]
150-
);
151-
});
152-
}
153-
154-
#[test]
155-
fn collect_program_metadata_defaults_unknown_program() {
156-
Python::with_gil(|py| {
157-
let metadata = with_sys_argv(py, ProgramArgs::empty(), || collect_program_metadata(py))
158-
.expect("metadata");
159-
assert_eq!(metadata.program, "<unknown>");
160-
assert!(metadata.args.is_empty());
161-
});
162-
}
163-
164100
#[test]
165101
fn prepare_bootstrap_populates_fields_and_creates_directory() {
166102
Python::with_gil(|py| {

codetracer-python-recorder/src/session/bootstrap/filesystem.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,44 @@ pub fn current_directory() -> Result<PathBuf> {
7878
.with_context("io", err.to_string())
7979
})
8080
}
81+
82+
#[cfg(test)]
83+
mod tests {
84+
use super::*;
85+
use tempfile::tempdir;
86+
87+
#[test]
88+
fn creates_missing_directory() {
89+
let tmp = tempdir().expect("tempdir");
90+
let target = tmp.path().join("trace-out");
91+
ensure_trace_directory(&target).expect("create directory");
92+
assert!(target.is_dir());
93+
}
94+
95+
#[test]
96+
fn rejects_existing_file_target() {
97+
let tmp = tempdir().expect("tempdir");
98+
let file_path = tmp.path().join("trace.bin");
99+
std::fs::write(&file_path, b"stub").expect("write stub file");
100+
let err = ensure_trace_directory(&file_path).expect_err("should reject file path");
101+
assert_eq!(err.code, ErrorCode::TraceDirectoryConflict);
102+
}
103+
104+
#[test]
105+
fn resolves_supported_formats() {
106+
assert!(matches!(
107+
resolve_trace_format("json").expect("json format"),
108+
TraceEventsFileFormat::Json
109+
));
110+
assert!(matches!(
111+
resolve_trace_format("binary").expect("binary format"),
112+
TraceEventsFileFormat::BinaryV0
113+
));
114+
}
115+
116+
#[test]
117+
fn rejects_unknown_format() {
118+
let err = resolve_trace_format("yaml").expect_err("should reject yaml");
119+
assert_eq!(err.code, ErrorCode::UnsupportedFormat);
120+
}
121+
}

codetracer-python-recorder/src/session/bootstrap/filters.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ fn discover_default_trace_filter(program: &str) -> Result<Option<PathBuf>> {
5151
pub mod tests {
5252
use super::*;
5353
use std::fs;
54-
use std::path::Path;
54+
use std::path::{Path, PathBuf};
5555
use tempfile::tempdir;
5656

5757
pub fn write_default_filter(root: &Path) -> PathBuf {
@@ -121,4 +121,44 @@ pub mod tests {
121121
discover_default_trace_filter(script.to_str().expect("utf8")).expect("discover");
122122
assert!(found.is_some());
123123
}
124+
125+
#[test]
126+
fn load_trace_filter_includes_builtin() {
127+
let temp = tempdir().expect("tempdir");
128+
let root = temp.path();
129+
let script = write_app(root);
130+
131+
let engine = load_trace_filter(None, script.to_str().expect("utf8"))
132+
.expect("load")
133+
.expect("engine");
134+
let summary = engine.summary();
135+
assert!(summary
136+
.entries
137+
.iter()
138+
.any(|entry| entry.path == PathBuf::from("<inline:builtin-default>")));
139+
}
140+
141+
#[test]
142+
fn load_trace_filter_merges_default_and_override() {
143+
let temp = tempdir().expect("tempdir");
144+
let root = temp.path();
145+
let script = write_app(root);
146+
let (default_filter_path, override_filter_path) = write_default_and_override(root);
147+
148+
let engine = load_trace_filter(
149+
Some(&[override_filter_path.clone()]),
150+
script.to_str().expect("utf8"),
151+
)
152+
.expect("load")
153+
.expect("engine");
154+
let paths: Vec<PathBuf> = engine
155+
.summary()
156+
.entries
157+
.iter()
158+
.map(|entry| entry.path.clone())
159+
.collect();
160+
assert!(paths.contains(&PathBuf::from("<inline:builtin-default>")));
161+
assert!(paths.contains(&default_filter_path));
162+
assert!(paths.contains(&override_filter_path));
163+
}
124164
}

codetracer-python-recorder/src/session/bootstrap/metadata.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,31 @@ pub mod tests {
8080
.expect("restore argv");
8181
result
8282
}
83+
84+
#[test]
85+
fn collects_program_and_args() {
86+
Python::with_gil(|py| {
87+
let metadata = with_sys_argv(
88+
py,
89+
ProgramArgs::new(["/tmp/prog.py", "--flag", "value"]),
90+
|| collect_program_metadata(py),
91+
)
92+
.expect("metadata");
93+
assert_eq!(metadata.program, "/tmp/prog.py");
94+
assert_eq!(
95+
metadata.args,
96+
vec!["--flag".to_string(), "value".to_string()]
97+
);
98+
});
99+
}
100+
101+
#[test]
102+
fn defaults_to_unknown_program() {
103+
Python::with_gil(|py| {
104+
let metadata = with_sys_argv(py, ProgramArgs::empty(), || collect_program_metadata(py))
105+
.expect("metadata");
106+
assert_eq!(metadata.program, "<unknown>");
107+
assert!(metadata.args.is_empty());
108+
});
109+
}
83110
}

design-docs/codetracer-architecture-refactor-implementation-plan.status.md

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,7 @@
5252
- `logger.rs` owns the log installation, filter parsing, policy application, and error-code scoping; it exposes helpers (`with_error_code`, `log_recorder_error`, `set_active_trace_id`) for the rest of the crate.
5353
- `metrics.rs` encapsulates the `RecorderMetrics` trait, sink installation, and testing harness; `trailer.rs` manages JSON error toggles and payload emission via the logger's context snapshot.
5454
- Updated facade tests (`structured_log_records`, `json_error_trailers_emit_payload`, metrics capture) to rely on the new modules; `just test` verifies Rust + Python suites after the split.
55-
- 🔄 Milestone 3 Kickoff: revisiting `session/bootstrap.rs` to catalogue filesystem, metadata, and filter responsibilities ahead of submodule extraction.
56-
- Filesystem prep: `ensure_trace_directory`, `resolve_trace_format`, directory walkers (`discover_default_trace_filter`, `resolve_program_directory`, `current_directory`).
57-
- Metadata capture: `ProgramMetadata` struct, `collect_program_metadata`.
58-
- Filter loading: constants (`TRACE_FILTER_*`, builtin include), `load_trace_filter`, `discover_default_trace_filter`, explicit override merge logic.
59-
- `TraceSessionBootstrap::prepare` orchestrates all helpers and owns the struct fields; tests cover directory creation, format validation, metadata fallbacks, filter discovery/merging.
60-
- ✅ Milestone 3 Step 1: established `session/bootstrap/{filesystem,metadata,filters}.rs`, re-exported `ProgramMetadata`, and migrated helper functions/tests without changing the facade API. `just test` validates the split scaffold.
55+
- ✅ Milestone 3 complete: `session/bootstrap` delegates to `filesystem`, `metadata`, and `filters` submodules, each with focused unit tests covering success and failure paths (e.g., unwritable directory, unsupported formats, missing filters). `TraceSessionBootstrap` now orchestrates these modules without additional helper functions, and `just test` (Rust + Python) confirms parity.
6156

6257
### Planned Extraction Order (Milestone 2)
6358
1. **Policy model split:** Move data structures (`OnRecorderError`, `IoCapturePolicy`, `RecorderPolicy`, `PolicyUpdate`, `PolicyPath`) and policy cell helpers (`policy_cell`, `policy_snapshot`, `apply_policy_update`) into `policy::model`. Expose minimal APIs for environment/FFI modules.
@@ -74,5 +69,5 @@
7469
5. **Tests:** After each move, update unit tests in `trace_filter` modules and dependent integration tests (`session/bootstrap.rs` tests, `runtime` tests). Targeted command: `just test` (covers Rust + Python suites).
7570

7671
## Next Actions
77-
1. Continue Milestone 3 by tightening module responsibilities: move remaining filesystem/metadata/filter helpers into their submodules (e.g., `TraceSessionBootstrap::prepare` orchestration steps).
78-
2. Add focused unit tests within the new modules for error scenarios (unwritable directory, unsupported format, missing default filter) before rerunning `just test`.
72+
1. Proceed to Milestone 4: begin the monitoring plumbing cleanup per the implementation roadmap.
73+
2. Watch for integration regressions in CLI/session flows now that bootstrap responsibilities are modularised; add follow-up tasks if any arise.

0 commit comments

Comments
 (0)