test: Add ensureScope tests for API package

Add comprehensive tests for the ensureScope function covering:
- Empty scope parameter (always allows access)
- No token in context (session-based request, allows access)
- Token with matching scope (allows access)
- Token with multiple scopes including required one (allows access)
- Token missing required scope (rejects with 403)
- Token with empty scopes (defaults to wildcard, allows access)
- Rejection returns proper JSON response format

Coverage: ensureScope 0% → 100%
Coverage: API package 32.1% → 32.2%
This commit is contained in:
rcourtman 2025-12-02 13:19:11 +00:00
parent 301338d800
commit ef6a370144

View file

@ -4,6 +4,7 @@ import (
"context"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/rcourtman/pulse-go-rewrite/internal/config"
@ -119,3 +120,115 @@ func TestGetAPITokenRecordFromRequestNoValue(t *testing.T) {
t.Fatal("expected nil when no token in context")
}
}
// ensureScope tests
func TestEnsureScope_EmptyScopeAllowsAll(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/", nil)
rr := httptest.NewRecorder()
// Empty scope should always return true
result := ensureScope(rr, req, "")
if !result {
t.Fatal("expected ensureScope to return true for empty scope")
}
if rr.Code != http.StatusOK {
t.Fatalf("expected no status written (200 default), got %d", rr.Code)
}
}
func TestEnsureScope_NoTokenAllowsAccess(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/", nil)
rr := httptest.NewRecorder()
// No token attached - should allow access (session-based request)
result := ensureScope(rr, req, config.ScopeSettingsWrite)
if !result {
t.Fatal("expected ensureScope to return true when no token (session request)")
}
}
func TestEnsureScope_TokenWithMatchingScopeAllowsAccess(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/", nil)
record := config.APITokenRecord{ID: "token-1", Scopes: []string{config.ScopeMonitoringRead}}
attachAPITokenRecord(req, &record)
rr := httptest.NewRecorder()
result := ensureScope(rr, req, config.ScopeMonitoringRead)
if !result {
t.Fatal("expected ensureScope to return true when token has matching scope")
}
}
func TestEnsureScope_TokenWithMultipleScopesAllowsAccess(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/", nil)
record := config.APITokenRecord{
ID: "token-multi",
Scopes: []string{config.ScopeMonitoringRead, config.ScopeSettingsWrite, config.ScopeDockerReport},
}
attachAPITokenRecord(req, &record)
rr := httptest.NewRecorder()
result := ensureScope(rr, req, config.ScopeSettingsWrite)
if !result {
t.Fatal("expected ensureScope to return true when token has required scope among multiple")
}
}
func TestEnsureScope_TokenMissingScopeRejects(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/", nil)
record := config.APITokenRecord{ID: "token-2", Scopes: []string{config.ScopeMonitoringRead}}
attachAPITokenRecord(req, &record)
rr := httptest.NewRecorder()
result := ensureScope(rr, req, config.ScopeSettingsWrite)
if result {
t.Fatal("expected ensureScope to return false when token missing required scope")
}
if rr.Code != http.StatusForbidden {
t.Fatalf("expected status 403, got %d", rr.Code)
}
}
func TestEnsureScope_TokenWithEmptyScopesDefaultsToWildcard(t *testing.T) {
// Note: Empty scopes defaults to wildcard access via ensureScopes()
req := httptest.NewRequest(http.MethodGet, "/", nil)
record := config.APITokenRecord{ID: "token-empty", Scopes: []string{}}
attachAPITokenRecord(req, &record)
rr := httptest.NewRecorder()
// Empty scopes defaults to ScopeWildcard, so any scope should be allowed
result := ensureScope(rr, req, config.ScopeDockerReport)
if !result {
t.Fatal("expected ensureScope to return true (empty scopes defaults to wildcard)")
}
}
func TestEnsureScope_RejectsWithProperJSONResponse(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/", nil)
record := config.APITokenRecord{ID: "token-3", Scopes: []string{"other:scope"}}
attachAPITokenRecord(req, &record)
rr := httptest.NewRecorder()
result := ensureScope(rr, req, config.ScopeSettingsWrite)
if result {
t.Fatal("expected ensureScope to return false")
}
// Check response format
if ct := rr.Header().Get("Content-Type"); ct != "application/json" {
t.Fatalf("expected Content-Type application/json, got %s", ct)
}
body := rr.Body.String()
if body == "" {
t.Fatal("expected JSON body in response")
}
// Body should contain error and requiredScope
if !strings.Contains(body, "missing_scope") {
t.Fatalf("expected 'missing_scope' in body, got: %s", body)
}
if !strings.Contains(body, config.ScopeSettingsWrite) {
t.Fatalf("expected required scope in body, got: %s", body)
}
}