github.com/m-lab/locate@v0.17.6/clientgeo/maxmind_test.go (about)

     1  package clientgeo
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"net/url"
     9  	"reflect"
    10  	"testing"
    11  
    12  	"github.com/m-lab/go/content"
    13  	"github.com/m-lab/go/rtx"
    14  )
    15  
    16  // Networks taken from https://github.com/maxmind/MaxMind-DB/blob/master/source-data/GeoIP2-City-Test.json
    17  var localIP = "175.16.199.3"
    18  var remoteIP = "2.125.160.216" // includes multiple subdivision annotations.
    19  
    20  func TestNewMaxmindLocator(t *testing.T) {
    21  	var localRawfile content.Provider
    22  
    23  	tests := []struct {
    24  		name       string
    25  		useHeaders map[string]string
    26  		remoteIP   string
    27  		want       *Location
    28  		filename   string
    29  		reloadDB   bool
    30  		wantErr    bool
    31  	}{
    32  		{
    33  			name: "success-using-X-Forwarded-For-header",
    34  			useHeaders: map[string]string{
    35  				"X-Forwarded-For": "2.125.160.216, 192.168.0.2",
    36  			},
    37  			remoteIP: remoteIP + ":1234",
    38  			want: &Location{
    39  				Latitude:  "51.750000",
    40  				Longitude: "-1.250000",
    41  				Headers: http.Header{
    42  					hLocateClientlatlon:       []string{"51.750000,-1.250000"},
    43  					hLocateClientlatlonMethod: []string{"maxmind-remoteip"},
    44  				},
    45  			},
    46  			filename: "file:./testdata/fake.tar.gz",
    47  		},
    48  		{
    49  			name:     "success-using-remote-ip",
    50  			remoteIP: remoteIP + ":1234",
    51  			want: &Location{
    52  				Latitude:  "51.750000",
    53  				Longitude: "-1.250000",
    54  				Headers: http.Header{
    55  					hLocateClientlatlon:       []string{"51.750000,-1.250000"},
    56  					hLocateClientlatlonMethod: []string{"maxmind-remoteip"},
    57  				},
    58  			},
    59  			filename: "file:./testdata/fake.tar.gz",
    60  		},
    61  		{
    62  			name:     "error-remote-ip-split-error",
    63  			remoteIP: "invalid-ip-1234",
    64  			filename: "file:./testdata/fake.tar.gz",
    65  			wantErr:  true,
    66  		},
    67  		{
    68  			name:     "error-remote-ip-parses-as-nil",
    69  			remoteIP: "invalid-ip:1234",
    70  			filename: "file:./testdata/fake.tar.gz",
    71  			wantErr:  true,
    72  		},
    73  		{
    74  			name:     "error-maxmind-db-error",
    75  			remoteIP: remoteIP + ":1234",
    76  			reloadDB: true,
    77  			filename: "file:./testdata/fake.tar.gz",
    78  			wantErr:  true,
    79  		},
    80  		{
    81  			name:     "error-wrong-db-type",
    82  			remoteIP: "127.0.0.1:1234",
    83  			filename: "file:./testdata/wrongtype.tar.gz",
    84  			wantErr:  true,
    85  		},
    86  		{
    87  			name:     "error-empty-response-from-db",
    88  			remoteIP: "127.0.0.1:1234",
    89  			filename: "file:./testdata/fake.tar.gz",
    90  			wantErr:  true,
    91  		},
    92  	}
    93  	for _, tt := range tests {
    94  		t.Run(tt.name, func(t *testing.T) {
    95  			u, err := url.Parse(tt.filename)
    96  			rtx.Must(err, "Could not parse URL")
    97  			localRawfile, err = content.FromURL(context.Background(), u)
    98  			rtx.Must(err, "Could not create content.Provider")
    99  
   100  			ctx := context.Background()
   101  			locator := NewMaxmindLocator(ctx, localRawfile)
   102  			if tt.reloadDB {
   103  				// This will result in a null maxmind db, b/c the localRawfile has not changed.
   104  				locator = NewMaxmindLocator(ctx, localRawfile)
   105  			}
   106  
   107  			req := httptest.NewRequest(http.MethodGet, "/anytarget", nil)
   108  			for key, value := range tt.useHeaders {
   109  				req.Header.Set(key, value)
   110  			}
   111  			req.RemoteAddr = tt.remoteIP
   112  
   113  			l, err := locator.Locate(req)
   114  			if (err != nil) && !tt.wantErr {
   115  				t.Errorf("MaxmindLocator.Locate got error: %v", err)
   116  			}
   117  			// fmt.Printf("%#v\n", l)
   118  			if !reflect.DeepEqual(l, tt.want) {
   119  				t.Errorf("NewMaxmindLocator() = %v, want %v", l, tt.want)
   120  			}
   121  		})
   122  	}
   123  }
   124  
   125  // workOnceProvider returns an error the second reload.
   126  type workOnceProvider struct {
   127  	provider content.Provider
   128  	called   bool
   129  }
   130  
   131  func (w *workOnceProvider) Get(ctx context.Context) ([]byte, error) {
   132  	if !w.called {
   133  		w.called = true
   134  		return w.provider.Get(ctx)
   135  	}
   136  	return nil, errors.New("fake error on second load")
   137  }
   138  
   139  // emptyReloadProvider returns an empty archive on the second reload.
   140  type emptyReloadProvider struct {
   141  	provider content.Provider
   142  	called   bool
   143  }
   144  
   145  func (e *emptyReloadProvider) Get(ctx context.Context) ([]byte, error) {
   146  	if !e.called {
   147  		e.called = true
   148  		return e.provider.Get(ctx)
   149  	}
   150  	return []byte("bad-content"), nil
   151  }
   152  
   153  func loadProvider(name string) content.Provider {
   154  	u, err := url.Parse(name)
   155  	rtx.Must(err, "Could not parse URL")
   156  	localRawfile, err := content.FromURL(context.Background(), u)
   157  	rtx.Must(err, "Could not create content.Provider")
   158  	return localRawfile
   159  }
   160  
   161  func TestMaxmindLocator_Reload(t *testing.T) {
   162  	tests := []struct {
   163  		name       string
   164  		workOnce   bool
   165  		emptyAfter bool
   166  	}{
   167  		{
   168  			name: "success",
   169  		},
   170  		{
   171  			name:     "success-fail-second-reload-error",
   172  			workOnce: true,
   173  		},
   174  		{
   175  			name:       "success-fail-second-reload-data",
   176  			emptyAfter: true,
   177  		},
   178  	}
   179  	for _, tt := range tests {
   180  		t.Run(tt.name, func(t *testing.T) {
   181  
   182  			localRawfile := loadProvider("file:./testdata/fake.tar.gz")
   183  
   184  			if tt.workOnce {
   185  				localRawfile = &workOnceProvider{
   186  					provider: localRawfile,
   187  				}
   188  			}
   189  			if tt.emptyAfter {
   190  				localRawfile = &emptyReloadProvider{
   191  					provider: localRawfile,
   192  				}
   193  			}
   194  
   195  			ctx := context.Background()
   196  			mml := NewMaxmindLocator(ctx, localRawfile)
   197  			mml.Reload(ctx)
   198  		})
   199  	}
   200  }