diff --git a/error_test.go b/error_test.go index 3e62159..913e245 100644 --- a/error_test.go +++ b/error_test.go @@ -12,6 +12,13 @@ func getTestError(str string) *Error { } } +func getTestBasicError(str string) *Error { + return &Error{ + StatusCode: 500, + ErrorString: str, + } +} + func TestError(t *testing.T) { tests := []struct { name string diff --git a/internal/transport/http.go b/internal/transport/http.go index 2a3c712..35bb3c4 100644 --- a/internal/transport/http.go +++ b/internal/transport/http.go @@ -14,6 +14,7 @@ type HTTPRequest struct { Method string URL string PostPayload []byte + PutPayload []byte } // HTTPResponse represents a http response. @@ -36,8 +37,17 @@ func (c *Client) Request(ctx context.Context, r *HTTPRequest) ([]byte, int, erro var req *http.Request switch r.Method { - case http.MethodGet: + case http.MethodGet, + http.MethodDelete: req, err = http.NewRequest(r.Method, r.URL, nil) + + case http.MethodPut: + if r.PutPayload == nil { + req, err = http.NewRequest(r.Method, r.URL, nil) + } else { + req, err = http.NewRequest(r.Method, r.URL, bytes.NewBuffer(r.PutPayload)) + req.Header.Set("Content-Type", "application/json") + } case http.MethodPost: if r.PostPayload == nil { return nil, 0, errors.New("Post payload missing from POST") diff --git a/mailbox.go b/mailbox.go new file mode 100644 index 0000000..47ba3d2 --- /dev/null +++ b/mailbox.go @@ -0,0 +1,390 @@ +package sbanken + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "net/url" + "strconv" + "time" + + "github.com/engvik/sbanken-go/internal/transport" +) + +// MailboxMessage represents a mailbox message. +// Sbanken API documentation: https://publicapi.sbanken.no/openapi/apibeta/index.html?urls.primaryName=API%20Beta%20V2#/Mailbox +type MailboxMessage struct { + Subject string `json:"subject"` + Category string `json:"category"` + Source string `json:"source"` + Status string `json:"status"` + LinkName string `json:"linkName"` + LinkURL string `json:"linkUrl"` + ReceivedDate string `json:"receivedDate"` + ID int `json:"id"` + Flag int `json:"flag"` + AttachmentID int `json:"attachmentId"` +} + +// MailboxQuery represents query parameteres for querying the mailbox. +type MailboxListQuery struct { + StartDate time.Time + EndDate time.Time + Status string + Index string + Length string +} + +func (q *MailboxListQuery) QueryString(u string) (string, error) { + parsedURL, err := url.Parse(u) + if err != nil { + return u, err + } + + query := parsedURL.Query() + + if !q.StartDate.IsZero() { + query.Add("startDate", q.StartDate.Format(time.RFC3339)) + } + + if !q.EndDate.IsZero() { + query.Add("endDate", q.EndDate.Format(time.RFC3339)) + } + + if q.Status != "" { + query.Add("status", q.Status) + } + + if q.Index != "" { + query.Add("index", q.Index) + } + + if q.Length != "" { + query.Add("length", q.Length) + } + + return query.Encode(), nil +} + +type MailboxCountQuery struct { + StartDate time.Time + EndDate time.Time + Status string +} + +func (q *MailboxCountQuery) QueryString(u string) (string, error) { + parsedURL, err := url.Parse(u) + if err != nil { + return u, err + } + + query := parsedURL.Query() + + if !q.StartDate.IsZero() { + query.Add("startDate", q.StartDate.Format(time.RFC3339)) + } + + if !q.EndDate.IsZero() { + query.Add("endDate", q.EndDate.Format(time.RFC3339)) + } + + if q.Status != "" { + query.Add("status", q.Status) + } + + return query.Encode(), nil +} + +type MailboxAttachment struct { + ID int `json:"attachmentID"` + Name string `json:"name"` + Attachment string `json:"attachment"` +} + +func (c *Client) ListArchiveMessages(ctx context.Context, q *MailboxListQuery) ([]MailboxMessage, error) { + url := fmt.Sprintf("%s/v2/Mailbox/archive", c.bankBaseURL) + + return c.listMessages(ctx, url, q, "ListArchiveMessages") +} + +func (c *Client) ListInboxMessages(ctx context.Context, q *MailboxListQuery) ([]MailboxMessage, error) { + url := fmt.Sprintf("%s/v2/Mailbox/inbox", c.bankBaseURL) + + return c.listMessages(ctx, url, q, "ListInboxMessages") +} + +func (c *Client) listMessages(ctx context.Context, url string, q *MailboxListQuery, caller string) ([]MailboxMessage, error) { + if q != nil { + qs, err := q.QueryString(url) + if err != nil { + return nil, fmt.Errorf("QueryString: %w", err) + } + + url = fmt.Sprintf("%s?%s", url, qs) + } + + res, sc, err := c.transport.Request(ctx, &transport.HTTPRequest{ + Method: http.MethodGet, + URL: url, + }) + if err != nil { + return nil, fmt.Errorf("request: %w", err) + } + + data := struct { + Messages []MailboxMessage `json:"items"` + transport.HTTPResponse + }{} + + if err := json.Unmarshal(res, &data); err != nil { + return data.Messages, fmt.Errorf("Unmarshal: %w", err) + } + + if data.IsError || sc != http.StatusOK { + return data.Messages, &Error{ + caller, + data.ErrorType, + data.ErrorMessage, + data.ErrorCode, + sc, + } + } + + return data.Messages, nil +} + +func (c *Client) ReadArchiveMessage(ctx context.Context, id int) (MailboxMessage, error) { + url := fmt.Sprintf("%s/v2/Mailbox/archive/%d", c.bankBaseURL, id) + + return c.readMessage(ctx, url, "ReadArchiveMessage") +} + +func (c *Client) ReadInboxMessage(ctx context.Context, id int) (MailboxMessage, error) { + url := fmt.Sprintf("%s/v2/Mailbox/inbox/%d", c.bankBaseURL, id) + + return c.readMessage(ctx, url, "ReadInboxMessage") +} + +func (c *Client) readMessage(ctx context.Context, url string, caller string) (MailboxMessage, error) { + res, sc, err := c.transport.Request(ctx, &transport.HTTPRequest{ + Method: http.MethodGet, + URL: url, + }) + if err != nil { + return MailboxMessage{}, fmt.Errorf("request: %w", err) + } + + var data MailboxMessage + + if err := json.Unmarshal(res, &data); err != nil { + return data, fmt.Errorf("Unmarshal: %w", err) + } + + if sc != http.StatusOK { + return data, &Error{ + caller, + "", + "", + 0, + sc, + } + } + + return data, nil +} + +func (c *Client) DeleteArchiveMessage(ctx context.Context, id int) error { + url := fmt.Sprintf("%s/v2/Mailbox/archive/%d", c.bankBaseURL, id) + return c.deleteMessage(ctx, url, "DeleteArchiveMessage") +} + +func (c *Client) DeleteInboxMessage(ctx context.Context, id int) error { + url := fmt.Sprintf("%s/v2/Mailbox/inbox/%d", c.bankBaseURL, id) + return c.deleteMessage(ctx, url, "DeleteInboxMessage") +} + +func (c *Client) deleteMessage(ctx context.Context, url string, caller string) error { + _, sc, err := c.transport.Request(ctx, &transport.HTTPRequest{ + Method: http.MethodDelete, + URL: url, + }) + if err != nil { + return fmt.Errorf("request: %w", err) + } + + if sc != http.StatusNoContent { + return &Error{ + caller, + "", + "", + 0, + sc, + } + } + + return nil +} + +func (c *Client) MoveArchiveMessage(ctx context.Context, id int) error { + url := fmt.Sprintf("%s/v2/Mailbox/archive/%d/move", c.bankBaseURL, id) + return c.moveMessage(ctx, url, "MoveArchiveMessage") +} + +func (c *Client) MoveInboxMessage(ctx context.Context, id int) error { + url := fmt.Sprintf("%s/v2/Mailbox/inbox/%d/move", c.bankBaseURL, id) + return c.moveMessage(ctx, url, "MoveInboxMessage") +} + +func (c *Client) moveMessage(ctx context.Context, url string, caller string) error { + _, sc, err := c.transport.Request(ctx, &transport.HTTPRequest{ + Method: http.MethodPut, + URL: url, + }) + if err != nil { + return fmt.Errorf("request: %w", err) + } + + if sc != http.StatusOK { + return &Error{ + caller, + "", + "", + 0, + sc, + } + } + + return nil +} + +func (c *Client) UpdateArchiveMessageReadStatus(ctx context.Context, id int, read bool) error { + url := fmt.Sprintf("%s/v2/Mailbox/archive/%d/readstatus", c.bankBaseURL, id) + return c.updateReadStatus(ctx, url, read, "UpdateArchiveMessageReadStatus") +} + +func (c *Client) UpdateInboxMessageReadStatus(ctx context.Context, id int, read bool) error { + url := fmt.Sprintf("%s/v2/Mailbox/inbox/%d/readstatus", c.bankBaseURL, id) + return c.updateReadStatus(ctx, url, read, "UpdateInboxMessageReadStatus") +} + +func (c *Client) updateReadStatus(ctx context.Context, url string, status bool, caller string) error { + var readStatus string + + if status { + readStatus = "Read" + } else { + readStatus = "Unread" + } + + data := struct { + Status string `json:"value"` + }{ + Status: readStatus, + } + + payload, err := json.Marshal(data) + if err != nil { + return fmt.Errorf("Marshal: %w", err) + } + + _, sc, err := c.transport.Request(ctx, &transport.HTTPRequest{ + Method: http.MethodPut, + URL: url, + PutPayload: payload, + }) + if err != nil { + return fmt.Errorf("request: %w", err) + } + + if sc != http.StatusNoContent { + return &Error{ + caller, + "", + "", + 0, + sc, + } + } + + return nil +} + +func (c *Client) GetArchiveMessageCount(ctx context.Context, q *MailboxCountQuery) (int, error) { + url := fmt.Sprintf("%s/v2/Mailbox/archive/count", c.bankBaseURL) + return c.getMessageCount(ctx, url, q, "GetArchiveMessageCount") +} + +func (c *Client) GetInboxMessageCount(ctx context.Context, q *MailboxCountQuery) (int, error) { + url := fmt.Sprintf("%s/v2/Mailbox/inbox/count", c.bankBaseURL) + return c.getMessageCount(ctx, url, q, "GetInboxMessageCount") +} + +func (c *Client) getMessageCount(ctx context.Context, url string, q *MailboxCountQuery, caller string) (int, error) { + if q != nil { + qs, err := q.QueryString(url) + if err != nil { + return 0, fmt.Errorf("QueryString: %w", err) + } + + url = fmt.Sprintf("%s?%s", url, qs) + } + + res, sc, err := c.transport.Request(ctx, &transport.HTTPRequest{ + Method: http.MethodGet, + URL: url, + }) + if err != nil { + return 0, fmt.Errorf("request: %w", err) + } + + if sc != http.StatusOK { + return 0, &Error{ + caller, + "", + "", + 0, + sc, + } + } + + return strconv.Atoi(string(res)) +} + +func (c *Client) ReadArchiveAttachment(ctx context.Context, messageID int, attachmentID int) (MailboxAttachment, error) { + url := fmt.Sprintf("%s/v2/Mailbox/archive/%d/attachment/%d", c.bankBaseURL, messageID, attachmentID) + return c.readAttachment(ctx, url, "ReadArchiveAttachment") +} + +func (c *Client) ReadInboxAttachment(ctx context.Context, messageID int, attachmentID int) (MailboxAttachment, error) { + url := fmt.Sprintf("%s/v2/Mailbox/inbox/%d/attachment/%d", c.bankBaseURL, messageID, attachmentID) + return c.readAttachment(ctx, url, "ReadInboxAttachment") +} + +func (c *Client) readAttachment(ctx context.Context, url string, caller string) (MailboxAttachment, error) { + res, sc, err := c.transport.Request(ctx, &transport.HTTPRequest{ + Method: http.MethodGet, + URL: url, + }) + if err != nil { + return MailboxAttachment{}, fmt.Errorf("request: %w", err) + } + + var data MailboxAttachment + + if err := json.Unmarshal(res, &data); err != nil { + return data, fmt.Errorf("Unmarshal: %w", err) + } + + if sc != http.StatusOK { + return data, &Error{ + caller, + "", + "", + 0, + sc, + } + } + + return data, nil +} diff --git a/mailbox_test.go b/mailbox_test.go new file mode 100644 index 0000000..b385a8b --- /dev/null +++ b/mailbox_test.go @@ -0,0 +1,732 @@ +package sbanken + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "net/url" + "reflect" + "testing" + "time" + + "github.com/engvik/sbanken-go/internal/transport" +) + +var testMailboxMessage = MailboxMessage{ + Subject: "Test Subject", + Category: "category", + Source: "source", + LinkName: "link-name", + LinkURL: "https://example.com", + ReceivedDate: time.Now().String(), + ID: 1337, + Flag: 1, + AttachmentID: 7331, +} + +func testListMessagesEndpointResponse(behavior string) ([]byte, int, error) { + d := struct { + Messages []MailboxMessage `json:"items"` + transport.HTTPResponse + }{ + Messages: []MailboxMessage{testMailboxMessage}, + } + + if behavior == "fail" { + d.IsError = testHTTPResponseError.IsError + d.ErrorCode = testHTTPResponseError.ErrorCode + d.ErrorMessage = testHTTPResponseError.ErrorMessage + d.ErrorType = testHTTPResponseError.ErrorType + } + + b, err := json.Marshal(d) + if err != nil { + return nil, 0, err + } + + if behavior == "fail" { + return b, http.StatusInternalServerError, nil + } + + return b, http.StatusOK, nil +} + +func testReadAndDeleteMessageEndpointResponse(behavior string) ([]byte, int, error) { + d := testMailboxMessage + + b, err := json.Marshal(d) + if err != nil { + return nil, 0, err + } + + switch behavior { + case "fail": + return b, http.StatusInternalServerError, nil + case "delete": + return b, http.StatusNoContent, nil + default: + return b, http.StatusOK, nil + } +} + +func testMoveMessageEndpointResponse(behavior string) ([]byte, int, error) { + if behavior == "fail" { + return nil, http.StatusInternalServerError, nil + } + + return nil, http.StatusOK, nil +} + +func testUpdateMessageReadStatusEndpointResponse(behavior string) ([]byte, int, error) { + if behavior == "fail" { + return nil, http.StatusInternalServerError, nil + } + + return nil, http.StatusNoContent, nil +} + +func testGetMessageCountEndpointResponse(behavior string) ([]byte, int, error) { + if behavior == "fail" { + return nil, http.StatusInternalServerError, nil + } + + return []byte("1337"), http.StatusOK, nil +} + +func TestMailboxListQuery(t *testing.T) { + timestamp := time.Now() + qsTimestamp := url.QueryEscape(timestamp.Format(time.RFC3339)) + + tests := []struct { + name string + q *MailboxListQuery + exp string + }{ + { + name: "should create correct query string", + q: &MailboxListQuery{}, + exp: "", + }, + { + name: "should create correct query string", + q: &MailboxListQuery{StartDate: timestamp}, + exp: fmt.Sprintf("startDate=%s", qsTimestamp), + }, + { + name: "should create correct query string", + q: &MailboxListQuery{ + StartDate: timestamp, + EndDate: timestamp, + }, + exp: fmt.Sprintf("endDate=%s&startDate=%s", qsTimestamp, qsTimestamp), + }, + { + name: "should create correct query string", + q: &MailboxListQuery{Status: "test"}, + exp: "status=test", + }, + { + name: "should create correct query string", + q: &MailboxListQuery{Index: "1", Length: "5"}, + exp: "index=1&length=5", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + u, err := tc.q.QueryString("https://example.com") + if err != nil { + t.Errorf("unexpected error: got %s", err) + + return + } + + if u != tc.exp { + t.Errorf("unexpected query string: got %s, exp %s", u, tc.exp) + } + }) + } +} + +func TestMailboxCountQuery(t *testing.T) { + timestamp := time.Now() + qsTimestamp := url.QueryEscape(timestamp.Format(time.RFC3339)) + + tests := []struct { + name string + q *MailboxCountQuery + exp string + }{ + { + name: "should create correct query string", + q: &MailboxCountQuery{}, + exp: "", + }, + { + name: "should create correct query string", + q: &MailboxCountQuery{StartDate: timestamp}, + exp: fmt.Sprintf("startDate=%s", qsTimestamp), + }, + { + name: "should create correct query string", + q: &MailboxCountQuery{ + StartDate: timestamp, + EndDate: timestamp, + }, + exp: fmt.Sprintf("endDate=%s&startDate=%s", qsTimestamp, qsTimestamp), + }, + { + name: "should create correct query string", + q: &MailboxCountQuery{Status: "test"}, + exp: "status=test", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + u, err := tc.q.QueryString("https://example.com") + if err != nil { + t.Errorf("unexpected error: got %s", err) + + return + } + + if u != tc.exp { + t.Errorf("unexpected query string: got %s, exp %s", u, tc.exp) + } + }) + } +} + +func TestListArchiveMessages(t *testing.T) { + ctx := context.Background() + c, err := newTestClient(ctx, t) + if err != nil { + t.Fatalf("error setting up test: %v", err) + } + + tests := []struct { + name string + q *MailboxListQuery + behavior string + exp []MailboxMessage + expErr error + }{ + { + name: "should return error when error occurs", + q: nil, + behavior: "fail", + exp: nil, + expErr: getTestError("ListArchiveMessages"), + }, + { + name: "should list archive mailbox messages without query", + q: nil, + exp: []MailboxMessage{testMailboxMessage}, + expErr: nil, + }, + { + name: "should list archive mailbox messages with query", + q: &MailboxListQuery{Index: "1"}, + exp: []MailboxMessage{testMailboxMessage}, + expErr: nil, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ctx = context.WithValue(ctx, testBehavior("test-behavior"), tc.behavior) + + a, err := c.ListArchiveMessages(ctx, tc.q) + if err != nil { + errStr := err.Error() + expErrStr := tc.expErr.Error() + if errStr != expErrStr { + t.Errorf("unexpected error: got %s, exp %s", errStr, expErrStr) + } + + return + } + + if !reflect.DeepEqual(a, tc.exp) { + t.Errorf("unexpected efaktura: got %v, exp %v", a, tc.exp) + } + }) + } +} + +func TestListInboxMessages(t *testing.T) { + ctx := context.Background() + c, err := newTestClient(ctx, t) + if err != nil { + t.Fatalf("error setting up test: %v", err) + } + + tests := []struct { + name string + q *MailboxListQuery + behavior string + exp []MailboxMessage + expErr error + }{ + { + name: "should return error when error occurs", + q: nil, + behavior: "fail", + exp: nil, + expErr: getTestError("ListInboxMessages"), + }, + { + name: "should list inbox mailbox messages without query", + q: nil, + exp: []MailboxMessage{testMailboxMessage}, + expErr: nil, + }, + { + name: "should list inbox mailbox messages with query", + q: &MailboxListQuery{Index: "1"}, + exp: []MailboxMessage{testMailboxMessage}, + expErr: nil, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ctx = context.WithValue(ctx, testBehavior("test-behavior"), tc.behavior) + + a, err := c.ListInboxMessages(ctx, tc.q) + if err != nil { + errStr := err.Error() + expErrStr := tc.expErr.Error() + if errStr != expErrStr { + t.Errorf("unexpected error: got %s, exp %s", errStr, expErrStr) + } + + return + } + + if !reflect.DeepEqual(a, tc.exp) { + t.Errorf("unexpected efaktura: got %v, exp %v", a, tc.exp) + } + }) + } +} + +func TestReadArchiveMessage(t *testing.T) { + ctx := context.Background() + c, err := newTestClient(ctx, t) + if err != nil { + t.Fatalf("error setting up test: %v", err) + } + + tests := []struct { + name string + messageID int + behavior string + exp MailboxMessage + expErr error + }{ + { + name: "should return error when error occurs", + messageID: 1337, + behavior: "fail", + exp: testMailboxMessage, + expErr: getTestBasicError("ReadArchiveMessage"), + }, + { + name: "should return mailbox message", + messageID: 1337, + exp: testMailboxMessage, + expErr: nil, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ctx = context.WithValue(ctx, testBehavior("test-behavior"), tc.behavior) + + a, err := c.ReadArchiveMessage(ctx, tc.messageID) + if err != nil { + errStr := err.Error() + expErrStr := tc.expErr.Error() + if errStr != expErrStr { + t.Errorf("unexpected error: got %s, exp %s", errStr, expErrStr) + } + + return + } + + if !reflect.DeepEqual(a, tc.exp) { + t.Errorf("unexpected efaktura: got %v, exp %v", a, tc.exp) + } + }) + } +} + +func TestReadInboxMessage(t *testing.T) { + ctx := context.Background() + c, err := newTestClient(ctx, t) + if err != nil { + t.Fatalf("error setting up test: %v", err) + } + + tests := []struct { + name string + messageID int + behavior string + exp MailboxMessage + expErr error + }{ + { + name: "should return error when error occurs", + messageID: 1337, + behavior: "fail", + exp: testMailboxMessage, + expErr: getTestBasicError("ReadInboxMessage"), + }, + { + name: "should return mailbox message", + messageID: 1337, + exp: testMailboxMessage, + expErr: nil, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ctx = context.WithValue(ctx, testBehavior("test-behavior"), tc.behavior) + + a, err := c.ReadInboxMessage(ctx, tc.messageID) + if err != nil { + errStr := err.Error() + expErrStr := tc.expErr.Error() + if errStr != expErrStr { + t.Errorf("unexpected error: got %s, exp %s", errStr, expErrStr) + } + + return + } + + if !reflect.DeepEqual(a, tc.exp) { + t.Errorf("unexpected efaktura: got %v, exp %v", a, tc.exp) + } + }) + } +} + +func TestDeleteArchiveMessage(t *testing.T) { + ctx := context.Background() + c, err := newTestClient(ctx, t) + if err != nil { + t.Fatalf("error setting up test: %v", err) + } + + tests := []struct { + name string + messageID int + behavior string + exp error + }{ + { + name: "should return error when error occurs", + messageID: 1337, + behavior: "fail", + exp: getTestBasicError("DeleteArchiveMessage"), + }, + { + name: "should not return error if successful", + messageID: 1337, + behavior: "delete", + exp: nil, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ctx = context.WithValue(ctx, testBehavior("test-behavior"), tc.behavior) + + err := c.DeleteArchiveMessage(ctx, tc.messageID) + if err != nil { + errStr := err.Error() + expErrStr := tc.exp.Error() + if errStr != expErrStr { + t.Errorf("unexpected error: got %s, exp %s", errStr, expErrStr) + } + } + }) + } +} + +func TestDeleteInboxMessage(t *testing.T) { + ctx := context.Background() + c, err := newTestClient(ctx, t) + if err != nil { + t.Fatalf("error setting up test: %v", err) + } + + tests := []struct { + name string + messageID int + behavior string + exp error + }{ + { + name: "should return error when error occurs", + messageID: 1337, + behavior: "fail", + exp: getTestBasicError("DeleteInboxMessage"), + }, + { + name: "should not return error if successful", + messageID: 1337, + behavior: "delete", + exp: nil, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ctx = context.WithValue(ctx, testBehavior("test-behavior"), tc.behavior) + + err := c.DeleteInboxMessage(ctx, tc.messageID) + if err != nil { + errStr := err.Error() + expErrStr := tc.exp.Error() + if errStr != expErrStr { + t.Errorf("unexpected error: got %s, exp %s", errStr, expErrStr) + } + } + }) + } +} + +func TestMoveArchiveMessage(t *testing.T) { + ctx := context.Background() + c, err := newTestClient(ctx, t) + if err != nil { + t.Fatalf("error setting up test: %v", err) + } + + tests := []struct { + name string + messageID int + behavior string + exp error + }{ + { + name: "should return error when error occurs", + messageID: 1337, + behavior: "fail", + exp: getTestBasicError("MoveArchiveMessage"), + }, + { + name: "should not return error if successful", + messageID: 1337, + exp: nil, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ctx = context.WithValue(ctx, testBehavior("test-behavior"), tc.behavior) + + err := c.MoveArchiveMessage(ctx, tc.messageID) + if err != nil { + errStr := err.Error() + expErrStr := tc.exp.Error() + if errStr != expErrStr { + t.Errorf("unexpected error: got %s, exp %s", errStr, expErrStr) + } + } + }) + } +} + +func TestMoveInboxMessage(t *testing.T) { + ctx := context.Background() + c, err := newTestClient(ctx, t) + if err != nil { + t.Fatalf("error setting up test: %v", err) + } + + tests := []struct { + name string + messageID int + behavior string + exp error + }{ + { + name: "should return error when error occurs", + messageID: 1337, + behavior: "fail", + exp: getTestBasicError("MoveInboxMessage"), + }, + { + name: "should not return error if successful", + messageID: 1337, + exp: nil, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ctx = context.WithValue(ctx, testBehavior("test-behavior"), tc.behavior) + + err := c.MoveInboxMessage(ctx, tc.messageID) + if err != nil { + errStr := err.Error() + expErrStr := tc.exp.Error() + if errStr != expErrStr { + t.Errorf("unexpected error: got %s, exp %s", errStr, expErrStr) + } + } + }) + } +} + +func TestUpdateArchiveMessageReadStatus(t *testing.T) { + ctx := context.Background() + c, err := newTestClient(ctx, t) + if err != nil { + t.Fatalf("error setting up test: %v", err) + } + + tests := []struct { + name string + status bool + messageID int + behavior string + exp error + }{ + { + name: "should return error when error occurs", + messageID: 1337, + behavior: "fail", + exp: getTestBasicError("UpdateArchiveMessageReadStatus"), + }, + { + name: "should not return error if successful - read", + status: true, + messageID: 1337, + exp: nil, + }, + { + name: "should not return error if successful - unread", + status: false, + messageID: 1337, + exp: nil, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ctx = context.WithValue(ctx, testBehavior("test-behavior"), tc.behavior) + + err := c.UpdateArchiveMessageReadStatus(ctx, tc.messageID, tc.status) + if err != nil { + errStr := err.Error() + expErrStr := tc.exp.Error() + if errStr != expErrStr { + t.Errorf("unexpected error: got %s, exp %s", errStr, expErrStr) + } + } + }) + } +} + +func TestUpdateInboxMessageReadStatus(t *testing.T) { + ctx := context.Background() + c, err := newTestClient(ctx, t) + if err != nil { + t.Fatalf("error setting up test: %v", err) + } + + tests := []struct { + name string + status bool + messageID int + behavior string + exp error + }{ + { + name: "should return error when error occurs", + messageID: 1337, + behavior: "fail", + exp: getTestBasicError("UpdateInboxMessageReadStatus"), + }, + { + name: "should not return error if successful - read", + status: true, + messageID: 1337, + exp: nil, + }, + { + name: "should not return error if successful - unread", + status: false, + messageID: 1337, + exp: nil, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ctx = context.WithValue(ctx, testBehavior("test-behavior"), tc.behavior) + + err := c.UpdateInboxMessageReadStatus(ctx, tc.messageID, tc.status) + if err != nil { + errStr := err.Error() + expErrStr := tc.exp.Error() + if errStr != expErrStr { + t.Errorf("unexpected error: got %s, exp %s", errStr, expErrStr) + } + } + }) + } +} + +func TestGetArchiveMessageCount(t *testing.T) { + ctx := context.Background() + c, err := newTestClient(ctx, t) + if err != nil { + t.Fatalf("error setting up test: %v", err) + } + + tests := []struct { + name string + behavior string + exp int + expErr error + }{ + { + name: "should return error when error occurs", + behavior: "fail", + exp: 0, + expErr: getTestBasicError("GetArchiveMessageCount"), + }, + { + name: "should not return error if successful - read", + exp: 1337, + expErr: nil, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ctx = context.WithValue(ctx, testBehavior("test-behavior"), tc.behavior) + + got, err := c.GetArchiveMessageCount(ctx, nil) + if err != nil { + errStr := err.Error() + expErrStr := tc.expErr.Error() + if errStr != expErrStr { + t.Errorf("unexpected error: got %s, exp %s", errStr, expErrStr) + } + } + + if got != tc.exp { + t.Errorf("unexpected count: got %d, exp %d", got, tc.exp) + } + }) + } +} diff --git a/sbanken_test.go b/sbanken_test.go index 639dd33..c8e9101 100644 --- a/sbanken_test.go +++ b/sbanken_test.go @@ -10,25 +10,37 @@ import ( const baseURL = "https://publicapi.sbanken.no/apibeta/api/" var ( - testListAccountsEndpoint = baseURL + "v2/Accounts" - testReadAccountEndpoint = baseURL + "v2/Accounts/test-account" - testListCardsEndpoint = baseURL + "v2/Cards" - testListEfakturasEndpoint = baseURL + "v2/Efaktura" - testListEfakturasQueryEndpoint = baseURL + "v2/Efaktura?index=1" - testPayEfakturaEndpoint = baseURL + "v2/Efaktura" - testListNewEfakturasEndpoint = baseURL + "v2/Efaktura/new" - testListNewEfakturasQueryEndpoint = baseURL + "v2/Efaktura/new?index=1" - testReadEfakturaEndpoint = baseURL + "v2/Efaktura/test-efaktura" - testListPaymentsEndpoint = baseURL + "v2/Payments/test-account" - testListPaymentsQueryEndpoint = baseURL + "v2/Payments/test-account?index=1" - testReadPaymentsEndpoint = baseURL + "v2/Payments/test-account/test-payment" - testListStandingOrdersEndpoint = baseURL + "v2/StandingOrders/test-account" - testListTransactionsEndpoint = baseURL + "v2/Transactions/test-account" - testListTransactionsQueryEndpoint = baseURL + "v2/Transactions/test-account?index=1" - testListArchivedTransactionsEndpoint = baseURL + "v2/Transactions/archive/test-account" - testListArchivedTransactionsQueryEndpoint = baseURL + "v2/Transactions/archive/test-account?index=1" - testTransferEndpoint = baseURL + "v2/Transfers" - testCustomersEndpoint = baseURL + "v2/Customers" + testListAccountsEndpoint = baseURL + "v2/Accounts" + testReadAccountEndpoint = baseURL + "v2/Accounts/test-account" + testListCardsEndpoint = baseURL + "v2/Cards" + testListEfakturasEndpoint = baseURL + "v2/Efaktura" + testListEfakturasQueryEndpoint = baseURL + "v2/Efaktura?index=1" + testPayEfakturaEndpoint = baseURL + "v2/Efaktura" + testListNewEfakturasEndpoint = baseURL + "v2/Efaktura/new" + testListNewEfakturasQueryEndpoint = baseURL + "v2/Efaktura/new?index=1" + testReadEfakturaEndpoint = baseURL + "v2/Efaktura/test-efaktura" + testListPaymentsEndpoint = baseURL + "v2/Payments/test-account" + testListPaymentsQueryEndpoint = baseURL + "v2/Payments/test-account?index=1" + testReadPaymentsEndpoint = baseURL + "v2/Payments/test-account/test-payment" + testListStandingOrdersEndpoint = baseURL + "v2/StandingOrders/test-account" + testListTransactionsEndpoint = baseURL + "v2/Transactions/test-account" + testListTransactionsQueryEndpoint = baseURL + "v2/Transactions/test-account?index=1" + testListArchivedTransactionsEndpoint = baseURL + "v2/Transactions/archive/test-account" + testListArchivedTransactionsQueryEndpoint = baseURL + "v2/Transactions/archive/test-account?index=1" + testTransferEndpoint = baseURL + "v2/Transfers" + testCustomersEndpoint = baseURL + "v2/Customers" + testListArchiveMessagesEndpoint = baseURL + "v2/Mailbox/archive" + testListArchiveMessagesQueryEndpoint = baseURL + "v2/Mailbox/archive?index=1" + testListInboxMessagesEndpoint = baseURL + "v2/Mailbox/inbox" + testListInboxMessagesQueryEndpoint = baseURL + "v2/Mailbox/inbox?index=1" + testReadAndDeleteArchiveMessageEndpoint = baseURL + "v2/Mailbox/archive/1337" + testReadAndDeleteInboxMessageEndpoint = baseURL + "v2/Mailbox/inbox/1337" + testMoveArchiveMessageEndpoint = baseURL + "v2/Mailbox/archive/1337/move" + testMoveInboxMessageEndpoint = baseURL + "v2/Mailbox/inbox/1337/move" + testUpdateArchiveMessageReadStatusEndpoint = baseURL + "v2/Mailbox/archive/1337/readstatus" + testUpdateInboxMessageReadStatusEndpoint = baseURL + "v2/Mailbox/inbox/1337/readstatus" + testGetArchiveMessageCountEndpoint = baseURL + "v2/Mailbox/archive/count" + testGetInboxMessageCountEndpoint = baseURL + "v2/Mailbox/inbox/count" ) type testBehavior string @@ -79,6 +91,30 @@ func (c testTransportClient) Request(ctx context.Context, r *transport.HTTPReque return testTransferEndpointResponse(getTestBehavior(ctx)) case testCustomersEndpoint: return testCustomersEndpointResponse(getTestBehavior(ctx)) + case testListArchiveMessagesEndpoint: + fallthrough + case testListArchiveMessagesQueryEndpoint: + fallthrough + case testListInboxMessagesEndpoint: + fallthrough + case testListInboxMessagesQueryEndpoint: + return testListMessagesEndpointResponse(getTestBehavior(ctx)) + case testReadAndDeleteArchiveMessageEndpoint: + fallthrough + case testReadAndDeleteInboxMessageEndpoint: + return testReadAndDeleteMessageEndpointResponse(getTestBehavior(ctx)) + case testMoveArchiveMessageEndpoint: + fallthrough + case testMoveInboxMessageEndpoint: + return testMoveMessageEndpointResponse(getTestBehavior(ctx)) + case testUpdateArchiveMessageReadStatusEndpoint: + fallthrough + case testUpdateInboxMessageReadStatusEndpoint: + return testUpdateMessageReadStatusEndpointResponse(getTestBehavior(ctx)) + case testGetArchiveMessageCountEndpoint: + fallthrough + case testGetInboxMessageCountEndpoint: + return testGetMessageCountEndpointResponse(getTestBehavior(ctx)) default: return nil, 0, nil }