package testing import ( "cwtch.im/cwtch/model" "cwtch.im/cwtch/peer" cwtchserver "cwtch.im/cwtch/server" "fmt" "github.com/s-rah/go-ricochet" "github.com/s-rah/go-ricochet/utils" "io/ioutil" "log" "os" "testing" "time" ) const ( keyfile = "./private_key" ) var ( aliceLines = []string{"Hello", "My name is Alice", "bye"} bobLines = []string{"Hi", "My name is Bob.", "toodles", "hello?"} ) func checkAndGenPrivateKey(privateKeyFile string) (generated bool) { if _, err := os.Stat(privateKeyFile); os.IsNotExist(err) { fmt.Println("generating new private key...") pk, err := utils.GeneratePrivateKey() if err != nil { log.Fatalf("error generating new private key: %v\n", err) } err = ioutil.WriteFile(privateKeyFile, []byte(utils.PrivateKeyToString(pk)), 0400) if err != nil { log.Fatalf("error writing new private key to file %s: %v\n", privateKeyFile, err) } return true } return false } func printAndVerifyTimeline(t *testing.T, timeline []model.Message) error { for _, message := range timeline { fmt.Printf("%v %v> %s [%t]\n", message.Timestamp, message.PeerID, message.Message, message.Verified) if !message.Verified { t.Errorf("Message '%s' from '%s' not verified!", message.Message, message.PeerID) } } return nil } func serverCheck(serverAddr string) bool { rc, err := goricochet.Open(serverAddr) if err == nil { rc.Conn.Close() return true } return false } func TestCwtchPeerIntegration(t *testing.T) { // Hide logging "noise" log.SetOutput(ioutil.Discard) // Todo: coung goroutines at beginning middle and end after shutdown // ***** Cwtch Server managment ***** generatedKey := checkAndGenPrivateKey(keyfile) serverKey, err := utils.LoadPrivateKeyFromFile(keyfile) if err != nil { t.Errorf("Could not load server's key from %v", keyfile) } serverAddr, _ := utils.GetOnionAddress(serverKey) serverOnline := false if !generatedKey { fmt.Printf("Checking if test server %v is online...\n", serverAddr) serverOnline = serverCheck(serverAddr) } if !serverOnline { // launch app server := new(cwtchserver.Server) fmt.Printf("No server found\nStarting cwtch server...\n") go server.Run(keyfile) // 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) } // ***** Peer setup ***** fmt.Println("Creating Alice...") alice := peer.NewCwtchPeer("Alice") go alice.Listen() fmt.Println("Alice created:", alice.Profile.Onion) fmt.Println("Creating Bob...") bob := peer.NewCwtchPeer("Bob") go bob.Listen() fmt.Println("Bob created:", bob.Profile.Onion) fmt.Println("Waiting for alice and bob to connection with onion network...") time.Sleep(time.Second * 60) // ***** Peering and group creation / invite ***** fmt.Println("Creating group on ", serverAddr, "...") groupId, _, err := alice.Profile.StartGroup(serverAddr) fmt.Printf("Created group: %v!\n", groupId) if err != nil { t.Errorf("Failed to start group: %v", err) return } fmt.Println("Peering Alice peering with bob...") alice.PeerWithOnion(bob.Profile.Onion) time.Sleep(time.Second * 15) fmt.Println("Alice inviting Bob to group...") err = alice.InviteOnionToGroup(bob.Profile.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 _, group := range bob.Profile.Groups { 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 * 3) fmt.Println("Alice joining server...") alice.JoinServer(serverAddr) fmt.Println("Bob joining server...") bob.JoinServer(serverAddr) // Wait for them to join the server time.Sleep(time.Second * 30) // ***** 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) // "instant" - could be either order? fmt.Println("Alice> ", aliceLines[1]) alice.SendMessageToGroup(groupId, aliceLines[1]) fmt.Println("Bob> ", bobLines[1]) bob.SendMessageToGroup(groupId, bobLines[1]) time.Sleep(time.Second * 10) fmt.Println("Alice> ", aliceLines[2]) alice.SendMessageToGroup(groupId, aliceLines[2]) // Todo: Alice disconnects time.Sleep(time.Second * 10) fmt.Println("Bob> ", bobLines[2]) bob.SendMessageToGroup(groupId, bobLines[2]) time.Sleep(time.Second * 10) // Todo: Alice reconects, gets missed messages (from bob) // ***** Verify Test ***** // final syncing time... time.Sleep(time.Second * 10) alicesGroup := alice.Profile.GetGroupByGroupID(groupId) fmt.Printf("alice Groups:\n") for k := range alice.Profile.Groups { fmt.Println(" " + k) } if alicesGroup == nil { t.Error("aliceGroup == nil") return } fmt.Printf("Alice TimeLine:\n") printAndVerifyTimeline(t, alicesGroup.GetTimeline()) bobsGroup := bob.Profile.GetGroupByGroupID(groupId) fmt.Printf("bob Groups:\n") for k := range bob.Profile.Groups { fmt.Println(" " + k) } if bobsGroup == nil { t.Error("bobGroup == nil") return } fmt.Printf("Bob TimeLine:\n") printAndVerifyTimeline(t, bobsGroup.GetTimeline()) if len(alicesGroup.Timeline.Messages) != 6 { t.Errorf("Alice's timeline does not have all messages") return } // check message 0,1 and 4,5 for content (2,3 could be out of order) aliceGroupTimeline := alicesGroup.GetTimeline() if aliceGroupTimeline[0].Message != aliceLines[0] || aliceGroupTimeline[1].Message != bobLines[0] || aliceGroupTimeline[4].Message != aliceLines[2] || aliceGroupTimeline[5].Message != bobLines[2] { t.Errorf("Some of the messages did not have the expected content!") } // Todo: shutdown users and server }