Просмотр исходного кода

increase test coverage on ChatNav handler

Mike 2 лет назад
Родитель
Сommit
e3713ca1ff
5 измененных файлов с 165 добавлено и 57 удалено
  1. 1 1
      cmd/main.go
  2. 27 26
      handler/chat_nav.go
  3. 116 26
      handler/chat_nav_test.go
  4. 12 4
      oscar/snacs.go
  5. 9 0
      state/chat_registry.go

+ 1 - 1
cmd/main.go

@@ -46,7 +46,7 @@ func main() {
 		oserviceBOSHandler := handler.NewOServiceServiceForBOS(*oserviceHandler, chatRegistry)
 		locateHandler := handler.NewLocateService(sessionManager, feedbagStore, feedbagStore)
 		newChatSessMgr := func() handler.SessionManager { return state.NewSessionManager(logger) }
-		chatNavHandler := handler.NewChatNavService(logger, chatRegistry, newChatSessMgr)
+		chatNavHandler := handler.NewChatNavService(logger, chatRegistry, state.NewChatRoom, newChatSessMgr)
 		feedbagHandler := handler.NewFeedbagService(sessionManager, feedbagStore)
 		icbmHandler := handler.NewICBMService(sessionManager, feedbagStore)
 

+ 27 - 26
handler/chat_nav.go

@@ -3,30 +3,24 @@ package handler
 import (
 	"context"
 	"errors"
+
 	"log/slog"
-	"time"
 
-	"github.com/google/uuid"
 	"github.com/mkaminski/goaim/oscar"
 	"github.com/mkaminski/goaim/state"
 )
 
-func NewChatRoom() state.ChatRoom {
-	return state.ChatRoom{
-		Cookie:     uuid.New().String(),
-		CreateTime: time.Now(),
-	}
-}
-
-func NewChatNavService(logger *slog.Logger, chatRegistry *state.ChatRegistry, newChatSessMgr func() SessionManager) *ChatNavService {
+// NewChatNavService creates a new instance of NewChatNavService.
+func NewChatNavService(logger *slog.Logger, chatRegistry *state.ChatRegistry, newChatRoom func() state.ChatRoom, newChatSessMgr func() SessionManager) *ChatNavService {
 	return &ChatNavService{
 		logger:         logger,
 		chatRegistry:   chatRegistry,
-		newChatRoom:    NewChatRoom,
+		newChatRoom:    newChatRoom,
 		newChatSessMgr: newChatSessMgr,
 	}
 }
 
+// ChatNavService provides handlers for the ChatNav food group.
 type ChatNavService struct {
 	logger         *slog.Logger
 	chatRegistry   *state.ChatRegistry
@@ -34,6 +28,8 @@ type ChatNavService struct {
 	newChatSessMgr func() SessionManager
 }
 
+// RequestChatRightsHandler returns SNAC oscar.ChatNavNavInfo, which contains
+// chat navigation service parameters and limits.
 func (s ChatNavService) RequestChatRightsHandler(_ context.Context, inFrame oscar.SNACFrame) oscar.SNACMessage {
 	return oscar.SNACMessage{
 		Frame: oscar.SNACFrame{
@@ -44,19 +40,19 @@ func (s ChatNavService) RequestChatRightsHandler(_ context.Context, inFrame osca
 		Body: oscar.SNAC_0x0D_0x09_ChatNavNavInfo{
 			TLVRestBlock: oscar.TLVRestBlock{
 				TLVList: oscar.TLVList{
-					oscar.NewTLV(0x02, uint8(10)),
-					oscar.NewTLV(0x03, oscar.SNAC_0x0D_0x09_TLVExchangeInfo{
+					oscar.NewTLV(oscar.ChatNavTLVMaxConcurrentRooms, uint8(10)),
+					oscar.NewTLV(oscar.ChatNavTLVExchangeInfo, oscar.SNAC_0x0D_0x09_TLVExchangeInfo{
 						Identifier: 4,
 						TLVBlock: oscar.TLVBlock{
 							TLVList: oscar.TLVList{
-								oscar.NewTLV(0x0002, uint16(0x0010)),
-								oscar.NewTLV(0x00c9, uint16(15)),
-								oscar.NewTLV(0x00d3, "default Exchange"),
-								oscar.NewTLV(0x00d5, uint8(2)),
-								oscar.NewTLV(0xd6, "us-ascii"),
-								oscar.NewTLV(0xd7, "en"),
-								oscar.NewTLV(0xd8, "us-ascii"),
-								oscar.NewTLV(0xd9, "en"),
+								oscar.NewTLV(oscar.ChatNavTLVClassPerms, uint16(0x0010)),
+								oscar.NewTLV(oscar.ChatNavTLVFlags, uint16(15)),
+								oscar.NewTLV(oscar.ChatNavTLVExchangeDesc, "default exchange"),
+								oscar.NewTLV(oscar.ChatNavTLVCreatePerms, uint8(2)),
+								oscar.NewTLV(oscar.ChatNavTLVCharSet1, "us-ascii"),
+								oscar.NewTLV(oscar.ChatNavTLVLang1, "en"),
+								oscar.NewTLV(oscar.ChatNavTLVCharSet2, "us-ascii"),
+								oscar.NewTLV(oscar.ChatNavTLVLang2, "en"),
 							},
 						},
 					}),
@@ -66,6 +62,9 @@ func (s ChatNavService) RequestChatRightsHandler(_ context.Context, inFrame osca
 	}
 }
 
+// CreateRoomHandler creates a chat room with the current user as the first
+// participant. It returns SNAC oscar.ChatNavNavInfo, which contains metadata
+// for the chat room.
 func (s ChatNavService) CreateRoomHandler(_ context.Context, sess *state.Session, inFrame oscar.SNACFrame, inBody oscar.SNAC_0x0E_0x02_ChatRoomInfoUpdate) (oscar.SNACMessage, error) {
 	name, hasName := inBody.GetString(oscar.ChatTLVRoomName)
 	if !hasName {
@@ -94,7 +93,7 @@ func (s ChatNavService) CreateRoomHandler(_ context.Context, sess *state.Session
 		Body: oscar.SNAC_0x0D_0x09_ChatNavNavInfo{
 			TLVRestBlock: oscar.TLVRestBlock{
 				TLVList: oscar.TLVList{
-					oscar.NewTLV(oscar.ChatNavTLVRoomInfo, oscar.SNAC_0x0E_0x02_ChatRoomInfoUpdate{
+					oscar.NewTLV(oscar.ChatNavRequestRoomInfo, oscar.SNAC_0x0E_0x02_ChatRoomInfoUpdate{
 						Exchange:       inBody.Exchange,
 						Cookie:         room.Cookie,
 						InstanceNumber: inBody.InstanceNumber,
@@ -109,6 +108,8 @@ func (s ChatNavService) CreateRoomHandler(_ context.Context, sess *state.Session
 	}, nil
 }
 
+// RequestRoomInfoHandler returns oscar.ChatNavNavInfo, which contains metadata
+// for the chat room specified in the inFrame.Cookie.
 func (s ChatNavService) RequestRoomInfoHandler(_ context.Context, inFrame oscar.SNACFrame, inBody oscar.SNAC_0x0D_0x04_ChatNavRequestRoomInfo) (oscar.SNACMessage, error) {
 	room, _, err := s.chatRegistry.Retrieve(string(inBody.Cookie))
 	if err != nil {
@@ -124,11 +125,11 @@ func (s ChatNavService) RequestRoomInfoHandler(_ context.Context, inFrame oscar.
 		Body: oscar.SNAC_0x0D_0x09_ChatNavNavInfo{
 			TLVRestBlock: oscar.TLVRestBlock{
 				TLVList: oscar.TLVList{
-					oscar.NewTLV(0x04, oscar.SNAC_0x0E_0x02_ChatRoomInfoUpdate{
-						Exchange:       4,
+					oscar.NewTLV(oscar.ChatNavRequestRoomInfo, oscar.SNAC_0x0E_0x02_ChatRoomInfoUpdate{
+						Exchange:       room.Exchange,
 						Cookie:         room.Cookie,
-						InstanceNumber: 100,
-						DetailLevel:    2,
+						InstanceNumber: room.InstanceNumber,
+						DetailLevel:    room.DetailLevel,
 						TLVBlock: oscar.TLVBlock{
 							TLVList: room.TLVList(),
 						},

+ 116 - 26
handler/chat_nav_test.go

@@ -10,10 +10,7 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
-func TestSendAndReceiveCreateRoom(t *testing.T) {
-	//
-	// build dependencies
-	//
+func TestChatNavService_CreateRoomHandler(t *testing.T) {
 	userSess := newTestSession("user-screen-name", sessOptCannedID)
 
 	chatRegistry := state.NewChatRegistry()
@@ -22,9 +19,16 @@ func TestSendAndReceiveCreateRoom(t *testing.T) {
 	sessionManager.EXPECT().NewSessionWithSN(userSess.ID(), userSess.ScreenName()).
 		Return(&state.Session{})
 
-	//
-	// send input SNAC
-	//
+	newChatRoom := func() state.ChatRoom {
+		return state.ChatRoom{
+			Cookie:     "dummy-cookie",
+			CreateTime: time.UnixMilli(0),
+		}
+	}
+	newChatSessMgr := func() SessionManager {
+		return sessionManager
+	}
+
 	inFrame := oscar.SNACFrame{
 		RequestID: 1234,
 	}
@@ -39,24 +43,11 @@ func TestSendAndReceiveCreateRoom(t *testing.T) {
 			},
 		},
 	}
-	svc := ChatNavService{
-		chatRegistry: chatRegistry,
-		newChatRoom: func() state.ChatRoom {
-			return state.ChatRoom{
-				Cookie:     "dummy-cookie",
-				CreateTime: time.UnixMilli(0),
-			}
-		},
-		newChatSessMgr: func() SessionManager {
-			return sessionManager
-		},
-	}
+
+	svc := NewChatNavService(nil, chatRegistry, newChatRoom, newChatSessMgr)
 	outputSNAC, err := svc.CreateRoomHandler(context.Background(), userSess, inFrame, inBody)
 	assert.NoError(t, err)
 
-	//
-	// verify chat room created by handler
-	//
 	expectChatRoom := state.ChatRoom{
 		Cookie:         "dummy-cookie",
 		CreateTime:     time.UnixMilli(0),
@@ -69,9 +60,6 @@ func TestSendAndReceiveCreateRoom(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, expectChatRoom, chatRoom)
 
-	//
-	// send input SNAC
-	//
 	expectSNAC := oscar.SNACMessage{
 		Frame: oscar.SNACFrame{
 			FoodGroup: oscar.ChatNav,
@@ -82,7 +70,7 @@ func TestSendAndReceiveCreateRoom(t *testing.T) {
 			TLVRestBlock: oscar.TLVRestBlock{
 				TLVList: oscar.TLVList{
 					oscar.NewTLV(
-						oscar.ChatNavTLVRoomInfo,
+						oscar.ChatNavRequestRoomInfo,
 						oscar.SNAC_0x0E_0x02_ChatRoomInfoUpdate{
 							Exchange:       chatRoom.Exchange,
 							Cookie:         chatRoom.Cookie,
@@ -100,3 +88,105 @@ func TestSendAndReceiveCreateRoom(t *testing.T) {
 
 	assert.Equal(t, expectSNAC, outputSNAC)
 }
+
+func TestChatNavService_RequestRoomInfoHandler(t *testing.T) {
+	tests := []struct {
+		name     string
+		chatRoom state.ChatRoom
+		// inputSNAC is the SNAC sent from the client to the server
+		inputSNAC oscar.SNACMessage
+		want      oscar.SNACMessage
+		wantErr   error
+	}{
+		{
+			name: "request room info",
+			chatRoom: state.ChatRoom{
+				Cookie:         "the-chat-id",
+				DetailLevel:    2,
+				Exchange:       4,
+				InstanceNumber: 8,
+			},
+			inputSNAC: oscar.SNACMessage{
+				Frame: oscar.SNACFrame{
+					RequestID: 1234,
+				},
+				Body: oscar.SNAC_0x0D_0x04_ChatNavRequestRoomInfo{
+					Cookie: []byte(`the-chat-id`),
+				},
+			},
+			want: oscar.SNACMessage{
+				Frame: oscar.SNACFrame{
+					FoodGroup: oscar.ChatNav,
+					SubGroup:  oscar.ChatNavNavInfo,
+					RequestID: 1234,
+				},
+				Body: oscar.SNAC_0x0D_0x09_ChatNavNavInfo{
+					TLVRestBlock: oscar.TLVRestBlock{
+						TLVList: oscar.TLVList{
+							oscar.NewTLV(0x04, oscar.SNAC_0x0E_0x02_ChatRoomInfoUpdate{
+								Cookie:         "the-chat-id",
+								DetailLevel:    2,
+								Exchange:       4,
+								InstanceNumber: 8,
+								TLVBlock: oscar.TLVBlock{
+									TLVList: state.ChatRoom{Cookie: "the-chat-id"}.TLVList(),
+								},
+							}),
+						},
+					},
+				},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			svc := NewChatNavService(nil, state.NewChatRegistry(), nil, nil)
+			svc.chatRegistry.Register(tt.chatRoom, nil)
+			got, err := svc.RequestRoomInfoHandler(nil, tt.inputSNAC.Frame,
+				tt.inputSNAC.Body.(oscar.SNAC_0x0D_0x04_ChatNavRequestRoomInfo))
+			assert.ErrorIs(t, err, tt.wantErr)
+			if tt.wantErr != nil {
+				return
+			}
+			assert.Equal(t, tt.want, got)
+		})
+	}
+}
+
+func TestChatNavService_RequestChatRightsHandler(t *testing.T) {
+	svc := NewChatNavService(nil, nil, nil, nil)
+
+	have := svc.RequestChatRightsHandler(nil, oscar.SNACFrame{RequestID: 1234})
+
+	want := oscar.SNACMessage{
+		Frame: oscar.SNACFrame{
+			FoodGroup: oscar.ChatNav,
+			SubGroup:  oscar.ChatNavNavInfo,
+			RequestID: 1234,
+		},
+		Body: oscar.SNAC_0x0D_0x09_ChatNavNavInfo{
+			TLVRestBlock: oscar.TLVRestBlock{
+				TLVList: oscar.TLVList{
+					oscar.NewTLV(oscar.ChatNavTLVMaxConcurrentRooms, uint8(10)),
+					oscar.NewTLV(oscar.ChatNavTLVExchangeInfo, oscar.SNAC_0x0D_0x09_TLVExchangeInfo{
+						Identifier: 4,
+						TLVBlock: oscar.TLVBlock{
+							TLVList: oscar.TLVList{
+								oscar.NewTLV(oscar.ChatNavTLVClassPerms, uint16(0x0010)),
+								oscar.NewTLV(oscar.ChatNavTLVFlags, uint16(15)),
+								oscar.NewTLV(oscar.ChatNavTLVExchangeDesc, "default exchange"),
+								oscar.NewTLV(oscar.ChatNavTLVCreatePerms, uint8(2)),
+								oscar.NewTLV(oscar.ChatNavTLVCharSet1, "us-ascii"),
+								oscar.NewTLV(oscar.ChatNavTLVLang1, "en"),
+								oscar.NewTLV(oscar.ChatNavTLVCharSet2, "us-ascii"),
+								oscar.NewTLV(oscar.ChatNavTLVLang2, "en"),
+							},
+						},
+					}),
+				},
+			},
+		},
+	}
+
+	assert.Equal(t, want, have)
+}

+ 12 - 4
oscar/snacs.go

@@ -484,10 +484,18 @@ const (
 	ChatNavCreateRoom          uint16 = 0x0008
 	ChatNavNavInfo             uint16 = 0x0009
 
-	ChatNavTLVRedirect     uint16 = 0x01
-	ChatNavTLVMaxRooms     uint16 = 0x02
-	ChatNavTLVExchangeInfo uint16 = 0x03
-	ChatNavTLVRoomInfo     uint16 = 0x04
+	// referenced from protocols/oscar/family_chatnav.c in lib purple
+
+	ChatNavTLVMaxConcurrentRooms uint16 = 0x02
+	ChatNavTLVClassPerms         uint16 = 0x02
+	ChatNavTLVExchangeInfo       uint16 = 0x03
+	ChatNavTLVFlags              uint16 = 0xC9
+	ChatNavTLVExchangeDesc       uint16 = 0xD3
+	ChatNavTLVCreatePerms        uint16 = 0xD5
+	ChatNavTLVCharSet1           uint16 = 0xD6
+	ChatNavTLVLang1              uint16 = 0xD7
+	ChatNavTLVCharSet2           uint16 = 0xD8
+	ChatNavTLVLang2              uint16 = 0xD9
 )
 
 type SNAC_0x0D_0x04_ChatNavRequestRoomInfo struct {

+ 9 - 0
state/chat_registry.go

@@ -5,6 +5,7 @@ import (
 	"sync"
 	"time"
 
+	"github.com/google/uuid"
 	"github.com/mkaminski/goaim/oscar"
 )
 
@@ -69,3 +70,11 @@ func (c ChatRoom) TLVList() []oscar.TLV {
 		oscar.NewTLV(0x00d3, c.Name),
 	}
 }
+
+// NewChatRoom creates new state.ChatRoom objects
+func NewChatRoom() ChatRoom {
+	return ChatRoom{
+		Cookie:     uuid.New().String(),
+		CreateTime: time.Now(),
+	}
+}