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!") } }