package oauth_test import ( "encoding/base64" "encoding/json" "net/http" "net/http/httptest" "net/url" "strings" "testing" "github.com/mathiasbq/hyperguild/ingestion/internal/oauth" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func newTokenServer() *httptest.Server { return httptest.NewServer(oauth.TokenHandler(oauth.TokenConfig{ ClientID: "the-client", ClientSecret: "the-secret", AccessToken: "BRAIN_TOKEN_VALUE", })) } func postForm(t *testing.T, srv *httptest.Server, vals url.Values, basic [2]string) *http.Response { t.Helper() req, err := http.NewRequest(http.MethodPost, srv.URL+"/oauth/token", strings.NewReader(vals.Encode())) require.NoError(t, err) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") if basic[0] != "" { req.SetBasicAuth(basic[0], basic[1]) } resp, err := http.DefaultClient.Do(req) require.NoError(t, err) return resp } func TestTokenHandler_ClientSecretPost_Success(t *testing.T) { srv := newTokenServer() defer srv.Close() resp := postForm(t, srv, url.Values{ "grant_type": {"client_credentials"}, "client_id": {"the-client"}, "client_secret": {"the-secret"}, }, [2]string{}) defer func() { _ = resp.Body.Close() }() assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) var body map[string]any require.NoError(t, json.NewDecoder(resp.Body).Decode(&body)) assert.Equal(t, "BRAIN_TOKEN_VALUE", body["access_token"]) assert.Equal(t, "bearer", body["token_type"]) } func TestTokenHandler_ClientSecretBasic_Success(t *testing.T) { srv := newTokenServer() defer srv.Close() resp := postForm(t, srv, url.Values{"grant_type": {"client_credentials"}}, [2]string{"the-client", "the-secret"}, ) defer func() { _ = resp.Body.Close() }() assert.Equal(t, http.StatusOK, resp.StatusCode) } func TestTokenHandler_WrongSecret(t *testing.T) { srv := newTokenServer() defer srv.Close() resp := postForm(t, srv, url.Values{ "grant_type": {"client_credentials"}, "client_id": {"the-client"}, "client_secret": {"wrong"}, }, [2]string{}) defer func() { _ = resp.Body.Close() }() assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) var body map[string]any require.NoError(t, json.NewDecoder(resp.Body).Decode(&body)) assert.Equal(t, "invalid_client", body["error"]) } func TestTokenHandler_BadGrantType(t *testing.T) { srv := newTokenServer() defer srv.Close() resp := postForm(t, srv, url.Values{ "grant_type": {"password"}, "client_id": {"the-client"}, "client_secret": {"the-secret"}, }, [2]string{}) defer func() { _ = resp.Body.Close() }() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) var body map[string]any require.NoError(t, json.NewDecoder(resp.Body).Decode(&body)) assert.Equal(t, "unsupported_grant_type", body["error"]) } func TestTokenHandler_RejectsGet(t *testing.T) { srv := newTokenServer() defer srv.Close() resp, err := http.Get(srv.URL + "/oauth/token") require.NoError(t, err) defer func() { _ = resp.Body.Close() }() assert.Equal(t, http.StatusMethodNotAllowed, resp.StatusCode) } func TestTokenHandler_BasicMalformed_FallsThrough(t *testing.T) { srv := newTokenServer() defer srv.Close() // Malformed (non-base64) Authorization header — handler should treat // the request as missing creds, not crash. req, _ := http.NewRequest(http.MethodPost, srv.URL+"/oauth/token", strings.NewReader("grant_type=client_credentials")) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Authorization", "Basic ###not-base64###") resp, err := http.DefaultClient.Do(req) require.NoError(t, err) defer func() { _ = resp.Body.Close() }() assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) } func TestTokenHandler_BasicNoColon(t *testing.T) { srv := newTokenServer() defer srv.Close() // "client-only" base64 — missing the `:secret` half. enc := base64.StdEncoding.EncodeToString([]byte("the-client")) req, _ := http.NewRequest(http.MethodPost, srv.URL+"/oauth/token", strings.NewReader("grant_type=client_credentials")) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Authorization", "Basic "+enc) resp, err := http.DefaultClient.Do(req) require.NoError(t, err) defer func() { _ = resp.Body.Close() }() assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }