mirror/merlin/arthur/arthur_test.go

284 lines
6.8 KiB
Go

package arthur
import (
"io/ioutil"
"net"
"os"
"testing"
"time"
"git.csclub.uwaterloo.ca/public/merlin/config"
"git.csclub.uwaterloo.ca/public/merlin/logger"
)
func TestStatusCommand(t *testing.T) {
r, w := net.Pipe()
go func() {
// will only finish write when EOF is sent
// only way to send EOF is to close
w.Write([]byte("status"))
w.Close()
}()
command, repoName := GetCommand(r)
if command != "status" {
t.Errorf("command for status should be \"status\", got " + command)
} else if repoName != "" {
t.Errorf("status should return an empty string for the repoName, got " + repoName)
}
}
func TestSyncCommand(t *testing.T) {
r, w := net.Pipe()
go func() {
w.Write([]byte("sync:ubuntu"))
w.Close()
}()
command, repoName := GetCommand(r)
r.Close()
if command != "sync" {
t.Errorf("command for sync:ubuntu should be \"sync\", got " + command)
} else if repoName != "ubuntu" {
t.Errorf("name of repo for sync:ubuntu should be \"ubuntu\", got " + repoName)
}
}
func TestSendStatus(t *testing.T) {
saveRepoMap := config.RepoMap
defer func() {
config.RepoMap = saveRepoMap
}()
repoMap := make(map[string]*config.Repo)
repoMap["eeeee"] = &config.Repo{
Frequency: 30 * 86400,
State: config.RepoState{
IsRunning: true,
LastAttemptStartTime: 1600000000,
},
}
repoMap["alinux"] = &config.Repo{
Frequency: 7*86400 + 3,
State: config.RepoState{
IsRunning: true,
LastAttemptStartTime: 1620000000,
},
}
repoMap["lnux"] = &config.Repo{
Frequency: 86400,
State: config.RepoState{
IsRunning: false,
LastAttemptStartTime: 1640000000,
},
}
config.RepoMap = repoMap
r, w := net.Pipe()
go func() {
SendStatus(w)
w.Close()
}()
msg, err := ioutil.ReadAll(r)
r.Close()
if err != nil {
t.Errorf(err.Error())
}
expected := `Repository Last Synced Next Expected Sync Running
alinux Sun, 02 May 2021 20:00:00 EDT Sun, 09 May 2021 20:00:03 EDT true
eeeee Sun, 13 Sep 2020 08:26:40 EDT Tue, 13 Oct 2020 08:26:40 EDT true
lnux Mon, 20 Dec 2021 06:33:20 EST Tue, 21 Dec 2021 06:33:20 EST false
`
if expected != string(msg) {
t.Errorf("Expected:\n" + expected + "\nGot:\n" + string(msg))
}
}
func TestForceSync(t *testing.T) {
saveRepos := config.Repos
saveRepoMap := config.RepoMap
doneChan := make(chan config.SyncResult)
defer func() {
config.Repos = saveRepos
config.RepoMap = saveRepoMap
close(doneChan)
}()
// Part 1: run a dummy sync
repo := config.Repo{
Name: "nux",
SyncType: "csc-sync-dummy",
Frequency: 7 * 86400,
MaxTime: 30,
Logger: logger.NewLogger("nux", "/tmp/merlin_force_sync_test_logs"),
StateFile: "/tmp/merlin_force_sync_test_state",
DoneChan: doneChan,
State: config.RepoState{
IsRunning: false,
LastAttemptStartTime: 0,
LastAttemptRunTime: 0,
LastAttemptExit: config.NOT_RUN_YET,
},
}
config.Repos = nil
config.Repos = append(config.Repos, &repo)
config.RepoMap = make(map[string]*config.Repo)
config.RepoMap["nux"] = &repo
r, w := net.Pipe()
go func() {
if !ForceSync(w, "nux") {
t.Errorf("Sync for nux did not start")
}
w.Close()
}()
msg, err := ioutil.ReadAll(r)
r.Close()
if err != nil {
t.Errorf(err.Error())
}
expected := "Forced sync for nux"
if expected != string(msg) {
t.Errorf("Expected:\n" + expected + "\nGot:\n" + string(msg))
}
select {
case result := <-doneChan:
if result.Exit != config.SUCCESS {
t.Errorf("Sync should exit with SUCCESS, got %d", result.Exit)
}
case <-time.After(3 * time.Second):
t.Errorf("Dummy sync should be done in 1 second, waited 3 seconds")
}
// Part 2: attempt the same thing but with repo.State.IsRunning = true
r, w = net.Pipe()
go func() {
if ForceSync(w, "nux") {
t.Errorf("Sync for nux should not have started")
}
w.Close()
}()
msg, err = ioutil.ReadAll(r)
r.Close()
if err != nil {
t.Errorf(err.Error())
}
expected = "Could not force sync: nux is already syncing."
if expected != string(msg) {
t.Errorf("Expected:\n" + expected + "\nGot:\n" + string(msg))
}
select {
case <-doneChan:
t.Errorf("Sync for nux should not have been started")
case <-time.After(2 * time.Second):
}
// Part 3: attempt a force sync with a repo that does not exist
r, w = net.Pipe()
go func() {
if ForceSync(w, "nixx") {
t.Errorf("Sync for nixx should not have started")
}
w.Close()
}()
msg, err = ioutil.ReadAll(r)
r.Close()
if err != nil {
t.Errorf(err.Error())
}
expected = "nixx is not tracked so cannot sync"
if expected != string(msg) {
t.Errorf("Expected:\n" + expected + "\nGot:\n" + string(msg))
}
}
func TestStartListener(t *testing.T) {
saveConf := config.Conf
connChan := make(chan net.Conn)
stopLisChan := make(chan struct{})
wait := make(chan struct{})
defer func() {
config.Conf = saveConf
close(connChan)
close(stopLisChan)
}()
config.Conf = config.Config{
SockPath: "/tmp/merlin_listener_test.sock",
}
// Test 1: check that closing/sending something to stopLisChan will stop the listener
// and that a new listener can be created after stopping the old one
go func() {
StartListener(connChan, stopLisChan)
wait <- struct{}{}
}()
stopLisChan <- struct{}{}
select {
case <-wait:
case <-time.After(3 * time.Second):
t.Errorf("StartListener should stop when struct{}{} is sent to stopLisChan")
}
go func() {
StartListener(connChan, stopLisChan)
wait <- struct{}{}
}()
close(stopLisChan)
select {
case <-wait:
case <-time.After(3 * time.Second):
t.Errorf("StartListener should stop when stopLisChan is closed")
}
close(wait)
// Test 2: check that connections can be made to the unix socket
// this test does not appear to be very stable (I think there is a race condition somewhere)
stopLisChan = make(chan struct{})
go StartListener(connChan, stopLisChan)
waitForMsg := func(expected string) {
select {
case conn := <-connChan:
msg, err := ioutil.ReadAll(conn)
if err != nil {
t.Errorf(err.Error())
} else if expected != string(msg) {
t.Errorf("Message expected was " + expected + " got " + string(msg))
}
conn.Close()
case <-time.After(3 * time.Second):
t.Errorf("StartListener should stop when struct{}{} is sent to stopLisChan")
}
}
sendMsg := func(msg string) {
<-time.After(500 * time.Millisecond)
send, err := net.Dial("unix", "/tmp/merlin_listener_test.sock")
if err != nil {
panic(err)
}
_, err = send.Write([]byte(msg))
if err != nil {
t.Errorf(err.Error())
}
send.Close()
}
go func() {
waitForMsg("status")
}()
sendMsg("status")
go func() {
waitForMsg("sync:uuunix")
}()
sendMsg("sync:uuunix")
go func() {
waitForMsg("$UPz2L2yWsE^UY8iG9JX@^dBb@5yb*")
}()
sendMsg("$UPz2L2yWsE^UY8iG9JX@^dBb@5yb*")
// unsure why I can't put this in the defer
os.Remove("/tmp/merlin_listener_test.sock")
}