mirror of
https://github.com/warmbly/warmbly.git
synced 2026-09-05 16:02:48 +00:00
104 lines
3.8 KiB
Go
104 lines
3.8 KiB
Go
package msgraph
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/warmbly/warmbly/internal/errx"
|
|
"golang.org/x/oauth2"
|
|
)
|
|
|
|
// graphStatus boots a client whose every Graph call answers with status. The
|
|
// token is live, so nothing is refreshed and the status under test is what the
|
|
// caller classifies.
|
|
func graphStatus(t *testing.T, status int, body string) *Client {
|
|
t.Helper()
|
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(status)
|
|
_, _ = io.WriteString(w, body)
|
|
}))
|
|
t.Cleanup(srv.Close)
|
|
|
|
c := &Client{Email: "sender@outlook.com"}
|
|
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, &http.Client{
|
|
Transport: rewriteTo(srv.URL),
|
|
})
|
|
token := &oauth2.Token{AccessToken: "live", Expiry: time.Now().Add(time.Hour)}
|
|
if merr := c.Init(ctx, token, oauth2.Config{}); merr != nil {
|
|
t.Fatalf("Init: %v", merr.Message)
|
|
}
|
|
return c
|
|
}
|
|
|
|
// rewriteTo sends every request to the test server instead of graph.microsoft.com,
|
|
// keeping the path and query the client actually built.
|
|
func rewriteTo(base string) http.RoundTripper {
|
|
target, err := url.Parse(base)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return roundTripFunc(func(req *http.Request) (*http.Response, error) {
|
|
clone := req.Clone(req.Context())
|
|
clone.URL.Scheme = target.Scheme
|
|
clone.URL.Host = target.Host
|
|
clone.Host = target.Host
|
|
return http.DefaultTransport.RoundTrip(clone)
|
|
})
|
|
}
|
|
|
|
type roundTripFunc func(*http.Request) (*http.Response, error)
|
|
|
|
func (f roundTripFunc) RoundTrip(r *http.Request) (*http.Response, error) { return f(r) }
|
|
|
|
// The classification the backfill's folder skip rests on. 404 is the only
|
|
// status that means "this folder is not on the tenant"; every other refusal,
|
|
// a 503 above all, has to stay an unreachable server so the caller retries
|
|
// instead of writing the folder off.
|
|
func TestHandleErrorSeparatesNotFoundFromUnreachable(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
status int
|
|
body string
|
|
want errx.MailErrorCode
|
|
}{
|
|
{"folder absent", http.StatusNotFound, `{"error":{"code":"ErrorItemNotFound","message":"The specified object was not found in the store."}}`, errx.MailErrorCodeNotFound},
|
|
{"graph incident", http.StatusServiceUnavailable, `{"error":{"code":"ServiceUnavailable","message":"Server busy."}}`, errx.MailErrorCodeServerUnreachable},
|
|
{"gateway", http.StatusBadGateway, `{}`, errx.MailErrorCodeServerUnreachable},
|
|
{"internal", http.StatusInternalServerError, `{}`, errx.MailErrorCodeServerUnreachable},
|
|
{"unrecognised", http.StatusTeapot, `{}`, errx.MailErrorCodeServerUnreachable},
|
|
{"expired grant", http.StatusUnauthorized, `{}`, errx.MailErrorCodeAuthenticationFailed},
|
|
{"missing scope", http.StatusForbidden, `{}`, errx.MailErrorCodeAuthorizationFailed},
|
|
{"throttled", http.StatusTooManyRequests, `{}`, errx.MailErrorCodeSendingTooFast},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
c := graphStatus(t, tc.status, tc.body)
|
|
_, _, err := c.ListMessagesSince(context.Background(), FolderArchive, time.Now().Add(-24*time.Hour), "", 10)
|
|
if got := mailErrorOf(t, err).Code; got != tc.want {
|
|
t.Errorf("status %d classified as %s, want %s", tc.status, got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// FetchMessage keeps its own 404 handling: a message that vanished between the
|
|
// delta item and the hydration is a skip, not an error.
|
|
func TestFetchMessageStillTreatsNotFoundAsASkip(t *testing.T) {
|
|
c := graphStatus(t, http.StatusNotFound, `{"error":{"code":"ErrorItemNotFound"}}`)
|
|
msg, err := c.FetchMessage(context.Background(), FolderInbox, "gone")
|
|
if err != nil {
|
|
t.Fatalf("FetchMessage returned %v, want a silent skip", err)
|
|
}
|
|
if msg != nil {
|
|
t.Fatalf("FetchMessage returned %+v, want nil", msg)
|
|
}
|
|
}
|