Skip to content

Commit 2f1bc7b

Browse files
authored
Add fakes (#37)
* Create fakes to allow others to create and start a real server to use for testing their own clients. * 100% coverage * added issue about packages
1 parent 266847d commit 2f1bc7b

File tree

6 files changed

+135
-3
lines changed

6 files changed

+135
-3
lines changed

asnannotator/asn.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,3 +169,45 @@ func loadNames(ctx context.Context, src content.Provider, oldvalue ipinfo.ASName
169169
}
170170
return ipinfo.Parse(data)
171171
}
172+
173+
// fakeASNAnnotator is just a real asnAnnotator that has a fixed dataset and
174+
// can't be reloaded.
175+
type fakeASNAnnotator struct {
176+
asnAnnotator
177+
}
178+
179+
func (*fakeASNAnnotator) Reload(ctx context.Context) {}
180+
181+
// NewFake returns an annotator that know about just one v4 IP (1.2.3.4) and one
182+
// v6 IP (1111:2222:3333:4444:5555:6666:7777:8888). This is useful for testing
183+
// other components when you don't want to carry around canonical datafiles, or
184+
// for building up a local IP annotation service with known outputs for testing.
185+
//
186+
// TODO(http://github.com/m-lab/uuid-annotator/issues/38): Consider moving this
187+
// fake to its own subpackage.
188+
func NewFake() ASNAnnotator {
189+
f := &fakeASNAnnotator{}
190+
191+
// Set up v4 data for 1.2.3.4.
192+
asn4Entry := routeview.IPNet{}
193+
_, v4net, err := net.ParseCIDR("1.2.3.4/32")
194+
rtx.Must(err, "Could not parse fixed string")
195+
asn4Entry.IPNet = *v4net
196+
asn4Entry.Systems = "5"
197+
f.asn4 = routeview.Index{asn4Entry}
198+
199+
// Set up v6 data for 1111:2222:3333:4444:5555:6666:7777:8888.
200+
asn6Entry := routeview.IPNet{}
201+
_, v6net, err := net.ParseCIDR("1111:2222:3333:4444:5555:6666:7777:8888/128")
202+
rtx.Must(err, "Could not parse fixed string")
203+
asn6Entry.IPNet = *v6net
204+
asn6Entry.Systems = "9"
205+
f.asn6 = routeview.Index{asn6Entry}
206+
207+
// Set up AS name entries for AS5 and AS9
208+
f.asnames = ipinfo.ASNames{
209+
5: "Test Number Five",
210+
9: "Test Number Nine",
211+
}
212+
return f
213+
}

asnannotator/asn_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,3 +288,20 @@ func Test_loadGZ_errors(t *testing.T) {
288288
t.Error("Should have had an error, not nil")
289289
}
290290
}
291+
292+
func TestNewFake(t *testing.T) {
293+
f := NewFake()
294+
f.Reload(context.Background()) // no crash == success
295+
n1 := f.AnnotateIP("1.2.3.4")
296+
if n1.ASName != "Test Number Five" {
297+
t.Error("Bad return value from AnnotateIP for 1.2.3.4:", n1)
298+
}
299+
n2 := f.AnnotateIP("1111:2222:3333:4444:5555:6666:7777:8888")
300+
if n2.ASName != "Test Number Nine" {
301+
t.Error("Bad return value from AnnotateIP for 1111:2222:3333:4444:5555:6666:7777:8888:", n2)
302+
}
303+
n3 := f.AnnotateIP("1.0.0.0")
304+
if !n3.Missing {
305+
t.Error("Should have had a missing return value, not", n3)
306+
}
307+
}

