package tcp import ( "encoding/json" "errors" "strconv" "testing" "spark-server/internal/infrastructure/id" convlogic "spark-server/internal/module/conversation/logic" convmodel "spark-server/internal/module/conversation/model" delivermodel "spark-server/internal/module/deliver/model" friendlogic "spark-server/internal/module/friend/logic" friendmodel "spark-server/internal/module/friend/model" inboxmodel "spark-server/internal/module/inbox/model" msglogic "spark-server/internal/module/message/logic" msgmodel "spark-server/internal/module/message/model" userentity "spark-server/internal/module/user/entity" "spark-server/internal/module/user/logictest" "spark-server/internal/pkg/errs" "spark-server/internal/pkg/reqctx" "spark-server/internal/transport/tcp/protocol" "spark-server/internal/transport/tcp/session" ) func TestReadMessageRegistered(t *testing.T) { svc, b, convID, msgID := readFixture(t) srv := &Server{messages: svc, router: NewRouter()} srv.registerDefaultHandlers() conn := session.NewConn("c", "127.0.0.1", 4) conn.Authenticate(b, 9) conn.SetRequestID("req-read") missing, err := srv.router.Dispatch(t.Context(), conn, protocol.Frame{ Version: protocol.CurrentVersion, Command: protocol.CmdReadMessage, Body: []byte(`{}`), }) if missing != nil || !errors.Is(err, errs.ErrInvalidArgument) { t.Fatalf("missing fields frame=%v err=%v", missing, err) } onlyLast, err := srv.router.Dispatch(t.Context(), conn, protocol.Frame{ Version: protocol.CurrentVersion, Command: protocol.CmdReadMessage, Body: []byte(`{"conversation_id":` + strconv.FormatInt(convID, 10) + `,"peer_id":1,"last_msg_id":0}`), }) if onlyLast != nil || !errors.Is(err, errs.ErrInvalidArgument) { t.Fatalf("zero cursor frame=%v err=%v", onlyLast, err) } resp, err := srv.router.Dispatch(t.Context(), conn, protocol.Frame{ Version: protocol.CurrentVersion, Command: protocol.CmdReadMessage, SequenceID: 4, Body: []byte(`{"conversation_id":` + strconv.FormatInt(convID, 10) + `,"msg_id":` + strconv.FormatInt(msgID, 10) + `}`), }) if err != nil || resp == nil || resp.Command != protocol.CmdReadMessage { t.Fatalf("read %+v %v", resp, err) } env, err := protocol.DecodeEnvelope(resp.Body) if err != nil || env.Code != protocol.CodeOK { t.Fatalf("envelope %+v %v", env, err) } var data msglogic.ReadResult if err := json.Unmarshal(env.Data, &data); err != nil || data.MsgID != msgID || data.ConversationID != convID { t.Fatalf("data %+v %v", data, err) } alias, err := srv.router.Dispatch(t.Context(), conn, protocol.Frame{ Version: protocol.CurrentVersion, Command: protocol.CmdReadMessage, SequenceID: 5, Body: []byte(`{"conversation_id":` + strconv.FormatInt(convID, 10) + `,"peer_id":1,"last_msg_id":` + strconv.FormatInt(msgID, 10) + `}`), }) if err != nil || alias == nil { t.Fatalf("last_msg_id %+v %v", alias, err) } aliasEnv, err := protocol.DecodeEnvelope(alias.Body) if err != nil || aliasEnv.Code != protocol.CodeOK { t.Fatalf("last_msg_id envelope %+v %v", aliasEnv, err) } var aliasData msglogic.ReadResult if err := json.Unmarshal(aliasEnv.Data, &aliasData); err != nil || aliasData.MsgID != msgID || aliasData.ConversationID != convID { t.Fatalf("last_msg_id data %+v %v", aliasData, err) } } func readFixture(t *testing.T) (*msglogic.Service, int64, int64, int64) { t.Helper() ids, err := id.New(1) if err != nil { t.Fatal(err) } users := logictest.Users(ids, nil) chats := convmodel.NewMemoryP2P() p2p := convlogic.NewP2PService(chats, ids) q := delivermodel.NewMemoryQueue() msgs := msgmodel.NewMemoryRepo() friends := friendlogic.NewService(friendmodel.NewMemoryRepo(), users, ids, friendlogic.NewGreeter(p2p, msgs, ids, q)) svc := msglogic.NewService(msgs, chats, p2p, friends, nil, ids, q) svc.BindInbox(inboxmodel.NewMemoryStore()) ctx := reqctx.With(t.Context(), &reqctx.RequestContext{Language: "zh-CN"}) a, err := users.AuthenticateEmail(ctx, "test01@spark.com", "1", &userentity.LoginDevice{DeviceID: 1, DeviceType: userentity.DeviceIOS}) if err != nil { t.Fatal(err) } b, err := users.AuthenticateEmail(ctx, "test02@spark.com", "1", &userentity.LoginDevice{DeviceID: 2, DeviceType: userentity.DeviceWeb}) if err != nil { t.Fatal(err) } applied, err := friends.Apply(ctx, a.ID, &friendlogic.ApplyInput{UID: b.ID}) if err != nil { t.Fatal(err) } if _, err := friends.Accept(ctx, b.ID, friendlogic.AcceptInput{RequestID: applied.RequestID}); err != nil { t.Fatal(err) } sent, err := svc.SendMessage(ctx, a.ID, 1, &msglogic.SendInput{ PeerID: b.ID, ClientMsgID: "read-1", Content: json.RawMessage(`{"t":1,"text":{"body":"hi"}}`), }) if err != nil { t.Fatal(err) } return svc, b.ID, sent.ConversationID, sent.MsgID }