feat: mail service
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
package internalhttp
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"galaxy/mail/internal/domain/common"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDecodeLoginCodeDeliveryCommandSuccess(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
request := httptest.NewRequest(http.MethodPost, LoginCodeDeliveriesPath, strings.NewReader(`{"email":" pilot@example.com ","code":"123456","locale":" en "}`))
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
request.Header.Set(IdempotencyKeyHeader, "challenge-1")
|
||||
|
||||
command, err := DecodeLoginCodeDeliveryCommand(request)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, LoginCodeDeliveryCommand{
|
||||
IdempotencyKey: common.IdempotencyKey("challenge-1"),
|
||||
Email: common.Email("pilot@example.com"),
|
||||
Code: "123456",
|
||||
Locale: common.Locale("en"),
|
||||
}, command)
|
||||
}
|
||||
|
||||
func TestDecodeLoginCodeDeliveryCommandRejectsInvalidRequests(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
contentType string
|
||||
headerValue string
|
||||
body string
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "missing content type",
|
||||
headerValue: "challenge-1",
|
||||
body: `{"email":"pilot@example.com","code":"123456","locale":"en"}`,
|
||||
wantErr: "Content-Type must be application/json",
|
||||
},
|
||||
{
|
||||
name: "missing idempotency key",
|
||||
contentType: "application/json",
|
||||
body: `{"email":"pilot@example.com","code":"123456","locale":"en"}`,
|
||||
wantErr: "Idempotency-Key header must not be empty",
|
||||
},
|
||||
{
|
||||
name: "idempotency key surrounding whitespace",
|
||||
contentType: "application/json",
|
||||
headerValue: " challenge-1 ",
|
||||
body: `{"email":"pilot@example.com","code":"123456","locale":"en"}`,
|
||||
wantErr: "Idempotency-Key header must not contain surrounding whitespace",
|
||||
},
|
||||
{
|
||||
name: "unknown field",
|
||||
contentType: "application/json",
|
||||
headerValue: "challenge-1",
|
||||
body: `{"email":"pilot@example.com","code":"123456","locale":"en","extra":true}`,
|
||||
wantErr: "decode request body",
|
||||
},
|
||||
{
|
||||
name: "trailing json",
|
||||
contentType: "application/json",
|
||||
headerValue: "challenge-1",
|
||||
body: `{"email":"pilot@example.com","code":"123456","locale":"en"}{}`,
|
||||
wantErr: "unexpected trailing JSON input",
|
||||
},
|
||||
{
|
||||
name: "code surrounding whitespace",
|
||||
contentType: "application/json",
|
||||
headerValue: "challenge-1",
|
||||
body: `{"email":"pilot@example.com","code":" 123456 ","locale":"en"}`,
|
||||
wantErr: "code must not contain surrounding whitespace",
|
||||
},
|
||||
{
|
||||
name: "invalid locale",
|
||||
contentType: "application/json",
|
||||
headerValue: "challenge-1",
|
||||
body: `{"email":"pilot@example.com","code":"123456","locale":"english"}`,
|
||||
wantErr: "locale:",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
request := httptest.NewRequest(http.MethodPost, LoginCodeDeliveriesPath, strings.NewReader(tt.body))
|
||||
if tt.contentType != "" {
|
||||
request.Header.Set("Content-Type", tt.contentType)
|
||||
}
|
||||
if tt.headerValue != "" {
|
||||
request.Header.Set(IdempotencyKeyHeader, tt.headerValue)
|
||||
}
|
||||
|
||||
_, err := DecodeLoginCodeDeliveryCommand(request)
|
||||
require.Error(t, err)
|
||||
assert.ErrorContains(t, err, tt.wantErr)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeLoginCodeDeliveryCommandRepeatedEquivalentRequestsMatch(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
first := httptest.NewRequest(http.MethodPost, LoginCodeDeliveriesPath, strings.NewReader(`{"email":"pilot@example.com","code":"123456","locale":"en"}`))
|
||||
first.Header.Set("Content-Type", "application/json")
|
||||
first.Header.Set(IdempotencyKeyHeader, "challenge-1")
|
||||
|
||||
second := httptest.NewRequest(http.MethodPost, LoginCodeDeliveriesPath, strings.NewReader(`{"email":" pilot@example.com ","code":"123456","locale":" en "}`))
|
||||
second.Header.Set("Content-Type", "application/json")
|
||||
second.Header.Set(IdempotencyKeyHeader, "challenge-1")
|
||||
|
||||
firstCommand, err := DecodeLoginCodeDeliveryCommand(first)
|
||||
require.NoError(t, err)
|
||||
secondCommand, err := DecodeLoginCodeDeliveryCommand(second)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, firstCommand, secondCommand)
|
||||
}
|
||||
|
||||
func TestLoginCodeDeliveryCommandFingerprintStableForEquivalentRequests(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
first := LoginCodeDeliveryCommand{
|
||||
IdempotencyKey: common.IdempotencyKey("challenge-1"),
|
||||
Email: common.Email("pilot@example.com"),
|
||||
Code: "123456",
|
||||
Locale: common.Locale("en"),
|
||||
}
|
||||
second := LoginCodeDeliveryCommand{
|
||||
IdempotencyKey: common.IdempotencyKey("challenge-1"),
|
||||
Email: common.Email("pilot@example.com"),
|
||||
Code: "123456",
|
||||
Locale: common.Locale("en"),
|
||||
}
|
||||
|
||||
firstFingerprint, err := first.Fingerprint()
|
||||
require.NoError(t, err)
|
||||
secondFingerprint, err := second.Fingerprint()
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, firstFingerprint, secondFingerprint)
|
||||
}
|
||||
|
||||
func TestLoginCodeDeliveryResponseValidate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require.NoError(t, LoginCodeDeliveryResponse{Outcome: LoginCodeDeliveryOutcomeSent}.Validate())
|
||||
require.NoError(t, LoginCodeDeliveryResponse{Outcome: LoginCodeDeliveryOutcomeSuppressed}.Validate())
|
||||
|
||||
err := LoginCodeDeliveryResponse{Outcome: LoginCodeDeliveryOutcome("queued")}.Validate()
|
||||
require.Error(t, err)
|
||||
assert.ErrorContains(t, err, "unsupported")
|
||||
}
|
||||
|
||||
func TestErrorResponseValidate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require.NoError(t, ErrorResponse{
|
||||
Error: ErrorBody{
|
||||
Code: ErrorCodeInvalidRequest,
|
||||
Message: "field-specific validation detail",
|
||||
},
|
||||
}.Validate())
|
||||
|
||||
err := ErrorResponse{
|
||||
Error: ErrorBody{
|
||||
Code: " invalid_request ",
|
||||
Message: "",
|
||||
},
|
||||
}.Validate()
|
||||
require.Error(t, err)
|
||||
assert.ErrorContains(t, err, "error code")
|
||||
}
|
||||
Reference in New Issue
Block a user