TestOrphanedSubsessionReference verifies that loading sessions gracefully handles orphaned subsession references (where the subsession was deleted but the reference in session_items remains).
(t *testing.T)
| 783 | // handles orphaned subsession references (where the subsession was deleted |
| 784 | // but the reference in session_items remains). |
| 785 | func TestOrphanedSubsessionReference(t *testing.T) { |
| 786 | t.Parallel() |
| 787 | |
| 788 | tempDB := filepath.Join(t.TempDir(), "test_orphaned.db") |
| 789 | |
| 790 | store, err := NewSQLiteSessionStore(t.Context(), tempDB) |
| 791 | require.NoError(t, err) |
| 792 | defer store.(*SQLiteSessionStore).Close() |
| 793 | |
| 794 | sqliteStore := store.(*SQLiteSessionStore) |
| 795 | |
| 796 | // Create parent session |
| 797 | parentSession := &Session{ |
| 798 | ID: "parent-session", |
| 799 | CreatedAt: time.Now(), |
| 800 | } |
| 801 | err = store.AddSession(t.Context(), parentSession) |
| 802 | require.NoError(t, err) |
| 803 | |
| 804 | // Add a message to parent |
| 805 | _, err = store.AddMessage(t.Context(), "parent-session", UserMessage("Start task")) |
| 806 | require.NoError(t, err) |
| 807 | |
| 808 | // Create and add a sub-session |
| 809 | subSession := &Session{ |
| 810 | ID: "sub-session-to-delete", |
| 811 | CreatedAt: time.Now(), |
| 812 | Messages: []Item{ |
| 813 | NewMessageItem(UserMessage("Sub task")), |
| 814 | }, |
| 815 | } |
| 816 | err = store.AddSubSession(t.Context(), "parent-session", subSession) |
| 817 | require.NoError(t, err) |
| 818 | |
| 819 | // Verify session loads correctly before deletion |
| 820 | retrieved, err := store.GetSession(t.Context(), "parent-session") |
| 821 | require.NoError(t, err) |
| 822 | assert.Len(t, retrieved.Messages, 2) // user message + subsession |
| 823 | |
| 824 | // Now delete the sub-session directly from the database |
| 825 | // This simulates a scenario where the sub-session is deleted but the |
| 826 | // reference in session_items remains (the FK sets it to NULL) |
| 827 | _, err = sqliteStore.db.ExecContext(t.Context(), "DELETE FROM sessions WHERE id = ?", "sub-session-to-delete") |
| 828 | require.NoError(t, err) |
| 829 | |
| 830 | // Loading the parent session should gracefully skip the orphaned reference |
| 831 | retrieved, err = store.GetSession(t.Context(), "parent-session") |
| 832 | require.NoError(t, err) |
| 833 | assert.Len(t, retrieved.Messages, 1) // Only the user message remains |
| 834 | assert.Equal(t, "Start task", retrieved.Messages[0].Message.Message.Content) |
| 835 | |
| 836 | // GetSessions should also work without error |
| 837 | sessions, err := store.GetSessions(t.Context()) |
| 838 | require.NoError(t, err) |
| 839 | assert.Len(t, sessions, 1) |
| 840 | assert.Equal(t, "parent-session", sessions[0].ID) |
| 841 | } |
| 842 |
nothing calls this directly
no test coverage detected