166 lines
4.9 KiB
Go
166 lines
4.9 KiB
Go
//go:build integration
|
|
|
|
package app_test
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.netflux.io/rob/octoplex/internal/app"
|
|
"git.netflux.io/rob/octoplex/internal/config"
|
|
"git.netflux.io/rob/octoplex/internal/domain"
|
|
"git.netflux.io/rob/octoplex/internal/terminal"
|
|
"git.netflux.io/rob/octoplex/internal/testhelpers"
|
|
dockerclient "github.com/docker/docker/client"
|
|
"github.com/gdamore/tcell/v2"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestIntegration(t *testing.T) {
|
|
ctx, cancel := context.WithTimeout(t.Context(), 2*time.Minute)
|
|
defer cancel()
|
|
|
|
logger := testhelpers.NewTestLogger().With("component", "integration")
|
|
dockerClient, err := dockerclient.NewClientWithOpts(dockerclient.FromEnv)
|
|
require.NoError(t, err)
|
|
|
|
// Fetching the screen contents is tricky at this level of the test pyramid,
|
|
// because we need to:
|
|
//
|
|
// 1. Somehow capture the screen contents, which is only available via the
|
|
// tcell.SimulationScreen, and...
|
|
// 2. Do so without triggering data races.
|
|
//
|
|
// We can achieve this by passing a channel into the terminal actor, which
|
|
// will send screen captures after each render. This can be stored locally
|
|
// and asserted against when needed.
|
|
var (
|
|
screenCells []tcell.SimCell
|
|
screenWidth int
|
|
screenMu sync.Mutex
|
|
)
|
|
|
|
getContents := func() []string {
|
|
screenMu.Lock()
|
|
defer screenMu.Unlock()
|
|
|
|
var lines []string
|
|
for n, _ := range screenCells {
|
|
y := n / screenWidth
|
|
|
|
if y > len(lines)-1 {
|
|
lines = append(lines, "")
|
|
}
|
|
lines[y] += string(screenCells[n].Runes[0])
|
|
}
|
|
|
|
return lines
|
|
}
|
|
|
|
screen := tcell.NewSimulationScreen("")
|
|
screenCaptureC := make(chan terminal.ScreenCapture, 1)
|
|
go func() {
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case capture := <-screenCaptureC:
|
|
screenMu.Lock()
|
|
screenCells = capture.Cells
|
|
screenWidth = capture.Width
|
|
screenMu.Unlock()
|
|
}
|
|
}
|
|
}()
|
|
|
|
done := make(chan struct{})
|
|
go func() {
|
|
err := app.Run(ctx, app.RunParams{
|
|
Config: config.Config{
|
|
// We use the mediaserver as the destination server, just because it is
|
|
// reachable from the docker network via mediaserver:1935.
|
|
Destinations: []config.Destination{
|
|
{
|
|
Name: "Local server 1",
|
|
URL: "rtmp://mediaserver:1935/live/dest1",
|
|
},
|
|
{
|
|
Name: "Local server 2",
|
|
URL: "rtmp://mediaserver:1935/live/dest2",
|
|
},
|
|
},
|
|
},
|
|
DockerClient: dockerClient,
|
|
Screen: &terminal.Screen{
|
|
Screen: screen,
|
|
Width: 160,
|
|
Height: 25,
|
|
CaptureC: screenCaptureC,
|
|
},
|
|
ClipboardAvailable: false,
|
|
BuildInfo: domain.BuildInfo{Version: "0.0.1", GoVersion: "go1.16.3"},
|
|
Logger: logger,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
done <- struct{}{}
|
|
}()
|
|
|
|
// Wait for mediaserver container to start:
|
|
time.Sleep(5 * time.Second)
|
|
|
|
// Start streaming a test video to the app:
|
|
testhelpers.StreamFLV(t, "rtmp://localhost:1935/live")
|
|
time.Sleep(10 * time.Second)
|
|
|
|
// Start destinations:
|
|
screen.PostEvent(tcell.NewEventKey(tcell.KeyRune, ' ', tcell.ModNone))
|
|
screen.PostEvent(tcell.NewEventKey(tcell.KeyDown, ' ', tcell.ModNone))
|
|
screen.PostEvent(tcell.NewEventKey(tcell.KeyRune, ' ', tcell.ModNone))
|
|
|
|
time.Sleep(15 * time.Second)
|
|
|
|
contents := getContents()
|
|
require.True(t, len(contents) > 2, "expected at least 3 lines of output")
|
|
|
|
require.Contains(t, contents[2], "Status receiving", "expected mediaserver status to be receiving")
|
|
require.Contains(t, contents[3], "Tracks H264", "expected mediaserver tracks to be H264")
|
|
require.Contains(t, contents[4], "Health healthy", "expected mediaserver to be healthy")
|
|
|
|
require.Contains(t, contents[2], "Local server 1", "expected local server 1 to be present")
|
|
assert.Contains(t, contents[2], "sending", "expected local server 1 to be sending")
|
|
assert.Contains(t, contents[2], "healthy", "expected local server 1 to be healthy")
|
|
|
|
require.Contains(t, contents[3], "Local server 2", "expected local server 2 to be present")
|
|
assert.Contains(t, contents[3], "sending", "expected local server 2 to be sending")
|
|
assert.Contains(t, contents[3], "healthy", "expected local server 2 to be healthy")
|
|
|
|
// Stop destinations:
|
|
screen.PostEvent(tcell.NewEventKey(tcell.KeyRune, ' ', tcell.ModNone))
|
|
screen.PostEvent(tcell.NewEventKey(tcell.KeyUp, ' ', tcell.ModNone))
|
|
screen.PostEvent(tcell.NewEventKey(tcell.KeyRune, ' ', tcell.ModNone))
|
|
|
|
time.Sleep(10 * time.Second)
|
|
|
|
contents = getContents()
|
|
require.True(t, len(contents) > 2, "expected at least 3 lines of output")
|
|
|
|
require.Contains(t, contents[2], "Local server 1", "expected local server 1 to be present")
|
|
assert.Contains(t, contents[2], "exited", "expected local server 1 to have exited")
|
|
|
|
require.Contains(t, contents[3], "Local server 2", "expected local server 2 to be present")
|
|
assert.Contains(t, contents[3], "exited", "expected local server 2 to have exited")
|
|
|
|
// TODO:
|
|
// - Source error
|
|
// - Destination error
|
|
// - Additional features (copy URL, etc.)
|
|
|
|
cancel()
|
|
|
|
<-done
|
|
}
|