@@ -260,9 +260,11 @@ def test_recent_activity_format_project_output_no_results():
260260
261261
262262def test_recent_activity_format_project_output_renders_all_entities_and_relations ():
263- """Regression for #784, part 1: when the result count is below the display cap
264- the formatter must render every row. Previously the cap was hardcoded at 5,
265- so a result set of 7 entities would show 5 rows under a heading that claimed 7.
263+ """Regression for #784: the formatter must render every row the API returned.
264+ Previously the body was hardcoded to `[:5]` while the heading reported the
265+ true total — a result set of N>5 entities would show 5 rows under a heading
266+ that claimed N, with no signal the body was truncated. `page_size` is now
267+ the only knob; heading count and body row count must always agree.
266268 """
267269 import importlib
268270
@@ -271,8 +273,10 @@ def test_recent_activity_format_project_output_renders_all_entities_and_relation
271273 recent_activity_module = importlib .import_module ("basic_memory.mcp.tools.recent_activity" )
272274 now = datetime .now (timezone .utc )
273275
274- entity_titles = [f"Entity { i } " for i in range (7 )]
275- relation_titles = [f"Relation { i } " for i in range (6 )]
276+ # Counts chosen to comfortably exceed the old hardcoded `[:5]` slice and any
277+ # plausible reintroduced default cap.
278+ entity_titles = [f"Entity { i } " for i in range (15 )]
279+ relation_titles = [f"Relation { i } " for i in range (12 )]
276280
277281 results = [
278282 ContextResult (
@@ -327,89 +331,9 @@ def test_recent_activity_format_project_output_renders_all_entities_and_relation
327331 assert f"[[Entity { i } ]] → references → [[Entity { i + 1 } ]]" in out , (
328332 f"Relation { i } missing from formatter output"
329333 )
330- # Below-cap path: no truncation footer should appear.
331- assert "more on this page" not in out
332-
333-
334- def test_recent_activity_format_project_output_caps_with_explicit_truncation ():
335- """Regression for #784, part 2: above the display cap the formatter must
336- truncate to the cap AND emit an explicit "…and N more" footer so the caller
337- can see that the body is a preview of a longer page (and knows to raise
338- page_size).
339- """
340- import importlib
341-
342- from basic_memory .schemas .memory import RelationSummary
343-
344- recent_activity_module = importlib .import_module ("basic_memory.mcp.tools.recent_activity" )
345- cap = recent_activity_module ._PROJECT_OUTPUT_DISPLAY_CAP
346- now = datetime .now (timezone .utc )
347-
348- entity_count = cap + 2
349- relation_count = cap + 3
350-
351- # Use prefixes that don't collide between entities and relation endpoints,
352- # so substring assertions don't accidentally match across rows.
353- results = [
354- ContextResult (
355- primary_result = EntitySummary (
356- external_id = f"550e8400-e29b-41d4-a716-44665544{ i :04d} " ,
357- entity_id = i ,
358- permalink = f"notes/entity-{ i } " ,
359- title = f"NoteFile { i } " ,
360- content = None ,
361- file_path = f"notes/entity-{ i } .md" ,
362- created_at = now ,
363- ),
364- observations = [],
365- related_results = [],
366- )
367- for i in range (entity_count )
368- ] + [
369- ContextResult (
370- primary_result = RelationSummary (
371- relation_id = 1000 + i ,
372- entity_id = i ,
373- title = f"Relation { i } " ,
374- file_path = f"notes/entity-{ i } .md" ,
375- permalink = f"notes/entity-{ i } " ,
376- relation_type = "references" ,
377- from_entity = f"FromNode { i } " ,
378- to_entity = f"ToNode { i } " ,
379- created_at = now ,
380- ),
381- observations = [],
382- related_results = [],
383- )
384- for i in range (relation_count )
385- ]
386-
387- activity = GraphContext (
388- results = results ,
389- metadata = MemoryMetadata (depth = 1 , generated_at = now ),
390- )
391-
392- out = recent_activity_module ._format_project_output (
393- project_name = "proj" ,
394- activity_data = activity ,
395- timeframe = "7d" ,
396- type_filter = ["entity" , "relation" ],
397- page = 1 ,
398- )
399-
400- # Heading reports the true total (independent of truncation).
401- assert f"Recent Notes & Documents ({ entity_count } )" in out
402- assert f"Recent Connections ({ relation_count } )" in out
403-
404- # Body shows exactly `cap` rows of each.
405- assert f"NoteFile { cap - 1 } " in out , "expected last in-cap entity to appear"
406- assert f"NoteFile { cap } " not in out , "entity beyond cap should be truncated"
407- assert f"[[FromNode { cap - 1 } ]] → references → [[ToNode { cap - 1 } ]]" in out
408- assert f"[[FromNode { cap } ]]" not in out , "relation beyond cap should be truncated"
409-
410- # Truncation footer is present and reports the correct hidden count.
411- assert f"…and { entity_count - cap } more on this page" in out
412- assert f"…and { relation_count - cap } more on this page" in out
334+ # Heading total matches the body — no silent truncation.
335+ assert f"Recent Notes & Documents ({ len (entity_titles )} )" in out
336+ assert f"Recent Connections ({ len (relation_titles )} )" in out
413337
414338
415339def test_recent_activity_format_project_output_includes_observation_truncation ():
0 commit comments