snapshot of https://github.com/ory/kratos.git (master, 2026-09-15; blobs > 131072B omitted)
Some checks failed
CI / Run tests and lints (push) Failing after 29s
CI / Build CLI docs (push) Has been skipped
CI / Run Playwright end-to-end tests (mysql) (push) Failing after 3s
CI / Run Playwright end-to-end tests (postgres) (push) Failing after 2s
CI / Run Playwright end-to-end tests (sqlite) (push) Failing after 3s
CodeQL / Analyze (go) (push) Failing after 4s
CodeQL / Analyze (javascript) (push) Failing after 3s
Docker Image Scanners / scanners (push) Failing after 4s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Run end-to-end tests (postgres) (push) Failing after 19s
CI / Run end-to-end tests (sqlite) (push) Failing after 2s
CI / Run Playwright end-to-end tests (cockroach) (push) Failing after 2s
CI / Draft newsletter (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
Some checks failed
CI / Run tests and lints (push) Failing after 29s
CI / Build CLI docs (push) Has been skipped
CI / Run Playwright end-to-end tests (mysql) (push) Failing after 3s
CI / Run Playwright end-to-end tests (postgres) (push) Failing after 2s
CI / Run Playwright end-to-end tests (sqlite) (push) Failing after 3s
CodeQL / Analyze (go) (push) Failing after 4s
CodeQL / Analyze (javascript) (push) Failing after 3s
Docker Image Scanners / scanners (push) Failing after 4s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Run end-to-end tests (postgres) (push) Failing after 19s
CI / Run end-to-end tests (sqlite) (push) Failing after 2s
CI / Run Playwright end-to-end tests (cockroach) (push) Failing after 2s
CI / Draft newsletter (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
This commit is contained in:
285
courier/handler_test.go
Normal file
285
courier/handler_test.go
Normal file
@@ -0,0 +1,285 @@
|
||||
// Copyright © 2023 Ory Corp
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package courier_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/go-faker/faker/v4"
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"github.com/ory/kratos/courier"
|
||||
"github.com/ory/kratos/driver/config"
|
||||
"github.com/ory/kratos/pkg"
|
||||
"github.com/ory/kratos/pkg/testhelpers"
|
||||
"github.com/ory/x/httprouterx"
|
||||
"github.com/ory/x/ioutilx"
|
||||
"github.com/ory/x/snapshotx"
|
||||
"github.com/ory/x/urlx"
|
||||
"github.com/ory/x/uuidx"
|
||||
)
|
||||
|
||||
var defaultPageToken = courier.Message{}.DefaultPageToken().Encrypt(nil)
|
||||
|
||||
func TestHandler(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
conf, reg := pkg.NewFastRegistryWithMocks(t)
|
||||
// Start kratos server
|
||||
publicTS, adminTS := testhelpers.NewKratosServerWithCSRF(t, reg)
|
||||
|
||||
tss := []struct {
|
||||
name string
|
||||
s *httptest.Server
|
||||
}{
|
||||
{
|
||||
name: "public",
|
||||
s: publicTS,
|
||||
},
|
||||
{
|
||||
name: "admin",
|
||||
s: adminTS,
|
||||
},
|
||||
}
|
||||
|
||||
mockServerURL := urlx.ParseOrPanic(publicTS.URL)
|
||||
conf.MustSet(ctx, config.ViperKeyAdminBaseURL, adminTS.URL)
|
||||
conf.MustSet(ctx, config.ViperKeyPublicBaseURL, mockServerURL.String())
|
||||
|
||||
get := func(t *testing.T, base *httptest.Server, href string, expectCode int) gjson.Result {
|
||||
t.Helper()
|
||||
res, err := base.Client().Get(base.URL + href)
|
||||
require.NoError(t, err)
|
||||
body, err := io.ReadAll(res.Body)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, res.Body.Close())
|
||||
|
||||
assert.EqualValuesf(t, expectCode, res.StatusCode, "%s", body)
|
||||
return gjson.ParseBytes(body)
|
||||
}
|
||||
|
||||
getList := func(t *testing.T, tsName string, qs string) gjson.Result {
|
||||
t.Helper()
|
||||
href := courier.AdminRouteListMessages + qs
|
||||
ts := adminTS
|
||||
|
||||
if tsName == "public" {
|
||||
href = httprouterx.AdminPrefix + href
|
||||
ts = publicTS
|
||||
}
|
||||
|
||||
parsed := get(t, ts, href, http.StatusOK)
|
||||
require.Truef(t, parsed.IsArray(), "%s", parsed.Raw)
|
||||
return parsed
|
||||
}
|
||||
|
||||
t.Run("case=should return an empty list of messages", func(t *testing.T) {
|
||||
for _, name := range []string{"public", "admin"} {
|
||||
t.Run("endpoint="+name, func(t *testing.T) {
|
||||
parsed := getList(t, name, "")
|
||||
assert.Len(t, parsed.Array(), 0)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("case=list messages", func(t *testing.T) {
|
||||
// Arrange test data
|
||||
const msgCount = 10 // total message count
|
||||
const procCount = 5 // how many messages' status should be equal to `processing`
|
||||
const rcptOryCount = 2 // how many messages' recipient should be equal to `noreply@ory.sh`
|
||||
messages := make([]courier.Message, msgCount)
|
||||
|
||||
for i := range messages {
|
||||
require.NoError(t, faker.FakeData(&messages[i]))
|
||||
messages[i].Type = courier.MessageTypeEmail
|
||||
messages[i].Body = "body content"
|
||||
if i < rcptOryCount {
|
||||
messages[i].Recipient = "noreply@ory.sh"
|
||||
}
|
||||
require.NoError(t, reg.CourierPersister().AddMessage(context.Background(), &messages[i]))
|
||||
}
|
||||
for i := range procCount {
|
||||
require.NoError(t, reg.CourierPersister().SetMessageStatus(context.Background(), messages[i].ID, courier.MessageStatusProcessing))
|
||||
}
|
||||
|
||||
t.Run("paging", func(t *testing.T) {
|
||||
t.Run("case=should return first half of the messages", func(t *testing.T) {
|
||||
qs := fmt.Sprintf("?page_token=%s&page_size=%d", defaultPageToken, msgCount/2)
|
||||
|
||||
for _, tc := range tss {
|
||||
t.Run("endpoint="+tc.name, func(t *testing.T) {
|
||||
parsed := getList(t, tc.name, qs)
|
||||
assert.Len(t, parsed.Array(), msgCount/2)
|
||||
})
|
||||
}
|
||||
})
|
||||
t.Run("case=should error with random page token", func(t *testing.T) {
|
||||
qs := fmt.Sprintf(`?page_token=%s&page_size=%s`, uuidx.NewV4().String(), "250")
|
||||
|
||||
for _, tc := range tss {
|
||||
t.Run("endpoint="+tc.name, func(t *testing.T) {
|
||||
path := courier.AdminRouteListMessages + qs
|
||||
if tc.name == "public" {
|
||||
path = httprouterx.AdminPrefix + path
|
||||
}
|
||||
resp, err := tc.s.Client().Get(tc.s.URL + path)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
t.Run("filtering", func(t *testing.T) {
|
||||
t.Run("case=should return all queued messages", func(t *testing.T) {
|
||||
qs := fmt.Sprintf(`?page_token=%s&page_size=250&status=queued`, defaultPageToken)
|
||||
|
||||
for _, tc := range tss {
|
||||
t.Run("endpoint="+tc.name, func(t *testing.T) {
|
||||
parsed := getList(t, tc.name, qs)
|
||||
assert.Len(t, parsed.Array(), msgCount-procCount)
|
||||
|
||||
for _, item := range parsed.Array() {
|
||||
assert.Equal(t, "queued", item.Get("status").String())
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
t.Run("case=should return all processing messages", func(t *testing.T) {
|
||||
qs := fmt.Sprintf(`?page_token=%s&page_size=250&status=processing`, defaultPageToken)
|
||||
|
||||
for _, tc := range tss {
|
||||
t.Run("endpoint="+tc.name, func(t *testing.T) {
|
||||
parsed := getList(t, tc.name, qs)
|
||||
assert.Len(t, parsed.Array(), procCount)
|
||||
|
||||
for _, item := range parsed.Array() {
|
||||
assert.Equal(t, "processing", item.Get("status").String())
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
t.Run("case=should return all messages with recipient equals to noreply@ory.sh", func(t *testing.T) {
|
||||
qs := fmt.Sprintf(`?page_token=%s&page_size=250&recipient=noreply@ory.sh`, defaultPageToken)
|
||||
|
||||
for _, tc := range tss {
|
||||
t.Run("endpoint="+tc.name, func(t *testing.T) {
|
||||
parsed := getList(t, tc.name, qs)
|
||||
assert.Len(t, parsed.Array(), rcptOryCount)
|
||||
|
||||
for _, item := range parsed.Array() {
|
||||
assert.Equal(t, "noreply@ory.sh", item.Get("recipient").String())
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
t.Run("case=body should be redacted if kratos is not in dev mode", func(t *testing.T) {
|
||||
conf.MustSet(ctx, "dev", false)
|
||||
for _, tc := range tss {
|
||||
t.Run("endpoint="+tc.name, func(t *testing.T) {
|
||||
parsed := getList(t, tc.name, "")
|
||||
require.Lenf(t, parsed.Array(), msgCount, "%s", parsed.Raw)
|
||||
|
||||
for _, item := range parsed.Array() {
|
||||
assert.Equal(t, "<redacted-unless-dev-mode>", item.Get("body").String())
|
||||
assert.Equal(t, "<redacted-unless-dev-mode>", item.Get("subject").String())
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
t.Run("case=body should not be redacted if kratos is in dev mode", func(t *testing.T) {
|
||||
conf.MustSet(ctx, "dev", true)
|
||||
for _, tc := range tss {
|
||||
t.Run("endpoint="+tc.name, func(t *testing.T) {
|
||||
parsed := getList(t, tc.name, "")
|
||||
require.Lenf(t, parsed.Array(), msgCount, "%s", parsed.Raw)
|
||||
|
||||
for _, item := range parsed.Array() {
|
||||
assert.Equal(t, "body content", item.Get("body").String())
|
||||
assert.NotEqual(t, "<redacted-unless-dev-mode>", item.Get("subject").String())
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
t.Run("case=should return with http status BadRequest when given status is invalid", func(t *testing.T) {
|
||||
qs := fmt.Sprintf(`?page_token=%s&page_size=250&status=invalid_status`, defaultPageToken)
|
||||
|
||||
res, err := adminTS.Client().Get(adminTS.URL + courier.AdminRouteListMessages + qs)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusBadRequest, res.StatusCode, "status code should be equal to StatusBadRequest")
|
||||
})
|
||||
|
||||
})
|
||||
t.Run("handler=getCourierMessage", func(t *testing.T) {
|
||||
|
||||
message := courier.Message{}
|
||||
require.NoError(t, faker.FakeData(&message))
|
||||
message.Type = courier.MessageTypeEmail
|
||||
message.Body = "body content"
|
||||
require.NoError(t, reg.CourierPersister().AddMessage(context.Background(), &message))
|
||||
require.NoError(t, reg.CourierPersister().RecordDispatch(ctx, message.ID, courier.CourierMessageDispatchStatusSuccess, errors.New("some error")))
|
||||
|
||||
getCourierMessag := func(s *httptest.Server, id string) gjson.Result {
|
||||
|
||||
r, err := s.Client().Get(s.URL + "/admin/courier/messages/" + id)
|
||||
require.NoError(t, err)
|
||||
return gjson.ParseBytes(ioutilx.MustReadAll(r.Body))
|
||||
}
|
||||
|
||||
t.Run("case=should return a message by id", func(t *testing.T) {
|
||||
conf.MustSet(ctx, "dev", true)
|
||||
|
||||
for _, tc := range tss {
|
||||
t.Run("endpoint="+tc.name, func(t *testing.T) {
|
||||
body := getCourierMessag(tc.s, message.ID.String())
|
||||
assert.Equal(t, message.ID.String(), body.Get("id").String())
|
||||
assert.Equal(t, message.Recipient, body.Get("recipient").String())
|
||||
assert.Equal(t, message.Body, body.Get("body").String())
|
||||
assert.Equal(t, message.Subject, body.Get("subject").String())
|
||||
|
||||
// assert Eager works
|
||||
assert.NotEmpty(t, body.Get("dispatches").Array())
|
||||
})
|
||||
}
|
||||
})
|
||||
t.Run("case=does not contain body if not in production", func(t *testing.T) {
|
||||
conf.MustSet(ctx, "dev", false)
|
||||
|
||||
for _, tc := range tss {
|
||||
t.Run("endpoint="+tc.name, func(t *testing.T) {
|
||||
body := getCourierMessag(tc.s, message.ID.String())
|
||||
assert.Equal(t, message.ID.String(), body.Get("id").String())
|
||||
assert.Equal(t, "<redacted-unless-dev-mode>", body.Get("body").String())
|
||||
assert.Equal(t, "<redacted-unless-dev-mode>", body.Get("subject").String())
|
||||
})
|
||||
}
|
||||
})
|
||||
t.Run("case=returns an error if parameter is malformed", func(t *testing.T) {
|
||||
for _, tc := range tss {
|
||||
t.Run("endpoint="+tc.name, func(t *testing.T) {
|
||||
body := getCourierMessag(tc.s, "not-a-uuid")
|
||||
|
||||
snapshotx.SnapshotTJSON(t, body.Raw)
|
||||
})
|
||||
}
|
||||
})
|
||||
t.Run("case=returns an error if no message is found", func(t *testing.T) {
|
||||
for _, tc := range tss {
|
||||
t.Run("endpoint="+tc.name, func(t *testing.T) {
|
||||
body := getCourierMessag(tc.s, uuid.Nil.String())
|
||||
snapshotx.SnapshotTJSON(t, body.Raw)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user