geoannotator/ipannotator.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,13 @@ func (g *geoannotator) annotateIPHoldingLock(ip net.IP, geo **annotator.Geolocat
7474
if ip == nil {
7575
return errors.New("can't annotate nil IP")
7676
}
77+
if g.maxmind == nil {
78+
log.Println("No maxmind DB present. This should only occur during testing.")
79+
*geo = &annotator.Geolocation{
80+
Missing: true,
81+
}
82+
return nil
83+
}
7784
record, err := g.maxmind.City(ip)
7885
if err != nil {
7986
return err
@@ -164,3 +171,19 @@ func New(ctx context.Context, geo content.Provider, localIPs []net.IP) GeoAnnota
164171
rtx.Must(err, "Could not load annotation db")
165172
return g
166173
}
174+
175+
type fakegeoannotator struct {
176+
geoannotator
177+
}
178+
179+
// Reload does nothing because you can't reload a fake.
180+
func (*fakegeoannotator) Reload(ctx context.Context) {}
181+
182+
// NewFake creates a fake GeoAnnotator that contains no data. This is to aid
183+
// others in creating their own annotation services for testing.
184+
//
185+
// TODO(http://github.com/m-lab/uuid-annotator/issues/38): Consider moving this
186+
// fake to its own subpackage.
187+
func NewFake() GeoAnnotator {
188+
return &fakegeoannotator{}
189+
}

geoannotator/ipannotator_test.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,9 @@ import (
1414
"github.com/m-lab/go/pretty"
1515
"github.com/m-lab/go/rtx"
1616
"github.com/m-lab/tcp-info/inetdiag"
17-
"github.com/oschwald/geoip2-golang"
18-
1917
"github.com/m-lab/uuid-annotator/annotator"
2018
"github.com/m-lab/uuid-annotator/tarreader"
19+
geoip2 "github.com/oschwald/geoip2-golang"
2120
)
2221

2322
var localRawfile content.Provider
@@ -302,3 +301,14 @@ func TestIPAnnotationMissingCityDB(t *testing.T) {
302301
t.Errorf("geoannotator.load() return non-nil ptr; got %v, want nil", mm)
303302
}
304303
}
304+
305+
func TestNewFake(t *testing.T) {
306+
f := NewFake()
307+
f.Reload(context.Background()) // no crash == success
308+
ann := &annotator.Geolocation{}
309+
ip := net.ParseIP("1.2.3.4")
310+
rtx.Must(f.AnnotateIP(ip, &ann), "could not annotate IP")
311+
if !ann.Missing {
312+
t.Error("Annotation should be missing.")
313+
}
314+
}

ipservice/ipservice_test.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,19 @@ func TestNewServerWithExistingFile(t *testing.T) {
219219
defer os.Remove(f.Name())
220220

221221
_, err = NewServer(f.Name(), asn, geo)
222-
rtx.Must(err, "Should not have had an error when starting NewServer wth an existing file")
222+
rtx.Must(err, "Should not have had an error when starting NewServer with an existing file")
223+
}
224+
225+
func TestNewServerWithBadFile(t *testing.T) {
226+
// Server creation should fail when the filename can't be created.
227+
dir, err := ioutil.TempDir("", "TextNewServerWithBadFile")
228+
rtx.Must(err, "Could not create tempdir")
229+
defer os.RemoveAll(dir)
230+
231+
_, err = NewServer(dir+"/this/subdir/does/not/exist/file.sock", asn, geo)
232+
if err == nil {
233+
t.Error("Should have had an error when starting NewServer with an uncreateable file")
234+
}
223235
}
224236

225237
func TestNewClient(t *testing.T) {

ipservice/server_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,16 @@ package ipservice
33
import (
44
"bytes"
55
"errors"
6+
"io/ioutil"
67
"log"
8+
"os"
79
"testing"
10+
"time"
811

912
"github.com/m-lab/go/rtx"
13+
"github.com/m-lab/go/warnonerror"
14+
"github.com/m-lab/uuid-annotator/asnannotator"
15+
"github.com/m-lab/uuid-annotator/geoannotator"
1016
)
1117

1218
func Test_logOnError(t *testing.T) {
@@ -109,3 +115,25 @@ func Test_logOnNil(t *testing.T) {
109115
})
110116
}
111117
}
118+
119+
func ExampleServer_forTesting() {
120+
dir, err := ioutil.TempDir("", "ExampleFakeServerForTesting")
121+
rtx.Must(err, "could not create tempdir")
122+
defer os.RemoveAll(dir)
123+
124+
*SocketFilename = dir + "/ipservice.sock"
125+
srv, err := NewServer(*SocketFilename, asnannotator.NewFake(), geoannotator.NewFake())
126+
rtx.Must(err, "Could not create server")
127+
defer warnonerror.Close(srv, "Could not stop the server")
128+
129+
go srv.Serve()
130+
_, err = os.Stat(*SocketFilename)
131+
for err != nil {
132+
time.Sleep(time.Millisecond)
133+
_, err = os.Stat(*SocketFilename)
134+
}
135+
136+
// Now the server exists, and clients can connect to it via:
137+
// c := NewClient(*SocketFilename)
138+
// and then you can call c.Annotate() and use the returned values.
139+
}

0 commit comments

Comments
 (0)