cwtch/testing/cwtch_peer_server_intergrat...

395 lines
12 KiB
Go

package testing
import (
"crypto/rsa"
"cwtch.im/cwtch/model"
"cwtch.im/cwtch/peer"
"cwtch.im/cwtch/peer/connections"
cwtchserver "cwtch.im/cwtch/server"
"fmt"
"git.openprivacy.ca/openprivacy/libricochet-go/utils"
"golang.org/x/net/proxy"
"io/ioutil"
"log"
"os"
"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!"}
)
// TODO: fix to load private key from server/app/serverConfig.json
func loadPrivateKey(t *testing.T) *rsa.PrivateKey {
if _, err := os.Stat(serverKeyfile); os.IsNotExist(err) {
return nil
}
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
}
func genPrivateKey(t *testing.T) *rsa.PrivateKey {
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)), 0600)
if err != nil {
t.Fatalf("error writing new private key to file %s: %v\n", localKeyfile, err)
}
return pk
}
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(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.CwtchPeerInterface, 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
serverKey := loadPrivateKey(t)
serverOnline := false
var serverAddr string
if serverKey != nil {
serverAddr, _ = utils.GetOnionAddress(serverKey)
fmt.Printf("Checking if test server %v is online...\n", serverAddr)
serverOnline = serverCheck(t, serverAddr)
}
if !serverOnline {
// launch app with new key
fmt.Println("No server found!")
serverKey = genPrivateKey(t)
serverAddr, _ = utils.GetOnionAddress(serverKey)
server = new(cwtchserver.Server)
fmt.Println("Starting cwtch server...")
config := cwtchserver.Config{PrivateKeyBytes: utils.PrivateKeyToString(serverKey), MaxBufferLines: 100, ServerReporting: cwtchserver.Reporting{}}
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()
// ***** Peer setup *****
fmt.Println("Creating Alice...")
alice := peer.NewCwtchPeer("Alice", "alicepass", "")
go alice.Listen()
fmt.Println("Alice created:", alice.GetProfile().Onion)
fmt.Println("Creating Bob...")
bob := peer.NewCwtchPeer("Bob", "bobpass", "")
go bob.Listen()
fmt.Println("Bob created:", bob.GetProfile().Onion)
fmt.Println("Creating Carol...")
carol := peer.NewCwtchPeer("Carol", "carolpass", "")
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 * 120)
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 != 5 {
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 != 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")
} 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.Errorf("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)
}
}