owpengram-server/internal/rpc/stats_real_rpc_test.go
2026-09-01 12:06:31 +03:00

133 lines
5.1 KiB
Go

package rpc
import (
"context"
"encoding/json"
"testing"
"time"
"github.com/iamxvbaba/td/tg"
"go.uber.org/zap/zaptest"
appchannels "telesrv/internal/app/channels"
appusers "telesrv/internal/app/users"
"telesrv/internal/domain"
"telesrv/internal/store/memory"
)
func TestStatsRPCProjectsRealGraphsAndPublicForwardPages(t *testing.T) {
ctx := context.Background()
const now = 1_700_611_200
users := memory.NewUserStore()
owner, err := users.Create(ctx, domain.User{AccessHash: 901, Phone: "15550000901", FirstName: "Stats"})
if err != nil {
t.Fatalf("create owner: %v", err)
}
channels := memory.NewChannelStore()
source, err := channels.CreateChannel(ctx, domain.CreateChannelRequest{
CreatorUserID: owner.ID, Title: "stats source", Broadcast: true, Date: now - 100,
})
if err != nil {
t.Fatalf("create source: %v", err)
}
post, err := channels.SendChannelMessage(ctx, domain.SendChannelMessageRequest{
UserID: owner.ID, ChannelID: source.Channel.ID, RandomID: 1, Message: "post", Date: now - 90,
})
if err != nil {
t.Fatalf("send post: %v", err)
}
if _, err := channels.GetChannelMessageViews(ctx, domain.ChannelMessageViewsRequest{
UserID: owner.ID, ChannelID: source.Channel.ID, IDs: []int{post.Message.ID}, Increment: true, Date: now - 80,
}); err != nil {
t.Fatalf("increment post view: %v", err)
}
if _, err := channels.SetChannelMessageReactions(ctx, domain.SetChannelMessageReactionsRequest{
UserID: owner.ID, ChannelID: source.Channel.ID, MessageID: post.Message.ID,
Reactions: []domain.MessageReaction{{Type: domain.MessageReactionEmoji, Emoticon: "🔥"}}, Date: now - 70,
}); err != nil {
t.Fatalf("react to post: %v", err)
}
destinationCreated, err := channels.CreateChannel(ctx, domain.CreateChannelRequest{
CreatorUserID: owner.ID, Title: "public destination", Broadcast: true, Date: now - 60,
})
if err != nil {
t.Fatalf("create destination: %v", err)
}
destination, err := channels.UpdateUsername(ctx, domain.UpdateChannelUsernameRequest{
UserID: owner.ID, ChannelID: destinationCreated.Channel.ID, Username: "stats_rpc_forward",
})
if err != nil {
t.Fatalf("make destination public: %v", err)
}
forward := &domain.MessageForward{
From: domain.Peer{Type: domain.PeerTypeChannel, ID: source.Channel.ID}, Date: post.Message.Date, ChannelPost: post.Message.ID,
}
for i := 0; i < 2; i++ {
if _, err := channels.SendChannelMessage(ctx, domain.SendChannelMessageRequest{
UserID: owner.ID, ChannelID: destination.ID, RandomID: int64(10 + i), Message: "forward", Forward: forward, Date: now - 50 + i,
}); err != nil {
t.Fatalf("send forward %d: %v", i, err)
}
}
r := New(Config{}, Deps{
Users: appusers.NewService(users), Channels: appchannels.NewService(channels),
}, zaptest.NewLogger(t), fixedClock{now: time.Unix(now, 0)})
requestCtx := WithUserID(ctx, owner.ID)
inputSource := &tg.InputChannel{ChannelID: source.Channel.ID, AccessHash: source.Channel.AccessHash}
broadcast, err := r.onStatsGetBroadcastStats(requestCtx, &tg.StatsGetBroadcastStatsRequest{Channel: inputSource})
if err != nil {
t.Fatalf("get broadcast stats: %v", err)
}
if broadcast.Followers.Current != 1 || broadcast.ViewsPerPost.Current != 1 ||
broadcast.SharesPerPost.Current != 2 || broadcast.ReactionsPerPost.Current != 1 {
t.Fatalf("broadcast metrics = %+v, want real durable values", broadcast)
}
graph, ok := broadcast.InteractionsGraph.(*tg.StatsGraph)
if !ok {
t.Fatalf("interactions graph = %T, want *tg.StatsGraph", broadcast.InteractionsGraph)
}
var payload struct {
Columns [][]any `json:"columns"`
Colors map[string]string `json:"colors"`
}
if err := json.Unmarshal([]byte(graph.JSON.Data), &payload); err != nil {
t.Fatalf("decode interactions graph: %v (%s)", err, graph.JSON.Data)
}
if len(payload.Columns) != 3 || payload.Colors["y0"] != "#4A90E2" {
t.Fatalf("interactions graph payload = %+v", payload)
}
if _, ok := broadcast.LanguagesGraph.(*tg.StatsGraphError); !ok {
t.Fatalf("unsupported language graph = %T, want explicit statsGraphError", broadcast.LanguagesGraph)
}
messageStats, err := r.onStatsGetMessageStats(requestCtx, &tg.StatsGetMessageStatsRequest{
Channel: inputSource, MsgID: post.Message.ID,
})
if err != nil {
t.Fatalf("get message stats: %v", err)
}
if _, ok := messageStats.ViewsGraph.(*tg.StatsGraph); !ok {
t.Fatalf("message views graph = %T, want real statsGraph", messageStats.ViewsGraph)
}
first, err := r.onStatsGetMessagePublicForwards(requestCtx, &tg.StatsGetMessagePublicForwardsRequest{
Channel: inputSource, MsgID: post.Message.ID, Limit: 1,
})
if err != nil {
t.Fatalf("get first public forwards page: %v", err)
}
next, ok := first.GetNextOffset()
if first.Count != 2 || len(first.Forwards) != 1 || !ok || next == "" || len(first.Chats) != 1 {
t.Fatalf("first public forwards page = %+v", first)
}
second, err := r.onStatsGetMessagePublicForwards(requestCtx, &tg.StatsGetMessagePublicForwardsRequest{
Channel: inputSource, MsgID: post.Message.ID, Offset: next, Limit: 1,
})
if err != nil {
t.Fatalf("get second public forwards page: %v", err)
}
if second.Count != 2 || len(second.Forwards) != 1 {
t.Fatalf("second public forwards page = %+v", second)
}
}