edge-connect-client/sdk/edgeconnect/v2/auth_test.go
Richard Robert Reitz 9772a072e8
All checks were successful
test / test (push) Successful in 46s
chore(linting): Fixed all linter errors
2025-10-22 12:47:15 +02:00

226 lines
7.2 KiB
Go

// ABOUTME: Unit tests for authentication providers including username/password token flow
// ABOUTME: Tests token caching, login flow, and error conditions with mock servers
package v2
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestStaticTokenProvider(t *testing.T) {
provider := NewStaticTokenProvider("test-token-123")
req, _ := http.NewRequest("GET", "https://example.com", nil)
ctx := context.Background()
err := provider.Attach(ctx, req)
require.NoError(t, err)
assert.Equal(t, "Bearer test-token-123", req.Header.Get("Authorization"))
}
func TestStaticTokenProvider_EmptyToken(t *testing.T) {
provider := NewStaticTokenProvider("")
req, _ := http.NewRequest("GET", "https://example.com", nil)
ctx := context.Background()
err := provider.Attach(ctx, req)
require.NoError(t, err)
assert.Empty(t, req.Header.Get("Authorization"))
}
func TestUsernamePasswordProvider_Success(t *testing.T) {
// Mock login server
loginServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
assert.Equal(t, "/api/v1/login", r.URL.Path)
assert.Equal(t, "application/json", r.Header.Get("Content-Type"))
// Verify request body
var creds map[string]string
err := json.NewDecoder(r.Body).Decode(&creds)
require.NoError(t, err)
assert.Equal(t, "testuser", creds["username"])
assert.Equal(t, "testpass", creds["password"])
// Return token
response := map[string]string{"token": "dynamic-token-456"}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(response)
}))
defer loginServer.Close()
provider := NewUsernamePasswordProvider(loginServer.URL, "testuser", "testpass", nil)
req, _ := http.NewRequest("GET", "https://api.example.com", nil)
ctx := context.Background()
err := provider.Attach(ctx, req)
require.NoError(t, err)
assert.Equal(t, "Bearer dynamic-token-456", req.Header.Get("Authorization"))
}
func TestUsernamePasswordProvider_LoginFailure(t *testing.T) {
// Mock login server that returns error
loginServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusUnauthorized)
_, _ = w.Write([]byte("Invalid credentials"))
}))
defer loginServer.Close()
provider := NewUsernamePasswordProvider(loginServer.URL, "baduser", "badpass", nil)
req, _ := http.NewRequest("GET", "https://api.example.com", nil)
ctx := context.Background()
err := provider.Attach(ctx, req)
require.Error(t, err)
assert.Contains(t, err.Error(), "login failed with status 401")
assert.Contains(t, err.Error(), "Invalid credentials")
}
func TestUsernamePasswordProvider_TokenCaching(t *testing.T) {
callCount := 0
// Mock login server that tracks calls
loginServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
callCount++
response := map[string]string{"token": "cached-token-789"}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(response)
}))
defer loginServer.Close()
provider := NewUsernamePasswordProvider(loginServer.URL, "testuser", "testpass", nil)
ctx := context.Background()
// First request should call login
req1, _ := http.NewRequest("GET", "https://api.example.com", nil)
err1 := provider.Attach(ctx, req1)
require.NoError(t, err1)
assert.Equal(t, "Bearer cached-token-789", req1.Header.Get("Authorization"))
assert.Equal(t, 1, callCount)
// Second request should use cached token (no additional login call)
req2, _ := http.NewRequest("GET", "https://api.example.com", nil)
err2 := provider.Attach(ctx, req2)
require.NoError(t, err2)
assert.Equal(t, "Bearer cached-token-789", req2.Header.Get("Authorization"))
assert.Equal(t, 1, callCount) // Still only 1 call
}
func TestUsernamePasswordProvider_TokenExpiry(t *testing.T) {
callCount := 0
loginServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
callCount++
response := map[string]string{"token": "refreshed-token-999"}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(response)
}))
defer loginServer.Close()
provider := NewUsernamePasswordProvider(loginServer.URL, "testuser", "testpass", nil)
// Manually set expired token
provider.mu.Lock()
provider.cachedToken = "expired-token"
provider.tokenExpiry = time.Now().Add(-1 * time.Hour) // Already expired
provider.mu.Unlock()
ctx := context.Background()
req, _ := http.NewRequest("GET", "https://api.example.com", nil)
err := provider.Attach(ctx, req)
require.NoError(t, err)
assert.Equal(t, "Bearer refreshed-token-999", req.Header.Get("Authorization"))
assert.Equal(t, 1, callCount) // New token retrieved
}
func TestUsernamePasswordProvider_InvalidateToken(t *testing.T) {
callCount := 0
loginServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
callCount++
response := map[string]string{"token": "new-token-after-invalidation"}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(response)
}))
defer loginServer.Close()
provider := NewUsernamePasswordProvider(loginServer.URL, "testuser", "testpass", nil)
ctx := context.Background()
// First request to get token
req1, _ := http.NewRequest("GET", "https://api.example.com", nil)
err1 := provider.Attach(ctx, req1)
require.NoError(t, err1)
assert.Equal(t, 1, callCount)
// Invalidate token
provider.InvalidateToken()
// Next request should get new token
req2, _ := http.NewRequest("GET", "https://api.example.com", nil)
err2 := provider.Attach(ctx, req2)
require.NoError(t, err2)
assert.Equal(t, "Bearer new-token-after-invalidation", req2.Header.Get("Authorization"))
assert.Equal(t, 2, callCount) // New login call made
}
func TestUsernamePasswordProvider_BadJSONResponse(t *testing.T) {
// Mock server returning invalid JSON
loginServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte("invalid json response"))
}))
defer loginServer.Close()
provider := NewUsernamePasswordProvider(loginServer.URL, "testuser", "testpass", nil)
req, _ := http.NewRequest("GET", "https://api.example.com", nil)
ctx := context.Background()
err := provider.Attach(ctx, req)
require.Error(t, err)
assert.Contains(t, err.Error(), "error parsing JSON")
}
func TestNoAuthProvider(t *testing.T) {
provider := NewNoAuthProvider()
req, _ := http.NewRequest("GET", "https://example.com", nil)
ctx := context.Background()
err := provider.Attach(ctx, req)
require.NoError(t, err)
assert.Empty(t, req.Header.Get("Authorization"))
}
func TestNewClientWithCredentials(t *testing.T) {
client := NewClientWithCredentials("https://example.com", "testuser", "testpass")
assert.Equal(t, "https://example.com", client.BaseURL)
// Check that auth provider is UsernamePasswordProvider
authProvider, ok := client.AuthProvider.(*UsernamePasswordProvider)
require.True(t, ok, "AuthProvider should be UsernamePasswordProvider")
assert.Equal(t, "testuser", authProvider.Username)
assert.Equal(t, "testpass", authProvider.Password)
assert.Equal(t, "https://example.com", authProvider.BaseURL)
}