rust-networking-proxy / testing_tool.go
testing_tool.go
Raw
package main

import (
	"fmt"
	"net"
	"os"
  "strings"
  "sync"
  "strconv"
)


// WARNING: This assumes that the proxy have not received any prior
// requests for the app "five-thousand"
// Listening on port 5300 is the app "five-thousand" which should route
// to two servers: (1) tcp-echo.fly.dev:5001 and (2) tcp-echo.fly.dev:5002
// (1) should return the data in uppercase
// (2) should return the data as is
// this function asserts that responses from connections to the app are
// different, so the proxy is indeed alternating the targets.
func test_alternating_targets() {
  message := "all lowercase message\n"
  response := connect_to("localhost:5300", message)
  second_response := connect_to("localhost:5300", message)

  uppercase_message := strings.ToUpper(message)
  
  // The proxy tries the targets in order. If it is the first connection
  // the first target will be used, and thus the message should be returned
  // as uppercase
  // for the second connection, the second target is used and the message
  // should be returned as is
  if response != uppercase_message {
    panic("Alternating targets failed")
  }
  if second_response != message {
    panic("Alternating targets failed")
  }
}

// App "six-thousand" has three targets. The first two are similar to 
// app "five-thousand", and the last one is a bad target that never works.
// This function shows that all requests are still correctly made, even
// though a server is down.
func test_bad_targets_are_ignored() {
  message := "some message\n"
  uppercase_message := strings.ToUpper(message)

  for requests := 0; requests < 10; requests++ {
    response := connect_to("localhost:6001", message)
    // Depending on the target, it's possible that the returned response
    // is either the originar version or its uppercase version
    // Both are fine for our purpose here, we just want to assert
    // that the requests are going through
    if response != message && response != uppercase_message {
      panic("Bad targets are ignored failed")
    }
  } 
}

// App "seven-thousand" has four ports and two targets.
// This test shows that
// 1 - All ports work (we are successfully connecting through all four ports)
// 2 - The connections are being processed concurrently
//     ^^^^ Actually this is NOT TRUE! 
// The users could be being processed serially by the proxy, and only
// concurrently through the goroutines. How to test that the proxy is concurrent?
// Manually we can see it's the case using telnet and such, but how to automate that?
func attempt_to_test_concurrent_users() {
  var wg sync.WaitGroup

  ip_with_port := []string { "localhost:7001", "localhost:7200", "localhost:7300", "localhost:7400"}
  for simulated_user := 0; simulated_user < 3; simulated_user++ {
    for _, ip := range ip_with_port {
      wg.Add(1)
      go func (user string, ip string) {
        defer wg.Done()
        connect_to(ip, "testing message\n")
        print("Completed a request from user: ", user, "\n")
      }(strconv.Itoa(simulated_user), ip)
    }
  }
  wg.Wait()
}

// Returns the response from the server
func connect_to(ip string, message string) string {
  connection, err := net.Dial("tcp", ip)
  if err != nil {
    fmt.Println("something wrong")
    os.Exit(1)
  }
  connection.Write([]byte(message))

  response_buffer := make([]byte, 1024)
  bytes_read, err := connection.Read(response_buffer)
  if err != nil {
    println("something wrong")
    os.Exit(1)
  }
  return string(response_buffer[:bytes_read])
}

func main() {
  test_alternating_targets()
  test_bad_targets_are_ignored()
  // See the comment on this function, I don't think it works as a test
  attempt_to_test_concurrent_users()
}