package snowflake_client import ( "fmt" "net" "testing" "time" . "github.com/smartystreets/goconvey/convey" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/event" ) type FakeDialer struct { max int } func (w FakeDialer) Catch() (*WebRTCPeer, error) { fmt.Println("Caught a dummy snowflake.") return &WebRTCPeer{closed: make(chan struct{})}, nil } func (w FakeDialer) GetMax() int { return w.max } type FakeSocksConn struct { net.Conn rejected bool } func (f FakeSocksConn) Reject() error { f.rejected = true return nil } func (f FakeSocksConn) Grant(addr *net.TCPAddr) error { return nil } func TestSnowflakeClient(t *testing.T) { Convey("Peers", t, func() { Convey("Can construct", func() { d := &FakeDialer{max: 1} p, _ := NewPeers(d) So(p.Tongue.GetMax(), ShouldEqual, 1) So(p.snowflakeChan, ShouldNotBeNil) So(cap(p.snowflakeChan), ShouldEqual, 1) }) Convey("Collecting a Snowflake requires a Tongue.", func() { p, err := NewPeers(nil) So(err, ShouldNotBeNil) // Set the dialer so that collection is possible. d := &FakeDialer{max: 1} p, err = NewPeers(d) _, err = p.Collect() So(err, ShouldBeNil) So(p.Count(), ShouldEqual, 1) // S _, err = p.Collect() }) Convey("Collection continues until capacity.", func() { c := 5 p, _ := NewPeers(FakeDialer{max: c}) // Fill up to capacity. for i := 0; i < c; i++ { fmt.Println("Adding snowflake ", i) _, err := p.Collect() So(err, ShouldBeNil) So(p.Count(), ShouldEqual, i+1) } // But adding another gives an error. So(p.Count(), ShouldEqual, c) _, err := p.Collect() So(err, ShouldNotBeNil) So(p.Count(), ShouldEqual, c) // But popping allows it to continue. s := p.Pop() s.Close() So(s, ShouldNotBeNil) So(p.Count(), ShouldEqual, c-1) _, err = p.Collect() So(err, ShouldBeNil) So(p.Count(), ShouldEqual, c) }) Convey("Count correctly purges peers marked for deletion.", func() { p, _ := NewPeers(FakeDialer{max: 5}) p.Collect() p.Collect() p.Collect() p.Collect() So(p.Count(), ShouldEqual, 4) s := p.Pop() s.Close() So(p.Count(), ShouldEqual, 3) s = p.Pop() s.Close() So(p.Count(), ShouldEqual, 2) }) Convey("End Closes all peers.", func() { cnt := 5 p, _ := NewPeers(FakeDialer{max: cnt}) for i := 0; i < cnt; i++ { p.activePeers.PushBack(&WebRTCPeer{closed: make(chan struct{})}) } So(p.Count(), ShouldEqual, cnt) p.End() <-p.Melted() So(p.Count(), ShouldEqual, 0) }) Convey("Pop skips over closed peers.", func() { p, _ := NewPeers(FakeDialer{max: 4}) wc1, _ := p.Collect() wc2, _ := p.Collect() wc3, _ := p.Collect() So(wc1, ShouldNotBeNil) So(wc2, ShouldNotBeNil) So(wc3, ShouldNotBeNil) wc1.Close() r := p.Pop() So(p.Count(), ShouldEqual, 2) So(r, ShouldEqual, wc2) wc4, _ := p.Collect() wc2.Close() wc3.Close() r = p.Pop() So(r, ShouldEqual, wc4) }) Convey("Terminate Connect() loop", func() { p, _ := NewPeers(FakeDialer{max: 4}) go func() { for { p.Collect() select { case <-p.Melted(): return default: } } }() <-time.After(10 * time.Second) p.End() <-p.Melted() So(p.Count(), ShouldEqual, 0) }) }) Convey("Dialers", t, func() { Convey("Can construct WebRTCDialer.", func() { broker := &BrokerChannel{} d := NewWebRTCDialer(broker, nil, 1) So(d, ShouldNotBeNil) So(d.BrokerChannel, ShouldNotBeNil) }) SkipConvey("WebRTCDialer can Catch a snowflake.", func() { broker := &BrokerChannel{} d := NewWebRTCDialer(broker, nil, 1) conn, err := d.Catch() So(conn, ShouldBeNil) So(err, ShouldNotBeNil) }) }) } func TestWebRTCPeer(t *testing.T) { Convey("WebRTCPeer", t, func(c C) { p := &WebRTCPeer{closed: make(chan struct{}), eventsLogger: event.NewSnowflakeEventDispatcher()} Convey("checks for staleness", func() { go p.checkForStaleness(time.Second) <-time.After(2 * time.Second) So(p.Closed(), ShouldEqual, true) }) }) } func TestICEServerParser(t *testing.T) { Convey("Test parsing of ICE servers", t, func() { for _, test := range []struct { input []string urls [][]string length int }{ { []string{"stun:stun.l.google.com:19302", "stun:stun.ekiga.net"}, [][]string{[]string{"stun:stun.l.google.com:19302"}, []string{"stun:stun.ekiga.net:3478"}}, 2, }, { []string{"stun:stun1.l.google.com:19302", "stun.ekiga.net", "stun:stun.example.com:1234/path?query", "https://example.com", "turn:relay.metered.ca:80?transport=udp"}, [][]string{[]string{"stun:stun1.l.google.com:19302"}}, 1, }, } { servers := parseIceServers(test.input) if test.urls == nil { So(servers, ShouldBeNil) } else { So(servers, ShouldNotBeNil) } So(len(servers), ShouldEqual, test.length) for _, server := range servers { So(test.urls, ShouldContain, server.URLs) } } }) }