@@ -9,7 +9,7 @@ use std::path::PathBuf;
99use commitbee:: config:: Config ;
1010use commitbee:: domain:: { ChangeStatus , CodeSymbol , CommitType , FileCategory , SymbolKind } ;
1111use commitbee:: services:: context:: ContextBuilder ;
12- use helpers:: { make_file_change, make_staged_changes} ;
12+ use helpers:: { make_file_change, make_renamed_file , make_staged_changes} ;
1313
1414// ─── Helpers ─────────────────────────────────────────────────────────────────
1515
@@ -1365,3 +1365,95 @@ fn connection_content_mentions_symbol_name() {
13651365 ctx. connections
13661366 ) ;
13671367}
1368+
1369+ // ─── Test Coverage: Deleted/Renamed status (#37) ──────────────────────────────
1370+
1371+ #[ test]
1372+ fn format_files_shows_deleted_marker ( ) {
1373+ let changes = make_staged_changes ( vec ! [ make_file_change(
1374+ "src/old.rs" ,
1375+ ChangeStatus :: Deleted ,
1376+ "-pub fn removed() {}" ,
1377+ 0 ,
1378+ 1 ,
1379+ ) ] ) ;
1380+ let ctx = ContextBuilder :: build ( & changes, & [ ] , & default_config ( ) ) ;
1381+ assert ! (
1382+ ctx. file_breakdown. contains( "[-]" ) ,
1383+ "deleted file should show [-] marker: {}" ,
1384+ ctx. file_breakdown
1385+ ) ;
1386+ }
1387+
1388+ #[ test]
1389+ fn format_files_shows_renamed_marker ( ) {
1390+ let changes = make_staged_changes ( vec ! [ make_renamed_file(
1391+ "src/old_name.rs" ,
1392+ "src/new_name.rs" ,
1393+ 95 ,
1394+ ) ] ) ;
1395+ let ctx = ContextBuilder :: build ( & changes, & [ ] , & default_config ( ) ) ;
1396+ assert ! (
1397+ ctx. file_breakdown. contains( "[R]" ) ,
1398+ "renamed file should show [R] marker: {}" ,
1399+ ctx. file_breakdown
1400+ ) ;
1401+ assert ! (
1402+ ctx. file_breakdown. contains( "95% similar" ) ,
1403+ "renamed file should show similarity: {}" ,
1404+ ctx. file_breakdown
1405+ ) ;
1406+ }
1407+
1408+ // ─── Test Coverage: classify_span_change None path (#39) ──────────────────────
1409+
1410+ #[ test]
1411+ fn whitespace_detection_returns_none_when_span_has_no_changes ( ) {
1412+ // Symbol at lines 50-60 but diff hunk is at lines 1-3 — no overlap
1413+ let changes = make_staged_changes ( vec ! [ make_file_change(
1414+ "src/lib.rs" ,
1415+ ChangeStatus :: Modified ,
1416+ "@@ -1,3 +1,3 @@\n fn other() {\n - old()\n + new()\n }" ,
1417+ 1 ,
1418+ 1 ,
1419+ ) ] ) ;
1420+ let mut sym_old = make_symbol ( "distant" , SymbolKind :: Function , "src/lib.rs" , true , false ) ;
1421+ sym_old. line = 50 ;
1422+ sym_old. end_line = 60 ;
1423+ let mut sym_new = make_symbol ( "distant" , SymbolKind :: Function , "src/lib.rs" , true , true ) ;
1424+ sym_new. line = 50 ;
1425+ sym_new. end_line = 60 ;
1426+ let ctx = ContextBuilder :: build ( & changes, & [ sym_old, sym_new] , & default_config ( ) ) ;
1427+ // Symbol is outside the hunk — classify_span_change returns None (no changes in span).
1428+ // The symbol still appears as "modified" (name+kind+file match) but with no
1429+ // whitespace classification. This is expected: it won't be filtered as whitespace-only.
1430+ // This test verifies the None path doesn't crash or produce false positives.
1431+ assert ! (
1432+ ctx. symbols_modified. contains( "distant" ) ,
1433+ "modified symbol outside hunk should still appear (with no ws classification): {}" ,
1434+ ctx. symbols_modified
1435+ ) ;
1436+ }
1437+
1438+ // ─── Test Coverage: HARD LIMIT dedup check (#28) ─────────────────────────────
1439+
1440+ #[ test]
1441+ fn prompt_hard_limit_includes_char_budget ( ) {
1442+ let changes = make_staged_changes ( vec ! [ make_file_change(
1443+ "src/lib.rs" ,
1444+ ChangeStatus :: Modified ,
1445+ "+fn foo() {}" ,
1446+ 1 ,
1447+ 0 ,
1448+ ) ] ) ;
1449+ let ctx = ContextBuilder :: build ( & changes, & [ ] , & default_config ( ) ) ;
1450+ let prompt = ctx. to_prompt ( ) ;
1451+ assert ! (
1452+ prompt. contains( "HARD LIMIT" ) ,
1453+ "prompt should contain HARD LIMIT section"
1454+ ) ;
1455+ assert ! (
1456+ prompt. contains( "chars" ) ,
1457+ "HARD LIMIT should mention char budget"
1458+ ) ;
1459+ }
0 commit comments