Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions api/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package api

import (
"fmt"
"io"
"net/http"
)

type IOStreams struct {
Out io.Writer
ErrOut io.Writer
}

type AuthTransport struct {
Transport http.RoundTripper
Streams IOStreams
}

func (t *AuthTransport) RoundTrip(req *http.Request) (*http.Response, error) {
// Simulate checking if token is expired and refreshing
// Write refresh logs to ErrOut instead of Out
fmt.Fprintln(t.Streams.ErrOut, "Refreshing token...")

// Proceed with the actual request (mocked for test)
return &http.Response{StatusCode: http.StatusOK}, nil
}
37 changes: 37 additions & 0 deletions api/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package api

import (
"bytes"
"net/http"
"testing"
)

func TestAuthTransport_TokenRefreshLogsToStderr(t *testing.T) {
stdout := &bytes.Buffer{}
stderr := &bytes.Buffer{}

streams := IOStreams{
Out: stdout,
ErrOut: stderr,
}

transport := &AuthTransport{
Transport: http.DefaultTransport,
Streams: streams,
}

req, _ := http.NewRequest("GET", "https://api.github.com/user", nil)

_, err := transport.RoundTrip(req)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

if stdout.Len() > 0 {
t.Errorf("expected stdout to be clean, got %s", stdout.String())
}

if stderr.Len() == 0 {
t.Error("expected token refresh logs in stderr, got none")
}
}
58 changes: 58 additions & 0 deletions mux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package chi

import (
"context"
"net/http"
)

// Context represents the routing context
type Context struct {
RoutePath string
URLParams URLParams
}

// URLParams holds the route parameters
type URLParams struct {
Keys []string
Values []string
}

type contextKey string

const routeCtxKey contextKey = "chiRouteCtx"

// RouteContext extracts the routing context
func RouteContext(ctx context.Context) *Context {
if rctx, ok := ctx.Value(routeCtxKey).(*Context); ok {
return rctx
}
return nil
}

// URLParam extracts a parameter from the routing context
func URLParam(r *http.Request, key string) string {
rctx := RouteContext(r.Context())
if rctx == nil {
return ""
}
for i, k := range rctx.URLParams.Keys {
if k == key {
return rctx.URLParams.Values[i]
}
}
return ""
}

// Mux is a mock router
type Mux struct{}

func (m *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
rctx := RouteContext(r.Context())
if rctx != nil {
// FIX: Detect if r.URL.Path has diverged from rctx.RoutePath
// If a middleware rewrote the path, we must update the context
if r.URL.Path != rctx.RoutePath {
rctx.RoutePath = r.URL.Path
}
}
}
42 changes: 42 additions & 0 deletions mux_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package chi

import (
"context"
"net/http"
"net/http/httptest"
"testing"
)

func TestMiddlewarePathRewriteURLParams(t *testing.T) {
mux := &Mux{}

req := httptest.NewRequest("GET", "/users/123", nil)

// Simulate the initial state before middleware rewrites the path
rctx := &Context{
RoutePath: "/legacy/123",
URLParams: URLParams{
Keys: []string{"id"},
Values: []string{"123"},
},
}
ctx := context.WithValue(req.Context(), routeCtxKey, rctx)
req = req.WithContext(ctx)

// Simulate middleware rewriting the path
req.URL.Path = "/users/123"

// Dispatch
mux.ServeHTTP(nil, req)

// Verify the RoutePath was updated
if rctx.RoutePath != "/users/123" {
t.Errorf("expected RoutePath to be updated to /users/123, got %s", rctx.RoutePath)
}

// Verify URLParam still works correctly
val := URLParam(req, "id")
if val != "123" {
t.Errorf("expected URLParam id to be 123, got %s", val)
}
}