github.com/prebid/prebid-server/v2@v2.18.0/stored_requests/events/http/http_test.go (about) 1 package http 2 3 import ( 4 "context" 5 "fmt" 6 httpCore "net/http" 7 "net/http/httptest" 8 "testing" 9 "time" 10 11 "github.com/prebid/prebid-server/v2/util/jsonutil" 12 "github.com/stretchr/testify/assert" 13 ) 14 15 func ctxProducer() (context.Context, func()) { 16 return context.WithTimeout(context.Background(), -1) 17 } 18 19 func TestStartup(t *testing.T) { 20 type testStep struct { 21 statusCode int 22 response string 23 timeout bool 24 saves string 25 invalidations string 26 } 27 testCases := []struct { 28 description string 29 tests []testStep 30 }{ 31 { 32 description: "Load requests at startup", 33 tests: []testStep{ 34 { 35 statusCode: httpCore.StatusOK, 36 response: `{"requests": {"request1": {"value":1}, "request2": {"value":2}}}`, 37 saves: `{"requests": {"request1": {"value":1}, "request2": {"value":2}}, "imps": null, "responses": null, "accounts": null}`, 38 }, 39 }, 40 }, 41 { 42 description: "Load imps at startup", 43 tests: []testStep{ 44 { 45 statusCode: httpCore.StatusOK, 46 response: `{"imps": {"imp1": {"value":1}}}`, 47 saves: `{"imps": {"imp1": {"value":1}}, "requests": null, "responses": null, "accounts": null}`, 48 }, 49 }, 50 }, 51 { 52 description: "Load responses at startup", 53 tests: []testStep{ 54 { 55 statusCode: httpCore.StatusOK, 56 response: `{"responses": {"resp1": {"value":1}}}`, 57 saves: `{"responses": {"resp1": {"value":1}}, "imps": null, "requests": null, "accounts": null}`, 58 }, 59 }, 60 }, 61 { 62 description: "Load requests imps and responses then update", 63 tests: []testStep{ 64 { 65 statusCode: httpCore.StatusOK, 66 response: `{"requests": {"request1": {"value":1}, "request2": {"value":2}}, "imps": {"imp1": {"value":3}, "imp2": {"value":4}}, "responses": {"resp1": {"value":5}, "resp2": {"value":6}}}`, 67 saves: `{"requests": {"request1": {"value":1}, "request2": {"value":2}}, "imps": {"imp1": {"value":3}, "imp2": {"value":4}}, "responses": {"resp1": {"value":5}, "resp2": {"value":6}}, "accounts":null}`, 68 }, 69 { 70 statusCode: httpCore.StatusOK, 71 response: `{"requests": {"request1": {"value":7}, "request2": {"deleted":true}}, "imps": {"imp1": {"deleted":true}, "imp2": {"value":8}}, "responses": {"resp1": {"deleted":true}, "resp2": {"value":9}}}`, 72 saves: `{"requests": {"request1": {"value":7}}, "imps": {"imp2": {"value":8}}, "responses": {"resp2": {"value":9}}, "accounts":null}`, 73 invalidations: `{"requests": ["request2"], "imps": ["imp1"], "responses": ["resp1"], "accounts": []}`, 74 }, 75 }, 76 }, 77 { 78 description: "Load accounts then update", 79 tests: []testStep{ 80 { 81 statusCode: httpCore.StatusOK, 82 response: `{"accounts":{"account1":{"value":1}, "account2":{"value":2}}}`, 83 saves: `{"accounts":{"account1":{"value":1}, "account2":{"value":2}}, "imps": null, "requests": null, "responses": null}`, 84 }, 85 { 86 statusCode: httpCore.StatusOK, 87 response: `{"accounts":{"account1":{"value":5}, "account2":{"deleted": true}}}`, 88 saves: `{"accounts":{"account1":{"value":5}}, "imps": null, "requests": null, "responses": null}`, 89 invalidations: `{"accounts":["account2"], "requests": [], "imps": [], "responses":[]}`, 90 }, 91 }, 92 }, 93 { 94 description: "Load nothing at startup", 95 tests: []testStep{ 96 { 97 statusCode: httpCore.StatusOK, 98 response: `{}`, 99 }, 100 }, 101 }, 102 { 103 description: "Malformed response at startup", 104 tests: []testStep{ 105 { 106 statusCode: httpCore.StatusOK, 107 response: `{some bad json`, 108 }, 109 }, 110 }, 111 { 112 description: "Server error at startup", 113 tests: []testStep{ 114 { 115 statusCode: httpCore.StatusInternalServerError, 116 response: ``, 117 }, 118 }, 119 }, 120 { 121 description: "HTTP timeout error at startup", 122 tests: []testStep{ 123 { 124 timeout: true, 125 }, 126 }, 127 }, 128 } 129 for _, tests := range testCases { 130 t.Run(tests.description, func(t *testing.T) { 131 handler := &mockResponseHandler{} 132 server := httptest.NewServer(handler) 133 defer server.Close() 134 135 var ev *HTTPEvents 136 137 for i, test := range tests.tests { 138 handler.statusCode = test.statusCode 139 handler.response = test.response 140 if i == 0 { // NewHTTPEvents() calls the API immediately 141 if test.timeout { 142 ev = NewHTTPEvents(server.Client(), server.URL, ctxProducer, -1) // force timeout 143 } else { 144 ev = NewHTTPEvents(server.Client(), server.URL, nil, -1) 145 } 146 } else { // Second test triggers API call by initiating a 1s refresh loop 147 timeChan := make(chan time.Time, 1) 148 timeChan <- time.Now() 149 go ev.refresh(timeChan) 150 } 151 t.Run(fmt.Sprintf("Step %d", i+1), func(t *testing.T) { 152 // Check expected Saves 153 if len(test.saves) > 0 { 154 saves, err := jsonutil.Marshal(<-ev.Saves()) 155 assert.NoError(t, err, `Failed to marshal event.Save object: %v`, err) 156 assert.JSONEq(t, test.saves, string(saves)) 157 } 158 assert.Empty(t, ev.Saves(), "Unexpected additional messages in save channel") 159 // Check expected Invalidations 160 if len(test.invalidations) > 0 { 161 invalidations, err := jsonutil.Marshal(<-ev.Invalidations()) 162 assert.NoError(t, err, `Failed to marshal event.Invalidation object: %v`, err) 163 assert.JSONEq(t, test.invalidations, string(invalidations)) 164 } 165 assert.Empty(t, ev.Invalidations(), "Unexpected additional messages in invalidations channel") 166 }) 167 } 168 }) 169 } 170 } 171 172 type mockResponseHandler struct { 173 statusCode int 174 response string 175 } 176 177 func (m *mockResponseHandler) ServeHTTP(rw httpCore.ResponseWriter, r *httpCore.Request) { 178 rw.WriteHeader(m.statusCode) 179 rw.Write([]byte(m.response)) 180 }