package oscar import ( "bytes" "context" "log/slog" "math" "testing" "github.com/mk6i/open-oscar-server/config" "github.com/mk6i/open-oscar-server/server/oscar/middleware" "github.com/mk6i/open-oscar-server/state" "github.com/mk6i/open-oscar-server/wire" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) func TestHandler_AdminConfirmRequest(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x07_0x06_AdminConfirmRequest serviceError error responseError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x07_0x06_AdminConfirmRequest{}, }, { name: "service error", inputBody: wire.SNAC_0x07_0x06_AdminConfirmRequest{}, serviceError: assert.AnError, expectedError: assert.AnError, }, { name: "response writer error", inputBody: wire.SNAC_0x07_0x06_AdminConfirmRequest{}, responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Admin, SubGroup: wire.AdminAcctConfirmRequest, }, Body: tt.inputBody, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Admin, SubGroup: wire.AdminAcctConfirmReply, }, Body: wire.SNAC_0x07_0x07_AdminConfirmReply{ Status: wire.AdminAcctConfirmStatusEmailSent, }, } svc := newMockAdminService(t) svc.EXPECT(). ConfirmRequest(mock.Anything, mock.Anything, input.Frame). Return(output, tt.serviceError) h := Handler{ AdminService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) if tt.serviceError == nil { responseWriter.EXPECT(). SendSNAC(output.Frame, output.Body). Return(tt.responseError) } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_AdminInfoQuery_RegistrationStatus(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x07_0x02_AdminInfoQuery serviceError error responseError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x07_0x02_AdminInfoQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.AdminTLVRegistrationStatus, uint16(0x00)), }, }, }, }, { name: "service error", inputBody: wire.SNAC_0x07_0x02_AdminInfoQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.AdminTLVRegistrationStatus, uint16(0x00)), }, }, }, serviceError: assert.AnError, expectedError: assert.AnError, }, { name: "response writer error", inputBody: wire.SNAC_0x07_0x02_AdminInfoQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.AdminTLVRegistrationStatus, uint16(0x00)), }, }, }, responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Admin, SubGroup: wire.AdminInfoQuery, }, Body: tt.inputBody, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Admin, SubGroup: wire.AdminInfoReply, }, Body: wire.SNAC_0x07_0x03_AdminInfoReply{ Permissions: wire.AdminInfoPermissionsReadWrite, TLVBlock: wire.TLVBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.AdminTLVRegistrationStatus, wire.AdminInfoRegStatusFullDisclosure), }, }, }, } svc := newMockAdminService(t) svc.EXPECT(). InfoQuery(mock.Anything, mock.Anything, input.Frame, input.Body). Return(output, tt.serviceError) h := Handler{ AdminService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) if tt.serviceError == nil { responseWriter.EXPECT(). SendSNAC(output.Frame, output.Body). Return(tt.responseError) } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_AdminInfoChangeRequest_ScreenNameFormatted(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x07_0x04_AdminInfoChangeRequest serviceError error responseError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x07_0x04_AdminInfoChangeRequest{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.AdminTLVScreenNameFormatted, "Chatting Chuck"), }, }, }, }, { name: "service error", inputBody: wire.SNAC_0x07_0x04_AdminInfoChangeRequest{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.AdminTLVScreenNameFormatted, "Chatting Chuck"), }, }, }, serviceError: assert.AnError, expectedError: assert.AnError, }, { name: "response writer error", inputBody: wire.SNAC_0x07_0x04_AdminInfoChangeRequest{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.AdminTLVScreenNameFormatted, "Chatting Chuck"), }, }, }, responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Admin, SubGroup: wire.AdminInfoChangeRequest, }, Body: tt.inputBody, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Admin, SubGroup: wire.AdminInfoChangeReply, }, Body: wire.SNAC_0x07_0x05_AdminChangeReply{ Permissions: wire.AdminInfoPermissionsReadWrite, TLVBlock: wire.TLVBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.AdminTLVScreenNameFormatted, "Chatting Chuck"), }, }, }, } svc := newMockAdminService(t) svc.EXPECT(). InfoChangeRequest(mock.Anything, mock.Anything, input.Frame, input.Body). Return(output, tt.serviceError) h := Handler{ AdminService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) if tt.serviceError == nil { responseWriter.EXPECT(). SendSNAC(output.Frame, output.Body). Return(tt.responseError) } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_AlertNotifyCapabilities(t *testing.T) { tests := []struct { name string inputBody wire.SNACFrame expectedError error }{ { name: "success", inputBody: wire.SNACFrame{}, }, { name: "empty body", inputBody: wire.SNACFrame{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Alert, SubGroup: wire.AlertNotifyCapabilities, }, Body: tt.inputBody, } h := Handler{ RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, nil, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_AlertNotifyDisplayCapabilities(t *testing.T) { tests := []struct { name string inputBody wire.SNACFrame expectedError error }{ { name: "success", inputBody: wire.SNACFrame{}, }, { name: "empty body", inputBody: wire.SNACFrame{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Alert, SubGroup: wire.AlertNotifyDisplayCapabilities, }, Body: tt.inputBody, } h := Handler{ RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, nil, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_BARTDownloadQuery(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x10_0x04_BARTDownloadQuery serviceError error responseError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x10_0x04_BARTDownloadQuery{}, }, { name: "service error", inputBody: wire.SNAC_0x10_0x04_BARTDownloadQuery{}, serviceError: assert.AnError, expectedError: assert.AnError, }, { name: "response writer error", inputBody: wire.SNAC_0x10_0x04_BARTDownloadQuery{}, responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.BART, SubGroup: wire.BARTDownloadQuery, }, Body: tt.inputBody, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.BART, SubGroup: wire.BARTDownloadReply, }, Body: wire.SNAC_0x10_0x05_BARTDownloadReply{ ScreenName: "the-screen-name", }, } svc := newMockBARTService(t) svc.EXPECT(). RetrieveItem(mock.Anything, input.Frame, input.Body). Return(output, tt.serviceError) h := Handler{ BARTService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) if tt.serviceError == nil { responseWriter.EXPECT(). SendSNAC(output.Frame, output.Body). Return(tt.responseError) } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_BARTDownload2Query(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x10_0x06_BARTDownload2Query serviceError error responseError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x10_0x06_BARTDownload2Query{}, }, { name: "service error", inputBody: wire.SNAC_0x10_0x06_BARTDownload2Query{}, serviceError: assert.AnError, expectedError: assert.AnError, }, { name: "response writer error", inputBody: wire.SNAC_0x10_0x06_BARTDownload2Query{}, responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.BART, SubGroup: wire.BARTDownload2Query, }, Body: tt.inputBody, } output := []wire.SNACMessage{ { Frame: wire.SNACFrame{ FoodGroup: wire.BART, SubGroup: wire.BARTDownload2Reply, }, Body: wire.SNAC_0x10_0x07_BARTDownload2Reply{ ScreenName: "the-screen-name", }, }, } svc := newMockBARTService(t) svc.EXPECT(). RetrieveItemV2(mock.Anything, input.Frame, input.Body). Return(output, tt.serviceError) h := Handler{ BARTService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) if tt.serviceError == nil { for _, msg := range output { responseWriter.EXPECT(). SendSNAC(msg.Frame, msg.Body). Return(tt.responseError) } } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_BARTUploadQuery(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x10_0x02_BARTUploadQuery serviceError error responseError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x10_0x02_BARTUploadQuery{ Type: 1, }, }, { name: "service error", inputBody: wire.SNAC_0x10_0x02_BARTUploadQuery{ Type: 1, }, serviceError: assert.AnError, expectedError: assert.AnError, }, { name: "response writer error", inputBody: wire.SNAC_0x10_0x02_BARTUploadQuery{ Type: 1, }, responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.BART, SubGroup: wire.BARTUploadQuery, }, Body: tt.inputBody, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.BART, SubGroup: wire.BARTUploadReply, }, Body: wire.SNAC_0x10_0x03_BARTUploadReply{ Code: wire.BARTReplyCodesSuccess, }, } svc := newMockBARTService(t) svc.EXPECT(). UpsertItem(mock.Anything, mock.Anything, input.Frame, input.Body). Return(output, tt.serviceError) h := Handler{ BARTService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) if tt.serviceError == nil { responseWriter.EXPECT(). SendSNAC(output.Frame, output.Body). Return(tt.responseError) } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_BuddyRightsQuery(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x03_0x02_BuddyRightsQuery responseError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x03_0x02_BuddyRightsQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(0x01, uint16(1000)), }, }, }, }, { name: "response writer error", inputBody: wire.SNAC_0x03_0x02_BuddyRightsQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(0x01, uint16(1000)), }, }, }, responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Buddy, SubGroup: wire.BuddyRightsQuery, }, Body: tt.inputBody, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Buddy, SubGroup: wire.BuddyRightsReply, }, Body: wire.SNAC_0x03_0x03_BuddyRightsReply{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(0x01, uint16(1000)), }, }, }, } svc := newMockBuddyService(t) svc.EXPECT(). RightsQuery(mock.Anything, input.Frame). Return(output) h := Handler{ BuddyService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) responseWriter.EXPECT(). SendSNAC(output.Frame, output.Body). Return(tt.responseError) buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_BuddyAddBuddies(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x03_0x04_BuddyAddBuddies serviceError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x03_0x04_BuddyAddBuddies{ Buddies: []struct { ScreenName string `oscar:"len_prefix=uint8"` }{ { ScreenName: "user1", }, }, }, }, { name: "service error", inputBody: wire.SNAC_0x03_0x04_BuddyAddBuddies{ Buddies: []struct { ScreenName string `oscar:"len_prefix=uint8"` }{ { ScreenName: "user1", }, }, }, serviceError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Buddy, SubGroup: wire.BuddyAddBuddies, }, Body: tt.inputBody, } svc := newMockBuddyService(t) svc.EXPECT(). AddBuddies(mock.Anything, mock.Anything, mock.Anything, input.Body). Return(nil, tt.serviceError) h := Handler{ BuddyService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_BuddyDelBuddies(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x03_0x05_BuddyDelBuddies serviceError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x03_0x05_BuddyDelBuddies{ Buddies: []struct { ScreenName string `oscar:"len_prefix=uint8"` }{ { ScreenName: "user1", }, }, }, }, { name: "service error", inputBody: wire.SNAC_0x03_0x05_BuddyDelBuddies{ Buddies: []struct { ScreenName string `oscar:"len_prefix=uint8"` }{ { ScreenName: "user1", }, }, }, serviceError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Buddy, SubGroup: wire.BuddyDelBuddies, }, Body: tt.inputBody, } svc := newMockBuddyService(t) svc.EXPECT(). DelBuddies(mock.Anything, mock.Anything, input.Body). Return(tt.serviceError) h := Handler{ BuddyService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_BuddyAddTempBuddies(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x03_0x0F_BuddyAddTempBuddies serviceError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x03_0x0F_BuddyAddTempBuddies{ Buddies: []struct { ScreenName string `oscar:"len_prefix=uint8"` }{ { ScreenName: "user1", }, }, }, }, { name: "service error", inputBody: wire.SNAC_0x03_0x0F_BuddyAddTempBuddies{ Buddies: []struct { ScreenName string `oscar:"len_prefix=uint8"` }{ { ScreenName: "user1", }, }, }, serviceError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Buddy, SubGroup: wire.BuddyAddTempBuddies, }, Body: tt.inputBody, } svc := newMockBuddyService(t) svc.EXPECT(). AddTempBuddies(mock.Anything, mock.Anything, mock.Anything, input.Body). Return(nil, tt.serviceError) h := Handler{ BuddyService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_BuddyDelTempBuddies(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x03_0x10_BuddyDelTempBuddies serviceError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x03_0x10_BuddyDelTempBuddies{ Buddies: []struct { ScreenName string `oscar:"len_prefix=uint8"` }{ { ScreenName: "user1", }, }, }, }, { name: "service error", inputBody: wire.SNAC_0x03_0x10_BuddyDelTempBuddies{ Buddies: []struct { ScreenName string `oscar:"len_prefix=uint8"` }{ { ScreenName: "user1", }, }, }, serviceError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Buddy, SubGroup: wire.BuddyDelTempBuddies, }, Body: tt.inputBody, } svc := newMockBuddyService(t) svc.EXPECT(). DelTempBuddies(mock.Anything, mock.Anything, input.Body). Return(tt.serviceError) h := Handler{ BuddyService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_ChatNavCreateRoom(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x0E_0x02_ChatRoomInfoUpdate serviceError error responseError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x0E_0x02_ChatRoomInfoUpdate{ Exchange: 1, }, }, { name: "service error", inputBody: wire.SNAC_0x0E_0x02_ChatRoomInfoUpdate{ Exchange: 1, }, serviceError: assert.AnError, expectedError: assert.AnError, }, { name: "response writer error", inputBody: wire.SNAC_0x0E_0x02_ChatRoomInfoUpdate{ Exchange: 1, }, responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.ChatNav, SubGroup: wire.ChatNavCreateRoom, }, Body: tt.inputBody, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.ChatNav, SubGroup: wire.ChatNavNavInfo, }, Body: wire.SNAC_0x0D_0x09_ChatNavNavInfo{}, } instance := state.NewSession().AddInstance() svc := newMockChatNavService(t) svc.EXPECT(). CreateRoom(mock.Anything, instance, input.Frame, input.Body). Return(output, tt.serviceError) h := Handler{ ChatNavService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) if tt.serviceError == nil { responseWriter.EXPECT(). SendSNAC(output.Frame, output.Body). Return(tt.responseError) } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, instance, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_ChatNavCreateRoom_ReadErr(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.ChatNav, SubGroup: wire.ChatNavCreateRoom, }, Body: wire.SNAC_0x0E_0x02_ChatRoomInfoUpdate{ Exchange: 1, }, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.ChatNav, SubGroup: wire.ChatNavNavInfo, }, Body: wire.SNAC_0x0D_0x09_ChatNavNavInfo{}, } instance := state.NewSession().AddInstance() svc := newMockChatNavService(t) svc.EXPECT(). CreateRoom(mock.Anything, instance, input.Frame, input.Body). Return(output, nil) h := Handler{ ChatNavService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } ss := newMockResponseWriter(t) ss.EXPECT(). SendSNAC(output.Frame, output.Body). Return(nil) buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) assert.NoError(t, h.Handle(context.TODO(), wire.BOS, instance, input.Frame, buf, ss, config.Listener{})) } func TestHandler_ChatNavRequestChatRights(t *testing.T) { tests := []struct { name string inputBody struct{} responseError error expectedError error }{ { name: "success", inputBody: struct{}{}, }, { name: "response writer error", inputBody: struct{}{}, responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.ChatNav, SubGroup: wire.ChatNavRequestChatRights, }, Body: tt.inputBody, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.ChatNav, SubGroup: wire.ChatNavNavInfo, }, Body: wire.SNAC_0x0D_0x09_ChatNavNavInfo{}, } svc := newMockChatNavService(t) svc.EXPECT(). RequestChatRights(mock.Anything, input.Frame). Return(output) h := Handler{ ChatNavService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) responseWriter.EXPECT(). SendSNAC(output.Frame, output.Body). Return(tt.responseError) buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_ChatNavRequestRoomInfo(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x0D_0x04_ChatNavRequestRoomInfo serviceError error responseError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x0D_0x04_ChatNavRequestRoomInfo{ Exchange: 1, }, }, { name: "service error", inputBody: wire.SNAC_0x0D_0x04_ChatNavRequestRoomInfo{ Exchange: 1, }, serviceError: assert.AnError, expectedError: assert.AnError, }, { name: "response writer error", inputBody: wire.SNAC_0x0D_0x04_ChatNavRequestRoomInfo{ Exchange: 1, }, responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.ChatNav, SubGroup: wire.ChatNavRequestRoomInfo, }, Body: tt.inputBody, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.ChatNav, SubGroup: wire.ChatNavNavInfo, }, Body: wire.SNAC_0x0D_0x09_ChatNavNavInfo{}, } svc := newMockChatNavService(t) svc.EXPECT(). RequestRoomInfo(mock.Anything, input.Frame, input.Body). Return(output, tt.serviceError) h := Handler{ ChatNavService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) if tt.serviceError == nil { responseWriter.EXPECT(). SendSNAC(output.Frame, output.Body). Return(tt.responseError) } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_ChatNavRequestExchangeInfo(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x0D_0x03_ChatNavRequestExchangeInfo serviceError error responseError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x0D_0x03_ChatNavRequestExchangeInfo{ Exchange: 4, }, }, { name: "service error", inputBody: wire.SNAC_0x0D_0x03_ChatNavRequestExchangeInfo{ Exchange: 4, }, serviceError: assert.AnError, expectedError: assert.AnError, }, { name: "response writer error", inputBody: wire.SNAC_0x0D_0x03_ChatNavRequestExchangeInfo{ Exchange: 4, }, responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.ChatNav, SubGroup: wire.ChatNavRequestExchangeInfo, }, Body: tt.inputBody, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.ChatNav, SubGroup: wire.ChatNavNavInfo, }, Body: wire.SNAC_0x0D_0x09_ChatNavNavInfo{}, } svc := newMockChatNavService(t) svc.EXPECT(). ExchangeInfo(mock.Anything, input.Frame, input.Body). Return(output, tt.serviceError) h := Handler{ ChatNavService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) if tt.serviceError == nil { responseWriter.EXPECT(). SendSNAC(output.Frame, output.Body). Return(tt.responseError) } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_ChatChannelMsgToHost(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x0E_0x05_ChatChannelMsgToHost serviceResponse *wire.SNACMessage serviceError error responseError error expectedError error }{ { name: "success with reflected response", inputBody: wire.SNAC_0x0E_0x05_ChatChannelMsgToHost{ Channel: 4, }, serviceResponse: &wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Chat, SubGroup: wire.ChatChannelMsgToClient, }, Body: wire.SNAC_0x0E_0x06_ChatChannelMsgToClient{ Channel: 4, }, }, }, { name: "service error with reflected response", inputBody: wire.SNAC_0x0E_0x05_ChatChannelMsgToHost{ Channel: 4, }, serviceResponse: &wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Chat, SubGroup: wire.ChatChannelMsgToClient, }, Body: wire.SNAC_0x0E_0x06_ChatChannelMsgToClient{ Channel: 4, }, }, serviceError: assert.AnError, expectedError: assert.AnError, }, { name: "response writer error with reflected response", inputBody: wire.SNAC_0x0E_0x05_ChatChannelMsgToHost{ Channel: 4, }, serviceResponse: &wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Chat, SubGroup: wire.ChatChannelMsgToClient, }, Body: wire.SNAC_0x0E_0x06_ChatChannelMsgToClient{ Channel: 4, }, }, responseError: assert.AnError, expectedError: assert.AnError, }, { name: "success without reflected response", inputBody: wire.SNAC_0x0E_0x05_ChatChannelMsgToHost{ Channel: 4, }, serviceResponse: nil, // nil response means no reflection back to caller }, { name: "service error without reflected response", inputBody: wire.SNAC_0x0E_0x05_ChatChannelMsgToHost{ Channel: 4, }, serviceResponse: nil, serviceError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Chat, SubGroup: wire.ChatChannelMsgToHost, }, Body: tt.inputBody, } svc := newMockChatService(t) svc.EXPECT(). ChannelMsgToHost(mock.Anything, mock.Anything, input.Frame, input.Body). Return(tt.serviceResponse, tt.serviceError) h := Handler{ ChatService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) if tt.serviceError == nil && tt.serviceResponse != nil { responseWriter.EXPECT(). SendSNAC(tt.serviceResponse.Frame, tt.serviceResponse.Body). Return(tt.responseError) } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_FeedbagDeleteItem(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x13_0x0A_FeedbagDeleteItem expectedOutput *wire.SNACMessage serviceError error responseError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x13_0x0A_FeedbagDeleteItem{ Items: []wire.FeedbagItem{ { Name: "my-item", }, }, }, expectedOutput: &wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Feedbag, SubGroup: wire.FeedbagStatus, }, Body: wire.SNAC_0x13_0x0E_FeedbagStatus{ Results: []uint16{1234}, }, }, }, { name: "success (nil output)", inputBody: wire.SNAC_0x13_0x0A_FeedbagDeleteItem{ Items: []wire.FeedbagItem{ { Name: "my-item", }, }, }, expectedOutput: nil, }, { name: "service error", inputBody: wire.SNAC_0x13_0x0A_FeedbagDeleteItem{ Items: []wire.FeedbagItem{ { Name: "my-item", }, }, }, expectedOutput: &wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Feedbag, SubGroup: wire.FeedbagStatus, }, Body: wire.SNAC_0x13_0x0E_FeedbagStatus{ Results: []uint16{1234}, }, }, serviceError: assert.AnError, expectedError: assert.AnError, }, { name: "response writer error", inputBody: wire.SNAC_0x13_0x0A_FeedbagDeleteItem{ Items: []wire.FeedbagItem{ { Name: "my-item", }, }, }, expectedOutput: &wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Feedbag, SubGroup: wire.FeedbagStatus, }, Body: wire.SNAC_0x13_0x0E_FeedbagStatus{ Results: []uint16{1234}, }, }, responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Feedbag, SubGroup: wire.FeedbagDeleteItem, }, Body: tt.inputBody, } svc := newMockFeedbagService(t) svc.EXPECT(). DeleteItem(mock.Anything, mock.Anything, input.Frame, input.Body). Return(tt.expectedOutput, tt.serviceError) h := Handler{ FeedbagService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) if tt.serviceError == nil && tt.expectedOutput != nil { responseWriter.EXPECT(). SendSNAC(tt.expectedOutput.Frame, tt.expectedOutput.Body). Return(tt.responseError) } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_FeedbagEndCluster(t *testing.T) { tests := []struct { name string inputBody struct{} expectedError error }{ { name: "success", inputBody: struct{}{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Feedbag, SubGroup: wire.FeedbagEndCluster, }, Body: tt.inputBody, } svc := newMockFeedbagService(t) svc.EXPECT(). EndCluster(mock.Anything, mock.Anything, input.Frame). Return(nil) h := Handler{ FeedbagService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_FeedbagInsertItem(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x13_0x08_FeedbagInsertItem expectedOutput *wire.SNACMessage serviceError error responseError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x13_0x08_FeedbagInsertItem{ Items: []wire.FeedbagItem{ { Name: "my-item", }, }, }, expectedOutput: nil, }, { name: "success (non-nil output)", inputBody: wire.SNAC_0x13_0x08_FeedbagInsertItem{ Items: []wire.FeedbagItem{ { Name: "my-item", }, }, }, expectedOutput: &wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Feedbag, SubGroup: wire.FeedbagStatus, }, Body: wire.SNAC_0x13_0x0E_FeedbagStatus{ Results: []uint16{1234}, }, }, }, { name: "service error", inputBody: wire.SNAC_0x13_0x08_FeedbagInsertItem{ Items: []wire.FeedbagItem{ { Name: "my-item", }, }, }, expectedOutput: &wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Feedbag, SubGroup: wire.FeedbagStatus, }, Body: wire.SNAC_0x13_0x0E_FeedbagStatus{ Results: []uint16{1234}, }, }, serviceError: assert.AnError, expectedError: assert.AnError, }, { name: "response writer error", inputBody: wire.SNAC_0x13_0x08_FeedbagInsertItem{ Items: []wire.FeedbagItem{ { Name: "my-item", }, }, }, expectedOutput: &wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Feedbag, SubGroup: wire.FeedbagStatus, }, Body: wire.SNAC_0x13_0x0E_FeedbagStatus{ Results: []uint16{1234}, }, }, responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Feedbag, SubGroup: wire.FeedbagInsertItem, }, Body: tt.inputBody, } svc := newMockFeedbagService(t) svc.EXPECT(). UpsertItem(mock.Anything, mock.Anything, input.Frame, tt.inputBody.Items). Return(tt.expectedOutput, tt.serviceError) h := Handler{ FeedbagService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) if tt.serviceError == nil && tt.expectedOutput != nil { responseWriter.EXPECT(). SendSNAC(tt.expectedOutput.Frame, tt.expectedOutput.Body). Return(tt.responseError) } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_FeedbagQuery(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x13_0x02_FeedbagRightsQuery serviceError error responseError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x13_0x02_FeedbagRightsQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ { Tag: 0x01, Value: []byte{1, 2, 3, 4}, }, }, }, }, }, { name: "service error", inputBody: wire.SNAC_0x13_0x02_FeedbagRightsQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ { Tag: 0x01, Value: []byte{1, 2, 3, 4}, }, }, }, }, serviceError: assert.AnError, expectedError: assert.AnError, }, { name: "response writer error", inputBody: wire.SNAC_0x13_0x02_FeedbagRightsQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ { Tag: 0x01, Value: []byte{1, 2, 3, 4}, }, }, }, }, responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Feedbag, SubGroup: wire.FeedbagQuery, }, Body: tt.inputBody, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Feedbag, SubGroup: wire.FeedbagReply, }, Body: wire.SNAC_0x13_0x06_FeedbagReply{ Version: 4, }, } svc := newMockFeedbagService(t) svc.EXPECT(). Query(mock.Anything, mock.Anything, input.Frame). Return(output, tt.serviceError) h := Handler{ FeedbagService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) if tt.serviceError == nil { responseWriter.EXPECT(). SendSNAC(output.Frame, output.Body). Return(tt.responseError) } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_FeedbagQueryIfModified(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x13_0x05_FeedbagQueryIfModified serviceError error responseError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x13_0x05_FeedbagQueryIfModified{ LastUpdate: 1234, }, }, { name: "service error", inputBody: wire.SNAC_0x13_0x05_FeedbagQueryIfModified{ LastUpdate: 1234, }, serviceError: assert.AnError, expectedError: assert.AnError, }, { name: "response writer error", inputBody: wire.SNAC_0x13_0x05_FeedbagQueryIfModified{ LastUpdate: 1234, }, responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Feedbag, SubGroup: wire.FeedbagQueryIfModified, }, Body: tt.inputBody, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Feedbag, SubGroup: wire.FeedbagReply, }, Body: wire.SNAC_0x13_0x06_FeedbagReply{ LastUpdate: 1234, }, } svc := newMockFeedbagService(t) svc.EXPECT(). QueryIfModified(mock.Anything, mock.Anything, input.Frame, input.Body). Return(output, tt.serviceError) h := Handler{ FeedbagService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) if tt.serviceError == nil { responseWriter.EXPECT(). SendSNAC(output.Frame, output.Body). Return(tt.responseError) } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_FeedbagRightsQuery(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x13_0x02_FeedbagRightsQuery responseError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x13_0x02_FeedbagRightsQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ { Tag: 0x01, Value: []byte{1, 2, 3, 4}, }, }, }, }, }, { name: "response writer error", inputBody: wire.SNAC_0x13_0x02_FeedbagRightsQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ { Tag: 0x01, Value: []byte{1, 2, 3, 4}, }, }, }, }, responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Feedbag, SubGroup: wire.FeedbagRightsQuery, }, Body: tt.inputBody, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Feedbag, SubGroup: wire.FeedbagRightsReply, }, Body: wire.SNAC_0x13_0x03_FeedbagRightsReply{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ { Tag: 0x01, Value: []byte{1, 2, 3, 4}, }, }, }, }, } svc := newMockFeedbagService(t) svc.EXPECT(). RightsQuery(mock.Anything, input.Frame). Return(output) h := Handler{ FeedbagService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) responseWriter.EXPECT(). SendSNAC(output.Frame, output.Body). Return(tt.responseError) buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_FeedbagStartCluster(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x13_0x11_FeedbagStartCluster expectedError error }{ { name: "success", inputBody: wire.SNAC_0x13_0x11_FeedbagStartCluster{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ { Tag: 0x01, Value: []byte{1, 2, 3, 4}, }, }, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Feedbag, SubGroup: wire.FeedbagStartCluster, }, Body: tt.inputBody, } svc := newMockFeedbagService(t) svc.EXPECT(). StartCluster(mock.Anything, mock.Anything, input.Frame, input.Body) h := Handler{ FeedbagService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_FeedbagUpdateItem(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x13_0x09_FeedbagUpdateItem expectedOutput *wire.SNACMessage serviceError error responseError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x13_0x09_FeedbagUpdateItem{ Items: []wire.FeedbagItem{ { Name: "my-item", }, }, }, expectedOutput: nil, }, { name: "success (non-nil output)", inputBody: wire.SNAC_0x13_0x09_FeedbagUpdateItem{ Items: []wire.FeedbagItem{ { Name: "my-item", }, }, }, expectedOutput: &wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Feedbag, SubGroup: wire.FeedbagStatus, }, Body: wire.SNAC_0x13_0x0E_FeedbagStatus{ Results: []uint16{1234}, }, }, }, { name: "service error", inputBody: wire.SNAC_0x13_0x09_FeedbagUpdateItem{ Items: []wire.FeedbagItem{ { Name: "my-item", }, }, }, expectedOutput: &wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Feedbag, SubGroup: wire.FeedbagStatus, }, Body: wire.SNAC_0x13_0x0E_FeedbagStatus{ Results: []uint16{1234}, }, }, serviceError: assert.AnError, expectedError: assert.AnError, }, { name: "response writer error", inputBody: wire.SNAC_0x13_0x09_FeedbagUpdateItem{ Items: []wire.FeedbagItem{ { Name: "my-item", }, }, }, expectedOutput: &wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Feedbag, SubGroup: wire.FeedbagStatus, }, Body: wire.SNAC_0x13_0x0E_FeedbagStatus{ Results: []uint16{1234}, }, }, responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Feedbag, SubGroup: wire.FeedbagUpdateItem, }, Body: tt.inputBody, } svc := newMockFeedbagService(t) svc.EXPECT(). UpsertItem(mock.Anything, mock.Anything, input.Frame, tt.inputBody.Items). Return(tt.expectedOutput, tt.serviceError) h := Handler{ FeedbagService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) if tt.serviceError == nil && tt.expectedOutput != nil { responseWriter.EXPECT(). SendSNAC(tt.expectedOutput.Frame, tt.expectedOutput.Body). Return(tt.responseError) } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_FeedbagUse(t *testing.T) { tests := []struct { name string inputBody struct{} serviceError error expectedError error }{ { name: "success", inputBody: struct{}{}, }, { name: "service error", inputBody: struct{}{}, serviceError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Feedbag, SubGroup: wire.FeedbagUse, }, Body: tt.inputBody, } svc := newMockFeedbagService(t) svc.EXPECT(). Use(mock.Anything, mock.Anything). Return(tt.serviceError) h := Handler{ FeedbagService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_FeedbagRespondAuthorizeToHost(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Feedbag, SubGroup: wire.FeedbagRespondAuthorizeToHost, }, Body: wire.SNAC_0x13_0x1A_FeedbagRespondAuthorizeToHost{ ScreenName: "theScreenName", }, } svc := newMockFeedbagService(t) svc.EXPECT(). RespondAuthorizeToHost(mock.Anything, mock.Anything, input.Frame, input.Body). Return(nil) h := Handler{ FeedbagService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) instance := state.NewSession().AddInstance() buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) assert.NoError(t, h.Handle(context.TODO(), wire.BOS, instance, input.Frame, buf, responseWriter, config.Listener{})) } func TestHandler_FeedbagPreAuthorizeBuddy(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Feedbag, SubGroup: wire.FeedbagPreAuthorizeBuddy, }, Body: wire.SNAC_0x13_0x14_FeedbagPreAuthorizeBuddy{ ScreenName: "100002", Message: "x", }, } svc := newMockFeedbagService(t) svc.EXPECT(). PreAuthorizeBuddy(mock.Anything, mock.Anything, input.Frame, input.Body). Return(nil, nil) h := Handler{ FeedbagService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) assert.NoError(t, h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{})) } func TestHandler_ICBMAddParameters(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x04_0x02_ICBMAddParameters expectedError error }{ { name: "success", inputBody: wire.SNAC_0x04_0x02_ICBMAddParameters{ Channel: 1, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.ICBM, SubGroup: wire.ICBMAddParameters, }, Body: tt.inputBody, } svc := newMockICBMService(t) h := Handler{ ICBMService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_ICBMChannelMsgToHost(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x04_0x06_ICBMChannelMsgToHost serviceError error responseError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{ ScreenName: "recipient-screen-name", }, }, { name: "service error", inputBody: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{ ScreenName: "recipient-screen-name", }, serviceError: assert.AnError, expectedError: assert.AnError, }, { name: "response writer error", inputBody: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{ ScreenName: "recipient-screen-name", }, responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.ICBM, SubGroup: wire.ICBMChannelMsgToHost, }, Body: tt.inputBody, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.ICBM, SubGroup: wire.ICBMHostAck, }, Body: wire.SNAC_0x04_0x0C_ICBMHostAck{ ChannelID: 4, }, } svc := newMockICBMService(t) svc.EXPECT(). ChannelMsgToHost(mock.Anything, mock.Anything, input.Frame, input.Body). Return(&output, tt.serviceError) h := Handler{ ICBMService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) if tt.serviceError == nil { responseWriter.EXPECT(). SendSNAC(output.Frame, output.Body). Return(tt.responseError) } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_ICBMClientErr(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x04_0x0B_ICBMClientErr serviceError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x04_0x0B_ICBMClientErr{ Code: 4, }, }, { name: "service error", inputBody: wire.SNAC_0x04_0x0B_ICBMClientErr{ Code: 4, }, serviceError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.ICBM, SubGroup: wire.ICBMClientErr, }, Body: tt.inputBody, } svc := newMockICBMService(t) svc.EXPECT(). ClientErr(mock.Anything, mock.Anything, input.Frame, input.Body). Return(tt.serviceError) h := Handler{ ICBMService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_ICBMClientEvent(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x04_0x14_ICBMClientEvent serviceError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x04_0x14_ICBMClientEvent{ ScreenName: "recipient-screen-name", }, }, { name: "service error", inputBody: wire.SNAC_0x04_0x14_ICBMClientEvent{ ScreenName: "recipient-screen-name", }, serviceError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.ICBM, SubGroup: wire.ICBMClientEvent, }, Body: tt.inputBody, } svc := newMockICBMService(t) svc.EXPECT(). ClientEvent(mock.Anything, mock.Anything, input.Frame, input.Body). Return(tt.serviceError) h := Handler{ ICBMService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_ICBMEvilRequest(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x04_0x08_ICBMEvilRequest serviceError error responseError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x04_0x08_ICBMEvilRequest{ ScreenName: "recipient-screen-name", }, }, { name: "service error", inputBody: wire.SNAC_0x04_0x08_ICBMEvilRequest{ ScreenName: "recipient-screen-name", }, serviceError: assert.AnError, expectedError: assert.AnError, }, { name: "response writer error", inputBody: wire.SNAC_0x04_0x08_ICBMEvilRequest{ ScreenName: "recipient-screen-name", }, responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.ICBM, SubGroup: wire.ICBMEvilRequest, }, Body: tt.inputBody, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.ICBM, SubGroup: wire.ICBMEvilReply, }, Body: wire.SNAC_0x04_0x09_ICBMEvilReply{ EvilDeltaApplied: 100, }, } svc := newMockICBMService(t) svc.EXPECT(). EvilRequest(mock.Anything, mock.Anything, input.Frame, input.Body). Return(output, tt.serviceError) h := Handler{ ICBMService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) if tt.serviceError == nil { responseWriter.EXPECT(). SendSNAC(output.Frame, output.Body). Return(tt.responseError) } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_ICBMOfflineRetrieve(t *testing.T) { tests := []struct { name string serviceError error responseError error expectedError error }{ { name: "success", }, { name: "service error", serviceError: assert.AnError, expectedError: assert.AnError, }, { name: "response writer error", responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.ICBM, SubGroup: wire.ICBMOfflineRetrieve, }, Body: wire.SNAC_0x04_0x0A_ICBMOfflineRetrieve{}, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.ICBM, SubGroup: wire.ICBMOfflineRetrieveReply, }, Body: wire.SNAC_0x04_0x17_ICBMOfflineRetrieveReply{}, } svc := newMockICBMService(t) svc.EXPECT(). OfflineRetrieve(mock.Anything, mock.Anything, input.Frame). Return(output, tt.serviceError) h := Handler{ ICBMService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) if tt.serviceError == nil { responseWriter.EXPECT(). SendSNAC(output.Frame, output.Body). Return(tt.responseError) } buf := &bytes.Buffer{} err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_ICBMParameterQuery(t *testing.T) { tests := []struct { name string inputBody struct{} responseError error expectedError error }{ { name: "success", inputBody: struct{}{}, }, { name: "response writer error", inputBody: struct{}{}, responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.ICBM, SubGroup: wire.ICBMParameterQuery, }, Body: tt.inputBody, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.ICBM, SubGroup: wire.ICBMParameterReply, }, Body: wire.SNAC_0x04_0x05_ICBMParameterReply{ MaxSlots: 100, }, } svc := newMockICBMService(t) svc.EXPECT(). ParameterQuery(mock.Anything, input.Frame). Return(output) h := Handler{ ICBMService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) responseWriter.EXPECT(). SendSNAC(output.Frame, output.Body). Return(tt.responseError) buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_ICQDBQuery(t *testing.T) { frame := wire.SNACFrame{ FoodGroup: wire.ICQ, SubGroup: wire.ICQDBQuery, } type ICQMetaRequest struct { wire.ICQMetadata ReqSubType uint16 MetaRequest any } type reqParams struct { instance *state.SessionInstance inFrame wire.SNACFrame inBody wire.SNAC_0x15_0x02_BQuery seq uint16 wantErr error } type mockParam struct { frame any req any wantErr error } type allMockParams struct { deleteMsgReq *mockParam findByDetails *mockParam findByEmail *mockParam findByEmail3 *mockParam findByInterests *mockParam findByUIN *mockParam findByUIN2 *mockParam findByWhitePages2 *mockParam fullUserInfo *mockParam offlineMsgReq *mockParam setAffiliations *mockParam setBasicInfo *mockParam setEmails *mockParam setFullInfo *mockParam setInterests *mockParam setMoreInfo *mockParam setPermissions *mockParam setICQPhone *mockParam setUserNotes *mockParam setWorkInfo *mockParam shortUserInfo *mockParam xmlReqData *mockParam } tests := []struct { name string reqParams reqParams allMockParams allMockParams }{ { name: "MetaReqFullInfo - happy path", reqParams: reqParams{ instance: state.NewSession().AddInstance(), inBody: wire.SNAC_0x15_0x02_BQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{ Message: ICQMetaRequest{ ICQMetadata: wire.ICQMetadata{ ReqType: wire.ICQDBQueryMetaReq, Seq: 1, }, ReqSubType: wire.ICQDBQueryMetaReqFullInfo, MetaRequest: wire.ICQ_0x07D0_0x051F_DBQueryMetaReqSearchByUIN{ UIN: 123456789, }, }, }), }, }, }, seq: 1, }, allMockParams: allMockParams{ fullUserInfo: &mockParam{ req: wire.ICQ_0x07D0_0x051F_DBQueryMetaReqSearchByUIN{ UIN: 123456789, }, }, }, }, { name: "MetaReqShortInfo - happy path", reqParams: reqParams{ instance: state.NewSession().AddInstance(), inBody: wire.SNAC_0x15_0x02_BQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{ Message: ICQMetaRequest{ ICQMetadata: wire.ICQMetadata{ ReqType: wire.ICQDBQueryMetaReq, Seq: 1, }, ReqSubType: wire.ICQDBQueryMetaReqShortInfo, MetaRequest: wire.ICQ_0x07D0_0x04BA_DBQueryMetaReqShortInfo{ UIN: 123456789, }, }, }), }, }, }, seq: 1, }, allMockParams: allMockParams{ shortUserInfo: &mockParam{ req: wire.ICQ_0x07D0_0x04BA_DBQueryMetaReqShortInfo{ UIN: 123456789, }, }, }, }, { name: "MetaReqFullInfo2 - happy path", reqParams: reqParams{ instance: state.NewSession().AddInstance(), inBody: wire.SNAC_0x15_0x02_BQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{ Message: ICQMetaRequest{ ICQMetadata: wire.ICQMetadata{ ReqType: wire.ICQDBQueryMetaReq, Seq: 1, }, ReqSubType: wire.ICQDBQueryMetaReqFullInfo2, MetaRequest: wire.ICQ_0x07D0_0x051F_DBQueryMetaReqSearchByUIN{ UIN: 123456789, }, }, }), }, }, }, seq: 1, }, allMockParams: allMockParams{ fullUserInfo: &mockParam{ req: wire.ICQ_0x07D0_0x051F_DBQueryMetaReqSearchByUIN{ UIN: 123456789, }, }, }, }, { name: "MetaReqXMLReq - happy path", reqParams: reqParams{ instance: state.NewSession().AddInstance(), inBody: wire.SNAC_0x15_0x02_BQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{ Message: ICQMetaRequest{ ICQMetadata: wire.ICQMetadata{ ReqType: wire.ICQDBQueryMetaReq, Seq: 1, }, ReqSubType: wire.ICQDBQueryMetaReqXMLReq, MetaRequest: wire.ICQ_0x07D0_0x0898_DBQueryMetaReqXMLReq{ XMLRequest: "", }, }, }), }, }, }, seq: 1, }, allMockParams: allMockParams{ xmlReqData: &mockParam{ req: wire.ICQ_0x07D0_0x0898_DBQueryMetaReqXMLReq{ XMLRequest: "", }, }, }, }, { name: "MetaReqSetPermissions - happy path", reqParams: reqParams{ instance: state.NewSession().AddInstance(), inBody: wire.SNAC_0x15_0x02_BQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{ Message: ICQMetaRequest{ ICQMetadata: wire.ICQMetadata{ ReqType: wire.ICQDBQueryMetaReq, Seq: 1, }, ReqSubType: wire.ICQDBQueryMetaReqSetPermissions, MetaRequest: wire.ICQ_0x07D0_0x0424_DBQueryMetaReqSetPermissions{ Authorization: 1, }, }, }), }, }, }, seq: 1, }, allMockParams: allMockParams{ setPermissions: &mockParam{ req: wire.ICQ_0x07D0_0x0424_DBQueryMetaReqSetPermissions{ Authorization: 1, }, }, }, }, { name: "MetaReqSetICQPhone - happy path", reqParams: reqParams{ instance: state.NewSession().AddInstance(), inBody: wire.SNAC_0x15_0x02_BQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{ Message: ICQMetaRequest{ ICQMetadata: wire.ICQMetadata{ ReqType: wire.ICQDBQueryMetaReq, Seq: 1, }, ReqSubType: wire.ICQDBQueryMetaReqSetICQPhone, MetaRequest: wire.ICQ_0x07D0_0x0654_DBQueryMetaReqSetICQPhone{}, }, }), }, }, }, seq: 1, }, allMockParams: allMockParams{ setICQPhone: &mockParam{ req: wire.ICQ_0x07D0_0x0654_DBQueryMetaReqSetICQPhone{}, }, }, }, { name: "MetaReqSearchByUIN - happy path", reqParams: reqParams{ instance: state.NewSession().AddInstance(), inBody: wire.SNAC_0x15_0x02_BQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{ Message: ICQMetaRequest{ ICQMetadata: wire.ICQMetadata{ ReqType: wire.ICQDBQueryMetaReq, Seq: 1, }, ReqSubType: wire.ICQDBQueryMetaReqSearchByUIN, MetaRequest: wire.ICQ_0x07D0_0x051F_DBQueryMetaReqSearchByUIN{ UIN: 123456789, }, }, }), }, }, }, seq: 1, }, allMockParams: allMockParams{ findByUIN: &mockParam{ req: wire.ICQ_0x07D0_0x051F_DBQueryMetaReqSearchByUIN{ UIN: 123456789, }, }, }, }, { name: "MetaReqSearchByUIN2 - happy path", reqParams: reqParams{ instance: state.NewSession().AddInstance(), inFrame: frame, inBody: wire.SNAC_0x15_0x02_BQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{ Message: ICQMetaRequest{ ICQMetadata: wire.ICQMetadata{ ReqType: wire.ICQDBQueryMetaReq, Seq: 1, }, ReqSubType: wire.ICQDBQueryMetaReqSearchByUIN2, MetaRequest: wire.ICQ_0x07D0_0x0569_DBQueryMetaReqSearchByUIN2{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(1, uint16(1)), }, }, }, }, }), }, }, }, seq: 1, }, allMockParams: allMockParams{ findByUIN2: &mockParam{ frame: frame, req: wire.ICQ_0x07D0_0x0569_DBQueryMetaReqSearchByUIN2{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(1, uint16(1)), }, }, }, }, }, }, { name: "MetaReqSearchByEmail - happy path", reqParams: reqParams{ instance: state.NewSession().AddInstance(), inBody: wire.SNAC_0x15_0x02_BQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{ Message: ICQMetaRequest{ ICQMetadata: wire.ICQMetadata{ ReqType: wire.ICQDBQueryMetaReq, Seq: 1, }, ReqSubType: wire.ICQDBQueryMetaReqSearchByEmail, MetaRequest: wire.ICQ_0x07D0_0x0529_DBQueryMetaReqSearchByEmail{ Email: "test@aol.com", }, }, }), }, }, }, seq: 1, }, allMockParams: allMockParams{ findByEmail: &mockParam{ req: wire.ICQ_0x07D0_0x0529_DBQueryMetaReqSearchByEmail{ Email: "test@aol.com", }, }, }, }, { name: "MetaReqSearchByEmail3 - happy path", reqParams: reqParams{ instance: state.NewSession().AddInstance(), inFrame: frame, inBody: wire.SNAC_0x15_0x02_BQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{ Message: ICQMetaRequest{ ICQMetadata: wire.ICQMetadata{ ReqType: wire.ICQDBQueryMetaReq, Seq: 1, }, ReqSubType: wire.ICQDBQueryMetaReqSearchByEmail3, MetaRequest: wire.ICQ_0x07D0_0x0573_DBQueryMetaReqSearchByEmail3{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(1, uint16(1)), }, }, }, }, }), }, }, }, seq: 1, }, allMockParams: allMockParams{ findByEmail3: &mockParam{ frame: frame, req: wire.ICQ_0x07D0_0x0573_DBQueryMetaReqSearchByEmail3{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(1, uint16(1)), }, }, }, }, }, }, { name: "MetaReqSearchByDetails - happy path", reqParams: reqParams{ instance: state.NewSession().AddInstance(), inBody: wire.SNAC_0x15_0x02_BQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{ Message: ICQMetaRequest{ ICQMetadata: wire.ICQMetadata{ ReqType: wire.ICQDBQueryMetaReq, Seq: 1, }, ReqSubType: wire.ICQDBQueryMetaReqSearchByDetails, MetaRequest: wire.ICQ_0x07D0_0x0515_DBQueryMetaReqSearchByDetails{ FirstName: "john", }, }, }), }, }, }, seq: 1, }, allMockParams: allMockParams{ findByDetails: &mockParam{ req: wire.ICQ_0x07D0_0x0515_DBQueryMetaReqSearchByDetails{ FirstName: "john", }, }, }, }, { name: "MetaReqSearchWhitePages - happy path", reqParams: reqParams{ instance: state.NewSession().AddInstance(), inBody: wire.SNAC_0x15_0x02_BQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{ Message: ICQMetaRequest{ ICQMetadata: wire.ICQMetadata{ ReqType: wire.ICQDBQueryMetaReq, Seq: 1, }, ReqSubType: wire.ICQDBQueryMetaReqSearchWhitePages, MetaRequest: wire.ICQ_0x07D0_0x0533_DBQueryMetaReqSearchWhitePages{ InterestsCode: 1, }, }, }), }, }, }, seq: 1, }, allMockParams: allMockParams{ findByInterests: &mockParam{ req: wire.ICQ_0x07D0_0x0533_DBQueryMetaReqSearchWhitePages{ InterestsCode: 1, }, }, }, }, { name: "MetaReqSearchWhitePages2 - happy path", reqParams: reqParams{ instance: state.NewSession().AddInstance(), inFrame: frame, inBody: wire.SNAC_0x15_0x02_BQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{ Message: ICQMetaRequest{ ICQMetadata: wire.ICQMetadata{ ReqType: wire.ICQDBQueryMetaReq, Seq: 1, }, ReqSubType: wire.ICQDBQueryMetaReqSearchWhitePages2, MetaRequest: wire.ICQ_0x07D0_0x055F_DBQueryMetaReqSearchWhitePages2{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(1, uint16(1)), }, }, }, }, }), }, }, }, seq: 1, }, allMockParams: allMockParams{ findByWhitePages2: &mockParam{ frame: frame, req: wire.ICQ_0x07D0_0x055F_DBQueryMetaReqSearchWhitePages2{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(1, uint16(1)), }, }, }, }, }, }, { name: "MetaReqSetBasicInfo - happy path", reqParams: reqParams{ instance: state.NewSession().AddInstance(), inBody: wire.SNAC_0x15_0x02_BQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{ Message: ICQMetaRequest{ ICQMetadata: wire.ICQMetadata{ ReqType: wire.ICQDBQueryMetaReq, Seq: 1, }, ReqSubType: wire.ICQDBQueryMetaReqSetBasicInfo, MetaRequest: wire.ICQ_0x07D0_0x03EA_DBQueryMetaReqSetBasicInfo{ FirstName: "john", }, }, }), }, }, }, seq: 1, }, allMockParams: allMockParams{ setBasicInfo: &mockParam{ req: wire.ICQ_0x07D0_0x03EA_DBQueryMetaReqSetBasicInfo{ FirstName: "john", }, }, }, }, { name: "MetaReqSetWorkInfo - happy path", reqParams: reqParams{ instance: state.NewSession().AddInstance(), inBody: wire.SNAC_0x15_0x02_BQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{ Message: ICQMetaRequest{ ICQMetadata: wire.ICQMetadata{ ReqType: wire.ICQDBQueryMetaReq, Seq: 1, }, ReqSubType: wire.ICQDBQueryMetaReqSetWorkInfo, MetaRequest: wire.ICQ_0x07D0_0x03F3_DBQueryMetaReqSetWorkInfo{ ZIP: "11111", }, }, }), }, }, }, seq: 1, }, allMockParams: allMockParams{ setWorkInfo: &mockParam{ req: wire.ICQ_0x07D0_0x03F3_DBQueryMetaReqSetWorkInfo{ ZIP: "11111", }, }, }, }, { name: "MetaReqSetMoreInfo - happy path", reqParams: reqParams{ instance: state.NewSession().AddInstance(), inBody: wire.SNAC_0x15_0x02_BQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{ Message: ICQMetaRequest{ ICQMetadata: wire.ICQMetadata{ ReqType: wire.ICQDBQueryMetaReq, Seq: 1, }, ReqSubType: wire.ICQDBQueryMetaReqSetMoreInfo, MetaRequest: wire.ICQ_0x07D0_0x03FD_DBQueryMetaReqSetMoreInfo{ Age: 100, }, }, }), }, }, }, seq: 1, }, allMockParams: allMockParams{ setMoreInfo: &mockParam{ req: wire.ICQ_0x07D0_0x03FD_DBQueryMetaReqSetMoreInfo{ Age: 100, }, }, }, }, { name: "MetaReqSetNotes - happy path", reqParams: reqParams{ instance: state.NewSession().AddInstance(), inBody: wire.SNAC_0x15_0x02_BQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{ Message: ICQMetaRequest{ ICQMetadata: wire.ICQMetadata{ ReqType: wire.ICQDBQueryMetaReq, Seq: 1, }, ReqSubType: wire.ICQDBQueryMetaReqSetNotes, MetaRequest: wire.ICQ_0x07D0_0x0406_DBQueryMetaReqSetNotes{ Notes: "my note", }, }, }), }, }, }, seq: 1, }, allMockParams: allMockParams{ setUserNotes: &mockParam{ req: wire.ICQ_0x07D0_0x0406_DBQueryMetaReqSetNotes{ Notes: "my note", }, }, }, }, { name: "MetaReqSetEmails - happy path", reqParams: reqParams{ instance: state.NewSession().AddInstance(), inBody: wire.SNAC_0x15_0x02_BQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{ Message: ICQMetaRequest{ ICQMetadata: wire.ICQMetadata{ ReqType: wire.ICQDBQueryMetaReq, Seq: 1, }, ReqSubType: wire.ICQDBQueryMetaReqSetEmails, MetaRequest: wire.ICQ_0x07D0_0x040B_DBQueryMetaReqSetEmails{ Emails: []struct { Publish uint8 Email string `oscar:"len_prefix=uint16,nullterm"` }{ { Email: "test@aol.com", }, }, }, }, }), }, }, }, seq: 1, }, allMockParams: allMockParams{ setEmails: &mockParam{ req: wire.ICQ_0x07D0_0x040B_DBQueryMetaReqSetEmails{ Emails: []struct { Publish uint8 Email string `oscar:"len_prefix=uint16,nullterm"` }{ { Email: "test@aol.com", }, }, }, }, }, }, { name: "MetaReqSetInterests - happy path", reqParams: reqParams{ instance: state.NewSession().AddInstance(), inBody: wire.SNAC_0x15_0x02_BQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{ Message: ICQMetaRequest{ ICQMetadata: wire.ICQMetadata{ ReqType: wire.ICQDBQueryMetaReq, Seq: 1, }, ReqSubType: wire.ICQDBQueryMetaReqSetInterests, MetaRequest: wire.ICQ_0x07D0_0x0410_DBQueryMetaReqSetInterests{ Interests: []struct { Code uint16 Keyword string `oscar:"len_prefix=uint16,nullterm"` }{ { Keyword: "an_interest", }, }, }, }, }), }, }, }, seq: 1, }, allMockParams: allMockParams{ setInterests: &mockParam{ req: wire.ICQ_0x07D0_0x0410_DBQueryMetaReqSetInterests{ Interests: []struct { Code uint16 Keyword string `oscar:"len_prefix=uint16,nullterm"` }{ { Keyword: "an_interest", }, }, }, }, }, }, { name: "MetaReqSetAffiliations - happy path", reqParams: reqParams{ instance: state.NewSession().AddInstance(), inBody: wire.SNAC_0x15_0x02_BQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{ Message: ICQMetaRequest{ ICQMetadata: wire.ICQMetadata{ ReqType: wire.ICQDBQueryMetaReq, Seq: 1, }, ReqSubType: wire.ICQDBQueryMetaReqSetAffiliations, MetaRequest: wire.ICQ_0x07D0_0x041A_DBQueryMetaReqSetAffiliations{ PastAffiliations: []struct { Code uint16 Keyword string `oscar:"len_prefix=uint16,nullterm"` }{ { Keyword: "a_past_affiliation", }, }, Affiliations: []struct { Code uint16 Keyword string `oscar:"len_prefix=uint16,nullterm"` }{ { Keyword: "an_affiliation", }, }, }, }, }), }, }, }, seq: 1, }, allMockParams: allMockParams{ setAffiliations: &mockParam{ req: wire.ICQ_0x07D0_0x041A_DBQueryMetaReqSetAffiliations{ PastAffiliations: []struct { Code uint16 Keyword string `oscar:"len_prefix=uint16,nullterm"` }{ { Keyword: "a_past_affiliation", }, }, Affiliations: []struct { Code uint16 Keyword string `oscar:"len_prefix=uint16,nullterm"` }{ { Keyword: "an_affiliation", }, }, }, }, }, }, { name: "MetaReqSetFullInfo - happy path", reqParams: reqParams{ instance: state.NewSession().AddInstance(), inBody: wire.SNAC_0x15_0x02_BQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{ Message: ICQMetaRequest{ ICQMetadata: wire.ICQMetadata{ ReqType: wire.ICQDBQueryMetaReq, Seq: 1, }, ReqSubType: wire.ICQDBQueryMetaReqSetFullInfo, MetaRequest: wire.ICQ_0x07D0_0x0C3A_DBQueryMetaReqSetFullInfo{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVLE(wire.ICQTLVTagsFirstName, struct { V string `oscar:"len_prefix=uint16,nullterm"` }{V: "john"}), }, }, }, }, }), }, }, }, seq: 1, }, allMockParams: allMockParams{ setFullInfo: &mockParam{ req: wire.ICQ_0x07D0_0x0C3A_DBQueryMetaReqSetFullInfo{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVLE(wire.ICQTLVTagsFirstName, struct { V string `oscar:"len_prefix=uint16,nullterm"` }{V: "john"}), }, }, }, }, }, }, { name: "MetaReqStat - happy path", reqParams: reqParams{ instance: state.NewSession().AddInstance(), inBody: wire.SNAC_0x15_0x02_BQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{ Message: ICQMetaRequest{ ICQMetadata: wire.ICQMetadata{ ReqType: wire.ICQDBQueryMetaReq, Seq: 1, }, ReqSubType: wire.ICQDBQueryMetaReqStat0a8c, MetaRequest: struct{}{}, }, }), }, }, }, seq: 1, }, }, { name: "unknown metadata request subtype", reqParams: reqParams{ instance: state.NewSession().AddInstance(), inBody: wire.SNAC_0x15_0x02_BQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{ Message: ICQMetaRequest{ ICQMetadata: wire.ICQMetadata{ ReqType: wire.ICQDBQueryMetaReq, Seq: 1, }, ReqSubType: 0xCA8E, MetaRequest: struct{}{}, }, }), }, }, }, seq: 1, wantErr: nil, }, }, { name: "OfflineMsgReq - happy path", reqParams: reqParams{ instance: state.NewSession().AddInstance(), inBody: wire.SNAC_0x15_0x02_BQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{ Message: wire.ICQMetadataWithSubType{ ICQMetadata: wire.ICQMetadata{ ReqType: wire.ICQDBQueryOfflineMsgReq, Seq: 1, }, }, }), }, }, }, seq: 1, }, allMockParams: allMockParams{ offlineMsgReq: &mockParam{}, }, }, { name: "DeleteMsgReq - happy path", reqParams: reqParams{ instance: state.NewSession().AddInstance(), inBody: wire.SNAC_0x15_0x02_BQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{ Message: wire.ICQMetadataWithSubType{ ICQMetadata: wire.ICQMetadata{ ReqType: wire.ICQDBQueryDeleteMsgReq, Seq: 1, }, }, }), }, }, }, seq: 1, }, allMockParams: allMockParams{ deleteMsgReq: &mockParam{}, }, }, { name: "unknown request type", reqParams: reqParams{ instance: state.NewSession().AddInstance(), inBody: wire.SNAC_0x15_0x02_BQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{ Message: ICQMetaRequest{ ICQMetadata: wire.ICQMetadata{ ReqType: 0x13B4, Seq: 1, }, ReqSubType: 0xCA8E, MetaRequest: struct{}{}, }, }), }, }, }, seq: 1, wantErr: errUnknownICQMetaReqType, }, }, // todo: add to a separate test } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { icqService := newMockICQService(t) switch { case tt.allMockParams.fullUserInfo != nil: icqService.EXPECT(). FullUserInfo(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.fullUserInfo.req, tt.reqParams.seq). Return(tt.allMockParams.fullUserInfo.wantErr) case tt.allMockParams.shortUserInfo != nil: icqService.EXPECT(). ShortUserInfo(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.shortUserInfo.req, tt.reqParams.seq). Return(tt.allMockParams.shortUserInfo.wantErr) case tt.allMockParams.xmlReqData != nil: icqService.EXPECT(). XMLReqData(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.xmlReqData.req, tt.reqParams.seq). Return(tt.allMockParams.xmlReqData.wantErr) case tt.allMockParams.setPermissions != nil: icqService.EXPECT(). SetPermissions(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.setPermissions.req, tt.reqParams.seq). Return(tt.allMockParams.setPermissions.wantErr) case tt.allMockParams.setICQPhone != nil: icqService.EXPECT(). SetICQPhone(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.setICQPhone.req, tt.reqParams.seq). Return(tt.allMockParams.setICQPhone.wantErr) case tt.allMockParams.findByUIN != nil: icqService.EXPECT(). FindByUIN(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.findByUIN.req, tt.reqParams.seq). Return(tt.allMockParams.findByUIN.wantErr) case tt.allMockParams.findByUIN2 != nil: icqService.EXPECT(). FindByUIN2(mock.Anything, tt.reqParams.instance, tt.allMockParams.findByUIN2.frame, tt.allMockParams.findByUIN2.req, tt.reqParams.seq). Return(tt.allMockParams.findByUIN2.wantErr) case tt.allMockParams.findByEmail != nil: icqService.EXPECT(). FindByICQEmail(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.findByEmail.req, tt.reqParams.seq). Return(tt.allMockParams.findByEmail.wantErr) case tt.allMockParams.findByEmail3 != nil: icqService.EXPECT(). FindByEmail3(mock.Anything, tt.reqParams.instance, tt.allMockParams.findByEmail3.frame, tt.allMockParams.findByEmail3.req, tt.reqParams.seq). Return(tt.allMockParams.findByEmail3.wantErr) case tt.allMockParams.findByDetails != nil: icqService.EXPECT(). FindByICQName(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.findByDetails.req, tt.reqParams.seq). Return(tt.allMockParams.findByDetails.wantErr) case tt.allMockParams.findByInterests != nil: icqService.EXPECT(). FindByICQInterests(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.findByInterests.req, tt.reqParams.seq). Return(tt.allMockParams.findByInterests.wantErr) case tt.allMockParams.findByWhitePages2 != nil: icqService.EXPECT(). FindByWhitePages2(mock.Anything, tt.reqParams.instance, tt.allMockParams.findByWhitePages2.frame, tt.allMockParams.findByWhitePages2.req, tt.reqParams.seq). Return(tt.allMockParams.findByWhitePages2.wantErr) case tt.allMockParams.setBasicInfo != nil: icqService.EXPECT(). SetBasicInfo(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.setBasicInfo.req, tt.reqParams.seq). Return(tt.allMockParams.setBasicInfo.wantErr) case tt.allMockParams.setWorkInfo != nil: icqService.EXPECT(). SetWorkInfo(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.setWorkInfo.req, tt.reqParams.seq). Return(tt.allMockParams.setWorkInfo.wantErr) case tt.allMockParams.setMoreInfo != nil: icqService.EXPECT(). SetMoreInfo(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.setMoreInfo.req, tt.reqParams.seq). Return(tt.allMockParams.setMoreInfo.wantErr) case tt.allMockParams.setUserNotes != nil: icqService.EXPECT(). SetUserNotes(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.setUserNotes.req, tt.reqParams.seq). Return(tt.allMockParams.setUserNotes.wantErr) case tt.allMockParams.setEmails != nil: icqService.EXPECT(). SetEmails(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.setEmails.req, tt.reqParams.seq). Return(tt.allMockParams.setEmails.wantErr) case tt.allMockParams.setInterests != nil: icqService.EXPECT(). SetInterests(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.setInterests.req, tt.reqParams.seq). Return(tt.allMockParams.setInterests.wantErr) case tt.allMockParams.setAffiliations != nil: icqService.EXPECT(). SetAffiliations(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.setAffiliations.req, tt.reqParams.seq). Return(tt.allMockParams.setAffiliations.wantErr) case tt.allMockParams.setFullInfo != nil: icqService.EXPECT(). SetICQInfo(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.setFullInfo.req, tt.reqParams.seq). Return(tt.allMockParams.setFullInfo.wantErr) case tt.allMockParams.offlineMsgReq != nil: icqService.EXPECT(). OfflineMsgReq(mock.Anything, frame, tt.reqParams.instance, tt.reqParams.seq). Return(tt.allMockParams.offlineMsgReq.wantErr) case tt.allMockParams.deleteMsgReq != nil: icqService.EXPECT(). DeleteMsgReq(mock.Anything, tt.reqParams.instance, tt.reqParams.seq). Return(tt.allMockParams.deleteMsgReq.wantErr) } h := Handler{ ICQService: icqService, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(tt.reqParams.inBody, buf)) err := h.Handle(context.TODO(), wire.BOS, tt.reqParams.instance, frame, buf, nil, config.Listener{}) assert.ErrorIs(t, err, tt.reqParams.wantErr) }) } } // Test workaround for QIP 2005 bug where TLV length is incorrect. func TestHandler_ICQDBQuery_QIP2005UINSearchBug(t *testing.T) { icqService := newMockICQService(t) type ICQMetaRequest struct { wire.ICQMetadata ReqSubType uint16 MetaRequest any } expect := wire.ICQ_0x07D0_0x0569_DBQueryMetaReqSearchByUIN2{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.ICQTLVTagsUIN, uint32(100009)), }, }, } instance := state.NewSession().AddInstance() icqService.EXPECT(). FindByUIN2(mock.Anything, instance, wire.SNACFrame{}, expect, uint16(1)). Return(nil) h := Handler{ ICQService: icqService, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } inBody := wire.SNAC_0x15_0x02_BQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{ Message: ICQMetaRequest{ ICQMetadata: wire.ICQMetadata{ ReqType: wire.ICQDBQueryMetaReq, Seq: 1, }, ReqSubType: wire.ICQDBQueryMetaReqSearchByUIN2, MetaRequest: wire.ICQ_0x07D0_0x0569_DBQueryMetaReqSearchByUIN2{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.ICQTLVTagsUIN, uint32(100009)), }, }, }, }, }), }, }, } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(inBody, buf)) b := buf.Bytes() b[18] = 6 // incorrectly set TLV length to 6 (should be 4) err := h.ICQDBQuery(context.TODO(), instance, wire.SNACFrame{}, buf, nil) assert.NoError(t, err) } func TestHandler_ODirInfoQuery(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x0F_0x02_InfoQuery serviceError error responseError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x0F_0x02_InfoQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(1, uint16(2)), }, }, }, }, { name: "service error", inputBody: wire.SNAC_0x0F_0x02_InfoQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(1, uint16(2)), }, }, }, serviceError: assert.AnError, expectedError: assert.AnError, }, { name: "response writer error", inputBody: wire.SNAC_0x0F_0x02_InfoQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(1, uint16(2)), }, }, }, responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.ODir, SubGroup: wire.ODirInfoQuery, }, Body: tt.inputBody, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.ODir, SubGroup: wire.ODirInfoReply, }, Body: wire.SNAC_0x0F_0x03_InfoReply{ Status: 5, // OK has results/not found }, } svc := newMockODirService(t) svc.EXPECT(). InfoQuery(mock.Anything, input.Frame, input.Body). Return(output, tt.serviceError) h := Handler{ ODirService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } ss := newMockResponseWriter(t) if tt.serviceError == nil { ss.EXPECT(). SendSNAC(output.Frame, output.Body). Return(tt.responseError) } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, ss, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_ODirKeywordListQuery(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x0F_0x02_InfoQuery serviceError error responseError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x0F_0x02_InfoQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(1, uint16(2)), }, }, }, }, { name: "service error", inputBody: wire.SNAC_0x0F_0x02_InfoQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(1, uint16(2)), }, }, }, serviceError: assert.AnError, expectedError: assert.AnError, }, { name: "response writer error", inputBody: wire.SNAC_0x0F_0x02_InfoQuery{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(1, uint16(2)), }, }, }, responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.ODir, SubGroup: wire.ODirKeywordListQuery, }, Body: tt.inputBody, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.ODir, SubGroup: wire.ODirKeywordListReply, }, Body: wire.SNAC_0x0F_0x04_KeywordListReply{ Status: 0x01, }, } svc := newMockODirService(t) svc.EXPECT(). KeywordListQuery(mock.Anything, input.Frame). Return(output, tt.serviceError) h := Handler{ ODirService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } ss := newMockResponseWriter(t) if tt.serviceError == nil { ss.EXPECT(). SendSNAC(output.Frame, output.Body). Return(tt.responseError) } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, ss, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_OServiceServiceClientOnline(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x01_0x02_OServiceClientOnline serviceError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x01_0x02_OServiceClientOnline{ GroupVersions: []struct { FoodGroup uint16 Version uint16 ToolID uint16 ToolVersion uint16 }{ { FoodGroup: 10, }, }, }, }, { name: "service error", inputBody: wire.SNAC_0x01_0x02_OServiceClientOnline{ GroupVersions: []struct { FoodGroup uint16 Version uint16 ToolID uint16 ToolVersion uint16 }{ { FoodGroup: 10, }, }, }, serviceError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.OService, SubGroup: wire.OServiceClientOnline, }, Body: tt.inputBody, } svc := newMockOServiceService(t) svc.EXPECT(). ClientOnline(mock.Anything, wire.BOS, input.Body, mock.Anything). Return(tt.serviceError) h := Handler{ OServiceService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, nil, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_OServiceServiceServiceRequest(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x01_0x04_OServiceServiceRequest serviceError error responseError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x01_0x04_OServiceServiceRequest{ FoodGroup: wire.Chat, }, }, { name: "service error", inputBody: wire.SNAC_0x01_0x04_OServiceServiceRequest{ FoodGroup: wire.Chat, }, serviceError: assert.AnError, expectedError: assert.AnError, }, { name: "response writer error", inputBody: wire.SNAC_0x01_0x04_OServiceServiceRequest{ FoodGroup: wire.Chat, }, responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.OService, SubGroup: wire.OServiceServiceRequest, }, Body: tt.inputBody, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.OService, SubGroup: wire.OServiceServiceResponse, }, Body: wire.SNAC_0x01_0x05_OServiceServiceResponse{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(0x01, uint16(1000)), }, }, }, } svc := newMockOServiceService(t) svc.EXPECT(). ServiceRequest(mock.Anything, wire.BOS, mock.Anything, input.Frame, input.Body, config.Listener{BOSAdvertisedHostPlain: "127.0.0.1:1234"}). Return(output, tt.serviceError) h := Handler{ OServiceService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) if tt.serviceError == nil { responseWriter.EXPECT(). SendSNAC(output.Frame, output.Body). Return(tt.responseError) } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{BOSAdvertisedHostPlain: "127.0.0.1:1234"}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_OServiceServiceIdleNotification(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x01_0x11_OServiceIdleNotification serviceError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x01_0x11_OServiceIdleNotification{ IdleTime: 10, }, }, { name: "service error", inputBody: wire.SNAC_0x01_0x11_OServiceIdleNotification{ IdleTime: 10, }, serviceError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.OService, SubGroup: wire.OServiceIdleNotification, }, Body: tt.inputBody, } svc := newMockOServiceService(t) svc.EXPECT(). IdleNotification(mock.Anything, mock.Anything, input.Body). Return(tt.serviceError) h := Handler{ OServiceService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_OServiceServiceClientVersions(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x01_0x17_OServiceClientVersions responseError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x01_0x17_OServiceClientVersions{ Versions: []uint16{ 10, }, }, }, { name: "response writer error", inputBody: wire.SNAC_0x01_0x17_OServiceClientVersions{ Versions: []uint16{ 10, }, }, responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.OService, SubGroup: wire.OServiceClientVersions, }, Body: tt.inputBody, } output := []wire.SNACMessage{ { Frame: wire.SNACFrame{ FoodGroup: wire.OService, SubGroup: wire.OServiceHostVersions, }, Body: wire.SNAC_0x01_0x18_OServiceHostVersions{ Versions: []uint16{ 10, }, }, }, { Frame: wire.SNACFrame{ FoodGroup: wire.OService, SubGroup: wire.OServiceMotd, RequestID: wire.ReqIDFromServer, }, Body: wire.SNAC_0x01_0x13_OServiceMOTD{ MessageType: 0x0004, TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(wire.OServiceTLVTagsMOTDMessage, "Welcome to Open OSCAR Server"), }, }, }, }, } instance := state.NewSession().AddInstance() svc := newMockOServiceService(t) svc.EXPECT(). ClientVersions(mock.Anything, instance, input.Frame, input.Body). Return(output) h := Handler{ OServiceService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) if tt.responseError == nil { for _, snac := range output { responseWriter.EXPECT(). SendSNAC(snac.Frame, snac.Body). Return(nil) } } else { responseWriter.EXPECT(). SendSNAC(output[0].Frame, output[0].Body). Return(tt.responseError) } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.Background(), wire.BOS, instance, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_OServiceServiceRateParamsQuery(t *testing.T) { tests := []struct { name string inputBody struct{} responseError error expectedError error }{ { name: "success", inputBody: struct{}{}, }, { name: "response writer error", inputBody: struct{}{}, responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.OService, SubGroup: wire.OServiceRateParamsQuery, }, Body: tt.inputBody, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.OService, SubGroup: wire.OServiceRateParamsReply, }, Body: wire.SNAC_0x01_0x07_OServiceRateParamsReply{ RateGroups: []struct { ID uint16 Pairs []struct { FoodGroup uint16 SubGroup uint16 } `oscar:"count_prefix=uint16"` }{ { ID: 1, }, }, }, } svc := newMockOServiceService(t) svc.EXPECT(). RateParamsQuery(mock.Anything, mock.Anything, input.Frame). Return(output) h := Handler{ OServiceService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) responseWriter.EXPECT(). SendSNAC(output.Frame, output.Body). Return(tt.responseError) buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_OServiceServiceRateParamsSubAdd(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x01_0x08_OServiceRateParamsSubAdd expectedError error }{ { name: "success", inputBody: wire.SNAC_0x01_0x08_OServiceRateParamsSubAdd{ ClassIDs: []uint16{1, 2, 3, 4}, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.OService, SubGroup: wire.OServiceRateParamsSubAdd, }, Body: tt.inputBody, } instance := state.NewSession().AddInstance() svc := newMockOServiceService(t) svc.EXPECT(). RateParamsSubAdd(mock.Anything, instance, input.Body) h := Handler{ OServiceService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.Background(), wire.BOS, instance, input.Frame, buf, nil, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_OServiceServiceSetUserInfoFields(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x01_0x1E_OServiceSetUserInfoFields serviceError error responseError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x01_0x1E_OServiceSetUserInfoFields{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(0x01, []byte{1, 2, 3, 4}), }, }, }, }, { name: "service error", inputBody: wire.SNAC_0x01_0x1E_OServiceSetUserInfoFields{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(0x01, []byte{1, 2, 3, 4}), }, }, }, serviceError: assert.AnError, expectedError: assert.AnError, }, { name: "response writer error", inputBody: wire.SNAC_0x01_0x1E_OServiceSetUserInfoFields{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(0x01, []byte{1, 2, 3, 4}), }, }, }, responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.OService, SubGroup: wire.OServiceSetUserInfoFields, }, Body: tt.inputBody, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.OService, SubGroup: wire.OServiceUserInfoUpdate, }, Body: wire.SNAC_0x01_0x0F_OServiceUserInfoUpdate{ UserInfo: []wire.TLVUserInfo{ {ScreenName: "screen-name"}, {ScreenName: "screen-name"}, }, }, } svc := newMockOServiceService(t) svc.EXPECT(). SetUserInfoFields(mock.Anything, mock.Anything, input.Frame, input.Body). Return(output, tt.serviceError) h := Handler{ OServiceService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) if tt.serviceError == nil { responseWriter.EXPECT(). SendSNAC(output.Frame, output.Body). Return(tt.responseError) } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_OServiceServiceUserInfoQuery(t *testing.T) { tests := []struct { name string inputBody struct{} responseError error expectedError error }{ { name: "success", inputBody: struct{}{}, }, { name: "response writer error", inputBody: struct{}{}, responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.OService, SubGroup: wire.OServiceUserInfoQuery, }, Body: tt.inputBody, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.OService, SubGroup: wire.OServiceUserInfoUpdate, }, Body: wire.SNAC_0x01_0x0F_OServiceUserInfoUpdate{ UserInfo: []wire.TLVUserInfo{ {ScreenName: "screen-name"}, {ScreenName: "screen-name"}, }, }, } svc := newMockOServiceService(t) svc.EXPECT(). UserInfoQuery(mock.Anything, mock.Anything, input.Frame). Return(output) h := Handler{ OServiceService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) responseWriter.EXPECT(). SendSNAC(output.Frame, output.Body). Return(tt.responseError) buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_OServiceServiceNoop(t *testing.T) { tests := []struct { name string inputBody struct{} expectedError error }{ { name: "success", inputBody: struct{}{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.OService, SubGroup: wire.OServiceNoop, }, Body: tt.inputBody, } h := Handler{ RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_OServiceServiceSetPrivacyFlags(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x01_0x14_OServiceSetPrivacyFlags expectedError error }{ { name: "success", inputBody: wire.SNAC_0x01_0x14_OServiceSetPrivacyFlags{ PrivacyFlags: wire.OServicePrivacyFlagMember, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.OService, SubGroup: wire.OServiceSetPrivacyFlags, }, Body: tt.inputBody, } svc := newMockOServiceService(t) svc.EXPECT(). SetPrivacyFlags(mock.Anything, input.Body) h := Handler{ OServiceService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_PermitDenyRightsQuery(t *testing.T) { tests := []struct { name string inputBody struct{} responseError error expectedError error }{ { name: "success", inputBody: struct{}{}, }, { name: "response writer error", inputBody: struct{}{}, responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.PermitDeny, SubGroup: wire.PermitDenyRightsQuery, }, Body: tt.inputBody, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.PermitDeny, SubGroup: wire.PermitDenyRightsReply, }, Body: wire.SNAC_0x09_0x03_PermitDenyRightsReply{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(0x01, uint16(1000)), }, }, }, } svc := newMockPermitDenyService(t) svc.EXPECT(). RightsQuery(mock.Anything, input.Frame). Return(output) h := Handler{ PermitDenyService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) responseWriter.EXPECT(). SendSNAC(output.Frame, output.Body). Return(tt.responseError) buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_PermitDenyAddDenyListEntries(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x09_0x07_PermitDenyAddDenyListEntries serviceError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x09_0x07_PermitDenyAddDenyListEntries{ Users: []struct { ScreenName string `oscar:"len_prefix=uint8"` }{ { ScreenName: "friend1", }, { ScreenName: "friend2", }, }, }, }, { name: "service error", inputBody: wire.SNAC_0x09_0x07_PermitDenyAddDenyListEntries{ Users: []struct { ScreenName string `oscar:"len_prefix=uint8"` }{ { ScreenName: "friend1", }, { ScreenName: "friend2", }, }, }, serviceError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { instance := state.NewSession().AddInstance() input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.PermitDeny, SubGroup: wire.PermitDenyAddDenyListEntries, }, Body: tt.inputBody, } svc := newMockPermitDenyService(t) svc.EXPECT(). AddDenyListEntries(mock.Anything, instance, input.Body). Return(tt.serviceError) h := Handler{ PermitDenyService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, instance, input.Frame, buf, nil, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_PermitDenyDelDenyListEntries(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x09_0x08_PermitDenyDelDenyListEntries serviceError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x09_0x08_PermitDenyDelDenyListEntries{ Users: []struct { ScreenName string `oscar:"len_prefix=uint8"` }{ { ScreenName: "friend1", }, { ScreenName: "friend2", }, }, }, }, { name: "service error", inputBody: wire.SNAC_0x09_0x08_PermitDenyDelDenyListEntries{ Users: []struct { ScreenName string `oscar:"len_prefix=uint8"` }{ { ScreenName: "friend1", }, { ScreenName: "friend2", }, }, }, serviceError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { instance := state.NewSession().AddInstance() input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.PermitDeny, SubGroup: wire.PermitDenyDelDenyListEntries, }, Body: tt.inputBody, } svc := newMockPermitDenyService(t) svc.EXPECT(). DelDenyListEntries(mock.Anything, instance, input.Body). Return(tt.serviceError) h := Handler{ PermitDenyService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, instance, input.Frame, buf, nil, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_PermitDenyAddPermListEntries(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x09_0x05_PermitDenyAddPermListEntries serviceError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x09_0x05_PermitDenyAddPermListEntries{ Users: []struct { ScreenName string `oscar:"len_prefix=uint8"` }{ { ScreenName: "friend1", }, { ScreenName: "friend2", }, }, }, }, { name: "service error", inputBody: wire.SNAC_0x09_0x05_PermitDenyAddPermListEntries{ Users: []struct { ScreenName string `oscar:"len_prefix=uint8"` }{ { ScreenName: "friend1", }, { ScreenName: "friend2", }, }, }, serviceError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { instance := state.NewSession().AddInstance() input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.PermitDeny, SubGroup: wire.PermitDenyAddPermListEntries, }, Body: tt.inputBody, } svc := newMockPermitDenyService(t) svc.EXPECT(). AddPermListEntries(mock.Anything, instance, input.Body). Return(tt.serviceError) h := Handler{ PermitDenyService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, instance, input.Frame, buf, nil, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_PermitDenyDelPermListEntries(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x09_0x06_PermitDenyDelPermListEntries serviceError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x09_0x06_PermitDenyDelPermListEntries{ Users: []struct { ScreenName string `oscar:"len_prefix=uint8"` }{ { ScreenName: "friend1", }, { ScreenName: "friend2", }, }, }, }, { name: "service error", inputBody: wire.SNAC_0x09_0x06_PermitDenyDelPermListEntries{ Users: []struct { ScreenName string `oscar:"len_prefix=uint8"` }{ { ScreenName: "friend1", }, { ScreenName: "friend2", }, }, }, serviceError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { instance := state.NewSession().AddInstance() input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.PermitDeny, SubGroup: wire.PermitDenyDelPermListEntries, }, Body: tt.inputBody, } svc := newMockPermitDenyService(t) svc.EXPECT(). DelPermListEntries(mock.Anything, instance, input.Body). Return(tt.serviceError) h := Handler{ PermitDenyService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, instance, input.Frame, buf, nil, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_PermitDenySetGroupPermitMask(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x09_0x06_PermitDenyDelPermListEntries expectedError error }{ { name: "success", inputBody: wire.SNAC_0x09_0x06_PermitDenyDelPermListEntries{ Users: []struct { ScreenName string `oscar:"len_prefix=uint8"` }{ { ScreenName: "friend1", }, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { instance := state.NewSession().AddInstance() input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.PermitDeny, SubGroup: wire.PermitDenySetGroupPermitMask, }, Body: tt.inputBody, } svc := newMockPermitDenyService(t) h := Handler{ PermitDenyService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, instance, input.Frame, buf, nil, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestUserLookupHandler_FindByEmail(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x0A_0x02_UserLookupFindByEmail serviceError error responseError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x0A_0x02_UserLookupFindByEmail{ Email: []byte("haha@aol.com"), }, }, { name: "service error", inputBody: wire.SNAC_0x0A_0x02_UserLookupFindByEmail{ Email: []byte("haha@aol.com"), }, serviceError: assert.AnError, expectedError: assert.AnError, }, { name: "response writer error", inputBody: wire.SNAC_0x0A_0x02_UserLookupFindByEmail{ Email: []byte("haha@aol.com"), }, responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.UserLookup, SubGroup: wire.UserLookupFindByEmail, }, Body: tt.inputBody, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.UserLookup, SubGroup: wire.UserLookupFindReply, }, Body: wire.SNAC_0x0A_0x03_UserLookupFindReply{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(0x01, uint16(0x02)), }, }, }, } svc := newMockUserLookupService(t) svc.EXPECT(). FindByEmail(mock.Anything, input.Frame, input.Body). Return(output, tt.serviceError) h := Handler{ UserLookupService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } ss := newMockResponseWriter(t) if tt.serviceError == nil { ss.EXPECT(). SendSNAC(output.Frame, output.Body). Return(tt.responseError) } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, ss, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_LocateGetDirInfo(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x02_0x0B_LocateGetDirInfo serviceError error responseError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x02_0x0B_LocateGetDirInfo{ ScreenName: "screen-name", }, }, { name: "service error", inputBody: wire.SNAC_0x02_0x0B_LocateGetDirInfo{ ScreenName: "screen-name", }, serviceError: assert.AnError, expectedError: assert.AnError, }, { name: "response writer error", inputBody: wire.SNAC_0x02_0x0B_LocateGetDirInfo{ ScreenName: "screen-name", }, responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Locate, SubGroup: wire.LocateGetDirInfo, }, Body: tt.inputBody, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Locate, SubGroup: wire.LocateGetDirReply, }, Body: wire.SNAC_0x02_0x0C_LocateGetDirReply{ Status: 1, }, } svc := newMockLocateService(t) svc.EXPECT(). DirInfo(mock.Anything, input.Frame, input.Body). Return(output, tt.serviceError) h := Handler{ LocateService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) if tt.serviceError == nil { responseWriter.EXPECT(). SendSNAC(output.Frame, output.Body). Return(tt.responseError) } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_LocateRightsQuery(t *testing.T) { tests := []struct { name string inputBody struct{} responseError error expectedError error }{ { name: "success", inputBody: struct{}{}, }, { name: "response writer error", inputBody: struct{}{}, responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Locate, SubGroup: wire.LocateRightsQuery, }, Body: tt.inputBody, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Locate, SubGroup: wire.LocateRightsReply, }, Body: wire.SNAC_0x02_0x03_LocateRightsReply{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ wire.NewTLVBE(0x01, uint16(1000)), }, }, }, } svc := newMockLocateService(t) svc.EXPECT(). RightsQuery(mock.Anything, input.Frame). Return(output) h := Handler{ LocateService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) responseWriter.EXPECT(). SendSNAC(output.Frame, output.Body). Return(tt.responseError) buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_LocateSetDirInfo(t *testing.T) { tests := []struct { name string inputBody wire.SNAC_0x02_0x09_LocateSetDirInfo serviceError error responseError error expectedError error }{ { name: "success", inputBody: wire.SNAC_0x02_0x09_LocateSetDirInfo{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ { Tag: 0x01, Value: []byte{1, 2, 3, 4}, }, }, }, }, }, { name: "service error", inputBody: wire.SNAC_0x02_0x09_LocateSetDirInfo{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ { Tag: 0x01, Value: []byte{1, 2, 3, 4}, }, }, }, }, serviceError: assert.AnError, expectedError: assert.AnError, }, { name: "response writer error", inputBody: wire.SNAC_0x02_0x09_LocateSetDirInfo{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ { Tag: 0x01, Value: []byte{1, 2, 3, 4}, }, }, }, }, responseError: assert.AnError, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Locate, SubGroup: wire.LocateSetDirInfo, }, Body: tt.inputBody, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Locate, SubGroup: wire.LocateSetDirReply, }, Body: wire.SNAC_0x02_0x0A_LocateSetDirReply{ Result: 1, }, } svc := newMockLocateService(t) svc.EXPECT(). SetDirInfo(mock.Anything, mock.Anything, input.Frame, input.Body). Return(output, tt.serviceError) h := Handler{ LocateService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) if tt.serviceError == nil { responseWriter.EXPECT(). SendSNAC(output.Frame, output.Body). Return(tt.responseError) } buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) err := h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{}) if tt.expectedError != nil { assert.ErrorIs(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestHandler_LocateSetInfo(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Locate, SubGroup: wire.LocateSetInfo, }, Body: wire.SNAC_0x02_0x04_LocateSetInfo{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ { Tag: 0x01, Value: []byte{1, 2, 3, 4}, }, }, }, }, } svc := newMockLocateService(t) svc.EXPECT(). SetInfo(mock.Anything, mock.Anything, input.Body). Return(nil) h := Handler{ LocateService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) assert.NoError(t, h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{})) } func TestHandler_LocateSetKeywordInfo(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Locate, SubGroup: wire.LocateSetKeywordInfo, }, Body: wire.SNAC_0x02_0x0F_LocateSetKeywordInfo{ TLVRestBlock: wire.TLVRestBlock{ TLVList: wire.TLVList{ { Tag: 0x01, Value: []byte{1, 2, 3, 4}, }, }, }, }, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Locate, SubGroup: wire.LocateSetKeywordReply, }, Body: wire.SNAC_0x02_0x10_LocateSetKeywordReply{ Unknown: 1, }, } svc := newMockLocateService(t) svc.EXPECT(). SetKeywordInfo(mock.Anything, mock.Anything, input.Frame, input.Body). Return(output, nil) h := Handler{ LocateService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) responseWriter.EXPECT(). SendSNAC(output.Frame, output.Body). Return(nil) buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) assert.NoError(t, h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{})) } func TestHandler_LocateUserInfoQuery(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Locate, SubGroup: wire.LocateUserInfoQuery, }, Body: wire.SNAC_0x02_0x05_LocateUserInfoQuery{ Type: 1, }, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Locate, SubGroup: wire.LocateUserInfoReply, }, Body: wire.SNAC_0x02_0x06_LocateUserInfoReply{ TLVUserInfo: wire.TLVUserInfo{ ScreenName: "screen-name", }, LocateInfo: wire.TLVRestBlock{ TLVList: wire.TLVList{ { Tag: 0x01, Value: []byte{1, 2, 3, 4}, }, }, }, }, } svc := newMockLocateService(t) svc.EXPECT(). UserInfoQuery(mock.Anything, mock.Anything, input.Frame, input.Body). Return(output, nil) h := Handler{ LocateService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) responseWriter.EXPECT(). SendSNAC(output.Frame, output.Body). Return(nil) buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) assert.NoError(t, h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{})) } func TestHandler_LocateUserInfoQuery2(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Locate, SubGroup: wire.LocateUserInfoQuery2, }, Body: wire.SNAC_0x02_0x15_LocateUserInfoQuery2{ Type2: 1, }, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Locate, SubGroup: wire.LocateUserInfoReply, }, Body: wire.SNAC_0x02_0x06_LocateUserInfoReply{ TLVUserInfo: wire.TLVUserInfo{ ScreenName: "screen-name", }, LocateInfo: wire.TLVRestBlock{ TLVList: wire.TLVList{ { Tag: 0x01, Value: []byte{1, 2, 3, 4}, }, }, }, }, } svc := newMockLocateService(t) svc.EXPECT(). UserInfoQuery(mock.Anything, mock.Anything, input.Frame, wire.SNAC_0x02_0x05_LocateUserInfoQuery{Type: 1}). Return(output, nil) h := Handler{ LocateService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } responseWriter := newMockResponseWriter(t) responseWriter.EXPECT(). SendSNAC(output.Frame, output.Body). Return(nil) buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) assert.NoError(t, h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, responseWriter, config.Listener{})) } func TestHandler_StatsReportEvents(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Stats, SubGroup: wire.StatsReportEvents, }, Body: wire.SNAC_0x0B_0x03_StatsReportEvents{}, } output := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Stats, SubGroup: wire.StatsReportAck, }, Body: wire.SNAC_0x0B_0x04_StatsReportAck{}, } svc := newMockStatsService(t) svc.EXPECT(). ReportEvents(mock.Anything, input.Frame, input.Body). Return(output) h := Handler{ StatsService: svc, RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } ss := newMockResponseWriter(t) ss.EXPECT(). SendSNAC(output.Frame, output.Body). Return(nil) buf := &bytes.Buffer{} assert.NoError(t, wire.MarshalBE(input.Body, buf)) assert.NoError(t, h.Handle(context.TODO(), wire.BOS, nil, input.Frame, buf, ss, config.Listener{})) } func TestHandler_RouteNotFound(t *testing.T) { input := wire.SNACMessage{ Frame: wire.SNACFrame{ FoodGroup: wire.Stats, SubGroup: math.MaxUint16, }, Body: wire.SNAC_0x0B_0x03_StatsReportEvents{}, } h := Handler{ RouteLogger: middleware.RouteLogger{ Logger: slog.Default(), }, } assert.ErrorIs(t, ErrRouteNotFound, h.Handle(context.TODO(), wire.BOS, nil, input.Frame, nil, nil, config.Listener{})) }