github.com/status-im/status-go@v1.1.0/server/handlers_test.go (about)

     1  package server
     2  
     3  import (
     4  	"database/sql"
     5  	"encoding/json"
     6  	"image/color"
     7  	"net/http"
     8  	"net/http/httptest"
     9  	"net/url"
    10  	"path/filepath"
    11  	"testing"
    12  
    13  	"github.com/status-im/status-go/images"
    14  
    15  	"github.com/status-im/status-go/eth-node/crypto"
    16  
    17  	"github.com/status-im/status-go/common/dbsetup"
    18  	"github.com/status-im/status-go/multiaccounts"
    19  	mc "github.com/status-im/status-go/multiaccounts/common"
    20  
    21  	"github.com/golang/protobuf/proto"
    22  	"github.com/stretchr/testify/suite"
    23  	"go.uber.org/zap"
    24  
    25  	"github.com/status-im/status-go/appdatabase"
    26  	"github.com/status-im/status-go/protocol/common"
    27  	"github.com/status-im/status-go/protocol/protobuf"
    28  	"github.com/status-im/status-go/protocol/sqlite"
    29  	"github.com/status-im/status-go/protocol/tt"
    30  	"github.com/status-im/status-go/t/helpers"
    31  )
    32  
    33  func TestHandlersSuite(t *testing.T) {
    34  	suite.Run(t, new(HandlersSuite))
    35  }
    36  
    37  type HandlersSuite struct {
    38  	suite.Suite
    39  	db     *sql.DB
    40  	logger *zap.Logger
    41  }
    42  
    43  func (s *HandlersSuite) SetupTest() {
    44  	db, err := helpers.SetupTestMemorySQLDB(appdatabase.DbInitializer{})
    45  	s.Require().NoError(err)
    46  
    47  	err = sqlite.Migrate(db)
    48  	s.Require().NoError(err)
    49  
    50  	s.logger = tt.MustCreateTestLogger()
    51  	s.db = db
    52  }
    53  
    54  func (s *HandlersSuite) saveUserMessage(msg *common.Message) {
    55  	whisperTimestamp := 0
    56  	source := ""
    57  	text := ""
    58  	contentType := 0
    59  	timestamp := 0
    60  	chatID := "1"
    61  	localChatID := "1"
    62  	responseTo := ""
    63  	clockValue := 0
    64  
    65  	stmt, err := s.db.Prepare(`
    66  		INSERT INTO user_messages (
    67  			id,
    68  			whisper_timestamp,
    69  			source,
    70  			text,
    71  			content_type,
    72  			timestamp,
    73  			chat_id,
    74  			local_chat_id,
    75  			response_to,
    76  			clock_value,
    77  			unfurled_links,
    78  		    unfurled_status_links
    79  		) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)
    80  	`)
    81  
    82  	s.Require().NoError(err)
    83  
    84  	links := []byte{}
    85  	statusLinks := []byte{}
    86  
    87  	if msg.UnfurledLinks != nil {
    88  		links, err = json.Marshal(msg.UnfurledLinks)
    89  		s.Require().NoError(err)
    90  	}
    91  
    92  	if msg.UnfurledStatusLinks != nil {
    93  		statusLinks, err = proto.Marshal(msg.UnfurledStatusLinks)
    94  		s.Require().NoError(err)
    95  	}
    96  
    97  	_, err = stmt.Exec(
    98  		msg.ID,
    99  		whisperTimestamp,
   100  		source,
   101  		text,
   102  		contentType,
   103  		timestamp,
   104  		chatID,
   105  		localChatID,
   106  		responseTo,
   107  		clockValue,
   108  		links,
   109  		statusLinks,
   110  	)
   111  	s.Require().NoError(err)
   112  }
   113  
   114  func (s *HandlersSuite) httpGetReqRecorder(handler http.HandlerFunc, reqURL string) *httptest.ResponseRecorder {
   115  	req, err := http.NewRequest("GET", reqURL, nil)
   116  	s.Require().NoError(err)
   117  
   118  	rr := httptest.NewRecorder()
   119  	handler.ServeHTTP(rr, req)
   120  
   121  	return rr
   122  }
   123  
   124  func (s *HandlersSuite) verifyHTTPResponseThumbnail(rr *httptest.ResponseRecorder, expectedPayload []byte) {
   125  	s.Require().Equal(expectedPayload, rr.Body.Bytes())
   126  	s.Require().Equal("image/jpeg", rr.HeaderMap.Get("Content-Type"))
   127  	s.Require().Equal("no-store", rr.HeaderMap.Get("Cache-Control"))
   128  }
   129  
   130  func (s *HandlersSuite) TestHandleLinkPreviewThumbnail() {
   131  	previewURL := "https://github.com"
   132  	defaultPayload := []byte{0xff, 0xd8, 0xff, 0xdb, 0x0, 0x84, 0x0, 0x50, 0x37, 0x3c, 0x46, 0x3c, 0x32, 0x50}
   133  
   134  	msg := common.Message{
   135  		ID: "1",
   136  		ChatMessage: &protobuf.ChatMessage{
   137  			UnfurledLinks: []*protobuf.UnfurledLink{
   138  				{
   139  					Type:            protobuf.UnfurledLink_LINK,
   140  					Url:             previewURL,
   141  					ThumbnailWidth:  100,
   142  					ThumbnailHeight: 200,
   143  				},
   144  			},
   145  		},
   146  	}
   147  	s.saveUserMessage(&msg)
   148  
   149  	testCases := []struct {
   150  		Name                   string
   151  		ExpectedHTTPStatusCode int
   152  		ThumbnailPayload       []byte
   153  		Parameters             url.Values
   154  		CheckFunc              func(s *HandlersSuite, rr *httptest.ResponseRecorder)
   155  	}{
   156  		{
   157  			Name:                   "Test happy path",
   158  			ExpectedHTTPStatusCode: http.StatusOK,
   159  			ThumbnailPayload:       defaultPayload,
   160  			Parameters: url.Values{
   161  				"message-id": {msg.ID},
   162  				"url":        {previewURL},
   163  			},
   164  			CheckFunc: func(s *HandlersSuite, rr *httptest.ResponseRecorder) {
   165  				s.verifyHTTPResponseThumbnail(rr, msg.UnfurledLinks[0].ThumbnailPayload)
   166  			},
   167  		},
   168  		{
   169  			Name:                   "Test request with missing 'url' parameter",
   170  			ThumbnailPayload:       defaultPayload,
   171  			ExpectedHTTPStatusCode: http.StatusBadRequest,
   172  			Parameters: url.Values{
   173  				"message-id": {msg.ID},
   174  			},
   175  			CheckFunc: func(s *HandlersSuite, rr *httptest.ResponseRecorder) {
   176  				s.Require().Equal("missing query parameter 'url'\n", rr.Body.String())
   177  			},
   178  		},
   179  		{
   180  			Name:                   "Test request with missing 'message-id' parameter",
   181  			ThumbnailPayload:       defaultPayload,
   182  			ExpectedHTTPStatusCode: http.StatusBadRequest,
   183  			Parameters: url.Values{
   184  				"url": {previewURL},
   185  			},
   186  			CheckFunc: func(s *HandlersSuite, rr *httptest.ResponseRecorder) {
   187  				s.Require().Equal("missing query parameter 'message-id'\n", rr.Body.String())
   188  			},
   189  		},
   190  		{
   191  			Name:                   "Test mime type not supported",
   192  			ThumbnailPayload:       []byte("unsupported image"),
   193  			ExpectedHTTPStatusCode: http.StatusNotImplemented,
   194  			Parameters: url.Values{
   195  				"message-id": {msg.ID},
   196  				"url":        {previewURL},
   197  			},
   198  		},
   199  	}
   200  
   201  	handler := handleLinkPreviewThumbnail(s.db, s.logger)
   202  
   203  	for _, tc := range testCases {
   204  		s.Run(tc.Name, func() {
   205  			msg.UnfurledLinks[0].ThumbnailPayload = tc.ThumbnailPayload
   206  			s.saveUserMessage(&msg)
   207  
   208  			requestURL := "/dummy?" + tc.Parameters.Encode()
   209  			rr := s.httpGetReqRecorder(handler, requestURL)
   210  			s.Require().Equal(tc.ExpectedHTTPStatusCode, rr.Code)
   211  			if tc.CheckFunc != nil {
   212  				tc.CheckFunc(s, rr)
   213  			}
   214  		})
   215  	}
   216  }
   217  
   218  func (s *HandlersSuite) TestHandleStatusLinkPreviewThumbnail() {
   219  	contact := &protobuf.UnfurledStatusContactLink{
   220  		PublicKey: []byte("PublicKey_1"),
   221  		Icon: &protobuf.UnfurledLinkThumbnail{
   222  			Width:   10,
   223  			Height:  20,
   224  			Payload: []byte{0xff, 0xd8, 0xff, 0xdb, 0x0, 0x84, 0x0, 0x50, 0x37, 0x3c, 0x46, 0x3c, 0x32, 0x50},
   225  		},
   226  	}
   227  
   228  	contactWithUnsupportedImage := &protobuf.UnfurledStatusContactLink{
   229  		PublicKey: []byte("PublicKey_2"),
   230  		Icon: &protobuf.UnfurledLinkThumbnail{
   231  			Width:   10,
   232  			Height:  20,
   233  			Payload: []byte("unsupported image"),
   234  		},
   235  	}
   236  
   237  	community := &protobuf.UnfurledStatusCommunityLink{
   238  		CommunityId: []byte("CommunityId_1"),
   239  		Icon: &protobuf.UnfurledLinkThumbnail{
   240  			Width:   30,
   241  			Height:  40,
   242  			Payload: []byte{0xff, 0xd8, 0xff, 0xdb, 0x0, 0x84, 0x0, 0x50, 0x37, 0x3c, 0x46, 0x3c, 0x32, 0x51},
   243  		},
   244  		Banner: &protobuf.UnfurledLinkThumbnail{
   245  			Width:   50,
   246  			Height:  60,
   247  			Payload: []byte{0xff, 0xd8, 0xff, 0xdb, 0x0, 0x84, 0x0, 0x50, 0x37, 0x3c, 0x46, 0x3c, 0x32, 0x52},
   248  		},
   249  	}
   250  
   251  	channel := &protobuf.UnfurledStatusChannelLink{
   252  		ChannelUuid: "ChannelUuid_1",
   253  		Community: &protobuf.UnfurledStatusCommunityLink{
   254  			CommunityId: []byte("CommunityId_2"),
   255  			Icon: &protobuf.UnfurledLinkThumbnail{
   256  				Width:   70,
   257  				Height:  80,
   258  				Payload: []byte{0xff, 0xd8, 0xff, 0xdb, 0x0, 0x84, 0x0, 0x50, 0x37, 0x3c, 0x46, 0x3c, 0x32, 0x53},
   259  			},
   260  			Banner: &protobuf.UnfurledLinkThumbnail{
   261  				Width:   90,
   262  				Height:  100,
   263  				Payload: []byte{0xff, 0xd8, 0xff, 0xdb, 0x0, 0x84, 0x0, 0x50, 0x37, 0x3c, 0x46, 0x3c, 0x32, 0x54},
   264  			},
   265  		},
   266  	}
   267  
   268  	unfurledContact := &protobuf.UnfurledStatusLink{
   269  		Url: "https://status.app/u/",
   270  		Payload: &protobuf.UnfurledStatusLink_Contact{
   271  			Contact: contact,
   272  		},
   273  	}
   274  
   275  	unfurledContactWithUnsupportedImage := &protobuf.UnfurledStatusLink{
   276  		Url: "https://status.app/u/",
   277  		Payload: &protobuf.UnfurledStatusLink_Contact{
   278  			Contact: contactWithUnsupportedImage,
   279  		},
   280  	}
   281  
   282  	unfurledCommunity := &protobuf.UnfurledStatusLink{
   283  		Url: "https://status.app/c/",
   284  		Payload: &protobuf.UnfurledStatusLink_Community{
   285  			Community: community,
   286  		},
   287  	}
   288  
   289  	unfurledChannel := &protobuf.UnfurledStatusLink{
   290  		Url: "https://status.app/cc/",
   291  		Payload: &protobuf.UnfurledStatusLink_Channel{
   292  			Channel: channel,
   293  		},
   294  	}
   295  
   296  	const (
   297  		messageIDContactOnly      = "1"
   298  		messageIDCommunityOnly    = "2"
   299  		messageIDChannelOnly      = "3"
   300  		messageIDAllLinks         = "4"
   301  		messageIDUnsupportedImage = "5"
   302  	)
   303  
   304  	s.saveUserMessage(&common.Message{
   305  		ID: messageIDContactOnly,
   306  		ChatMessage: &protobuf.ChatMessage{
   307  			UnfurledStatusLinks: &protobuf.UnfurledStatusLinks{
   308  				UnfurledStatusLinks: []*protobuf.UnfurledStatusLink{
   309  					unfurledContact,
   310  				},
   311  			},
   312  		},
   313  	})
   314  
   315  	s.saveUserMessage(&common.Message{
   316  		ID: messageIDCommunityOnly,
   317  		ChatMessage: &protobuf.ChatMessage{
   318  			UnfurledStatusLinks: &protobuf.UnfurledStatusLinks{
   319  				UnfurledStatusLinks: []*protobuf.UnfurledStatusLink{
   320  					unfurledCommunity,
   321  				},
   322  			},
   323  		},
   324  	})
   325  
   326  	s.saveUserMessage(&common.Message{
   327  		ID: messageIDChannelOnly,
   328  		ChatMessage: &protobuf.ChatMessage{
   329  			UnfurledStatusLinks: &protobuf.UnfurledStatusLinks{
   330  				UnfurledStatusLinks: []*protobuf.UnfurledStatusLink{
   331  					unfurledChannel,
   332  				},
   333  			},
   334  		},
   335  	})
   336  
   337  	s.saveUserMessage(&common.Message{
   338  		ID: messageIDAllLinks,
   339  		ChatMessage: &protobuf.ChatMessage{
   340  			UnfurledStatusLinks: &protobuf.UnfurledStatusLinks{
   341  				UnfurledStatusLinks: []*protobuf.UnfurledStatusLink{
   342  					unfurledContact,
   343  					unfurledCommunity,
   344  					unfurledChannel,
   345  				},
   346  			},
   347  		},
   348  	})
   349  
   350  	s.saveUserMessage(&common.Message{
   351  		ID: messageIDUnsupportedImage,
   352  		ChatMessage: &protobuf.ChatMessage{
   353  			UnfurledStatusLinks: &protobuf.UnfurledStatusLinks{
   354  				UnfurledStatusLinks: []*protobuf.UnfurledStatusLink{
   355  					unfurledContactWithUnsupportedImage,
   356  				},
   357  			},
   358  		},
   359  	})
   360  
   361  	testCases := []struct {
   362  		Name                   string
   363  		ExpectedHTTPStatusCode int
   364  		Parameters             url.Values
   365  		CheckFunc              func(s *HandlersSuite, rr *httptest.ResponseRecorder)
   366  	}{
   367  		{
   368  			Name: "Test valid contact icon link",
   369  			Parameters: url.Values{
   370  				"message-id": {messageIDContactOnly},
   371  				"url":        {unfurledContact.Url},
   372  				"image-id":   {string(common.MediaServerContactIcon)},
   373  			},
   374  			ExpectedHTTPStatusCode: http.StatusOK,
   375  			CheckFunc: func(s *HandlersSuite, rr *httptest.ResponseRecorder) {
   376  				s.verifyHTTPResponseThumbnail(rr, unfurledContact.GetContact().Icon.Payload)
   377  			},
   378  		},
   379  		{
   380  			Name: "Test invalid request for community icon in a contact link",
   381  			Parameters: url.Values{
   382  				"message-id": {messageIDContactOnly},
   383  				"url":        {unfurledContact.Url},
   384  				"image-id":   {string(common.MediaServerCommunityIcon)},
   385  			},
   386  			ExpectedHTTPStatusCode: http.StatusBadRequest,
   387  			CheckFunc: func(s *HandlersSuite, rr *httptest.ResponseRecorder) {
   388  				s.Require().Equal("invalid query parameter 'image-id' value: this is not a community link\n", rr.Body.String())
   389  			},
   390  		},
   391  		{
   392  			Name: "Test invalid request for cahnnel community banner in a contact link",
   393  			Parameters: url.Values{
   394  				"message-id": {messageIDContactOnly},
   395  				"url":        {unfurledContact.Url},
   396  				"image-id":   {string(common.MediaServerChannelCommunityBanner)},
   397  			},
   398  			ExpectedHTTPStatusCode: http.StatusBadRequest,
   399  			CheckFunc: func(s *HandlersSuite, rr *httptest.ResponseRecorder) {
   400  				s.Require().Equal("invalid query parameter 'image-id' value: this is not a community channel link\n", rr.Body.String())
   401  			},
   402  		},
   403  		{
   404  			Name: "Test invalid request for channel community banner in a contact link",
   405  			Parameters: url.Values{
   406  				"message-id": {messageIDContactOnly},
   407  				"url":        {unfurledContact.Url},
   408  				"image-id":   {"contact-banner"},
   409  			},
   410  			ExpectedHTTPStatusCode: http.StatusBadRequest,
   411  			CheckFunc: func(s *HandlersSuite, rr *httptest.ResponseRecorder) {
   412  				s.Require().Equal("invalid query parameter 'image-id' value: value not supported\n", rr.Body.String())
   413  			},
   414  		},
   415  		{
   416  			Name: "Test valid community icon link",
   417  			Parameters: url.Values{
   418  				"message-id": {messageIDCommunityOnly},
   419  				"url":        {unfurledCommunity.Url},
   420  				"image-id":   {string(common.MediaServerCommunityIcon)},
   421  			},
   422  			ExpectedHTTPStatusCode: http.StatusOK,
   423  			CheckFunc: func(s *HandlersSuite, rr *httptest.ResponseRecorder) {
   424  				s.verifyHTTPResponseThumbnail(rr, unfurledCommunity.GetCommunity().Icon.Payload)
   425  			},
   426  		},
   427  		{
   428  			Name: "Test valid community banner link",
   429  			Parameters: url.Values{
   430  				"message-id": {messageIDCommunityOnly},
   431  				"url":        {unfurledCommunity.Url},
   432  				"image-id":   {string(common.MediaServerCommunityBanner)},
   433  			},
   434  			ExpectedHTTPStatusCode: http.StatusOK,
   435  			CheckFunc: func(s *HandlersSuite, rr *httptest.ResponseRecorder) {
   436  				s.verifyHTTPResponseThumbnail(rr, unfurledCommunity.GetCommunity().Banner.Payload)
   437  			},
   438  		},
   439  		{
   440  			Name: "Test valid channel community icon link",
   441  			Parameters: url.Values{
   442  				"message-id": {messageIDChannelOnly},
   443  				"url":        {unfurledChannel.Url},
   444  				"image-id":   {string(common.MediaServerChannelCommunityIcon)},
   445  			},
   446  			ExpectedHTTPStatusCode: http.StatusOK,
   447  			CheckFunc: func(s *HandlersSuite, rr *httptest.ResponseRecorder) {
   448  				s.verifyHTTPResponseThumbnail(rr, unfurledChannel.GetChannel().GetCommunity().Icon.Payload)
   449  			},
   450  		},
   451  		{
   452  			Name: "Test valid channel community banner link",
   453  			Parameters: url.Values{
   454  				"message-id": {messageIDChannelOnly},
   455  				"url":        {unfurledChannel.Url},
   456  				"image-id":   {string(common.MediaServerChannelCommunityBanner)},
   457  			},
   458  			ExpectedHTTPStatusCode: http.StatusOK,
   459  			CheckFunc: func(s *HandlersSuite, rr *httptest.ResponseRecorder) {
   460  				s.verifyHTTPResponseThumbnail(rr, unfurledChannel.GetChannel().GetCommunity().Banner.Payload)
   461  			},
   462  		},
   463  		{
   464  			Name: "Test valid contact icon link in a diverse message",
   465  			Parameters: url.Values{
   466  				"message-id": {messageIDAllLinks},
   467  				"url":        {unfurledContact.Url},
   468  				"image-id":   {string(common.MediaServerContactIcon)},
   469  			},
   470  			ExpectedHTTPStatusCode: http.StatusOK,
   471  			CheckFunc: func(s *HandlersSuite, rr *httptest.ResponseRecorder) {
   472  				s.verifyHTTPResponseThumbnail(rr, unfurledContact.GetContact().Icon.Payload)
   473  			},
   474  		},
   475  		{
   476  			Name: "Test valid community icon link in a diverse message",
   477  			Parameters: url.Values{
   478  				"message-id": {messageIDAllLinks},
   479  				"url":        {unfurledCommunity.Url},
   480  				"image-id":   {string(common.MediaServerCommunityIcon)},
   481  			},
   482  			ExpectedHTTPStatusCode: http.StatusOK,
   483  			CheckFunc: func(s *HandlersSuite, rr *httptest.ResponseRecorder) {
   484  				s.verifyHTTPResponseThumbnail(rr, unfurledCommunity.GetCommunity().Icon.Payload)
   485  			},
   486  		},
   487  		{
   488  			Name: "Test valid channel community icon link in a diverse message",
   489  			Parameters: url.Values{
   490  				"message-id": {messageIDAllLinks},
   491  				"url":        {unfurledChannel.Url},
   492  				"image-id":   {string(common.MediaServerChannelCommunityIcon)},
   493  			},
   494  			ExpectedHTTPStatusCode: http.StatusOK,
   495  			CheckFunc: func(s *HandlersSuite, rr *httptest.ResponseRecorder) {
   496  				s.verifyHTTPResponseThumbnail(rr, unfurledChannel.GetChannel().GetCommunity().Icon.Payload)
   497  			},
   498  		},
   499  		{
   500  			Name: "Test mime type not supported",
   501  			Parameters: url.Values{
   502  				"message-id": {messageIDUnsupportedImage},
   503  				"url":        {unfurledContactWithUnsupportedImage.Url},
   504  				"image-id":   {string(common.MediaServerContactIcon)},
   505  			},
   506  			ExpectedHTTPStatusCode: http.StatusNotImplemented,
   507  		},
   508  		{
   509  			Name: "Test request with missing 'message-id' parameter",
   510  			Parameters: url.Values{
   511  				"url":      {unfurledCommunity.Url},
   512  				"image-id": {string(common.MediaServerCommunityIcon)},
   513  			},
   514  			ExpectedHTTPStatusCode: http.StatusBadRequest,
   515  			CheckFunc: func(s *HandlersSuite, rr *httptest.ResponseRecorder) {
   516  				s.Require().Equal("missing query parameter 'message-id'\n", rr.Body.String())
   517  			},
   518  		},
   519  		{
   520  			Name: "Test request with missing 'url' parameter",
   521  			Parameters: url.Values{
   522  				"message-id": {messageIDCommunityOnly},
   523  				"image-id":   {string(common.MediaServerCommunityIcon)},
   524  			},
   525  			ExpectedHTTPStatusCode: http.StatusBadRequest,
   526  			CheckFunc: func(s *HandlersSuite, rr *httptest.ResponseRecorder) {
   527  				s.Require().Equal("missing query parameter 'url'\n", rr.Body.String())
   528  			},
   529  		},
   530  		{
   531  			Name: "Test request with missing 'image-id' parameter",
   532  			Parameters: url.Values{
   533  				"message-id": {messageIDCommunityOnly},
   534  				"url":        {unfurledCommunity.Url},
   535  			},
   536  			ExpectedHTTPStatusCode: http.StatusBadRequest,
   537  			CheckFunc: func(s *HandlersSuite, rr *httptest.ResponseRecorder) {
   538  				s.Require().Equal("missing query parameter 'image-id'\n", rr.Body.String())
   539  			},
   540  		},
   541  	}
   542  
   543  	handler := handleStatusLinkPreviewThumbnail(s.db, s.logger)
   544  
   545  	for _, tc := range testCases {
   546  		s.Run(tc.Name, func() {
   547  			requestURL := "/dummy?" + tc.Parameters.Encode()
   548  
   549  			rr := s.httpGetReqRecorder(handler, requestURL)
   550  			s.Require().Equal(tc.ExpectedHTTPStatusCode, rr.Code)
   551  
   552  			if tc.CheckFunc != nil {
   553  				tc.CheckFunc(s, rr)
   554  			}
   555  		})
   556  	}
   557  }
   558  
   559  func (s *HandlersSuite) validateResponse(w *httptest.ResponseRecorder) {
   560  	s.Require().Equal(http.StatusOK, w.Code)
   561  	s.Require().Equal("image/png", w.Header().Get("Content-Type"))
   562  	n, err := w.Result().Body.Read(make([]byte, 100))
   563  	s.Require().NoError(err)
   564  	s.Require().Greater(n, 0)
   565  }
   566  
   567  // TestHandleAccountInitialsImpl tests the handleAccountInitialsImpl function
   568  func (s *HandlersSuite) TestHandleAccountInitialsImpl() {
   569  	// given an account without public key, and request to generate ring with keyUID of the account,
   570  	// it should still response with a valid image without ring rather than response with empty image
   571  	dbFile := filepath.Join(s.T().TempDir(), "accounts-tests-")
   572  	db, err := multiaccounts.InitializeDB(dbFile)
   573  	s.Require().NoError(err)
   574  	defer db.Close()
   575  	keyUID := "0x1"
   576  	name := "Lopsided Goodnatured Bedbug"
   577  	expected := multiaccounts.Account{Name: name, KeyUID: keyUID, CustomizationColor: mc.CustomizationColorBlue, ColorHash: nil, ColorID: 10, KDFIterations: dbsetup.ReducedKDFIterationsNumber, Timestamp: 1712856359}
   578  	s.Require().NoError(db.SaveAccount(expected))
   579  	accounts, err := db.GetAccounts()
   580  	s.Require().NoError(err)
   581  	s.Require().Len(accounts, 1)
   582  	s.Require().Equal(expected, accounts[0])
   583  
   584  	w := httptest.NewRecorder()
   585  	f, err := filepath.Abs("../_assets/tests/UbuntuMono-Regular.ttf")
   586  	s.Require().NoError(err)
   587  	p := ImageParams{
   588  		Ring:           true,
   589  		RingWidth:      1,
   590  		KeyUID:         keyUID,
   591  		InitialsLength: 2,
   592  		BgColor:        color.Transparent,
   593  		Color:          color.Transparent,
   594  		FontFile:       f,
   595  		BgSize:         1,
   596  		FontSize:       1,
   597  		UppercaseRatio: 1.0,
   598  	}
   599  	handleAccountInitialsImpl(db, s.logger, w, p)
   600  	s.validateResponse(w)
   601  
   602  	// pass a public key to generate ring
   603  	k, err := crypto.GenerateKey()
   604  	s.Require().NoError(err)
   605  	p.PublicKey = common.PubkeyToHex(&k.PublicKey)
   606  	w = httptest.NewRecorder()
   607  	handleAccountInitialsImpl(db, s.logger, w, p)
   608  	s.Require().Equal(http.StatusOK, w.Code)
   609  }
   610  
   611  // TestHandleAccountImagesImpl tests the handleAccountImagesImpl function
   612  func (s *HandlersSuite) TestHandleAccountImagesImpl() {
   613  	// given an account with identity images and without public key, and request to generate ring with keyUID of the account,
   614  	// it should still response with a valid image without ring rather than response with empty image
   615  	dbFile := filepath.Join(s.T().TempDir(), "accounts-tests-")
   616  	db, err := multiaccounts.InitializeDB(dbFile)
   617  	s.Require().NoError(err)
   618  	defer db.Close()
   619  	keyUID := "0x1"
   620  	name := "Lopsided Goodnatured Bedbug"
   621  	expected := multiaccounts.Account{
   622  		Name:               name,
   623  		KeyUID:             keyUID,
   624  		CustomizationColor: mc.CustomizationColorBlue,
   625  		ColorHash:          nil,
   626  		ColorID:            10,
   627  		KDFIterations:      dbsetup.ReducedKDFIterationsNumber,
   628  		Timestamp:          1712856359,
   629  		Images:             images.SampleIdentityImageForQRCode(),
   630  	}
   631  	s.Require().NoError(db.SaveAccount(expected))
   632  	accounts, err := db.GetAccounts()
   633  	s.Require().NoError(err)
   634  	s.Require().Len(accounts, 1)
   635  	s.Require().Equal(expected, accounts[0])
   636  
   637  	w := httptest.NewRecorder()
   638  	f, err := filepath.Abs("../_assets/tests/UbuntuMono-Regular.ttf")
   639  	s.Require().NoError(err)
   640  	p := ImageParams{
   641  		Ring:           true,
   642  		RingWidth:      1,
   643  		KeyUID:         keyUID,
   644  		InitialsLength: 2,
   645  		BgColor:        color.Transparent,
   646  		Color:          color.Transparent,
   647  		FontFile:       f,
   648  		BgSize:         1,
   649  		FontSize:       1,
   650  		UppercaseRatio: 1.0,
   651  		ImageName:      images.LargeDimName,
   652  	}
   653  	handleAccountImagesImpl(db, s.logger, w, p)
   654  	s.validateResponse(w)
   655  
   656  	// pass a public key to generate ring
   657  	k, err := crypto.GenerateKey()
   658  	s.Require().NoError(err)
   659  	p.PublicKey = common.PubkeyToHex(&k.PublicKey)
   660  	w = httptest.NewRecorder()
   661  	handleAccountImagesImpl(db, s.logger, w, p)
   662  	s.Require().Equal(http.StatusOK, w.Code)
   663  }