mediamtx/internal/conf/conf_test.go

251 lines
6.3 KiB
Go

package conf
import (
"crypto/rand"
"encoding/base64"
"io"
"os"
"testing"
"time"
"github.com/bluenviron/gortsplib/v3"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/nacl/secretbox"
"github.com/aler9/mediamtx/internal/logger"
)
func writeTempFile(byts []byte) (string, error) {
tmpf, err := os.CreateTemp(os.TempDir(), "rtsp-")
if err != nil {
return "", err
}
defer tmpf.Close()
_, err = tmpf.Write(byts)
if err != nil {
return "", err
}
return tmpf.Name(), nil
}
func TestConfFromFile(t *testing.T) {
func() {
tmpf, err := writeTempFile([]byte("logLevel: debug\n" +
"paths:\n" +
" cam1:\n" +
" runOnDemandStartTimeout: 5s\n"))
require.NoError(t, err)
defer os.Remove(tmpf)
conf, hasFile, err := Load(tmpf)
require.NoError(t, err)
require.Equal(t, true, hasFile)
require.Equal(t, LogLevel(logger.Debug), conf.LogLevel)
pa, ok := conf.Paths["cam1"]
require.Equal(t, true, ok)
require.Equal(t, &PathConf{
Source: "publisher",
SourceOnDemandStartTimeout: 10 * StringDuration(time.Second),
SourceOnDemandCloseAfter: 10 * StringDuration(time.Second),
RPICameraWidth: 1920,
RPICameraHeight: 1080,
RPICameraContrast: 1,
RPICameraSaturation: 1,
RPICameraSharpness: 1,
RPICameraFPS: 30,
RPICameraIDRPeriod: 60,
RPICameraBitrate: 1000000,
RPICameraProfile: "main",
RPICameraLevel: "4.1",
RPICameraTextOverlay: "%Y-%m-%d %H:%M:%S - MediaMTX",
RunOnDemandStartTimeout: 5 * StringDuration(time.Second),
RunOnDemandCloseAfter: 10 * StringDuration(time.Second),
}, pa)
}()
func() {
tmpf, err := writeTempFile([]byte(``))
require.NoError(t, err)
defer os.Remove(tmpf)
_, _, err = Load(tmpf)
require.NoError(t, err)
}()
func() {
tmpf, err := writeTempFile([]byte(`paths:`))
require.NoError(t, err)
defer os.Remove(tmpf)
_, _, err = Load(tmpf)
require.NoError(t, err)
}()
func() {
tmpf, err := writeTempFile([]byte(
"paths:\n" +
" mypath:\n"))
require.NoError(t, err)
defer os.Remove(tmpf)
_, _, err = Load(tmpf)
require.NoError(t, err)
}()
}
func TestConfFromFileAndEnv(t *testing.T) {
os.Setenv("RTSP_PATHS_CAM1_SOURCE", "rtsp://testing")
defer os.Unsetenv("RTSP_PATHS_CAM1_SOURCE")
os.Setenv("RTSP_PROTOCOLS", "tcp")
defer os.Unsetenv("RTSP_PROTOCOLS")
tmpf, err := writeTempFile([]byte("{}"))
require.NoError(t, err)
defer os.Remove(tmpf)
conf, hasFile, err := Load(tmpf)
require.NoError(t, err)
require.Equal(t, true, hasFile)
require.Equal(t, Protocols{Protocol(gortsplib.TransportTCP): {}}, conf.Protocols)
pa, ok := conf.Paths["cam1"]
require.Equal(t, true, ok)
require.Equal(t, &PathConf{
Source: "rtsp://testing",
SourceOnDemandStartTimeout: 10 * StringDuration(time.Second),
SourceOnDemandCloseAfter: 10 * StringDuration(time.Second),
RPICameraWidth: 1920,
RPICameraHeight: 1080,
RPICameraContrast: 1,
RPICameraSaturation: 1,
RPICameraSharpness: 1,
RPICameraFPS: 30,
RPICameraIDRPeriod: 60,
RPICameraBitrate: 1000000,
RPICameraProfile: "main",
RPICameraLevel: "4.1",
RPICameraTextOverlay: "%Y-%m-%d %H:%M:%S - MediaMTX",
RunOnDemandStartTimeout: 10 * StringDuration(time.Second),
RunOnDemandCloseAfter: 10 * StringDuration(time.Second),
}, pa)
}
func TestConfFromEnvOnly(t *testing.T) {
os.Setenv("RTSP_PATHS_CAM1_SOURCE", "rtsp://testing")
defer os.Unsetenv("RTSP_PATHS_CAM1_SOURCE")
conf, hasFile, err := Load("mediamtx.yml")
require.NoError(t, err)
require.Equal(t, false, hasFile)
pa, ok := conf.Paths["cam1"]
require.Equal(t, true, ok)
require.Equal(t, &PathConf{
Source: "rtsp://testing",
SourceOnDemandStartTimeout: 10 * StringDuration(time.Second),
SourceOnDemandCloseAfter: 10 * StringDuration(time.Second),
RPICameraWidth: 1920,
RPICameraHeight: 1080,
RPICameraContrast: 1,
RPICameraSaturation: 1,
RPICameraSharpness: 1,
RPICameraFPS: 30,
RPICameraIDRPeriod: 60,
RPICameraBitrate: 1000000,
RPICameraProfile: "main",
RPICameraLevel: "4.1",
RPICameraTextOverlay: "%Y-%m-%d %H:%M:%S - MediaMTX",
RunOnDemandStartTimeout: 10 * StringDuration(time.Second),
RunOnDemandCloseAfter: 10 * StringDuration(time.Second),
}, pa)
}
func TestConfEncryption(t *testing.T) {
key := "testing123testin"
plaintext := "paths:\n" +
" path1:\n" +
" path2:\n"
encryptedConf := func() string {
var secretKey [32]byte
copy(secretKey[:], key)
var nonce [24]byte
if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil {
panic(err)
}
encrypted := secretbox.Seal(nonce[:], []byte(plaintext), &nonce, &secretKey)
return base64.StdEncoding.EncodeToString(encrypted)
}()
os.Setenv("RTSP_CONFKEY", key)
defer os.Unsetenv("RTSP_CONFKEY")
tmpf, err := writeTempFile([]byte(encryptedConf))
require.NoError(t, err)
defer os.Remove(tmpf)
conf, hasFile, err := Load(tmpf)
require.NoError(t, err)
require.Equal(t, true, hasFile)
_, ok := conf.Paths["path1"]
require.Equal(t, true, ok)
_, ok = conf.Paths["path2"]
require.Equal(t, true, ok)
}
func TestConfErrors(t *testing.T) {
for _, ca := range []struct {
name string
conf string
err string
}{
{
"non existent parameter 1",
`invalid: param`,
"json: unknown field \"invalid\"",
},
{
"non existent parameter 2",
"paths:\n" +
" mypath:\n" +
" invalid: parameter\n",
"json: unknown field \"invalid\"",
},
{
"invalid path name",
"paths:\n" +
" '':\n" +
" source: publisher\n",
"invalid path name '': cannot be empty",
},
{
"double raspberry pi camera",
"paths:\n" +
" cam1:\n" +
" source: rpiCamera\n" +
" cam2:\n" +
" source: rpiCamera\n",
"'rpiCamera' with same camera ID 0 is used as source in two paths, 'cam1' and 'cam2'",
},
} {
t.Run(ca.name, func(t *testing.T) {
tmpf, err := writeTempFile([]byte(ca.conf))
require.NoError(t, err)
defer os.Remove(tmpf)
_, _, err = Load(tmpf)
require.EqualError(t, err, ca.err)
})
}
}