request_test.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. package nodestats
  2. import (
  3. "io"
  4. "net/http"
  5. "strings"
  6. "testing"
  7. )
  8. // MockHttpClient is a mock implementation of HttpClient interface for testing
  9. type MockHttpClient struct {
  10. // capturedRequest stores the last request that was sent
  11. capturedRequest *http.Request
  12. // responseToReturn is the response that will be returned by Do
  13. responseToReturn *http.Response
  14. // errorToReturn is the error that will be returned by Do
  15. errorToReturn error
  16. }
  17. // Do captures the request and returns the configured response/error
  18. func (m *MockHttpClient) Do(req *http.Request) (*http.Response, error) {
  19. m.capturedRequest = req
  20. return m.responseToReturn, m.errorToReturn
  21. }
  22. // GetCapturedRequest returns the last captured request
  23. func (m *MockHttpClient) GetCapturedRequest() *http.Request {
  24. return m.capturedRequest
  25. }
  26. func TestNodeHttpClient_BearerTokenCapitalization(t *testing.T) {
  27. tests := []struct {
  28. name string
  29. bearerToken string
  30. wantHeader string
  31. }{
  32. {
  33. name: "Bearer token with correct capitalization",
  34. bearerToken: "test-token-123",
  35. wantHeader: "Bearer test-token-123",
  36. },
  37. {
  38. name: "Empty bearer token should not set header",
  39. bearerToken: "",
  40. wantHeader: "",
  41. },
  42. {
  43. name: "Bearer token with special characters",
  44. bearerToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.test",
  45. wantHeader: "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.test",
  46. },
  47. }
  48. for _, tt := range tests {
  49. t.Run(tt.name, func(t *testing.T) {
  50. // Create a mock HTTP client
  51. mockClient := &MockHttpClient{
  52. responseToReturn: &http.Response{
  53. StatusCode: 200,
  54. Body: io.NopCloser(strings.NewReader("")),
  55. },
  56. }
  57. // Create NodeHttpClient with mock
  58. nodeClient := NewNodeHttpClient(mockClient)
  59. // Make request through the client
  60. _, err := nodeClient.AttemptEndPoint("GET", "http://example.com/test", tt.bearerToken)
  61. if err != nil {
  62. t.Fatalf("AttemptEndPoint returned error: %v", err)
  63. }
  64. // Get the captured request
  65. capturedReq := mockClient.GetCapturedRequest()
  66. if capturedReq == nil {
  67. t.Fatal("Expected request to be captured, but got nil")
  68. }
  69. // Verify the Authorization header
  70. authHeader := capturedReq.Header.Get("Authorization")
  71. if tt.wantHeader == "" {
  72. // Empty token case - should not have Authorization header
  73. if authHeader != "" {
  74. t.Errorf("Expected no Authorization header, but got: %s", authHeader)
  75. }
  76. } else {
  77. // Verify exact header value including capitalization
  78. if authHeader != tt.wantHeader {
  79. t.Errorf("Authorization header = %q, want %q", authHeader, tt.wantHeader)
  80. }
  81. // Specifically verify "Bearer" capitalization (capital B, lowercase rest)
  82. if !strings.HasPrefix(authHeader, "Bearer ") {
  83. t.Errorf("Authorization header does not start with 'Bearer ' (with capital B): %s", authHeader)
  84. }
  85. // Check for common incorrect capitalizations
  86. if strings.HasPrefix(authHeader, "bearer ") {
  87. t.Error("Authorization header uses lowercase 'bearer' instead of 'Bearer'")
  88. }
  89. if strings.HasPrefix(authHeader, "BEARER ") {
  90. t.Error("Authorization header uses uppercase 'BEARER' instead of 'Bearer'")
  91. }
  92. }
  93. })
  94. }
  95. }
  96. func TestNodeHttpClient_BearerCapitalization_HappyPath(t *testing.T) {
  97. // HAPPY PATH TEST: Verify "Bearer" uses correct capitalization (capital B, lowercase e-a-r-e-r)
  98. // According to RFC 6750, the correct format is "Bearer" not "bearer" or "BEARER"
  99. mockClient := &MockHttpClient{
  100. responseToReturn: &http.Response{
  101. StatusCode: 200,
  102. Body: io.NopCloser(strings.NewReader("")),
  103. },
  104. }
  105. nodeClient := NewNodeHttpClient(mockClient)
  106. bearerToken := "test-token-123"
  107. _, err := nodeClient.makeRequest("GET", "http://example.com/api", bearerToken)
  108. if err != nil {
  109. t.Fatalf("makeRequest returned error: %v", err)
  110. }
  111. capturedReq := mockClient.GetCapturedRequest()
  112. authHeader := capturedReq.Header.Get("Authorization")
  113. // PRIMARY ASSERTION: Verify exact string match with correct capitalization
  114. expectedHeader := "Bearer test-token-123"
  115. if authHeader != expectedHeader {
  116. t.Errorf("FAILED: Authorization header = %q, want %q", authHeader, expectedHeader)
  117. t.Errorf(" This means 'Bearer' capitalization is incorrect")
  118. }
  119. // EXPLICIT CHECK: Verify "Bearer" has capital B
  120. if !strings.HasPrefix(authHeader, "Bearer ") {
  121. t.Errorf("FAILED: Authorization header must start with 'Bearer ' (capital B, lowercase e-a-r-e-r)")
  122. t.Errorf(" Got: %q", authHeader)
  123. }
  124. // SUCCESS: Log happy path success
  125. if authHeader == expectedHeader {
  126. t.Logf("✓ PASS: Bearer token has correct capitalization: %q", authHeader)
  127. }
  128. }
  129. func TestNodeHttpClient_MakeRequestWithBearerToken(t *testing.T) {
  130. // Create a mock HTTP client
  131. mockClient := &MockHttpClient{
  132. responseToReturn: &http.Response{
  133. StatusCode: 200,
  134. Body: io.NopCloser(strings.NewReader(`{"status":"ok"}`)),
  135. },
  136. }
  137. // Create NodeHttpClient with mock
  138. nodeClient := NewNodeHttpClient(mockClient)
  139. // Test with a bearer token
  140. bearerToken := "my-secret-token"
  141. _, err := nodeClient.makeRequest("GET", "http://example.com/api", bearerToken)
  142. if err != nil {
  143. t.Fatalf("makeRequest returned error: %v", err)
  144. }
  145. // Verify the Authorization header
  146. capturedReq := mockClient.GetCapturedRequest()
  147. if capturedReq == nil {
  148. t.Fatal("Expected request to be captured")
  149. }
  150. authHeader := capturedReq.Header.Get("Authorization")
  151. expectedHeader := "Bearer my-secret-token"
  152. if authHeader != expectedHeader {
  153. t.Errorf("Authorization header = %q, want %q", authHeader, expectedHeader)
  154. }
  155. // Verify the exact capitalization of "Bearer"
  156. if !strings.HasPrefix(authHeader, "Bearer ") {
  157. t.Errorf("Expected 'Bearer ' with capital B, got: %s", authHeader)
  158. }
  159. }
  160. func TestNodeHttpClient_NoAuthorizationHeaderWhenTokenEmpty(t *testing.T) {
  161. // Create a mock HTTP client
  162. mockClient := &MockHttpClient{
  163. responseToReturn: &http.Response{
  164. StatusCode: 200,
  165. Body: io.NopCloser(strings.NewReader("")),
  166. },
  167. }
  168. // Create NodeHttpClient with mock
  169. nodeClient := NewNodeHttpClient(mockClient)
  170. // Test with empty bearer token
  171. _, err := nodeClient.makeRequest("GET", "http://example.com/api", "")
  172. if err != nil {
  173. t.Fatalf("makeRequest returned error: %v", err)
  174. }
  175. // Verify no Authorization header is set
  176. capturedReq := mockClient.GetCapturedRequest()
  177. if capturedReq == nil {
  178. t.Fatal("Expected request to be captured")
  179. }
  180. authHeader := capturedReq.Header.Get("Authorization")
  181. if authHeader != "" {
  182. t.Errorf("Expected no Authorization header when token is empty, got: %s", authHeader)
  183. }
  184. }
  185. func TestNodeHttpClient_RequestMethod(t *testing.T) {
  186. tests := []struct {
  187. name string
  188. method string
  189. }{
  190. {"GET request", "GET"},
  191. {"POST request", "POST"},
  192. {"PUT request", "PUT"},
  193. {"DELETE request", "DELETE"},
  194. }
  195. for _, tt := range tests {
  196. t.Run(tt.name, func(t *testing.T) {
  197. mockClient := &MockHttpClient{
  198. responseToReturn: &http.Response{
  199. StatusCode: 200,
  200. Body: io.NopCloser(strings.NewReader("")),
  201. },
  202. }
  203. nodeClient := NewNodeHttpClient(mockClient)
  204. _, err := nodeClient.makeRequest(tt.method, "http://example.com/test", "token123")
  205. if err != nil {
  206. t.Fatalf("makeRequest returned error: %v", err)
  207. }
  208. capturedReq := mockClient.GetCapturedRequest()
  209. if capturedReq.Method != tt.method {
  210. t.Errorf("Request method = %s, want %s", capturedReq.Method, tt.method)
  211. }
  212. // Also verify Bearer token is set correctly regardless of HTTP method
  213. authHeader := capturedReq.Header.Get("Authorization")
  214. if authHeader != "Bearer token123" {
  215. t.Errorf("Authorization header = %q, want %q", authHeader, "Bearer token123")
  216. }
  217. })
  218. }
  219. }