111 lines
4.2 KiB
Go
111 lines
4.2 KiB
Go
package postgres
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/jackc/pgx/v5"
|
|
|
|
"telesrv/internal/domain"
|
|
"telesrv/internal/observability/dbtrace"
|
|
)
|
|
|
|
func TestPlainPrivateSendHotPathClassifier(t *testing.T) {
|
|
base := domain.SendPrivateTextRequest{Message: "plain"}
|
|
if !plainPrivateSendHotPath(base, privateSendTxHooks{}) {
|
|
t.Fatal("plain text request did not select hot path")
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
edit func(*domain.SendPrivateTextRequest, *privateSendTxHooks)
|
|
}{
|
|
{"entity", func(req *domain.SendPrivateTextRequest, _ *privateSendTxHooks) {
|
|
req.Entities = []domain.MessageEntity{{Type: domain.MessageEntityBold, Length: 1}}
|
|
}},
|
|
{"reply", func(req *domain.SendPrivateTextRequest, _ *privateSendTxHooks) {
|
|
req.ReplyTo = &domain.MessageReply{MessageID: 1}
|
|
}},
|
|
{"silent", func(req *domain.SendPrivateTextRequest, _ *privateSendTxHooks) { req.Silent = true }},
|
|
{"automation", func(req *domain.SendPrivateTextRequest, _ *privateSendTxHooks) {
|
|
req.BusinessAutomationKind = domain.BusinessAutomationGreeting
|
|
}},
|
|
{"hook", func(_ *domain.SendPrivateTextRequest, hooks *privateSendTxHooks) {
|
|
hooks.after = func(context.Context, pgx.Tx, domain.SendPrivateTextResult) error { return nil }
|
|
}},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
req := base
|
|
hooks := privateSendTxHooks{}
|
|
tt.edit(&req, &hooks)
|
|
if plainPrivateSendHotPath(req, hooks) {
|
|
t.Fatal("request with additional semantics selected hot path")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPlainPrivateSendHotPathDurableFactsAndQueryCount(t *testing.T) {
|
|
pool := testPool(t)
|
|
baseCtx := context.Background()
|
|
users := NewUserStore(pool)
|
|
suffix := randomSuffix(t)
|
|
sender := createTestUser(t, baseCtx, users, "+1667"+suffix+"01", "HotSender", "")
|
|
recipient := createTestUser(t, baseCtx, users, "+1667"+suffix+"02", "HotRecipient", "")
|
|
t.Cleanup(func() {
|
|
_, _ = pool.Exec(baseCtx, "DELETE FROM users WHERE id = ANY($1::bigint[])", []int64{sender.ID, recipient.ID})
|
|
})
|
|
|
|
messages := NewMessageStore(pool, WithMessageAllocators(&perUserCounterAllocator{}))
|
|
ctx, stats := dbtrace.WithStats(baseCtx)
|
|
authKeyID := [8]byte{0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x80}
|
|
result, err := messages.SendPrivateText(ctx, domain.SendPrivateTextRequest{
|
|
SenderUserID: sender.ID,
|
|
RecipientUserID: recipient.ID,
|
|
RandomID: 250825001,
|
|
Message: "plain hot path",
|
|
Date: 1800000001,
|
|
IdempotencyPreflighted: true,
|
|
OriginAuthKeyID: authKeyID,
|
|
OriginSessionID: 250825,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("SendPrivateText: %v", err)
|
|
}
|
|
if snapshot := stats.Snapshot(); snapshot.Queries != 5 || snapshot.Errors != 0 {
|
|
t.Fatalf("query stats = %+v, want BEGIN + lock + logical + projection + COMMIT", snapshot)
|
|
}
|
|
if result.SenderMessage.ID != 1 || result.RecipientMessage.ID != 1 ||
|
|
result.SenderMessage.Pts != 1 || result.RecipientMessage.Pts != 1 {
|
|
t.Fatalf("result = %+v, want first box/PTS for both owners", result)
|
|
}
|
|
|
|
for _, fact := range []struct {
|
|
name string
|
|
query string
|
|
want int
|
|
}{
|
|
{"boxes", `SELECT count(*) FROM message_boxes WHERE private_message_id=$1`, 2},
|
|
{"events", `SELECT count(*) FROM user_update_events e JOIN message_boxes m ON (m.owner_user_id,m.box_id)=(e.user_id,e.message_box_id) WHERE m.private_message_id=$1`, 2},
|
|
{"outbox", `SELECT count(*) FROM dispatch_outbox d JOIN user_update_events e ON (e.user_id,e.pts)=(d.target_user_id,d.pts) JOIN message_boxes m ON (m.owner_user_id,m.box_id)=(e.user_id,e.message_box_id) WHERE m.private_message_id=$1`, 2},
|
|
} {
|
|
var got int
|
|
if err := pool.QueryRow(baseCtx, fact.query, result.SenderMessage.UID).Scan(&got); err != nil {
|
|
t.Fatalf("count %s: %v", fact.name, err)
|
|
}
|
|
if got != fact.want {
|
|
t.Fatalf("%s rows = %d, want %d", fact.name, got, fact.want)
|
|
}
|
|
}
|
|
|
|
var excludeAuthKeyID, excludeSessionID int64
|
|
if err := pool.QueryRow(baseCtx, `
|
|
SELECT exclude_auth_key_id, exclude_session_id
|
|
FROM dispatch_outbox
|
|
WHERE target_user_id=$1`, sender.ID).Scan(&excludeAuthKeyID, &excludeSessionID); err != nil {
|
|
t.Fatalf("load sender exclusion: %v", err)
|
|
}
|
|
if excludeAuthKeyID != authKeyIDToInt64(authKeyID) || excludeSessionID != 250825 {
|
|
t.Fatalf("sender exclusion = %d/%d, want exact auth key/session", excludeAuthKeyID, excludeSessionID)
|
|
}
|
|
}
|