ext_test.go 3.18 KB
Newer Older
Jeromy's avatar
Jeromy committed
1 2 3 4 5
package dht

import (
	"testing"

6 7
	"code.google.com/p/goprotobuf/proto"

Jeromy's avatar
Jeromy committed
8 9
	peer "github.com/jbenet/go-ipfs/peer"
	swarm "github.com/jbenet/go-ipfs/swarm"
10 11
	u "github.com/jbenet/go-ipfs/util"
	ma "github.com/jbenet/go-multiaddr"
Jeromy's avatar
Jeromy committed
12 13 14 15 16 17 18

	"time"
)

// fauxNet is a standin for a swarm.Network in order to more easily recreate
// different testing scenarios
type fauxNet struct {
19 20
	Chan     *swarm.Chan
	handlers []mesHandleFunc
Jeromy's avatar
Jeromy committed
21 22 23 24

	swarm.Network
}

25 26 27 28
// mesHandleFunc is a function that takes in outgoing messages
// and can respond to them, simulating other peers on the network.
// returning nil will chose not to respond and pass the message onto the
// next registered handler
Jeromy's avatar
Jeromy committed
29 30 31 32 33 34 35 36 37
type mesHandleFunc func(*swarm.Message) *swarm.Message

func newFauxNet() *fauxNet {
	fn := new(fauxNet)
	fn.Chan = swarm.NewChan(8)

	return fn
}

38 39 40
// Instead of 'Listening' Start up a goroutine that will check
// all outgoing messages against registered message handlers,
// and reply if needed
Jeromy's avatar
Jeromy committed
41 42 43 44 45
func (f *fauxNet) Listen() error {
	go func() {
		for {
			select {
			case in := <-f.Chan.Outgoing:
46
				for _, h := range f.handlers {
Jeromy's avatar
Jeromy committed
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
					reply := h(in)
					if reply != nil {
						f.Chan.Incoming <- reply
						break
					}
				}
			}
		}
	}()
	return nil
}

func (f *fauxNet) AddHandler(fn func(*swarm.Message) *swarm.Message) {
	f.handlers = append(f.handlers, fn)
}

func (f *fauxNet) Send(mes *swarm.Message) {
64 65
	f.Chan.Outgoing <- mes
}
Jeromy's avatar
Jeromy committed
66

67 68
func (f *fauxNet) GetChan() *swarm.Chan {
	return f.Chan
Jeromy's avatar
Jeromy committed
69 70
}

71 72 73 74 75
func (f *fauxNet) Connect(addr *ma.Multiaddr) (*peer.Peer, error) {
	return nil, nil
}

func TestGetFailures(t *testing.T) {
Jeromy's avatar
Jeromy committed
76 77 78 79
	fn := newFauxNet()
	fn.Listen()

	local := new(peer.Peer)
80
	local.ID = peer.ID("test_peer")
Jeromy's avatar
Jeromy committed
81 82 83

	d := NewDHT(local, fn)

84 85
	other := &peer.Peer{ID: peer.ID("other_peer")}

Jeromy's avatar
Jeromy committed
86 87
	d.Start()

88 89 90
	d.Update(other)

	// This one should time out
91
	_, err := d.GetValue(u.Key("test"), time.Millisecond*10)
Jeromy's avatar
Jeromy committed
92
	if err != nil {
93
		if err != u.ErrTimeout {
94 95 96 97
			t.Fatal("Got different error than we expected.")
		}
	} else {
		t.Fatal("Did not get expected error!")
Jeromy's avatar
Jeromy committed
98 99
	}

100
	// Reply with failures to every message
101 102 103 104 105 106 107
	fn.AddHandler(func(mes *swarm.Message) *swarm.Message {
		pmes := new(PBDHTMessage)
		err := proto.Unmarshal(mes.Data, pmes)
		if err != nil {
			t.Fatal(err)
		}

Chas Leichner's avatar
Chas Leichner committed
108
		resp := Message{
109
			Type:     pmes.GetType(),
Chas Leichner's avatar
Chas Leichner committed
110
			ID:       pmes.GetId(),
111 112 113 114 115 116 117
			Response: true,
			Success:  false,
		}
		return swarm.NewMessage(mes.Peer, resp.ToProtobuf())
	})

	// This one should fail with NotFound
118
	_, err = d.GetValue(u.Key("test"), time.Millisecond*1000)
119 120
	if err != nil {
		if err != u.ErrNotFound {
121
			t.Fatalf("Expected ErrNotFound, got: %s", err)
122 123 124 125
		}
	} else {
		t.Fatal("expected error, got none.")
	}
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142

	success := make(chan struct{})
	fn.handlers = nil
	fn.AddHandler(func(mes *swarm.Message) *swarm.Message {
		resp := new(PBDHTMessage)
		err := proto.Unmarshal(mes.Data, resp)
		if err != nil {
			t.Fatal(err)
		}
		if resp.GetSuccess() {
			t.Fatal("Get returned success when it shouldnt have.")
		}
		success <- struct{}{}
		return nil
	})

	// Now we test this DHT's handleGetValue failure
Chas Leichner's avatar
Chas Leichner committed
143
	req := Message{
144 145
		Type:  PBDHTMessage_GET_VALUE,
		Key:   "hello",
Chas Leichner's avatar
Chas Leichner committed
146
		ID:    GenerateMessageID(),
147 148 149 150 151
		Value: []byte{0},
	}
	fn.Chan.Incoming <- swarm.NewMessage(other, req.ToProtobuf())

	<-success
Jeromy's avatar
Jeromy committed
152
}