| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 |
- package handlers
- import (
- "log/slog"
- "net/http"
- "net/http/httptest"
- "strings"
- "testing"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/mock"
- "github.com/mk6i/open-oscar-server/state"
- "github.com/mk6i/open-oscar-server/wire"
- )
- // buddyPrefsFeedbag builds a feedbag reply carrying a single buddy-prefs item
- // with the given prefs pre-set.
- func buddyPrefsFeedbag(prefs map[uint16]bool) wire.SNACMessage {
- var list wire.TLVList
- for num, val := range prefs {
- list = wire.SetBuddyPref(list, num, val)
- }
- return wire.SNACMessage{
- Body: wire.SNAC_0x13_0x06_FeedbagReply{
- Items: []wire.FeedbagItem{
- {ClassID: wire.FeedbagClassIdBuddyPrefs, ItemID: 1, TLVLBlock: wire.TLVLBlock{TLVList: list}},
- },
- },
- }
- }
- func TestPreferenceHandler_SetPreferences(t *testing.T) {
- fs := &MockFeedbagService{}
- oscarInstance := state.NewSession().AddInstance()
- sessionMgr, aimsid := createTestSessionManagerWithOSCAR("testuser", oscarInstance)
- // Existing feedbag already has displayLogin (0x00) enabled; it must survive.
- fs.On("Query", mock.Anything, oscarInstance, mock.Anything).
- Return(buddyPrefsFeedbag(map[uint16]bool{0x00: true}), nil)
- var upserted []wire.FeedbagItem
- fs.On("UpsertItem", mock.Anything, oscarInstance, mock.Anything, mock.Anything).
- Run(func(args mock.Arguments) { upserted = args.Get(3).([]wire.FeedbagItem) }).
- Return(nil, nil)
- handler := &PreferenceHandler{
- SessionManager: sessionMgr,
- FeedbagService: fs,
- Logger: slog.Default(),
- }
- req, _ := http.NewRequest("GET", "/preference/set?aimsid="+aimsid+"&playIMSound=0&discloseTyping=1", nil)
- rr := httptest.NewRecorder()
- handler.SetPreferences(rr, req)
- assert.Equal(t, http.StatusOK, rr.Code)
- if assert.Len(t, upserted, 1) {
- item := upserted[0]
- assert.Equal(t, wire.FeedbagClassIdBuddyPrefs, item.ClassID)
- assertPref := func(num uint16, wantValid, wantValue bool) {
- valid, value := wire.BuddyPref(item.TLVList, num)
- assert.Equalf(t, wantValid, valid, "pref 0x%02x valid", num)
- assert.Equalf(t, wantValue, value, "pref 0x%02x value", num)
- }
- assertPref(0x00, true, true) // preserved
- assertPref(0x15, true, false) // playIMSound off
- assertPref(0x16, true, true) // discloseTyping on
- }
- fs.AssertExpectations(t)
- }
- func TestPreferenceHandler_GetPreferences_Selected(t *testing.T) {
- fs := &MockFeedbagService{}
- oscarInstance := state.NewSession().AddInstance()
- sessionMgr, aimsid := createTestSessionManagerWithOSCAR("testuser", oscarInstance)
- // playIMSound (0x15) explicitly disabled in the feedbag.
- fs.On("Query", mock.Anything, oscarInstance, mock.Anything).
- Return(buddyPrefsFeedbag(map[uint16]bool{0x15: false}), nil)
- handler := &PreferenceHandler{
- SessionManager: sessionMgr,
- FeedbagService: fs,
- Logger: slog.Default(),
- }
- // Request two prefs: playIMSound (stored=false) and acceptIcons (unset -> default true).
- req, _ := http.NewRequest("GET", "/preference/get?aimsid="+aimsid+"&playIMSound&acceptIcons", nil)
- rr := httptest.NewRecorder()
- handler.GetPreferences(rr, req)
- assert.Equal(t, http.StatusOK, rr.Code)
- body := rr.Body.String()
- assert.Contains(t, body, `"playIMSound":0`)
- assert.Contains(t, body, `"acceptIcons":1`)
- // Only the two requested prefs should be present.
- assert.NotContains(t, body, `"displayLogin"`)
- }
- func TestPreferenceHandler_GetPreferences_All(t *testing.T) {
- fs := &MockFeedbagService{}
- oscarInstance := state.NewSession().AddInstance()
- sessionMgr, aimsid := createTestSessionManagerWithOSCAR("testuser", oscarInstance)
- // Empty feedbag -> every pref resolves to its spec default.
- fs.On("Query", mock.Anything, oscarInstance, mock.Anything).
- Return(wire.SNACMessage{Body: wire.SNAC_0x13_0x06_FeedbagReply{}}, nil)
- handler := &PreferenceHandler{
- SessionManager: sessionMgr,
- FeedbagService: fs,
- Logger: slog.Default(),
- }
- req, _ := http.NewRequest("GET", "/preference/get?aimsid="+aimsid, nil)
- rr := httptest.NewRecorder()
- handler.GetPreferences(rr, req)
- assert.Equal(t, http.StatusOK, rr.Code)
- body := rr.Body.String()
- assert.Contains(t, body, `"displayLogin":1`) // default true
- assert.Contains(t, body, `"acceptCustomBart":0`) // default false
- }
- func TestPreferenceHandler_GetPreferences_AMF(t *testing.T) {
- fs := &MockFeedbagService{}
- oscarInstance := state.NewSession().AddInstance()
- sessionMgr, aimsid := createTestSessionManagerWithOSCAR("testuser", oscarInstance)
- fs.On("Query", mock.Anything, oscarInstance, mock.Anything).
- Return(buddyPrefsFeedbag(map[uint16]bool{0x15: false}), nil)
- handler := &PreferenceHandler{
- SessionManager: sessionMgr,
- FeedbagService: fs,
- Logger: slog.Default(),
- }
- // Single-pref AMF request returns a numeric value (not wrapped in jsonData).
- req, _ := http.NewRequest("GET", "/preference/get?aimsid="+aimsid+"&f=amf&playIMSound", nil)
- rr := httptest.NewRecorder()
- handler.GetPreferences(rr, req)
- assert.Equal(t, http.StatusOK, rr.Code)
- body := rr.Body.String()
- // AMF-encoded response: the value is a numeric 0 (AMF integer marker 0x04
- // followed by 0x00), not the string "0".
- assert.Contains(t, body, "playIMSound\x04\x00")
- assert.NotContains(t, body, `playIMSound":"0"`)
- }
- func TestValidBuddyPrefs_OnlyValidBits(t *testing.T) {
- // Only playIMSound (set false) and viewIMsInBubbles (set true) are valid;
- // everything else must be omitted rather than defaulted.
- var list wire.TLVList
- list = wire.SetBuddyPref(list, wire.FeedbagBuddyPrefsPlayIMSound, false)
- list = wire.SetBuddyPref(list, wire.FeedbagBuddyPrefsViewIMsInBubbles, true)
- got := validBuddyPrefs(list)
- assert.Equal(t, map[string]interface{}{
- "playIMSound": 0,
- "viewIMsInBubbles": 1,
- }, got)
- }
- func TestValidBuddyPrefs_EmptyWhenNothingSet(t *testing.T) {
- assert.Empty(t, validBuddyPrefs(wire.TLVList{}))
- }
- func TestPreferenceHandler_SetPreferences_NoOSCARSession(t *testing.T) {
- fs := &MockFeedbagService{}
- sessionMgr, aimsid := createTestSessionManager("webonly") // nil OSCARSession
- handler := &PreferenceHandler{
- SessionManager: sessionMgr,
- FeedbagService: fs,
- Logger: slog.Default(),
- }
- req, _ := http.NewRequest("GET", "/preference/set?aimsid="+aimsid+"&playIMSound=1", nil)
- rr := httptest.NewRecorder()
- handler.SetPreferences(rr, req)
- assert.Equal(t, http.StatusBadRequest, rr.Code)
- fs.AssertNotCalled(t, "Query", mock.Anything, mock.Anything, mock.Anything)
- _ = strings.TrimSpace(rr.Body.String())
- }
|