@@ -6520,6 +6520,137 @@ func TestProcessOverrideSaveHandlerWritesToDefaultProject(t *testing.T) {
65206520 }
65216521}
65226522
6523+ // ─── #403/#413: handleSessionSummary process-override tests ──────────────────
6524+
6525+ // TestSessionSummary_ProcessOverrideWritesToDefaultProject verifies that when
6526+ // cfg.DefaultProject is set (process-level override via ENGRAM_PROJECT / --project),
6527+ // handleSessionSummary writes under that project instead of falling back to cwd
6528+ // detection. Mirrors TestProcessOverrideSaveHandlerWritesToDefaultProject for save.
6529+ func TestSessionSummary_ProcessOverrideWritesToDefaultProject (t * testing.T ) {
6530+ // Use a temp dir that has no git repo — without the fix, resolveWriteProject()
6531+ // would return an error or a wrong project; with the fix it uses the override.
6532+ dir := t .TempDir ()
6533+ t .Chdir (dir )
6534+
6535+ s := newMCPTestStore (t )
6536+ h := handleSessionSummary (s , MCPConfig {DefaultProject : "Trusted Project" }, NewSessionActivity (10 * time .Minute ))
6537+
6538+ res , err := h (context .Background (), mcppkg.CallToolRequest {Params : mcppkg.CallToolParams {Arguments : map [string ]any {
6539+ "content" : "## Goal\n Process override session summary" ,
6540+ }}})
6541+ if err != nil || res .IsError {
6542+ t .Fatalf ("session summary error: err=%v isError=%v text=%q" , err , res .IsError , callResultText (t , res ))
6543+ }
6544+
6545+ obs , err := s .RecentObservations ("trusted project" , "project" , 5 )
6546+ if err != nil {
6547+ t .Fatalf ("RecentObservations: %v" , err )
6548+ }
6549+ if len (obs ) == 0 {
6550+ t .Fatal ("expected session_summary observation under 'trusted project' (process override); got none" )
6551+ }
6552+
6553+ m := callResultJSON (t , res )
6554+ if got := m ["project" ]; got != "trusted project" {
6555+ t .Errorf ("response envelope project = %v; want 'trusted project'" , got )
6556+ }
6557+ if got := m ["project_source" ]; got != sourceProcessOverride {
6558+ t .Errorf ("response envelope project_source = %v; want %s" , got , sourceProcessOverride )
6559+ }
6560+ }
6561+
6562+ // TestSessionSummary_ProcessOverrideBypassesAmbiguousCWD verifies that an
6563+ // ambiguous cwd (parent dir with multiple git repos) is bypassed when
6564+ // cfg.DefaultProject is set.
6565+ func TestSessionSummary_ProcessOverrideBypassesAmbiguousCWD (t * testing.T ) {
6566+ parent := t .TempDir ()
6567+ for _ , name := range []string {"repo-ss-1" , "repo-ss-2" } {
6568+ child := filepath .Join (parent , name )
6569+ if err := os .MkdirAll (child , 0o755 ); err != nil {
6570+ t .Fatal (err )
6571+ }
6572+ initTestGitRepo (t , child )
6573+ }
6574+ t .Chdir (parent )
6575+
6576+ s := newMCPTestStore (t )
6577+ h := handleSessionSummary (s , MCPConfig {DefaultProject : "override-project" }, NewSessionActivity (10 * time .Minute ))
6578+
6579+ res , err := h (context .Background (), mcppkg.CallToolRequest {Params : mcppkg.CallToolParams {Arguments : map [string ]any {
6580+ "content" : "## Goal\n Ambiguous override test" ,
6581+ }}})
6582+ if err != nil || res .IsError {
6583+ t .Fatalf ("expected success via process override; err=%v isError=%v text=%q" , err , res .IsError , callResultText (t , res ))
6584+ }
6585+
6586+ obs , err := s .RecentObservations ("override-project" , "project" , 5 )
6587+ if err != nil {
6588+ t .Fatalf ("RecentObservations: %v" , err )
6589+ }
6590+ if len (obs ) == 0 {
6591+ t .Fatal ("expected session_summary under 'override-project'; got none" )
6592+ }
6593+ }
6594+
6595+ // ─── #393: handleSessionSummary empty-content guard tests ────────────────────
6596+
6597+ // TestSessionSummary_EmptyContentRejected verifies that an empty content string
6598+ // is rejected before AddObservation is called, mirroring the guard in handleSave.
6599+ func TestSessionSummary_EmptyContentRejected (t * testing.T ) {
6600+ dir := t .TempDir ()
6601+ initTestGitRepo (t , dir )
6602+ t .Chdir (dir )
6603+
6604+ s := newMCPTestStore (t )
6605+ h := handleSessionSummary (s , MCPConfig {}, NewSessionActivity (10 * time .Minute ))
6606+
6607+ res , err := h (context .Background (), mcppkg.CallToolRequest {Params : mcppkg.CallToolParams {Arguments : map [string ]any {
6608+ "content" : "" ,
6609+ }}})
6610+ if err != nil {
6611+ t .Fatalf ("handler returned Go error: %v" , err )
6612+ }
6613+ if ! res .IsError {
6614+ t .Fatal ("expected tool error for empty content; got success" )
6615+ }
6616+ text := callResultText (t , res )
6617+ if ! strings .Contains (text , "content" ) {
6618+ t .Errorf ("error message should mention 'content'; got: %q" , text )
6619+ }
6620+
6621+ // No observation must have been persisted.
6622+ obs , err := s .RecentObservations ("" , "project" , 10 )
6623+ if err != nil {
6624+ t .Fatalf ("RecentObservations: %v" , err )
6625+ }
6626+ if len (obs ) != 0 {
6627+ t .Fatalf ("expected 0 observations after empty-content rejection; got %d" , len (obs ))
6628+ }
6629+ }
6630+
6631+ // TestSessionSummary_WhitespaceOnlyContentRejected verifies that whitespace-only
6632+ // content is also rejected (mirrors handleSave behaviour).
6633+ func TestSessionSummary_WhitespaceOnlyContentRejected (t * testing.T ) {
6634+ dir := t .TempDir ()
6635+ initTestGitRepo (t , dir )
6636+ t .Chdir (dir )
6637+
6638+ s := newMCPTestStore (t )
6639+ h := handleSessionSummary (s , MCPConfig {}, NewSessionActivity (10 * time .Minute ))
6640+
6641+ res , err := h (context .Background (), mcppkg.CallToolRequest {Params : mcppkg.CallToolParams {Arguments : map [string ]any {
6642+ "content" : " \n \t " ,
6643+ }}})
6644+ if err != nil {
6645+ t .Fatalf ("handler returned Go error: %v" , err )
6646+ }
6647+ if ! res .IsError {
6648+ t .Fatal ("expected tool error for whitespace-only content; got success" )
6649+ }
6650+ }
6651+
6652+ // ─── #408/#346: cross-project search and timezone tests ──────────────────────
6653+
65236654// seedCrossProjectMemories inserts one observation per project so cross-project
65246655// search tests have something to find. Returns the session IDs created.
65256656func seedCrossProjectMemories (t * testing.T , s * store.Store ) {
@@ -6647,4 +6778,3 @@ func TestHandleSearchWithoutAllProjectsStillScopesToCurrentProject(t *testing.T)
66476778 t .Fatalf ("beta result should not leak into a scoped search; got: %s" , text )
66486779 }
66496780}
6650-
0 commit comments