Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions tests/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ mod helpers;

use std::path::PathBuf;

use commitbee::classify_diff_span;
use commitbee::config::Config;
use commitbee::domain::{
ChangeStatus, CodeSymbol, CommitType, FileCategory, IntentKind, SymbolKind,
Expand Down Expand Up @@ -1439,6 +1440,57 @@ fn whitespace_detection_returns_none_when_span_has_no_changes() {
);
}

// Direct assertions on the `None` branch of `classify_span_change` via the
// public `classify_diff_span` wrapper re-exported from `lib.rs`. The function
// returns `None` exactly when no added/removed lines fall inside the requested
// (`old_start..=old_end`) / (`new_start..=new_end`) spans — see the
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

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

The comment describing the classify_span_change contract lists the span order as (old_start..=old_end) / (new_start..=new_end), but the actual API (and implementation in ContextBuilder::classify_span_change) takes/uses new_start..=new_end for added lines and old_start..=old_end for removed lines. Please update the comment to match the parameter order to avoid misleading future readers.

Suggested change
// (`old_start..=old_end`) / (`new_start..=new_end`) spans — see the
// (`new_start..=new_end`) / (`old_start..=old_end`) spans — see the

Copilot uses AI. Check for mistakes.
// `added_in_span.is_empty() && removed_in_span.is_empty()` guard in
// `src/services/context.rs`. These tests pin that contract so a future
// refactor cannot silently convert the "no changes in span" case into a
// false-positive `Some(true)` whitespace-only classification.

#[test]
fn classify_span_change_returns_none_when_span_is_outside_hunk() {
// Hunk touches lines 1-3 in both old and new files, but we query a
// symbol span at lines 50-60. No +/- line lands inside the span, so
// the function must short-circuit to `None` before the whitespace
// comparison runs.
let diff = "@@ -1,3 +1,3 @@\n fn other() {\n- old()\n+ new()\n }\n";
assert_eq!(
classify_diff_span(diff, 50, 60, 50, 60),
None,
"span entirely outside the hunk must yield None"
);
}

#[test]
fn classify_span_change_returns_none_for_empty_diff() {
// Empty diff: the parse loop never enters a hunk, so both
// `added_in_span` and `removed_in_span` stay empty and the function
// hits the early-return `None`.
assert_eq!(
classify_diff_span("", 1, 10, 1, 10),
None,
"empty diff must yield None"
);
}

#[test]
fn classify_span_change_returns_none_when_span_range_is_empty() {
// Span with `new_start > new_end` (and likewise for `old`) never
// matches any +/- line because `in_new_span`/`in_old_span` evaluate
// `false` for every counter value. This is a degenerate but reachable
// input from callers that derive span bounds from AST nodes whose
// `end < start` (e.g. zero-length nodes), so the `None` short-circuit
// must hold here too.
let diff = "@@ -1,3 +1,3 @@\n fn f() {\n- old()\n+ new()\n }\n";
assert_eq!(
classify_diff_span(diff, 100, 50, 100, 50),
Comment on lines +1482 to +1488
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

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

classify_span_change_returns_none_when_span_range_is_empty currently uses spans 100..50, which are both inverted and completely outside the hunk (lines 1–3). That means the test would still pass even if the implementation started normalizing inverted ranges (or otherwise mishandled the inversion), because the out-of-hunk condition alone yields None. Consider using an inverted range that would otherwise overlap the hunk (e.g., new_start=3, new_end=1, old_start=3, old_end=1) to specifically validate the inverted-span behavior.

Suggested change
// `false` for every counter value. This is a degenerate but reachable
// input from callers that derive span bounds from AST nodes whose
// `end < start` (e.g. zero-length nodes), so the `None` short-circuit
// must hold here too.
let diff = "@@ -1,3 +1,3 @@\n fn f() {\n- old()\n+ new()\n }\n";
assert_eq!(
classify_diff_span(diff, 100, 50, 100, 50),
// `false` for every counter value. Keep the inverted bounds within the
// hunk's 1-3 line range so this specifically validates the inverted-span
// behavior rather than the separate "outside hunk" case covered above.
let diff = "@@ -1,3 +1,3 @@\n fn f() {\n- old()\n+ new()\n }\n";
assert_eq!(
classify_diff_span(diff, 3, 1, 3, 1),

Copilot uses AI. Check for mistakes.
None,
"inverted span (start > end) must yield None"
);
}

// ─── Import change detection ─────────────────────────────────────────────────

#[test]
Expand Down
Loading