pricing_service_test.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. package repository
  2. import (
  3. "context"
  4. "net/http"
  5. "net/http/httptest"
  6. "testing"
  7. "github.com/stretchr/testify/require"
  8. "github.com/stretchr/testify/suite"
  9. )
  10. type PricingServiceSuite struct {
  11. suite.Suite
  12. ctx context.Context
  13. srv *httptest.Server
  14. client *pricingRemoteClient
  15. }
  16. func (s *PricingServiceSuite) SetupTest() {
  17. s.ctx = context.Background()
  18. client, ok := NewPricingRemoteClient("", false).(*pricingRemoteClient)
  19. require.True(s.T(), ok, "type assertion failed")
  20. s.client = client
  21. }
  22. func (s *PricingServiceSuite) TearDownTest() {
  23. if s.srv != nil {
  24. s.srv.Close()
  25. s.srv = nil
  26. }
  27. }
  28. func (s *PricingServiceSuite) setupServer(handler http.HandlerFunc) {
  29. s.srv = newLocalTestServer(s.T(), handler)
  30. }
  31. func (s *PricingServiceSuite) TestFetchPricingJSON_Success() {
  32. s.setupServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  33. if r.URL.Path == "/ok" {
  34. w.WriteHeader(http.StatusOK)
  35. _, _ = w.Write([]byte(`{"ok":true}`))
  36. return
  37. }
  38. w.WriteHeader(http.StatusInternalServerError)
  39. }))
  40. body, err := s.client.FetchPricingJSON(s.ctx, s.srv.URL+"/ok")
  41. require.NoError(s.T(), err, "FetchPricingJSON")
  42. require.Equal(s.T(), `{"ok":true}`, string(body), "body mismatch")
  43. }
  44. func (s *PricingServiceSuite) TestFetchPricingJSON_NonOKStatus() {
  45. s.setupServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  46. w.WriteHeader(http.StatusInternalServerError)
  47. }))
  48. _, err := s.client.FetchPricingJSON(s.ctx, s.srv.URL+"/err")
  49. require.Error(s.T(), err, "expected error for non-200 status")
  50. }
  51. func (s *PricingServiceSuite) TestFetchHashText_ParsesFields() {
  52. s.setupServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  53. switch r.URL.Path {
  54. case "/hashfile":
  55. w.WriteHeader(http.StatusOK)
  56. _, _ = w.Write([]byte("abc123 model_prices.json\n"))
  57. case "/hashonly":
  58. w.WriteHeader(http.StatusOK)
  59. _, _ = w.Write([]byte("def456\n"))
  60. default:
  61. w.WriteHeader(http.StatusNotFound)
  62. }
  63. }))
  64. hash, err := s.client.FetchHashText(s.ctx, s.srv.URL+"/hashfile")
  65. require.NoError(s.T(), err, "FetchHashText")
  66. require.Equal(s.T(), "abc123", hash, "hash mismatch")
  67. hash2, err := s.client.FetchHashText(s.ctx, s.srv.URL+"/hashonly")
  68. require.NoError(s.T(), err, "FetchHashText")
  69. require.Equal(s.T(), "def456", hash2, "hash mismatch")
  70. }
  71. func (s *PricingServiceSuite) TestFetchHashText_NonOKStatus() {
  72. s.setupServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  73. w.WriteHeader(http.StatusNotFound)
  74. }))
  75. _, err := s.client.FetchHashText(s.ctx, s.srv.URL+"/nope")
  76. require.Error(s.T(), err, "expected error for non-200 status")
  77. }
  78. func (s *PricingServiceSuite) TestFetchPricingJSON_InvalidURL() {
  79. _, err := s.client.FetchPricingJSON(s.ctx, "://invalid-url")
  80. require.Error(s.T(), err, "expected error for invalid URL")
  81. }
  82. func (s *PricingServiceSuite) TestFetchHashText_EmptyBody() {
  83. s.setupServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  84. w.WriteHeader(http.StatusOK)
  85. // empty body
  86. }))
  87. hash, err := s.client.FetchHashText(s.ctx, s.srv.URL+"/empty")
  88. require.NoError(s.T(), err, "FetchHashText empty body should not error")
  89. require.Equal(s.T(), "", hash, "expected empty hash")
  90. }
  91. func (s *PricingServiceSuite) TestFetchHashText_WhitespaceOnly() {
  92. s.setupServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  93. w.WriteHeader(http.StatusOK)
  94. _, _ = w.Write([]byte(" \n"))
  95. }))
  96. hash, err := s.client.FetchHashText(s.ctx, s.srv.URL+"/ws")
  97. require.NoError(s.T(), err, "FetchHashText whitespace body should not error")
  98. require.Equal(s.T(), "", hash, "expected empty hash after trimming")
  99. }
  100. func (s *PricingServiceSuite) TestFetchPricingJSON_ContextCancel() {
  101. started := make(chan struct{})
  102. s.setupServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  103. close(started)
  104. <-r.Context().Done()
  105. }))
  106. ctx, cancel := context.WithCancel(s.ctx)
  107. done := make(chan error, 1)
  108. go func() {
  109. _, err := s.client.FetchPricingJSON(ctx, s.srv.URL+"/block")
  110. done <- err
  111. }()
  112. <-started
  113. cancel()
  114. err := <-done
  115. require.Error(s.T(), err)
  116. }
  117. func TestNewPricingRemoteClient_InvalidProxy_NoFallback(t *testing.T) {
  118. client := NewPricingRemoteClient("://bad", false)
  119. _, ok := client.(*pricingRemoteClientError)
  120. require.True(t, ok, "should return error client when proxy is invalid and fallback disabled")
  121. _, err := client.FetchPricingJSON(context.Background(), "http://example.com")
  122. require.Error(t, err)
  123. require.Contains(t, err.Error(), "proxy client init failed")
  124. }
  125. func TestNewPricingRemoteClient_InvalidProxy_WithFallback(t *testing.T) {
  126. client := NewPricingRemoteClient("://bad", true)
  127. _, ok := client.(*pricingRemoteClient)
  128. require.True(t, ok, "should fallback to direct client when allowed")
  129. }
  130. func TestPricingServiceSuite(t *testing.T) {
  131. suite.Run(t, new(PricingServiceSuite))
  132. }