feat: sync AI compose and ChatBot features
This commit is contained in:
parent
35e5d38f4d
commit
b7269b135f
75 changed files with 5426 additions and 123 deletions
86
internal/store/postgres/ai_integration_test.go
Normal file
86
internal/store/postgres/ai_integration_test.go
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"testing"
|
||||
|
||||
"telesrv/internal/domain"
|
||||
)
|
||||
|
||||
// TestAIComposeStoreRoundTripPostgres 验证自定义 AI tone 与保存列表持久化,
|
||||
// 含 creator/saved 视角、slug/id 解析和删除级联。
|
||||
func TestAIComposeStoreRoundTripPostgres(t *testing.T) {
|
||||
pool := testPool(t)
|
||||
ctx := context.Background()
|
||||
store := NewAIComposeStore(pool)
|
||||
users := NewUserStore(pool)
|
||||
suffix := randomSuffix(t)
|
||||
|
||||
owner, err := users.Create(ctx, domain.User{AccessHash: randomAIComposeID(), Phone: "+1771" + suffix + "01", FirstName: "AIOwner"})
|
||||
if err != nil {
|
||||
t.Fatalf("create owner: %v", err)
|
||||
}
|
||||
other, err := users.Create(ctx, domain.User{AccessHash: randomAIComposeID(), Phone: "+1771" + suffix + "02", FirstName: "AISaver"})
|
||||
if err != nil {
|
||||
t.Fatalf("create other: %v", err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
_, _ = pool.Exec(ctx, "DELETE FROM users WHERE id = ANY($1)", []int64{owner.ID, other.ID})
|
||||
})
|
||||
|
||||
tone := domain.AIComposeTone{
|
||||
ID: randomAIComposeID(),
|
||||
AccessHash: randomAIComposeID(),
|
||||
OwnerUserID: owner.ID,
|
||||
Slug: "ai-pg-" + suffix,
|
||||
Title: "Sharp",
|
||||
Prompt: "Make it direct and crisp.",
|
||||
DisplayAuthor: true,
|
||||
CreatedAt: 1700000000,
|
||||
UpdatedAt: 1700000000,
|
||||
}
|
||||
if err := store.CreateAIComposeTone(ctx, tone); err != nil {
|
||||
t.Fatalf("create tone: %v", err)
|
||||
}
|
||||
if got, ok, err := store.GetAIComposeToneByID(ctx, tone.ID, tone.AccessHash); err != nil || !ok || got.Slug != tone.Slug || got.AuthorID != owner.ID {
|
||||
t.Fatalf("get by id = ok %v tone %#v err %v", ok, got, err)
|
||||
}
|
||||
if err := store.SaveAIComposeTone(ctx, other.ID, tone.ID); err != nil {
|
||||
t.Fatalf("save tone: %v", err)
|
||||
}
|
||||
list, err := store.ListAIComposeTonesForUser(ctx, other.ID)
|
||||
if err != nil || len(list) != 1 {
|
||||
t.Fatalf("list saved = %d err %v, want 1", len(list), err)
|
||||
}
|
||||
if list[0].ID != tone.ID || !list[0].Saved || list[0].Creator {
|
||||
t.Fatalf("saved view = %#v, want saved non-creator", list[0])
|
||||
}
|
||||
if got, ok, err := store.GetAIComposeToneBySlug(ctx, tone.Slug); err != nil || !ok || got.InstallsCount != 1 {
|
||||
t.Fatalf("get by slug installs = ok %v tone %#v err %v, want installs=1", ok, got, err)
|
||||
}
|
||||
if count, err := store.SavedAIComposeToneCount(ctx, other.ID); err != nil || count != 1 {
|
||||
t.Fatalf("saved count = %d err %v, want 1", count, err)
|
||||
}
|
||||
if err := store.DeleteAIComposeTone(ctx, owner.ID, tone.ID); err != nil {
|
||||
t.Fatalf("delete tone: %v", err)
|
||||
}
|
||||
if _, ok, err := store.GetAIComposeToneBySlug(ctx, tone.Slug); err != nil || ok {
|
||||
t.Fatalf("get after delete = ok %v err %v, want missing", ok, err)
|
||||
}
|
||||
if list, err := store.ListAIComposeTonesForUser(ctx, other.ID); err != nil || len(list) != 0 {
|
||||
t.Fatalf("list after delete = %d err %v, want empty", len(list), err)
|
||||
}
|
||||
}
|
||||
|
||||
func randomAIComposeID() int64 {
|
||||
for {
|
||||
var b [8]byte
|
||||
_, _ = rand.Read(b[:])
|
||||
v := int64(binary.BigEndian.Uint64(b[:]) & 0x7fffffffffffffff)
|
||||
if v != 0 {
|
||||
return v
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue