github.com/prebid/prebid-server@v0.275.0/stored_requests/backends/http_fetcher/fetcher_test.go (about)

     1  package http_fetcher
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"net/http"
     8  	"net/http/httptest"
     9  	"strings"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/stretchr/testify/assert"
    14  )
    15  
    16  func TestSingleReq(t *testing.T) {
    17  	fetcher, close := newTestFetcher(t, []string{"req-1"}, nil)
    18  	defer close()
    19  
    20  	reqData, impData, errs := fetcher.FetchRequests(context.Background(), []string{"req-1"}, nil)
    21  	assert.Empty(t, errs, "Unexpected errors fetching known requests")
    22  	assertMapKeys(t, reqData, "req-1")
    23  	assert.Empty(t, impData, "Unexpected imps returned fetching just requests")
    24  }
    25  
    26  func TestSeveralReqs(t *testing.T) {
    27  	fetcher, close := newTestFetcher(t, []string{"req-1", "req-2"}, nil)
    28  	defer close()
    29  
    30  	reqData, impData, errs := fetcher.FetchRequests(context.Background(), []string{"req-1", "req-2"}, nil)
    31  	assert.Empty(t, errs, "Unexpected errors fetching known requests")
    32  	assertMapKeys(t, reqData, "req-1", "req-2")
    33  	assert.Empty(t, impData, "Unexpected imps returned fetching just requests")
    34  }
    35  
    36  func TestSingleImp(t *testing.T) {
    37  	fetcher, close := newTestFetcher(t, nil, []string{"imp-1"})
    38  	defer close()
    39  
    40  	reqData, impData, errs := fetcher.FetchRequests(context.Background(), nil, []string{"imp-1"})
    41  	assert.Empty(t, errs, "Unexpected errors fetching known imps")
    42  	assert.Empty(t, reqData, "Unexpected requests returned fetching just imps")
    43  	assertMapKeys(t, impData, "imp-1")
    44  }
    45  
    46  func TestSeveralImps(t *testing.T) {
    47  	fetcher, close := newTestFetcher(t, nil, []string{"imp-1", "imp-2"})
    48  	defer close()
    49  
    50  	reqData, impData, errs := fetcher.FetchRequests(context.Background(), nil, []string{"imp-1", "imp-2"})
    51  	assert.Empty(t, errs, "Unexpected errors fetching known imps")
    52  	assert.Empty(t, reqData, "Unexpected requests returned fetching just imps")
    53  	assertMapKeys(t, impData, "imp-1", "imp-2")
    54  }
    55  
    56  func TestReqsAndImps(t *testing.T) {
    57  	fetcher, close := newTestFetcher(t, []string{"req-1"}, []string{"imp-1"})
    58  	defer close()
    59  
    60  	reqData, impData, errs := fetcher.FetchRequests(context.Background(), []string{"req-1"}, []string{"imp-1"})
    61  	assert.Empty(t, errs, "Unexpected errors fetching known reqs and imps")
    62  	assertMapKeys(t, reqData, "req-1")
    63  	assertMapKeys(t, impData, "imp-1")
    64  }
    65  
    66  func TestMissingValues(t *testing.T) {
    67  	fetcher, close := newEmptyFetcher(t, []string{"req-1", "req-2"}, []string{"imp-1"})
    68  	defer close()
    69  
    70  	reqData, impData, errs := fetcher.FetchRequests(context.Background(), []string{"req-1", "req-2"}, []string{"imp-1"})
    71  	assert.Empty(t, reqData, "Fetching unknown reqs should return no reqs")
    72  	assert.Empty(t, impData, "Fetching unknown imps should return no imps")
    73  	assert.Len(t, errs, 3, "Fetching 3 unknown reqs+imps should return 3 errors")
    74  }
    75  
    76  func TestFetchAccounts(t *testing.T) {
    77  	fetcher, close := newTestAccountFetcher(t, []string{"acc-1", "acc-2"})
    78  	defer close()
    79  
    80  	accData, errs := fetcher.FetchAccounts(context.Background(), []string{"acc-1", "acc-2"})
    81  	assert.Empty(t, errs, "Unexpected error fetching known accounts")
    82  	assertMapKeys(t, accData, "acc-1", "acc-2")
    83  }
    84  
    85  func TestFetchAccountsNoData(t *testing.T) {
    86  	fetcher, close := newFetcherBrokenBackend()
    87  	defer close()
    88  
    89  	accData, errs := fetcher.FetchAccounts(context.Background(), []string{"req-1"})
    90  	assert.Len(t, errs, 1, "Fetching unknown account should have returned an error")
    91  	assert.Nil(t, accData, "Fetching unknown account should return nil account map")
    92  }
    93  
    94  func TestFetchAccountsBadJSON(t *testing.T) {
    95  	fetcher, close := newFetcherBadJSON()
    96  	defer close()
    97  
    98  	accData, errs := fetcher.FetchAccounts(context.Background(), []string{"req-1"})
    99  	assert.Len(t, errs, 1, "Fetching account with broken json should have returned an error")
   100  	assert.Nil(t, accData, "Fetching account with broken json should return nil account map")
   101  }
   102  
   103  func TestFetchAccountsNoIDsProvided(t *testing.T) {
   104  	fetcher, close := newTestAccountFetcher(t, []string{"acc-1", "acc-2"})
   105  	defer close()
   106  
   107  	accData, errs := fetcher.FetchAccounts(nil, []string{})
   108  	assert.Empty(t, errs, "Unexpected error fetching empty account list")
   109  	assert.Nil(t, accData, "Fetching empty account list should return nil")
   110  }
   111  
   112  // Force build request failure by not providing a context
   113  func TestFetchAccountsFailedBuildRequest(t *testing.T) {
   114  	fetcher, close := newTestAccountFetcher(t, []string{"acc-1", "acc-2"})
   115  	defer close()
   116  
   117  	accData, errs := fetcher.FetchAccounts(nil, []string{"acc-1"})
   118  	assert.Len(t, errs, 1, "Fetching accounts without context should result in error ")
   119  	assert.Nil(t, accData, "Fetching accounts without context should return nil")
   120  }
   121  
   122  // Force http error via request timeout
   123  func TestFetchAccountsContextTimeout(t *testing.T) {
   124  	fetcher, close := newTestAccountFetcher(t, []string{"acc-1", "acc-2"})
   125  	defer close()
   126  
   127  	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(0))
   128  	defer cancel()
   129  	accData, errs := fetcher.FetchAccounts(ctx, []string{"acc-1"})
   130  	assert.Len(t, errs, 1, "Expected context timeout error")
   131  	assert.Nil(t, accData, "Unexpected account data returned instead of timeout")
   132  }
   133  
   134  func TestFetchAccount(t *testing.T) {
   135  	fetcher, close := newTestAccountFetcher(t, []string{"acc-1"})
   136  	defer close()
   137  
   138  	account, errs := fetcher.FetchAccount(context.Background(), json.RawMessage(`{"disabled":true}`), "acc-1")
   139  	assert.Empty(t, errs, "Unexpected error fetching existing account")
   140  	assert.JSONEq(t, `{"disabled": true, "id":"acc-1"}`, string(account), "Unexpected account data fetching existing account")
   141  }
   142  
   143  func TestAccountMergeError(t *testing.T) {
   144  	fetcher, close := newTestAccountFetcher(t, []string{"acc-1"})
   145  	defer close()
   146  
   147  	_, errs := fetcher.FetchAccount(context.Background(), json.RawMessage(`{"disabled"}`), "acc-1")
   148  	assert.Error(t, errs[0])
   149  	assert.Equal(t, fmt.Errorf("Invalid JSON Document"), errs[0])
   150  }
   151  
   152  func TestFetchAccountNoData(t *testing.T) {
   153  	fetcher, close := newFetcherBrokenBackend()
   154  	defer close()
   155  
   156  	unknownAccount, errs := fetcher.FetchAccount(context.Background(), json.RawMessage(`{disabled":true}`), "unknown-acc")
   157  	assert.NotEmpty(t, errs, "Retrieving unknown account should return error")
   158  	assert.Nil(t, unknownAccount, "Retrieving unknown account should return nil json.RawMessage")
   159  }
   160  
   161  func TestFetchAccountNoIDProvided(t *testing.T) {
   162  	fetcher, close := newTestAccountFetcher(t, nil)
   163  	defer close()
   164  
   165  	account, errs := fetcher.FetchAccount(context.Background(), json.RawMessage(`{disabled":true}`), "")
   166  	assert.Len(t, errs, 1, "Fetching account with empty id should error")
   167  	assert.Nil(t, account, "Fetching account with empty id should return nil")
   168  }
   169  
   170  func TestErrResponse(t *testing.T) {
   171  	fetcher, close := newFetcherBrokenBackend()
   172  	defer close()
   173  	reqData, impData, errs := fetcher.FetchRequests(context.Background(), []string{"req-1"}, []string{"imp-1"})
   174  	assertMapKeys(t, reqData)
   175  	assertMapKeys(t, impData)
   176  	assert.Len(t, errs, 1)
   177  }
   178  
   179  func newFetcherBrokenBackend() (fetcher *HttpFetcher, closer func()) {
   180  	handler := func(w http.ResponseWriter, r *http.Request) {
   181  		w.WriteHeader(http.StatusInternalServerError)
   182  	}
   183  	server := httptest.NewServer(http.HandlerFunc(handler))
   184  	return NewFetcher(server.Client(), server.URL), server.Close
   185  }
   186  
   187  func newFetcherBadJSON() (fetcher *HttpFetcher, closer func()) {
   188  	handler := func(w http.ResponseWriter, r *http.Request) {
   189  		w.Write([]byte(`broken JSON`))
   190  	}
   191  	server := httptest.NewServer(http.HandlerFunc(handler))
   192  	return NewFetcher(server.Client(), server.URL), server.Close
   193  }
   194  
   195  func newEmptyFetcher(t *testing.T, expectReqIDs []string, expectImpIDs []string) (fetcher *HttpFetcher, closer func()) {
   196  	handler := newHandler(t, expectReqIDs, expectImpIDs, jsonifyToNull)
   197  	server := httptest.NewServer(http.HandlerFunc(handler))
   198  	return NewFetcher(server.Client(), server.URL), server.Close
   199  }
   200  
   201  func newTestFetcher(t *testing.T, expectReqIDs []string, expectImpIDs []string) (fetcher *HttpFetcher, closer func()) {
   202  	handler := newHandler(t, expectReqIDs, expectImpIDs, jsonifyID)
   203  	server := httptest.NewServer(http.HandlerFunc(handler))
   204  	return NewFetcher(server.Client(), server.URL), server.Close
   205  }
   206  
   207  func newHandler(t *testing.T, expectReqIDs []string, expectImpIDs []string, jsonifier func(string) json.RawMessage) func(w http.ResponseWriter, r *http.Request) {
   208  	return func(w http.ResponseWriter, r *http.Request) {
   209  		query := r.URL.Query()
   210  		gotReqIDs := richSplit(query.Get("request-ids"))
   211  		gotImpIDs := richSplit(query.Get("imp-ids"))
   212  
   213  		assertMatches(t, gotReqIDs, expectReqIDs)
   214  		assertMatches(t, gotImpIDs, expectImpIDs)
   215  
   216  		reqIDResponse := make(map[string]json.RawMessage, len(gotReqIDs))
   217  		impIDResponse := make(map[string]json.RawMessage, len(gotImpIDs))
   218  
   219  		for _, reqID := range gotReqIDs {
   220  			if reqID != "" {
   221  				reqIDResponse[reqID] = jsonifier(reqID)
   222  			}
   223  		}
   224  
   225  		for _, impID := range gotImpIDs {
   226  			if impID != "" {
   227  				impIDResponse[impID] = jsonifier(impID)
   228  			}
   229  		}
   230  
   231  		respObj := responseContract{
   232  			Requests: reqIDResponse,
   233  			Imps:     impIDResponse,
   234  		}
   235  
   236  		if respBytes, err := json.Marshal(respObj); err != nil {
   237  			t.Errorf("failed to marshal responseContract in test:  %v", err)
   238  			w.WriteHeader(http.StatusInternalServerError)
   239  		} else {
   240  			w.Write(respBytes)
   241  		}
   242  	}
   243  }
   244  
   245  func newTestAccountFetcher(t *testing.T, expectAccIDs []string) (fetcher *HttpFetcher, closer func()) {
   246  	handler := newAccountHandler(t, expectAccIDs)
   247  	server := httptest.NewServer(http.HandlerFunc(handler))
   248  	return NewFetcher(server.Client(), server.URL), server.Close
   249  }
   250  
   251  func newAccountHandler(t *testing.T, expectAccIDs []string) func(w http.ResponseWriter, r *http.Request) {
   252  	return func(w http.ResponseWriter, r *http.Request) {
   253  		query := r.URL.Query()
   254  		gotAccIDs := richSplit(query.Get("account-ids"))
   255  
   256  		assertMatches(t, gotAccIDs, expectAccIDs)
   257  
   258  		accIDResponse := make(map[string]json.RawMessage, len(gotAccIDs))
   259  
   260  		for _, accID := range gotAccIDs {
   261  			if accID != "" {
   262  				accIDResponse[accID] = json.RawMessage(`{"id":"` + accID + `"}`)
   263  			}
   264  		}
   265  
   266  		respObj := accountsResponseContract{
   267  			Accounts: accIDResponse,
   268  		}
   269  
   270  		if respBytes, err := json.Marshal(respObj); err != nil {
   271  			t.Errorf("failed to marshal responseContract in test:  %v", err)
   272  			w.WriteHeader(http.StatusInternalServerError)
   273  		} else {
   274  			w.Write(respBytes)
   275  		}
   276  	}
   277  }
   278  
   279  func assertMatches(t *testing.T, queryVals []string, expected []string) {
   280  	t.Helper()
   281  
   282  	if len(queryVals) == 1 && queryVals[0] == "" {
   283  		if len(expected) != 0 {
   284  			t.Errorf("Expected no query vals, but got %v", queryVals)
   285  		}
   286  		return
   287  	}
   288  	if len(queryVals) != len(expected) {
   289  		t.Errorf("Query vals did not match. Expected %v, got %v", expected, queryVals)
   290  		return
   291  	}
   292  
   293  	for _, expectedQuery := range expected {
   294  		found := false
   295  		for _, queryVal := range queryVals {
   296  			if queryVal == expectedQuery {
   297  				found = true
   298  				break
   299  			}
   300  		}
   301  		if !found {
   302  			t.Errorf("Query string missing expected key %s.", expectedQuery)
   303  		}
   304  	}
   305  }
   306  
   307  // Split the id values properly
   308  func richSplit(queryVal string) []string {
   309  	// Get rid of the bounding []
   310  	// Not doing real validation, as this is a test routine, and given a malformed input we want to fail anyway.
   311  	if len(queryVal) > 2 {
   312  		queryVal = queryVal[1 : len(queryVal)-1]
   313  	}
   314  	values := strings.Split(queryVal, "\",\"")
   315  	if len(values) > 0 {
   316  		//Fix leading and trailing "
   317  		if len(values[0]) > 0 {
   318  			values[0] = values[0][1:]
   319  		}
   320  		l := len(values) - 1
   321  		if len(values[l]) > 0 {
   322  			values[l] = values[l][:len(values[l])-1]
   323  		}
   324  	}
   325  
   326  	return values
   327  }
   328  
   329  func jsonifyID(id string) json.RawMessage {
   330  	if b, err := json.Marshal(id); err != nil {
   331  		return json.RawMessage([]byte("\"error encoding ID=" + id + "\""))
   332  	} else {
   333  		return json.RawMessage(b)
   334  	}
   335  }
   336  
   337  func jsonifyToNull(id string) json.RawMessage {
   338  	return json.RawMessage("null")
   339  }
   340  
   341  func assertMapKeys(t *testing.T, m map[string]json.RawMessage, keys ...string) {
   342  	t.Helper()
   343  
   344  	if len(m) != len(keys) {
   345  		t.Errorf("Expected %d map keys. Map was: %v", len(keys), m)
   346  	}
   347  
   348  	for _, key := range keys {
   349  		if _, ok := m[key]; !ok {
   350  			t.Errorf("Map missing expected key %s. Data was %v", key, m)
   351  		}
   352  	}
   353  }