package testing import ( "cwtch.im/cwtch/model" "cwtch.im/cwtch/peer" "cwtch.im/cwtch/peer/connections" cwtchserver "cwtch.im/cwtch/server" "fmt" "golang.org/x/net/proxy" "io/ioutil" "log" "runtime" "testing" "time" ) const ( serverKeyfile = "./../server/app/private_key" localKeyfile = "./private_key" ) var ( aliceLines = []string{"Hello, I'm Alice", "bye"} bobLines = []string{"Hi, my name is Bob.", "toodles", "welcome"} carolLines = []string{"Howdy, thanks!"} ) func printAndCountVerifedTimeline(t *testing.T, timeline []model.Message) int { numVerified := 0 for _, message := range timeline { fmt.Printf("%v %v> %s\n", message.Timestamp, message.PeerID, message.Message) numVerified++ } return numVerified } func serverCheck(t *testing.T, serverAddr string) bool { torDialer, err := proxy.SOCKS5("tcp", "127.0.0.1:9050", nil, proxy.Direct) if err != nil { t.Logf("Could not get SOCKS5 proxy: %v", err) return false } // Doesn't seem to be a way to turn the default timeout of 2 minutes down conn, err := torDialer.Dial("tcp", serverAddr+".onion:9878") if err != nil { t.Logf("Could not dial %v: %v", serverAddr, err) return false } conn.Close() return true } func waitForPeerConnection(t *testing.T, peer peer.CwtchPeer, server string) { for { servers := peer.GetServers() state, ok := servers[server] if ok { if state == connections.FAILED { t.Fatalf("%v could not connect to %v", peer.GetProfile().Onion, server) } if state != connections.AUTHENTICATED { time.Sleep(time.Second * 10) continue } } else { t.Fatalf("peer server connectiond %v should have entry for server %v", servers, server) } break } return } func TestCwtchPeerIntegration(t *testing.T) { // Hide logging "noise" log.SetOutput(ioutil.Discard) numGoRoutinesStart := runtime.NumGoroutine() // ***** Cwtch Server managment ***** var server *cwtchserver.Server serverOnline := false var serverAddr string if !serverOnline { // launch app with new key fmt.Println("No server found!") server = new(cwtchserver.Server) fmt.Println("Starting cwtch server...") config := cwtchserver.LoadConfig(".", "server-test.json") identity := config.Identity() serverAddr = identity.Hostname() go server.Run(config) // let tor get established fmt.Printf("Establishing Tor hidden service: %v...\n", serverAddr) } else { fmt.Printf("Found existing cwtch server %v, using for tests...\n", serverAddr) } numGoRoutinesPostServer := runtime.NumGoroutine() // ***** cwtchPeer setup ***** fmt.Println("Creating Alice...") alice := peer.NewCwtchPeer("Alice") go alice.Listen() fmt.Println("Alice created:", alice.GetProfile().Onion) fmt.Println("Creating Bob...") bob := peer.NewCwtchPeer("Bob") go bob.Listen() fmt.Println("Bob created:", bob.GetProfile().Onion) fmt.Println("Creating Carol...") carol := peer.NewCwtchPeer("Carol") go carol.Listen() fmt.Println("Carol created:", carol.GetProfile().Onion) fmt.Println("Waiting for Alice, Bob, and Carol to connection with onion network...") time.Sleep(time.Second * 90) numGoRoutinesPostPeerStart := runtime.NumGoroutine() // ***** Peering, server joining, group creation / invite ***** fmt.Println("Creating group on ", serverAddr, "...") groupID, _, err := alice.StartGroup(serverAddr) fmt.Printf("Created group: %v!\n", groupID) if err != nil { t.Errorf("Failed to init group: %v", err) return } fmt.Println("Alice peering with Bob...") alice.PeerWithOnion(bob.GetProfile().Onion) fmt.Println("Alice peering with Carol...") alice.PeerWithOnion(carol.GetProfile().Onion) fmt.Println("Alice joining server...") alice.JoinServer(serverAddr) fmt.Println("Bob joining server...") bob.JoinServer(serverAddr) fmt.Println("Waiting for peerings and server joins...") time.Sleep(time.Second * 240) fmt.Println("Alice inviting Bob to group...") err = alice.InviteOnionToGroup(bob.GetProfile().Onion, groupID) if err != nil { t.Fatalf("Error for Alice inviting Bob to group: %v", err) } time.Sleep(time.Second * 10) fmt.Println("Bob examining groups and accepting invites...") for _, groupID := range bob.GetGroups() { group := bob.GetGroup(groupID) fmt.Printf("Bob group: %v (Accepted: %v)\n", group.GroupID, group.Accepted) if group.Accepted == false { fmt.Printf("Bob received and accepting group invite: %v\n", group.GroupID) bob.AcceptInvite(group.GroupID) } } time.Sleep(time.Second * 5) numGoRoutinesPostServerConnect := runtime.NumGoroutine() // ***** Fill up message history of server ****** /* // filler group will be used to fill up the servers message history a bit to stress test fetch later for carol fillerGroupId, _, err := alice.Profile.StartGroup(serverAddr) if err != nil { t.Errorf("Failed to init filler group: %v", err) return } fmt.Println("Alice filling message history of server...") for i := 0; i < 100; i++ { go func (x int) { time.Sleep(time.Second * time.Duration(x)) err := alice.SendMessageToGroup(fillerGroupId, aliceLines[0]) if err != nil { fmt.Println("SEND", x, "ERROR:", err) } else { fmt.Println("SEND", x, " SUCCESS!") } }(i) } time.Sleep(time.Second * 110) */ // Wait for them to join the server waitForPeerConnection(t, alice, serverAddr) waitForPeerConnection(t, bob, serverAddr) //numGouRoutinesPostServerConnect := runtime.NumGoroutine() // ***** Conversation ***** fmt.Println("Starting conversation in group...") // Conversation fmt.Println("Alice> ", aliceLines[0]) err = alice.SendMessageToGroup(groupID, aliceLines[0]) if err != nil { t.Fatalf("Alice failed to send a message to the group: %v", err) } time.Sleep(time.Second * 10) fmt.Println("Bob> ", bobLines[0]) err = bob.SendMessageToGroup(groupID, bobLines[0]) if err != nil { t.Fatalf("Bob failed to send a message to the group: %v", err) } time.Sleep(time.Second * 10) fmt.Println("Alice> ", aliceLines[1]) alice.SendMessageToGroup(groupID, aliceLines[1]) time.Sleep(time.Second * 10) fmt.Println("Bob> ", bobLines[1]) bob.SendMessageToGroup(groupID, bobLines[1]) time.Sleep(time.Second * 10) fmt.Println("Alice inviting Carol to group...") err = alice.InviteOnionToGroup(carol.GetProfile().Onion, groupID) if err != nil { t.Fatalf("Error for Alice inviting Carol to group: %v", err) } time.Sleep(time.Second * 10) fmt.Println("Carol examining groups and accepting invites...") for _, groupID := range carol.GetGroups() { group := carol.GetGroup(groupID) fmt.Printf("Carol group: %v (Accepted: %v)\n", group.GroupID, group.Accepted) if group.Accepted == false { fmt.Printf("Carol received and accepting group invite: %v\n", group.GroupID) carol.AcceptInvite(group.GroupID) } } fmt.Println("Shutting down Alice...") alice.Shutdown() time.Sleep(time.Second * 5) numGoRoutinesPostAlice := runtime.NumGoroutine() fmt.Println("Carol joining server...") carol.JoinServer(serverAddr) waitForPeerConnection(t, carol, serverAddr) numGoRotinesPostCarolConnect := runtime.NumGoroutine() fmt.Println("Bob> ", bobLines[2]) bob.SendMessageToGroup(groupID, bobLines[2]) time.Sleep(time.Second * 10) fmt.Println("Carol> ", carolLines[0]) carol.SendMessageToGroup(groupID, carolLines[0]) time.Sleep(time.Second * 10) // ***** Verify Test ***** fmt.Println("Final syncing time...") time.Sleep(time.Second * 30) alicesGroup := alice.GetGroup(groupID) if alicesGroup == nil { t.Error("aliceGroup == nil") return } fmt.Printf("Alice's TimeLine:\n") aliceVerified := printAndCountVerifedTimeline(t, alicesGroup.GetTimeline()) if aliceVerified != 4 { t.Errorf("Alice did not have 4 verified messages") } bobsGroup := bob.GetGroup(groupID) if bobsGroup == nil { t.Error("bobGroup == nil") return } fmt.Printf("Bob's TimeLine:\n") bobVerified := printAndCountVerifedTimeline(t, bobsGroup.GetTimeline()) if bobVerified != 6 { t.Errorf("Bob did not have 5 verified messages") } carolsGroup := carol.GetGroup(groupID) fmt.Printf("Carol's TimeLine:\n") carolVerified := printAndCountVerifedTimeline(t, carolsGroup.GetTimeline()) if carolVerified != 6 { t.Errorf("Carol did not have 3 verified messages") } if len(alicesGroup.GetTimeline()) != 4 { t.Errorf("Alice's timeline does not have all messages") } else { // check message 0,1,2,3 aliceGroupTimeline := alicesGroup.GetTimeline() if aliceGroupTimeline[0].Message != aliceLines[0] || aliceGroupTimeline[1].Message != bobLines[0] || aliceGroupTimeline[2].Message != aliceLines[1] || aliceGroupTimeline[3].Message != bobLines[1] { t.Errorf("Some of Alice's timeline messages did not have the expected content!") } } if len(bobsGroup.GetTimeline()) != 6 { t.Errorf("Bob's timeline does not have all messages") } else { // check message 0,1,2,3,4,5 bobGroupTimeline := bobsGroup.GetTimeline() if bobGroupTimeline[0].Message != aliceLines[0] || bobGroupTimeline[1].Message != bobLines[0] || bobGroupTimeline[2].Message != aliceLines[1] || bobGroupTimeline[3].Message != bobLines[1] || bobGroupTimeline[4].Message != bobLines[2] || bobGroupTimeline[5].Message != carolLines[0] { t.Errorf("Some of Bob's timeline messages did not have the expected content!") } } if len(carolsGroup.GetTimeline()) != 6 { t.Errorf("Carol's timeline does not have all messages") } else { // check message 0,1,2,3,4,5 carolGroupTimeline := carolsGroup.GetTimeline() if carolGroupTimeline[0].Message != aliceLines[0] || carolGroupTimeline[1].Message != bobLines[0] || carolGroupTimeline[2].Message != aliceLines[1] || carolGroupTimeline[3].Message != bobLines[1] || carolGroupTimeline[4].Message != bobLines[2] || carolGroupTimeline[5].Message != carolLines[0] { t.Errorf("Some of Carol's timeline messages did not have the expected content!") } } fmt.Println("Shutting down Bob...") bob.Shutdown() time.Sleep(time.Second * 3) numGoRoutinesPostBob := runtime.NumGoroutine() if server != nil { fmt.Println("Shutting down server...") server.Shutdown() time.Sleep(time.Second * 3) } numGoRoutinesPostServerShutdown := runtime.NumGoroutine() fmt.Println("Shuttind down Carol...") carol.Shutdown() time.Sleep(time.Second * 3) numGoRoutinesPostCarol := runtime.NumGoroutine() fmt.Printf("numGoRoutinesStart: %v\nnumGoRoutinesPostServer: %v\nnumGoRoutinesPostPeerStart: %v\nnumGoRoutinesPostPeerAndServerConnect: %v\n"+ "numGoRoutinesPostAlice: %v\nnumGoRotinesPostCarolConnect: %v\nnumGoRoutinesPostBob: %v\nnumGoRoutinesPostServerShutdown: %v\nnumGoRoutinesPostCarol: %v\n", numGoRoutinesStart, numGoRoutinesPostServer, numGoRoutinesPostPeerStart, numGoRoutinesPostServerConnect, numGoRoutinesPostAlice, numGoRotinesPostCarolConnect, numGoRoutinesPostBob, numGoRoutinesPostServerShutdown, numGoRoutinesPostCarol) if numGoRoutinesStart != numGoRoutinesPostCarol { t.Logf("Number of GoRoutines at start (%v) does not match number of goRoutines after cleanup of peers and servers (%v), clean up failed, leak detected!", numGoRoutinesStart, numGoRoutinesPostCarol) } }