WIP: ACN state handling & tests [ci skip]
Some checks failed
continuous-integration/drone/pr Build is failing

This commit is contained in:
decentral1se 2023-10-01 15:32:50 +02:00
parent c453bd831c
commit 9cebb1fbd3
Signed by: decentral1se
GPG Key ID: 03789458B3D0C410
4 changed files with 144 additions and 54 deletions

26
acn.go
View File

@ -35,17 +35,6 @@ func isConnected(m model) (bool, tea.Msg) {
}
func turnAcnOn(m *model) tea.Msg {
if ok, msg := isConnected(*m); !ok {
switch msg.(type) {
case acnConnectingMsg:
return msg
case acnAlreadyOnlineMsg:
return msg
}
}
m.acnState = connecting
mrand.New(mrand.NewSource(int64(time.Now().Nanosecond())))
port := mrand.Intn(1000) + 9600
controlPort := port + 1
@ -113,22 +102,9 @@ func turnAcnOn(m *model) tea.Msg {
}
func turnAcnOff(m *model, quitProgram bool) tea.Msg {
switch m.acnState {
case offline:
if !quitProgram {
return acnOfflineMsg{}
}
case connecting:
return acnConnectingMsg{}
}
if m.app != nil {
if m.acnState != offline {
m.app.Shutdown()
}
if m.acn != nil {
m.acn.Close()
}
return acnOffMsg{quitProgram: quitProgram}
}

114
main_test.go Normal file
View File

@ -0,0 +1,114 @@
package main
import (
"bytes"
"fmt"
"os"
"os/user"
"testing"
"time"
openPrivacyLog "git.openprivacy.ca/openprivacy/log"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/x/exp/teatest"
)
const (
cairdeTestLogFile = "cairde-test.log"
cwtchTestLogFile = "cwtch-test.log"
)
func setup(t *testing.T) (model, *os.File, *openPrivacyLog.Logger) {
_, err := tea.LogToFile(cairdeTestLogFile, "debug")
if err != nil {
t.Fatal(err)
}
cairdeLogHandler, err := os.OpenFile(cairdeTestLogFile, os.O_RDONLY, 0644)
if err != nil {
t.Fatal(err)
}
cwtchLog, err := openPrivacyLog.NewFile(openPrivacyLog.LevelDebug, cwtchTestLogFile)
if err == nil {
openPrivacyLog.SetStd(cwtchLog)
}
user, err := user.Current()
if err != nil {
t.Fatal(err)
}
return newModel(user.Username, user.HomeDir, true), cairdeLogHandler, cwtchLog
}
func teardown(t *testing.T, cairdeLog *os.File) {
if err := cairdeLog.Close(); err != nil {
t.Fatal(err)
}
if err := os.Remove(cairdeTestLogFile); err != nil {
t.Fatal(err)
}
if err := os.Remove(cwtchTestLogFile); err != nil {
t.Fatal(err)
}
}
func TestOnOff(t *testing.T) {
m, cairdeLog, _ := setup(t)
testModel := teatest.NewTestModel(t, m)
testModel.Send(tea.KeyMsg{
Type: tea.KeyRunes,
Runes: []rune("ctrl+c"),
})
testModel.WaitFinished(t, teatest.WithFinalTimeout(time.Second))
teardown(t, cairdeLog)
}
func TestAcnOnOff(t *testing.T) {
m, cairdeLog, _ := setup(t)
testModel := teatest.NewTestModel(t, m)
testModel.Type("/acn on")
testModel.Send(tea.KeyMsg{Type: tea.KeyEnter})
teatest.WaitFor(
t, cairdeLog,
func(bts []byte) bool {
fmt.Println(string(bts))
return bytes.Contains(bts, []byte("ACN is up and running, all engines go"))
},
teatest.WithCheckInterval(time.Millisecond*100),
teatest.WithDuration(time.Second*20),
)
testModel.Type("/acn off")
testModel.Send(tea.KeyMsg{Type: tea.KeyEnter})
time.Sleep(1 * time.Second)
testModel.Send(tea.KeyMsg{
Type: tea.KeyRunes,
Runes: []rune("ctrl+c"),
})
testModel.WaitFinished(t, teatest.WithFinalTimeout(time.Second))
finalModel := testModel.FinalModel(t)
_, ok := finalModel.(model)
if !ok {
t.Fatalf("final model has wrong type: %T", finalModel)
}
teardown(t, cairdeLog)
}
// test acn on and then quit, clean
// test acn on and then on, asks to hold
// test acn on, then off, asks to hold, wait, then off
// test acn on and then quit, asks to hold
// test acn off, tells it is off

View File

@ -133,6 +133,9 @@ func (m model) sendStatusCmd(lines ...string) tea.Cmd {
// we want to render messages to the status buffer but still have work to do.
func (m model) sendStatus(lines ...string) {
for _, line := range lines {
if m.debug {
log.Printf(line)
}
m.statusBuffer <- line
}
}
@ -263,6 +266,17 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
cmds = append(cmds, m.sendStatusCmd(msg.output...))
case turnAcnOffMsg:
switch m.acnState {
case offline:
if !msg.quitProgram {
cmds = append(cmds, func() tea.Msg { return acnOfflineMsg{} })
return m, tea.Batch(cmds...)
}
case connecting:
cmds = append(cmds, func() tea.Msg { return acnConnectingMsg{} })
return m, tea.Batch(cmds...)
}
m.input.Reset()
cmds = append(cmds, func() tea.Msg {
return turnAcnOff(&m, msg.quitProgram)
@ -276,9 +290,19 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.acnState = offline
case turnAcnOnMsg:
// TODO: stop if we're already connecting!
// state management is getting a little tricky :/
switch m.acnState {
case connecting:
cmds = append(cmds, func() tea.Msg { return acnConnectingMsg{} })
return m, tea.Batch(cmds...)
case connected:
cmds = append(cmds, func() tea.Msg { return acnAlreadyOnlineMsg{} })
return m, tea.Batch(cmds...)
}
m.acnState = connecting
m.input.Reset()
cmds = append(cmds, func() tea.Msg {
return turnAcnOn(&m)
})
@ -390,6 +414,9 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case sendStatusMsg:
cmds = append(cmds, func() tea.Msg {
for _, line := range msg.lines {
if m.debug {
log.Printf(line)
}
m.statusBuffer <- line
}
return nil

View File

@ -1,27 +0,0 @@
package main
import (
"os/user"
"testing"
"time"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/x/exp/teatest"
)
func TestCairdeOnOffWorks(t *testing.T) {
user, err := user.Current()
if err != nil {
t.Fatal(err)
}
m := newModel(user.Username, user.HomeDir, false)
tm := teatest.NewTestModel(t, m)
tm.Send(tea.KeyMsg{
Type: tea.KeyRunes,
Runes: []rune("ctrl+c"),
})
tm.WaitFinished(t, teatest.WithFinalTimeout(time.Second))
}