@@ -1210,6 +1210,102 @@ fn search_depth_rejected_child_dirs_are_not_counted() {
12101210 assert_exact_envelope ( & native, & rss. result ) ;
12111211}
12121212
1213+ fn assert_search_exact_envelope (
1214+ label : & str ,
1215+ files : & [ ( & str , & str ) ] ,
1216+ mut config_edit : impl FnMut ( & mut FileToolConfig ) ,
1217+ arguments : Value ,
1218+ ) -> ( ToolResult , RssRun ) {
1219+ let fixture = Fixture :: new ( label) ;
1220+ for ( name, contents) in files {
1221+ fs:: write ( fixture. root . join ( name) , contents) . unwrap ( ) ;
1222+ }
1223+ let mut config = fixture. config ( ) ;
1224+ config_edit ( & mut config) ;
1225+ config. artifact_store . root = fixture. parent . join ( format ! ( "artifacts-{label}" ) ) ;
1226+ let native = native_execute (
1227+ & fixture. tools_with_config ( config. clone ( ) ) ,
1228+ NativeToolExecutor :: SearchFiles ,
1229+ & arguments,
1230+ ) ;
1231+ let rss = run_rss_search ( & fixture, & config, arguments) ;
1232+ assert_exact_envelope ( & native, & rss. result ) ;
1233+ ( native, rss)
1234+ }
1235+
1236+ #[ test]
1237+ fn search_exact_fill_scan_cap_matches_all_lines_without_truncation ( ) {
1238+ let one_line = "needle\n " ;
1239+ let ( native, rss) = assert_search_exact_envelope (
1240+ "search-exact-fill-one" ,
1241+ & [ ( "exact.txt" , one_line) ] ,
1242+ |config| config. max_search_scanned_bytes = one_line. len ( ) ,
1243+ json ! ( { "pattern" : "needle" } ) ,
1244+ ) ;
1245+ assert ! ( native. ok, "native={native:?}" ) ;
1246+ assert ! ( !native. truncated, "native={native:?}" ) ;
1247+ assert_eq ! ( native. data[ "match_count" ] , json!( 1 ) ) ;
1248+ assert_eq ! ( rss. result[ "truncated" ] , json!( false ) , "rss={}" , rss. result) ;
1249+ assert_eq ! ( rss. result[ "data" ] [ "match_count" ] , json!( 1 ) ) ;
1250+
1251+ let multi = "n1\n n2\n " ;
1252+ let ( native, rss) = assert_search_exact_envelope (
1253+ "search-exact-fill-multi" ,
1254+ & [ ( "exact.txt" , multi) ] ,
1255+ |config| config. max_search_scanned_bytes = multi. len ( ) ,
1256+ json ! ( { "pattern" : "n" } ) ,
1257+ ) ;
1258+ assert ! ( native. ok, "native={native:?}" ) ;
1259+ assert ! ( !native. truncated, "native={native:?}" ) ;
1260+ assert_eq ! ( native. data[ "match_count" ] , json!( 2 ) ) ;
1261+ assert_eq ! ( rss. result[ "truncated" ] , json!( false ) , "rss={}" , rss. result) ;
1262+ assert_eq ! ( rss. result[ "data" ] [ "match_count" ] , json!( 2 ) ) ;
1263+ }
1264+
1265+ #[ test]
1266+ fn search_exact_fill_then_later_positive_file_truncates_like_native ( ) {
1267+ let first = "n1\n n2\n " ;
1268+ let ( native, rss) = assert_search_exact_envelope (
1269+ "search-exact-fill-later" ,
1270+ & [ ( "a.txt" , first) , ( "b.txt" , "n3\n " ) ] ,
1271+ |config| config. max_search_scanned_bytes = first. len ( ) ,
1272+ json ! ( { "pattern" : "n" } ) ,
1273+ ) ;
1274+ assert ! ( native. ok, "native={native:?}" ) ;
1275+ assert ! ( native. truncated, "native={native:?}" ) ;
1276+ assert_eq ! ( native. data[ "match_count" ] , json!( 2 ) ) ;
1277+ assert ! (
1278+ native. content. contains( "a.txt" ) ,
1279+ "exact-fill file must match: native={native:?}"
1280+ ) ;
1281+ assert ! (
1282+ !native. content. contains( "b.txt" ) ,
1283+ "later file must not match after exact fill: native={native:?}"
1284+ ) ;
1285+ assert_eq ! ( rss. result[ "truncated" ] , json!( true ) , "rss={}" , rss. result) ;
1286+ assert_eq ! ( rss. result[ "data" ] [ "match_count" ] , json!( 2 ) ) ;
1287+ }
1288+
1289+ #[ test]
1290+ fn search_final_files_visited_slot_is_fully_matched ( ) {
1291+ let ( native, rss) = assert_search_exact_envelope (
1292+ "search-final-file-slot" ,
1293+ & [ ( "a.txt" , "n0\n " ) , ( "b.txt" , "n1\n n2\n " ) ] ,
1294+ |config| config. max_search_files = 4 ,
1295+ json ! ( { "pattern" : "n" } ) ,
1296+ ) ;
1297+ assert ! ( native. ok, "native={native:?}" ) ;
1298+ assert ! ( !native. truncated, "native={native:?}" ) ;
1299+ assert_eq ! ( native. data[ "files_visited" ] , json!( 2 ) ) ;
1300+ assert_eq ! ( native. data[ "match_count" ] , json!( 3 ) ) ;
1301+ assert ! (
1302+ native. content. contains( "b.txt:1:n1" ) && native. content. contains( "b.txt:2:n2" ) ,
1303+ "final files_visited slot must be fully matched: native={native:?}"
1304+ ) ;
1305+ assert_eq ! ( rss. result[ "truncated" ] , json!( false ) , "rss={}" , rss. result) ;
1306+ assert_eq ! ( rss. result[ "data" ] [ "match_count" ] , json!( 3 ) ) ;
1307+ }
1308+
12131309fn write_search_files ( fixture : & Fixture , relative_paths : & [ & str ] ) {
12141310 for name in relative_paths {
12151311 let path = fixture. root . join ( name) ;
0 commit comments