mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-07-25 12:22:44 +00:00
Add server and RPC handler tests: - gateway/ratelimit_test.go: rate limiter pure unit - gateway/event_filter_test.go: event routing logic - gateway/server_test.go: handleHealth, tokenAuth, checkOrigin, desktopCORS - gateway/methods/sessions_test.go: sessions RPC handlers - gateway/methods/skills_test.go: skills RPC handlers - gateway/methods/cron_test.go: cron RPC handlers - http/files_path_security_test.go: path traversal, workspace boundary, auth - http/auth_helpers_test.go: extractBearerToken, tokenMatch, extractUserID/AgentID
185 lines
5.6 KiB
Go
185 lines
5.6 KiB
Go
package http
|
|
|
|
import (
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// Tests for auth helper functions not covered by the existing auth_test.go.
|
|
// auth_test.go covers resolveAuth/requireAuth at a higher level.
|
|
// These tests exercise the low-level helpers directly.
|
|
|
|
// ---- extractBearerToken ----
|
|
|
|
func TestExtractBearerToken_ValidBearer(t *testing.T) {
|
|
r := httptest.NewRequest("GET", "/", nil)
|
|
r.Header.Set("Authorization", "Bearer my-secret-token")
|
|
got := extractBearerToken(r)
|
|
if got != "my-secret-token" {
|
|
t.Errorf("extractBearerToken = %q, want %q", got, "my-secret-token")
|
|
}
|
|
}
|
|
|
|
func TestExtractBearerToken_NoAuthHeader_ReturnsEmpty(t *testing.T) {
|
|
r := httptest.NewRequest("GET", "/", nil)
|
|
if got := extractBearerToken(r); got != "" {
|
|
t.Errorf("extractBearerToken with no header = %q, want empty", got)
|
|
}
|
|
}
|
|
|
|
func TestExtractBearerToken_BasicScheme_ReturnsEmpty(t *testing.T) {
|
|
r := httptest.NewRequest("GET", "/", nil)
|
|
r.Header.Set("Authorization", "Basic dXNlcjpwYXNz")
|
|
if got := extractBearerToken(r); got != "" {
|
|
t.Errorf("extractBearerToken with Basic scheme = %q, want empty", got)
|
|
}
|
|
}
|
|
|
|
func TestExtractBearerToken_EmptyBearerValue(t *testing.T) {
|
|
r := httptest.NewRequest("GET", "/", nil)
|
|
r.Header.Set("Authorization", "Bearer ")
|
|
got := extractBearerToken(r)
|
|
// "Bearer " → TrimPrefix("Bearer ") = "" — returns empty string
|
|
if got != "" {
|
|
t.Errorf("extractBearerToken with empty bearer value = %q, want empty", got)
|
|
}
|
|
}
|
|
|
|
// ---- tokenMatch ----
|
|
|
|
func TestTokenMatch_EmptyExpected_AlwaysTrue(t *testing.T) {
|
|
// No auth configured → tokenMatch returns true for any provided value.
|
|
if !tokenMatch("anything", "") {
|
|
t.Error("tokenMatch with empty expected should return true (no auth mode)")
|
|
}
|
|
if !tokenMatch("", "") {
|
|
t.Error("tokenMatch with both empty should return true")
|
|
}
|
|
}
|
|
|
|
func TestTokenMatch_CorrectToken_ReturnsTrue(t *testing.T) {
|
|
if !tokenMatch("secret123", "secret123") {
|
|
t.Error("tokenMatch with matching token should return true")
|
|
}
|
|
}
|
|
|
|
func TestTokenMatch_WrongToken_ReturnsFalse(t *testing.T) {
|
|
if tokenMatch("wrong", "secret123") {
|
|
t.Error("tokenMatch with wrong token should return false")
|
|
}
|
|
}
|
|
|
|
func TestTokenMatch_EmptyProvided_ReturnsFalse(t *testing.T) {
|
|
if tokenMatch("", "secret123") {
|
|
t.Error("tokenMatch with empty provided should return false when expected is set")
|
|
}
|
|
}
|
|
|
|
// ---- extractUserID ----
|
|
|
|
func TestExtractUserID_ValidHeader(t *testing.T) {
|
|
r := httptest.NewRequest("GET", "/", nil)
|
|
r.Header.Set("X-GoClaw-User-Id", "user-abc-123")
|
|
if got := extractUserID(r); got != "user-abc-123" {
|
|
t.Errorf("extractUserID = %q, want %q", got, "user-abc-123")
|
|
}
|
|
}
|
|
|
|
func TestExtractUserID_NoHeader_ReturnsEmpty(t *testing.T) {
|
|
r := httptest.NewRequest("GET", "/", nil)
|
|
if got := extractUserID(r); got != "" {
|
|
t.Errorf("extractUserID with no header = %q, want empty", got)
|
|
}
|
|
}
|
|
|
|
func TestExtractUserID_TooLongID_ReturnsEmpty(t *testing.T) {
|
|
// MaxUserIDLength is 255 — send 256 chars
|
|
longID := strings.Repeat("a", 256)
|
|
r := httptest.NewRequest("GET", "/", nil)
|
|
r.Header.Set("X-GoClaw-User-Id", longID)
|
|
if got := extractUserID(r); got != "" {
|
|
t.Errorf("extractUserID with too-long ID = %q, want empty (security rejection)", got)
|
|
}
|
|
}
|
|
|
|
// ---- extractAgentID ----
|
|
|
|
func TestExtractAgentID_FromModelPrefix_Goclaw(t *testing.T) {
|
|
got := extractAgentID(httptest.NewRequest("GET", "/", nil), "goclaw:my-agent")
|
|
if got != "my-agent" {
|
|
t.Errorf("extractAgentID goclaw: prefix = %q, want %q", got, "my-agent")
|
|
}
|
|
}
|
|
|
|
func TestExtractAgentID_FromModelPrefix_Agent(t *testing.T) {
|
|
got := extractAgentID(httptest.NewRequest("GET", "/", nil), "agent:support-bot")
|
|
if got != "support-bot" {
|
|
t.Errorf("extractAgentID agent: prefix = %q, want %q", got, "support-bot")
|
|
}
|
|
}
|
|
|
|
func TestExtractAgentID_FromHeader_XGoClawAgentId(t *testing.T) {
|
|
r := httptest.NewRequest("GET", "/", nil)
|
|
r.Header.Set("X-GoClaw-Agent-Id", "header-agent")
|
|
got := extractAgentID(r, "some-other-model")
|
|
if got != "header-agent" {
|
|
t.Errorf("extractAgentID from X-GoClaw-Agent-Id = %q, want %q", got, "header-agent")
|
|
}
|
|
}
|
|
|
|
func TestExtractAgentID_FromHeader_XGoClawAgent(t *testing.T) {
|
|
r := httptest.NewRequest("GET", "/", nil)
|
|
r.Header.Set("X-GoClaw-Agent", "legacy-agent")
|
|
got := extractAgentID(r, "")
|
|
if got != "legacy-agent" {
|
|
t.Errorf("extractAgentID from X-GoClaw-Agent = %q, want %q", got, "legacy-agent")
|
|
}
|
|
}
|
|
|
|
func TestExtractAgentID_DefaultsToDefault(t *testing.T) {
|
|
r := httptest.NewRequest("GET", "/", nil)
|
|
got := extractAgentID(r, "gpt-4o")
|
|
if got != "default" {
|
|
t.Errorf("extractAgentID no match = %q, want 'default'", got)
|
|
}
|
|
}
|
|
|
|
func TestExtractAgentID_ModelPrefixTakesPriorityOverHeader(t *testing.T) {
|
|
r := httptest.NewRequest("GET", "/", nil)
|
|
r.Header.Set("X-GoClaw-Agent-Id", "header-agent")
|
|
got := extractAgentID(r, "goclaw:model-agent")
|
|
if got != "model-agent" {
|
|
t.Errorf("extractAgentID model prefix should take priority, got %q", got)
|
|
}
|
|
}
|
|
|
|
// ---- isHTTPOwnerID ----
|
|
|
|
func TestIsHTTPOwnerID_MatchesConfiguredOwner(t *testing.T) {
|
|
if !isHTTPOwnerID("alice", []string{"alice", "bob"}) {
|
|
t.Error("alice should be owner")
|
|
}
|
|
}
|
|
|
|
func TestIsHTTPOwnerID_EmptyUserID_NotOwner(t *testing.T) {
|
|
if isHTTPOwnerID("", []string{"alice"}) {
|
|
t.Error("empty user ID should never be owner")
|
|
}
|
|
}
|
|
|
|
func TestIsHTTPOwnerID_EmptyOwnerList_OnlySystemIsOwner(t *testing.T) {
|
|
if !isHTTPOwnerID("system", nil) {
|
|
t.Error("'system' is default owner when no owner IDs configured")
|
|
}
|
|
if isHTTPOwnerID("admin", nil) {
|
|
t.Error("non-system user should not be owner with empty owner list")
|
|
}
|
|
}
|
|
|
|
func TestIsHTTPOwnerID_UnknownUser_NotOwner(t *testing.T) {
|
|
if isHTTPOwnerID("charlie", []string{"alice", "bob"}) {
|
|
t.Error("charlie is not in owner list")
|
|
}
|
|
}
|