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  }