Replaces the shared GITEA_MCP_DEFAULT_TOKEN for all callers. When a request's bearer validates directly against Gitea's own /api/v1/user, that token is used for every upstream call this request makes instead of the service PAT, and the caller identity comes from Gitea's own login rather than the proxy header. Any other bearer (static token, JWT, none) falls through unchanged to the existing chassis auth. Prep: Authentik now SSOs into Gitea (infra a32801c), so each real user can mint their own PAT from their own linked account. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
108 lines
3.4 KiB
Go
108 lines
3.4 KiB
Go
package gitea_test
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"sync/atomic"
|
|
"testing"
|
|
|
|
"git.d-ma.be/mathias/gitea-mcp/internal/gitea"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestClientGetsTokenInHeader(t *testing.T) {
|
|
var gotAuth string
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
gotAuth = r.Header.Get("Authorization")
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_, _ = w.Write([]byte(`{"ok":true}`))
|
|
}))
|
|
defer srv.Close()
|
|
|
|
c := gitea.NewClient(srv.URL, "test-token")
|
|
body, status, err := c.GetJSON(context.Background(), "/api/v1/user")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 200, status)
|
|
assert.Contains(t, string(body), `"ok":true`)
|
|
assert.Equal(t, "token test-token", gotAuth)
|
|
}
|
|
|
|
func TestRetryOn5xxGetSucceedsOnSecondAttempt(t *testing.T) {
|
|
var attempts int32
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
n := atomic.AddInt32(&attempts, 1)
|
|
if n == 1 {
|
|
http.Error(w, "boom", http.StatusServiceUnavailable)
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_, _ = w.Write([]byte(`{"ok":true}`))
|
|
}))
|
|
defer srv.Close()
|
|
|
|
c := gitea.NewClient(srv.URL, "tok")
|
|
body, status, err := c.GetJSON(context.Background(), "/api/v1/test")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 200, status)
|
|
assert.Contains(t, string(body), `"ok":true`)
|
|
assert.Equal(t, int32(2), atomic.LoadInt32(&attempts))
|
|
}
|
|
|
|
func TestClientPrefersTokenFromContextOverDefaultToken(t *testing.T) {
|
|
var gotAuth string
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
gotAuth = r.Header.Get("Authorization")
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_, _ = w.Write([]byte(`{"ok":true}`))
|
|
}))
|
|
defer srv.Close()
|
|
|
|
c := gitea.NewClient(srv.URL, "default-token")
|
|
ctx := gitea.WithToken(context.Background(), "caller-token")
|
|
_, status, err := c.GetJSON(ctx, "/api/v1/user")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 200, status)
|
|
assert.Equal(t, "token caller-token", gotAuth)
|
|
}
|
|
|
|
func TestValidateTokenReturnsLoginOnSuccess(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
assert.Equal(t, "token candidate-token", r.Header.Get("Authorization"))
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_, _ = w.Write([]byte(`{"login":"alice"}`))
|
|
}))
|
|
defer srv.Close()
|
|
|
|
c := gitea.NewClient(srv.URL, "default-token")
|
|
login, ok := c.ValidateToken(context.Background(), "candidate-token")
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "alice", login)
|
|
}
|
|
|
|
func TestValidateTokenReturnsFalseOn401(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
|
}))
|
|
defer srv.Close()
|
|
|
|
c := gitea.NewClient(srv.URL, "default-token")
|
|
login, ok := c.ValidateToken(context.Background(), "bad-token")
|
|
assert.False(t, ok)
|
|
assert.Empty(t, login)
|
|
}
|
|
|
|
func TestRetryOnPostNotRetried(t *testing.T) {
|
|
var attempts int32
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
atomic.AddInt32(&attempts, 1)
|
|
http.Error(w, "boom", http.StatusServiceUnavailable)
|
|
}))
|
|
defer srv.Close()
|
|
|
|
c := gitea.NewClient(srv.URL, "tok")
|
|
_, _, _ = c.PostJSON(context.Background(), "/api/v1/test", []byte(`{}`))
|
|
assert.Equal(t, int32(1), atomic.LoadInt32(&attempts), "POST should not retry")
|
|
}
|