forked from cwtch.im/cwtch
355 lines
11 KiB
Go
355 lines
11 KiB
Go
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"
|
|
"os"
|
|
"runtime"
|
|
"testing"
|
|
"time"
|
|
"crypto/rsa"
|
|
"io/ioutil"
|
|
"log"
|
|
)
|
|
|
|
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 loadOrGenPrivateKey(t *testing.T) (pk *rsa.PrivateKey, generated bool) {
|
|
if _, err := os.Stat(serverKeyfile); os.IsNotExist(err) {
|
|
fmt.Println("generating new private key...")
|
|
pk, err := utils.GeneratePrivateKey()
|
|
if err != nil {
|
|
t.Fatalf("error generating new private key: %v\n", err)
|
|
}
|
|
err = ioutil.WriteFile(localKeyfile, []byte(utils.PrivateKeyToString(pk)), 0400)
|
|
if err != nil {
|
|
log.Fatalf("error writing new private key to file %s: %v\n", localKeyfile, err)
|
|
}
|
|
|
|
return pk,true
|
|
}
|
|
fmt.Println("Found server key " + serverKeyfile + ", loading...")
|
|
pk, err := utils.LoadPrivateKeyFromFile(serverKeyfile)
|
|
if err != nil {
|
|
t.Fatalf("Could not load server's key from %v", serverKeyfile)
|
|
}
|
|
return pk, false
|
|
}
|
|
|
|
func printAndCountVerifedTimeline(t *testing.T, timeline []model.Message) int {
|
|
numVerified := 0
|
|
for _, message := range timeline {
|
|
fmt.Printf("%v %v> %s [%t]\n", message.Timestamp, message.PeerID, message.Message, message.Verified)
|
|
if message.Verified {
|
|
numVerified++
|
|
}
|
|
}
|
|
return numVerified
|
|
}
|
|
|
|
func serverCheck(serverAddr string) bool {
|
|
rc, err := goricochet.Open(serverAddr)
|
|
// Won't actaully free thread because rc.start() will wait for the uncalled rc.Process to read from errorChannel...
|
|
rc.Conn.Close()
|
|
|
|
if err == nil {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func TestCwtchPeerIntegration(t *testing.T) {
|
|
// Hide logging "noise"
|
|
log.SetOutput(ioutil.Discard)
|
|
numGoRoutinesStart := runtime.NumGoroutine()
|
|
|
|
// ***** Cwtch Server managment *****
|
|
var server *cwtchserver.Server = nil
|
|
serverKey, generatedKey := loadOrGenPrivateKey(t)
|
|
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(localKeyfile)
|
|
|
|
// 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)
|
|
}
|
|
|
|
time.Sleep(time.Second * 2)
|
|
numGoRoutinesPostServer := runtime.NumGoroutine()
|
|
|
|
// ***** 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("Creating Carol...")
|
|
carol := peer.NewCwtchPeer("Carol")
|
|
go carol.Listen()
|
|
fmt.Println("Carol created:", carol.Profile.Onion)
|
|
|
|
fmt.Println("Waiting for Alice, Bob, and Carol to connection with onion network...")
|
|
time.Sleep(time.Second * 70)
|
|
numGoRoutinesPostPeerStart := runtime.NumGoroutine()
|
|
|
|
// ***** Peering, server joining, 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 init group: %v", err)
|
|
return
|
|
}
|
|
|
|
fmt.Println("Alice peering with Bob...")
|
|
alice.PeerWithOnion(bob.Profile.Onion)
|
|
fmt.Println("Alice peering with Carol...")
|
|
alice.PeerWithOnion(carol.Profile.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 * 60)
|
|
|
|
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 * 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)
|
|
*/
|
|
|
|
// ***** 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.Profile.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 _, group := range carol.Profile.Groups {
|
|
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)
|
|
|
|
time.Sleep(time.Second * 60)
|
|
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 *****
|
|
|
|
// final syncing time...
|
|
time.Sleep(time.Second * 15)
|
|
|
|
alicesGroup := alice.Profile.GetGroupByGroupID(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.Profile.GetGroupByGroupID(groupId)
|
|
if bobsGroup == nil {
|
|
t.Error("bobGroup == nil")
|
|
return
|
|
}
|
|
fmt.Printf("Bob's TimeLine:\n")
|
|
bobVerified := printAndCountVerifedTimeline(t, bobsGroup.GetTimeline())
|
|
if bobVerified != 5 {
|
|
t.Errorf("Bob did not have 5 verified messages")
|
|
}
|
|
|
|
carolsGroup := carol.Profile.GetGroupByGroupID(groupId)
|
|
fmt.Printf("Carol's TimeLine:\n")
|
|
carolVerified := printAndCountVerifedTimeline(t, carolsGroup.GetTimeline())
|
|
if carolVerified != 3 {
|
|
t.Errorf("Carol did not have 3 verified messages")
|
|
}
|
|
|
|
if len(alicesGroup.GetTimeline()) != 4 {
|
|
t.Errorf("Alice's timeline does not have all messages")
|
|
return
|
|
}
|
|
|
|
if len(bobsGroup.GetTimeline()) != 6 {
|
|
t.Errorf("Bob's timeline does not have all messages")
|
|
}
|
|
|
|
// 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!")
|
|
}
|
|
|
|
// 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")
|
|
}
|
|
|
|
// 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 numGoRoutinesPostServer != numGoRoutinesPostCarol {
|
|
t.Errorf("Number of GoRoutines once server checks were completed (%v) does not match number of goRoutines after cleanup of peers and servers (%v), clean up failed, leak detected!", numGoRoutinesPostServer, numGoRoutinesPostCarol)
|
|
}
|
|
}
|