Delete and rename conversations

This commit is contained in:
dwrz
2026-10-31 12:13:36 +00:00
parent 9020167a92
commit 62bb1cbf32
10 changed files with 552 additions and 33 deletions
+58
View File
@@ -286,6 +286,64 @@ func (q *Queries) SelectConversationsUserId(ctx context.Context, userId string)
return conversations, nil
}
const updateConversationTitle = `
UPDATE conversations
SET title = ?
WHERE id = ?
RETURNING created_at, id, title, updated_at, user_id
`
// UpdateConversationTitleParams holds arguments for
// UpdateConversationTitle.
type UpdateConversationTitleParams struct {
Id string
Title string
}
// UpdateConversationTitle sets the title of one conversation and
// returns the updated row.
//
// The update keeps updated_at. That timestamp is the
// conversation's synchronization token for its message set, and a
// title is not a message: a rename must not move the conversation
// in the picker's ordering, which sorts by updated_at.
//
// A conversation that is missing returns sql.ErrNoRows.
func (q *Queries) UpdateConversationTitle(
ctx context.Context,
arg UpdateConversationTitleParams,
) (*Conversation, error) {
row := q.db.QueryRowContext(
ctx,
updateConversationTitle,
arg.Title,
arg.Id,
)
var (
c Conversation
createdAt string
updatedAt string
err error
)
if err := row.Scan(
&createdAt,
&c.Id,
&c.Title,
&updatedAt,
&c.UserId,
); err != nil {
return nil, fmt.Errorf("UpdateConversationTitle: %w", err)
}
if c.CreatedAt, err = time.Parse(time.RFC3339, createdAt); err != nil {
return nil, fmt.Errorf("UpdateConversationTitle: parse created_at: %w", err)
}
if c.UpdatedAt, err = time.Parse(time.RFC3339, updatedAt); err != nil {
return nil, fmt.Errorf("UpdateConversationTitle: parse updated_at: %w", err)
}
return &c, nil
}
const deleteConversationId = `
DELETE FROM conversations
WHERE id = ?
+41
View File
@@ -2,6 +2,7 @@ package db
import (
"context"
"database/sql"
"encoding/json/jsontext"
"encoding/json/v2"
"errors"
@@ -134,6 +135,46 @@ func testConversation(t *testing.T, ctx context.Context, db *SQLite) {
if len(conversations) != 1 {
t.Fatalf("expected 1 conversation, got %d", len(conversations))
}
// Rename the conversation. Insert a message first: the insert
// trigger moves updated_at, so the rename can show that it
// keeps the timestamp.
if _, err := qw.InsertMessage(ctx, InsertMessageParams{
ConversationId: c.Id,
Sequence: 1,
Role: MessageRoleUser,
Content: jsontext.Value(`{"role":"user","content":"Hello"}`),
}); err != nil {
t.Fatalf("InsertMessage: %v", err)
}
before, err := qr.SelectConversationId(ctx, c.Id)
if err != nil {
t.Fatalf("SelectConversationId: %v", err)
}
renamed, err := qw.UpdateConversationTitle(ctx, UpdateConversationTitleParams{
Id: c.Id,
Title: "renamed",
})
if err != nil {
t.Fatalf("UpdateConversationTitle: %v", err)
}
if renamed.Title == nil || *renamed.Title != "renamed" {
t.Fatalf("Title = %v, want renamed", renamed.Title)
}
if !renamed.UpdatedAt.Equal(before.UpdatedAt) {
t.Fatalf(
"UpdatedAt = %v, want %v: a rename must not move updated_at",
renamed.UpdatedAt, before.UpdatedAt,
)
}
// A rename of a missing conversation reports no rows.
if _, err := qw.UpdateConversationTitle(ctx, UpdateConversationTitleParams{
Id: "missing",
Title: "renamed",
}); !errors.Is(err, sql.ErrNoRows) {
t.Fatalf("UpdateConversationTitle(missing) = %v, want sql.ErrNoRows", err)
}
}
func testMessaging(t *testing.T, ctx context.Context, db *SQLite) {