client_test.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
  2. // SPDX-License-Identifier: Apache-2.0
  3. package client
  4. import (
  5. "errors"
  6. "io"
  7. "net"
  8. "net/http"
  9. "net/http/httptest"
  10. "testing"
  11. "time"
  12. "miniflux.app/v2/internal/config"
  13. "miniflux.app/v2/internal/version"
  14. )
  15. func TestNewClientWithoutBlockingPrivateNetworks(t *testing.T) {
  16. server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  17. w.WriteHeader(http.StatusOK)
  18. }))
  19. defer server.Close()
  20. client := NewClientWithOptions(Options{Timeout: 5 * time.Second})
  21. resp, err := client.Get(server.URL)
  22. if err != nil {
  23. t.Fatalf("Expected no error, got %v", err)
  24. }
  25. defer resp.Body.Close()
  26. if resp.StatusCode != http.StatusOK {
  27. t.Fatalf("Expected status 200, got %d", resp.StatusCode)
  28. }
  29. }
  30. func TestBlockPrivateNetworksBlocksLoopback(t *testing.T) {
  31. server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  32. w.WriteHeader(http.StatusOK)
  33. }))
  34. defer server.Close()
  35. client := NewClientWithOptions(Options{Timeout: 5 * time.Second, BlockPrivateNetworks: true})
  36. _, err := client.Get(server.URL)
  37. if err == nil {
  38. t.Fatal("Expected an error when connecting to loopback address, got nil")
  39. }
  40. if !errors.Is(err, ErrPrivateNetwork) {
  41. t.Fatalf("Expected ErrPrivateNetwork, got %v", err)
  42. }
  43. }
  44. func TestBlockPrivateNetworksAllowsPublicIPs(t *testing.T) {
  45. client := NewClientWithOptions(Options{Timeout: 5 * time.Second, BlockPrivateNetworks: true})
  46. if client == nil {
  47. t.Fatal("Expected non-nil client")
  48. }
  49. transport, ok := client.Transport.(*http.Transport)
  50. if !ok {
  51. t.Fatal("Expected custom http.Transport when blockPrivateNetworks is true")
  52. }
  53. if transport.DialContext == nil {
  54. t.Fatal("Expected custom DialContext when blockPrivateNetworks is true")
  55. }
  56. }
  57. func TestNoCustomTransportWhenNotBlocking(t *testing.T) {
  58. client := NewClientWithOptions(Options{Timeout: 5 * time.Second})
  59. if client.Transport != nil {
  60. t.Fatal("Expected nil transport when blockPrivateNetworks is false")
  61. }
  62. }
  63. func TestBlockPrivateNetworksBlocksPrivateIP(t *testing.T) {
  64. listener, err := net.Listen("tcp", "127.0.0.1:0")
  65. if err != nil {
  66. t.Fatalf("Failed to create listener: %v", err)
  67. }
  68. defer listener.Close()
  69. server := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  70. w.WriteHeader(http.StatusOK)
  71. }))
  72. server.Listener = listener
  73. server.Start()
  74. defer server.Close()
  75. client := NewClientWithOptions(Options{Timeout: 5 * time.Second, BlockPrivateNetworks: true})
  76. _, err = client.Get(server.URL)
  77. if err == nil {
  78. t.Fatal("Expected error when connecting to private IP")
  79. }
  80. if !errors.Is(err, ErrPrivateNetwork) {
  81. t.Fatalf("Expected ErrPrivateNetwork, got: %v", err)
  82. }
  83. }
  84. func TestBlockPrivateNetworksAllowsLoopbackWhenDisabled(t *testing.T) {
  85. server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  86. w.WriteHeader(http.StatusOK)
  87. }))
  88. defer server.Close()
  89. client := NewClientWithOptions(Options{Timeout: 5 * time.Second})
  90. resp, err := client.Get(server.URL)
  91. if err != nil {
  92. t.Fatalf("Expected no error when blockPrivateNetworks is false, got %v", err)
  93. }
  94. defer resp.Body.Close()
  95. if resp.StatusCode != http.StatusOK {
  96. t.Fatalf("Expected status 200, got %d", resp.StatusCode)
  97. }
  98. }
  99. func TestRequestBuilderWithJSON(t *testing.T) {
  100. configureIntegrationAllowPrivateNetworksOption(t)
  101. var gotMethod, gotContentType, gotUserAgent, gotAuth, gotBody string
  102. server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  103. gotMethod = r.Method
  104. gotContentType = r.Header.Get("Content-Type")
  105. gotUserAgent = r.Header.Get("User-Agent")
  106. gotAuth = r.Header.Get("Authorization")
  107. body, _ := io.ReadAll(r.Body)
  108. gotBody = string(body)
  109. w.WriteHeader(http.StatusCreated)
  110. }))
  111. defer server.Close()
  112. response, err := NewRequestBuilder(server.URL).
  113. WithMethod(http.MethodPost).
  114. WithHeader("Authorization", "Bearer secret").
  115. WithJSON(map[string]string{"hello": "world"}).
  116. Do()
  117. if err != nil {
  118. t.Fatalf("request execution failed: %v", err)
  119. }
  120. defer response.Body.Close()
  121. if response.StatusCode != http.StatusCreated {
  122. t.Errorf("expected status %d, got %d", http.StatusCreated, response.StatusCode)
  123. }
  124. if gotMethod != http.MethodPost {
  125. t.Errorf("expected method POST, got %s", gotMethod)
  126. }
  127. if gotContentType != "application/json" {
  128. t.Errorf("expected Content-Type application/json, got %q", gotContentType)
  129. }
  130. if want := "Miniflux/" + version.Version; gotUserAgent != want {
  131. t.Errorf("expected User-Agent %q, got %q", want, gotUserAgent)
  132. }
  133. if gotAuth != "Bearer secret" {
  134. t.Errorf("expected Authorization %q, got %q", "Bearer secret", gotAuth)
  135. }
  136. if gotBody != `{"hello":"world"}` {
  137. t.Errorf("expected body %q, got %q", `{"hello":"world"}`, gotBody)
  138. }
  139. }
  140. func TestRequestBuilderWithInvalidEndpoint(t *testing.T) {
  141. _, err := NewRequestBuilder("://invalid").WithMethod(http.MethodPost).WithJSON(nil).Do()
  142. if err == nil {
  143. t.Fatal("expected an error for an invalid endpoint, got nil")
  144. }
  145. }
  146. func configureIntegrationAllowPrivateNetworksOption(t *testing.T) {
  147. t.Helper()
  148. t.Setenv("INTEGRATION_ALLOW_PRIVATE_NETWORKS", "1")
  149. configParser := config.NewConfigParser()
  150. parsedOptions, err := configParser.ParseEnvironmentVariables()
  151. if err != nil {
  152. t.Fatalf("Unable to configure test options: %v", err)
  153. }
  154. previousOptions := config.Opts
  155. config.Opts = parsedOptions
  156. t.Cleanup(func() {
  157. config.Opts = previousOptions
  158. })
  159. }