forked from openprivacy/libricochet-go
241 lines
7.0 KiB
Go
241 lines
7.0 KiB
Go
package testing
|
|
|
|
import (
|
|
"fmt"
|
|
"git.openprivacy.ca/openprivacy/libricochet-go/application"
|
|
"git.openprivacy.ca/openprivacy/libricochet-go/channels"
|
|
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
|
|
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
|
|
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
|
"git.openprivacy.ca/openprivacy/libricochet-go/utils"
|
|
"runtime"
|
|
"strconv"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
type Message struct {
|
|
From, To string
|
|
Message string
|
|
}
|
|
|
|
type MessageStack interface {
|
|
Add(from, to, message string)
|
|
Get() []Message
|
|
}
|
|
|
|
type Messages struct {
|
|
messages []Message
|
|
lock sync.Mutex
|
|
}
|
|
|
|
func (messages *Messages) Init() {
|
|
messages.messages = []Message{}
|
|
}
|
|
|
|
func (messages *Messages) Add(from, to, message string) {
|
|
messages.lock.Lock()
|
|
messages.messages = append(messages.messages, Message{from, to, message})
|
|
messages.lock.Unlock()
|
|
}
|
|
|
|
func (messages *Messages) Get() []Message {
|
|
return messages.messages
|
|
}
|
|
|
|
type ChatEchoBot struct {
|
|
onion string
|
|
rai *application.Instance
|
|
n int
|
|
Messages MessageStack
|
|
}
|
|
|
|
// We always want bidirectional chat channels
|
|
func (bot *ChatEchoBot) OpenInbound() {
|
|
log.Infoln("OpenInbound() ChatChannel handler called...")
|
|
outboutChatChannel := bot.rai.Connection.Channel("im.ricochet.chat", channels.Outbound)
|
|
if outboutChatChannel == nil {
|
|
bot.rai.Connection.Do(func() error {
|
|
bot.rai.Connection.RequestOpenChannel("im.ricochet.chat",
|
|
&channels.ChatChannel{
|
|
Handler: bot,
|
|
})
|
|
return nil
|
|
})
|
|
}
|
|
}
|
|
|
|
func (bot *ChatEchoBot) ChatMessage(messageID uint32, when time.Time, message string) bool {
|
|
log.Infof("ChatMessage(from: %v, %v", bot.rai.RemoteHostname, message)
|
|
bot.Messages.Add(bot.rai.RemoteHostname, bot.onion, message)
|
|
SendMessage(bot.rai, strconv.Itoa(bot.n)+" witty response")
|
|
bot.n++
|
|
return true
|
|
}
|
|
|
|
func SendMessage(rai *application.Instance, message string) error {
|
|
log.Infof("SendMessage(to: %v, %v)\n", rai.RemoteHostname, message)
|
|
return rai.Connection.Do(func() error {
|
|
|
|
log.Infof("Finding Chat Channel")
|
|
channel := rai.Connection.Channel("im.ricochet.chat", channels.Outbound)
|
|
_, err := channels.SendMessageOnChatChannel(channel, message)
|
|
if err != nil {
|
|
log.Infof("Could not find chat channel")
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func (bot *ChatEchoBot) ChatMessageAck(messageID uint32, accepted bool) {
|
|
|
|
}
|
|
|
|
func TestApplicationIntegration(t *testing.T) {
|
|
log.SetLevel(log.LevelDebug)
|
|
startGoRoutines := runtime.NumGoroutine()
|
|
|
|
acn, err := connectivity.StartTor(".", "")
|
|
if err != nil {
|
|
t.Fatalf("Could not start tor: %v", err)
|
|
}
|
|
|
|
time.Sleep(1 * time.Second)
|
|
acnStartGoRoutines := runtime.NumGoroutine()
|
|
|
|
messageStack := &Messages{}
|
|
messageStack.Init()
|
|
|
|
fmt.Println("Initializing application factory...")
|
|
af := application.InstanceFactory{}
|
|
af.Init()
|
|
|
|
af.AddHandler("im.ricochet.contact.request", func(rai *application.Instance) func() channels.Handler {
|
|
return func() channels.Handler {
|
|
contact := new(channels.ContactRequestChannel)
|
|
contact.Handler = new(application.AcceptAllContactHandler)
|
|
return contact
|
|
}
|
|
})
|
|
|
|
fmt.Println("Starting alice...")
|
|
alice := new(application.RicochetApplication)
|
|
fmt.Println("Generating alice's pk...")
|
|
apubk, apk, _ := utils.GeneratePrivateKeyV3()
|
|
aliceAddr := utils.GetTorV3Hostname(apubk)
|
|
fmt.Println("Seting up alice's onion " + aliceAddr + "...")
|
|
al, err := acn.Listen(apk, application.RicochetPort)
|
|
if err != nil {
|
|
t.Fatalf("Could not setup Onion for Alice: %v", err)
|
|
}
|
|
|
|
fmt.Println("Initializing alice...")
|
|
af.AddHandler("im.ricochet.chat", func(rai *application.Instance) func() channels.Handler {
|
|
return func() channels.Handler {
|
|
chat := new(channels.ChatChannel)
|
|
chat.Handler = &ChatEchoBot{rai: rai, n: 0, Messages: messageStack, onion: aliceAddr}
|
|
return chat
|
|
}
|
|
})
|
|
alice.Init(acn, "Alice", identity.InitializeV3("Alice", &apk, &apubk), af, new(application.AcceptAllContactManager))
|
|
fmt.Println("Running alice...")
|
|
go alice.Run(al)
|
|
|
|
fmt.Println("Starting bob...")
|
|
bob := new(application.RicochetApplication)
|
|
bpubk, bpk, err := utils.GeneratePrivateKeyV3()
|
|
if err != nil {
|
|
t.Fatalf("Could not setup Onion for Alice: %v", err)
|
|
}
|
|
bobAddr := utils.GetTorV3Hostname(bpubk)
|
|
fmt.Println("Seting up bob's onion " + bobAddr + "...")
|
|
bl, _ := acn.Listen(bpk, application.RicochetPort)
|
|
af.AddHandler("im.ricochet.chat", func(rai *application.Instance) func() channels.Handler {
|
|
return func() channels.Handler {
|
|
chat := new(channels.ChatChannel)
|
|
chat.Handler = &ChatEchoBot{rai: rai, n: 0, Messages: messageStack, onion: bobAddr}
|
|
return chat
|
|
}
|
|
})
|
|
bob.Init(acn, "Bob", identity.InitializeV3("Bob", &bpk, &bpubk), af, new(application.AcceptAllContactManager))
|
|
go bob.Run(bl)
|
|
|
|
fmt.Println("Waiting for alice and bob hidden services to percolate...")
|
|
time.Sleep(60 * time.Second)
|
|
runningGoRoutines := runtime.NumGoroutine()
|
|
|
|
fmt.Println("Alice connecting to Bob...")
|
|
// out going rc from alice to bob
|
|
alicei, err := alice.Open(bobAddr, "It's alice")
|
|
if err != nil {
|
|
t.Fatalf("Error Alice connecting to Bob: %v", err)
|
|
}
|
|
time.Sleep(30 * time.Second)
|
|
|
|
fmt.Println("Alice request open chat channel...")
|
|
// TODO: opening a channel should be easier?
|
|
err = alicei.Connection.Do(func() error {
|
|
handler, err := alicei.OnOpenChannelRequest("im.ricochet.chat")
|
|
if err != nil {
|
|
log.Infof("Could not get chat handler!\n")
|
|
return err
|
|
}
|
|
_, err = alicei.Connection.RequestOpenChannel("im.ricochet.chat", handler)
|
|
return err
|
|
})
|
|
|
|
if err != nil {
|
|
t.Errorf("Error Opening a Channel: %v", err)
|
|
}
|
|
|
|
time.Sleep(30 * time.Second)
|
|
|
|
fmt.Println("Alice sending message to Bob...")
|
|
err = SendMessage(alicei, "Hello Bob!")
|
|
|
|
if err != nil {
|
|
t.Errorf("Error dialing from Alice to Bob: %v", err)
|
|
}
|
|
|
|
time.Sleep(10 * time.Second)
|
|
|
|
// should now be connected to bob
|
|
connectedGoRoutines := runtime.NumGoroutine()
|
|
|
|
fmt.Println("Shutting bob down...")
|
|
bob.Shutdown()
|
|
|
|
time.Sleep(15 * time.Second)
|
|
|
|
bobShutdownGoRoutines := runtime.NumGoroutine()
|
|
|
|
fmt.Println("Shutting alice down...")
|
|
alice.Shutdown()
|
|
time.Sleep(15 * time.Second)
|
|
aliceShutdownGoRoutines := runtime.NumGoroutine()
|
|
|
|
fmt.Println("Shutting down bine/tor")
|
|
acn.Close()
|
|
|
|
time.Sleep(5 * time.Second)
|
|
|
|
finalGoRoutines := runtime.NumGoroutine()
|
|
|
|
fmt.Printf("startGoRoutines: %v\nacnStartedGoRoutines: %v\nrunningGoRoutines: %v\nconnectedGoRoutines: %v\nBobShutdownGoRoutines: %v\naliceShutdownGoRoutines: %v\nfinalGoRoutines: %v\n", startGoRoutines, acnStartGoRoutines, runningGoRoutines, connectedGoRoutines, bobShutdownGoRoutines, aliceShutdownGoRoutines, finalGoRoutines)
|
|
|
|
if finalGoRoutines != startGoRoutines {
|
|
t.Errorf("After shutting alice and bob down, go routines were not at start value. Expected: %v Actual: %v", startGoRoutines, finalGoRoutines)
|
|
}
|
|
|
|
fmt.Println("Messages:")
|
|
for _, message := range messageStack.Get() {
|
|
fmt.Printf(" from:%v to:%v '%v'\n", message.From, message.To, message.Message)
|
|
}
|
|
|
|
messages := messageStack.Get()
|
|
if messages[0].Message != "Hello Bob!" || messages[1].Message != "0 witty response" {
|
|
t.Errorf("Message history did not contain first two expected messages!")
|
|
}
|
|
}
|