mirror of
https://github.com/AlexxIT/go2rtc.git
synced 2025-09-27 04:36:12 +08:00
Fix YAML patch in some cases #1626
This commit is contained in:
@@ -18,7 +18,7 @@ func LoadConfig(v any) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func PatchConfig(key string, value any, path ...string) error {
|
func PatchConfig(path []string, value any) error {
|
||||||
if ConfigPath == "" {
|
if ConfigPath == "" {
|
||||||
return errors.New("config file disabled")
|
return errors.New("config file disabled")
|
||||||
}
|
}
|
||||||
@@ -26,7 +26,7 @@ func PatchConfig(key string, value any, path ...string) error {
|
|||||||
// empty config is OK
|
// empty config is OK
|
||||||
b, _ := os.ReadFile(ConfigPath)
|
b, _ := os.ReadFile(ConfigPath)
|
||||||
|
|
||||||
b, err := yaml.Patch(b, key, value, path...)
|
b, err := yaml.Patch(b, path, value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@@ -103,7 +103,7 @@ func apiPair(id, url string) error {
|
|||||||
|
|
||||||
streams.New(id, conn.URL())
|
streams.New(id, conn.URL())
|
||||||
|
|
||||||
return app.PatchConfig(id, conn.URL(), "streams")
|
return app.PatchConfig([]string{"streams", id}, conn.URL())
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiUnpair(id string) error {
|
func apiUnpair(id string) error {
|
||||||
@@ -123,7 +123,7 @@ func apiUnpair(id string) error {
|
|||||||
|
|
||||||
streams.Delete(id)
|
streams.Delete(id)
|
||||||
|
|
||||||
return app.PatchConfig(id, nil, "streams")
|
return app.PatchConfig([]string{"streams", id}, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func findHomeKitURLs() map[string]*url.URL {
|
func findHomeKitURLs() map[string]*url.URL {
|
||||||
|
@@ -222,7 +222,7 @@ func (s *server) DelPair(conn net.Conn, id string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) PatchConfig() {
|
func (s *server) PatchConfig() {
|
||||||
if err := app.PatchConfig("pairings", s.pairings, "homekit", s.stream); err != nil {
|
if err := app.PatchConfig([]string{"homekit", s.stream, "pairings"}, s.pairings); err != nil {
|
||||||
log.Error().Err(err).Msgf(
|
log.Error().Err(err).Msgf(
|
||||||
"[homekit] can't save %s pairings=%v", s.stream, s.pairings,
|
"[homekit] can't save %s pairings=%v", s.stream, s.pairings,
|
||||||
)
|
)
|
||||||
|
@@ -53,7 +53,7 @@ func apiStreams(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := app.PatchConfig(name, query["src"], "streams"); err != nil {
|
if err := app.PatchConfig([]string{"streams", name}, query["src"]); err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ func apiStreams(w http.ResponseWriter, r *http.Request) {
|
|||||||
case "DELETE":
|
case "DELETE":
|
||||||
delete(streams, src)
|
delete(streams, src)
|
||||||
|
|
||||||
if err := app.PatchConfig(src, nil, "streams"); err != nil {
|
if err := app.PatchConfig([]string{"streams", src}, nil); err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
236
pkg/yaml/yaml.go
236
pkg/yaml/yaml.go
@@ -23,149 +23,157 @@ func Encode(v any, indent int) ([]byte, error) {
|
|||||||
return b.Bytes(), nil
|
return b.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Patch - change key/value pair in YAML file without break formatting
|
func Patch(in []byte, path []string, value any) ([]byte, error) {
|
||||||
func Patch(src []byte, key string, value any, path ...string) ([]byte, error) {
|
out, err := patch(in, path, value)
|
||||||
nodeParent, err := FindParent(src, path...)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var dst []byte
|
// validate
|
||||||
|
if err = yaml.Unmarshal(out, map[string]any{}); err != nil {
|
||||||
if nodeParent != nil {
|
|
||||||
dst, err = AddOrReplace(src, key, value, nodeParent)
|
|
||||||
} else {
|
|
||||||
dst, err = AddToEnd(src, key, value, path...)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = yaml.Unmarshal(dst, map[string]any{}); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return dst, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindParent - return YAML Node from path of keys (tree)
|
func patch(in []byte, path []string, value any) ([]byte, error) {
|
||||||
func FindParent(src []byte, path ...string) (*yaml.Node, error) {
|
|
||||||
if len(src) == 0 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var root yaml.Node
|
var root yaml.Node
|
||||||
if err := yaml.Unmarshal(src, &root); err != nil {
|
if err := yaml.Unmarshal(in, &root); err != nil {
|
||||||
|
// invalid yaml
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if root.Content == nil {
|
// empty in
|
||||||
return nil, nil
|
if len(root.Content) != 1 {
|
||||||
|
return addToEnd(in, path, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
parent := root.Content[0] // yaml.DocumentNode
|
// yaml is not dict
|
||||||
for _, name := range path {
|
if root.Content[0].Kind != yaml.MappingNode {
|
||||||
if parent == nil {
|
return nil, errors.New("yaml: can't patch")
|
||||||
break
|
|
||||||
}
|
|
||||||
_, parent = FindChild(parent, name)
|
|
||||||
}
|
}
|
||||||
return parent, nil
|
|
||||||
|
// dict items list
|
||||||
|
nodes := root.Content[0].Content
|
||||||
|
|
||||||
|
n := len(path) - 1
|
||||||
|
|
||||||
|
// parent node key/value
|
||||||
|
pKey, pVal := findNode(nodes, path[:n])
|
||||||
|
if pKey == nil {
|
||||||
|
// no parent node
|
||||||
|
return addToEnd(in, path, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
var paste []byte
|
||||||
|
|
||||||
|
if value != nil {
|
||||||
|
// nil value means delete key
|
||||||
|
var err error
|
||||||
|
v := map[string]any{path[n]: value}
|
||||||
|
if paste, err = Encode(v, 2); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iKey, _ := findNode(pVal.Content, path[n:])
|
||||||
|
if iKey != nil {
|
||||||
|
// key item not nil (replace value)
|
||||||
|
paste = addIndent(paste, iKey.Column-1)
|
||||||
|
|
||||||
|
i0, i1 := nodeBounds(in, iKey)
|
||||||
|
return join(in[:i0], paste, in[i1:]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if pVal.Content != nil {
|
||||||
|
// parent value not nil (use first child indent)
|
||||||
|
paste = addIndent(paste, pVal.Column-1)
|
||||||
|
} else {
|
||||||
|
// parent value is nil (use parent indent + 2)
|
||||||
|
paste = addIndent(paste, pKey.Column+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, i1 := nodeBounds(in, pKey)
|
||||||
|
return join(in[:i1], paste, in[i1:]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindChild - search and return YAML key/value pair for current Node
|
func findNode(nodes []*yaml.Node, keys []string) (key, value *yaml.Node) {
|
||||||
func FindChild(node *yaml.Node, name string) (key, value *yaml.Node) {
|
for i, name := range keys {
|
||||||
for i, child := range node.Content {
|
for j := 0; j < len(nodes); j += 2 {
|
||||||
if child.Value != name {
|
if nodes[j].Value == name {
|
||||||
continue
|
if i < len(keys)-1 {
|
||||||
|
nodes = nodes[j+1].Content
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return nodes[j], nodes[j+1]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return child, node.Content[i+1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func FirstChild(node *yaml.Node) *yaml.Node {
|
func nodeBounds(in []byte, node *yaml.Node) (offset0, offset1 int) {
|
||||||
if node.Content == nil {
|
// start from next line after node
|
||||||
return node
|
offset0 = lineOffset(in, node.Line)
|
||||||
}
|
offset1 = lineOffset(in, node.Line+1)
|
||||||
return node.Content[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
func LastChild(node *yaml.Node) *yaml.Node {
|
if offset1 < 0 {
|
||||||
if node.Content == nil {
|
return offset0, len(in)
|
||||||
return node
|
|
||||||
}
|
|
||||||
return LastChild(node.Content[len(node.Content)-1])
|
|
||||||
}
|
|
||||||
|
|
||||||
func AddOrReplace(src []byte, key string, value any, nodeParent *yaml.Node) ([]byte, error) {
|
|
||||||
v := map[string]any{key: value}
|
|
||||||
put, err := Encode(v, 2)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if nodeKey, nodeValue := FindChild(nodeParent, key); nodeKey != nil {
|
for i := offset1; i < len(in); {
|
||||||
put = AddIndent(put, nodeKey.Column-1)
|
indent, length := parseLine(in[i:])
|
||||||
|
if indent+1 != length {
|
||||||
i0 := LineOffset(src, nodeKey.Line)
|
if node.Column < indent+1 {
|
||||||
i1 := LineOffset(src, LastChild(nodeValue).Line+1)
|
offset1 = i + length
|
||||||
|
} else {
|
||||||
if i1 < 0 { // no new line on the end of file
|
break
|
||||||
if value != nil {
|
|
||||||
return append(src[:i0], put...), nil
|
|
||||||
}
|
}
|
||||||
return src[:i0], nil
|
|
||||||
}
|
}
|
||||||
|
i += length
|
||||||
dst := make([]byte, 0, len(src)+len(put))
|
|
||||||
dst = append(dst, src[:i0]...)
|
|
||||||
if value != nil {
|
|
||||||
dst = append(dst, put...)
|
|
||||||
}
|
|
||||||
return append(dst, src[i1:]...), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
put = AddIndent(put, FirstChild(nodeParent).Column-1)
|
return
|
||||||
|
|
||||||
i := LineOffset(src, LastChild(nodeParent).Line+1)
|
|
||||||
|
|
||||||
if i < 0 { // no new line on the end of file
|
|
||||||
src = append(src, '\n')
|
|
||||||
if value != nil {
|
|
||||||
src = append(src, put...)
|
|
||||||
}
|
|
||||||
return src, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
dst := make([]byte, 0, len(src)+len(put))
|
|
||||||
dst = append(dst, src[:i]...)
|
|
||||||
if value != nil {
|
|
||||||
dst = append(dst, put...)
|
|
||||||
}
|
|
||||||
return append(dst, src[i:]...), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddToEnd(src []byte, key string, value any, path ...string) ([]byte, error) {
|
func addToEnd(in []byte, path []string, value any) ([]byte, error) {
|
||||||
if len(path) > 1 || value == nil {
|
if len(path) != 2 || value == nil {
|
||||||
return nil, errors.New("config: path not exist")
|
return nil, errors.New("yaml: path not exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
v := map[string]map[string]any{
|
v := map[string]map[string]any{
|
||||||
path[0]: {key: value},
|
path[0]: {path[1]: value},
|
||||||
}
|
}
|
||||||
put, err := Encode(v, 2)
|
paste, err := Encode(v, 2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
dst := make([]byte, 0, len(src)+len(put)+10)
|
return join(in, paste), nil
|
||||||
dst = append(dst, src...)
|
|
||||||
if l := len(src); l > 0 && src[l-1] != '\n' {
|
|
||||||
dst = append(dst, '\n')
|
|
||||||
}
|
|
||||||
return append(dst, put...), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddPrefix(src, pre []byte) (dst []byte) {
|
func join(items ...[]byte) []byte {
|
||||||
|
n := len(items) - 1
|
||||||
|
for _, b := range items {
|
||||||
|
n += len(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, 0, n)
|
||||||
|
for _, b := range items {
|
||||||
|
if len(b) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if n = len(buf); n > 0 && buf[n-1] != '\n' {
|
||||||
|
buf = append(buf, '\n')
|
||||||
|
}
|
||||||
|
buf = append(buf, b...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
func addPrefix(src, pre []byte) (dst []byte) {
|
||||||
for len(src) > 0 {
|
for len(src) > 0 {
|
||||||
dst = append(dst, pre...)
|
dst = append(dst, pre...)
|
||||||
i := bytes.IndexByte(src, '\n') + 1
|
i := bytes.IndexByte(src, '\n') + 1
|
||||||
@@ -180,21 +188,21 @@ func AddPrefix(src, pre []byte) (dst []byte) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddIndent(src []byte, indent int) (dst []byte) {
|
func addIndent(in []byte, indent int) (dst []byte) {
|
||||||
pre := make([]byte, indent)
|
pre := make([]byte, indent)
|
||||||
for i := 0; i < indent; i++ {
|
for i := 0; i < indent; i++ {
|
||||||
pre[i] = ' '
|
pre[i] = ' '
|
||||||
}
|
}
|
||||||
return AddPrefix(src, pre)
|
return addPrefix(in, pre)
|
||||||
}
|
}
|
||||||
|
|
||||||
func LineOffset(b []byte, line int) (offset int) {
|
func lineOffset(in []byte, line int) (offset int) {
|
||||||
for l := 1; ; l++ {
|
for l := 1; ; l++ {
|
||||||
if l == line {
|
if l == line {
|
||||||
return offset
|
return offset
|
||||||
}
|
}
|
||||||
|
|
||||||
i := bytes.IndexByte(b[offset:], '\n') + 1
|
i := bytes.IndexByte(in[offset:], '\n') + 1
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -202,3 +210,21 @@ func LineOffset(b []byte, line int) (offset int) {
|
|||||||
}
|
}
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseLine(b []byte) (indent int, length int) {
|
||||||
|
prefix := true
|
||||||
|
for ; length < len(b); length++ {
|
||||||
|
switch b[length] {
|
||||||
|
case ' ':
|
||||||
|
if prefix {
|
||||||
|
indent++
|
||||||
|
}
|
||||||
|
case '\n':
|
||||||
|
length++
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
prefix = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
@@ -7,140 +7,103 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestPatch(t *testing.T) {
|
func TestPatch(t *testing.T) {
|
||||||
b := []byte(`# prefix`)
|
tests := []struct {
|
||||||
|
name string
|
||||||
// 1. Add first
|
src string
|
||||||
b, err := Patch(b, "camera1", "url1", "streams")
|
path []string
|
||||||
require.Nil(t, err)
|
value any
|
||||||
|
expect string
|
||||||
require.Equal(t, `# prefix
|
}{
|
||||||
streams:
|
{
|
||||||
camera1: url1
|
name: "empty config",
|
||||||
`, string(b))
|
src: "",
|
||||||
|
path: []string{"streams", "camera1"},
|
||||||
// 2. Add second
|
value: "val1",
|
||||||
b, err = Patch(b, "camera2", []string{"url2", "url3"}, "streams")
|
expect: "streams:\n camera1: val1\n",
|
||||||
require.Nil(t, err)
|
},
|
||||||
|
{
|
||||||
require.Equal(t, `# prefix
|
name: "empty main key",
|
||||||
streams:
|
src: "#dummy",
|
||||||
camera1: url1
|
path: []string{"streams", "camera1"},
|
||||||
camera2:
|
value: "val1",
|
||||||
- url2
|
expect: "#dummy\nstreams:\n camera1: val1\n",
|
||||||
- url3
|
},
|
||||||
`, string(b))
|
{
|
||||||
|
name: "single line value",
|
||||||
// 3. Replace first
|
src: "streams:\n camera1: url1\n camera2: url2",
|
||||||
b, err = Patch(b, "camera1", "url4", "streams")
|
path: []string{"streams", "camera1"},
|
||||||
require.Nil(t, err)
|
value: "val1",
|
||||||
|
expect: "streams:\n camera1: val1\n camera2: url2",
|
||||||
require.Equal(t, `# prefix
|
},
|
||||||
streams:
|
{
|
||||||
camera1: url4
|
name: "next line value",
|
||||||
camera2:
|
src: "streams:\n camera1:\n url1\n camera2: url2",
|
||||||
- url2
|
path: []string{"streams", "camera1"},
|
||||||
- url3
|
value: "val1",
|
||||||
`, string(b))
|
expect: "streams:\n camera1: val1\n camera2: url2",
|
||||||
|
},
|
||||||
// 4. Replace second
|
{
|
||||||
b, err = Patch(b, "camera2", "url5", "streams")
|
name: "two lines value",
|
||||||
require.Nil(t, err)
|
src: "streams:\n camera1: url1\n url2\n camera2: url2",
|
||||||
|
path: []string{"streams", "camera1"},
|
||||||
require.Equal(t, `# prefix
|
value: "val1",
|
||||||
streams:
|
expect: "streams:\n camera1: val1\n camera2: url2",
|
||||||
camera1: url4
|
},
|
||||||
camera2: url5
|
{
|
||||||
`, string(b))
|
name: "next two lines value",
|
||||||
|
src: "streams:\n camera1:\n url1\n url2\n camera2: url2",
|
||||||
// 5. Delete first
|
path: []string{"streams", "camera1"},
|
||||||
b, err = Patch(b, "camera1", nil, "streams")
|
value: "val1",
|
||||||
require.Nil(t, err)
|
expect: "streams:\n camera1: val1\n camera2: url2",
|
||||||
|
},
|
||||||
require.Equal(t, `# prefix
|
{
|
||||||
streams:
|
name: "add array",
|
||||||
camera2: url5
|
src: "",
|
||||||
`, string(b))
|
path: []string{"streams", "camera1"},
|
||||||
}
|
value: []string{"val1", "val2"},
|
||||||
|
expect: "streams:\n camera1:\n - val1\n - val2\n",
|
||||||
func TestPatchParings(t *testing.T) {
|
},
|
||||||
b := []byte(`homekit:
|
{
|
||||||
camera1:
|
name: "remove value",
|
||||||
pin: 123-45-678
|
src: "streams:\n camera1: url1\n camera2: url2",
|
||||||
streams:
|
path: []string{"streams", "camera1"},
|
||||||
camera1: url1
|
value: nil,
|
||||||
`)
|
expect: "streams:\n camera2: url2",
|
||||||
|
},
|
||||||
// 1. Add new key
|
{
|
||||||
pairings := []string{"client1", "client2"}
|
name: "add pairings",
|
||||||
|
src: "homekit:\n camera1:\nstreams:\n camera1: url1",
|
||||||
b, err := Patch(b, "pairings", pairings, "homekit", "camera1")
|
path: []string{"homekit", "camera1", "pairings"},
|
||||||
require.Nil(t, err)
|
value: []string{"val1"},
|
||||||
|
expect: "homekit:\n camera1:\n pairings:\n - val1\nstreams:\n camera1: url1",
|
||||||
require.Equal(t, `homekit:
|
},
|
||||||
camera1:
|
{
|
||||||
pin: 123-45-678
|
name: "remove pairings",
|
||||||
pairings:
|
src: "homekit:\n camera1:\n pairings:\n - val1\nstreams:\n camera1: url1",
|
||||||
- client1
|
path: []string{"homekit", "camera1", "pairings"},
|
||||||
- client2
|
value: nil,
|
||||||
streams:
|
expect: "homekit:\n camera1:\nstreams:\n camera1: url1",
|
||||||
camera1: url1
|
},
|
||||||
`, string(b))
|
{
|
||||||
}
|
name: "no new line",
|
||||||
|
src: "streams:\n camera1: url1",
|
||||||
func TestPatch2(t *testing.T) {
|
path: []string{"streams", "camera1"},
|
||||||
b := []byte(`streams:
|
value: "val1",
|
||||||
camera1:
|
expect: "streams:\n camera1: val1\n",
|
||||||
- url1
|
},
|
||||||
- url2
|
{
|
||||||
`)
|
name: "no new line",
|
||||||
|
src: "streams:\n camera1: url1\nhomekit:\n camera1:\n name: dummy",
|
||||||
b, err := Patch(b, "camera2", "url3", "streams")
|
path: []string{"homekit", "camera1", "pairings"},
|
||||||
require.Nil(t, err)
|
value: []string{"val1"},
|
||||||
|
expect: "streams:\n camera1: url1\nhomekit:\n camera1:\n name: dummy\n pairings:\n - val1\n",
|
||||||
require.Equal(t, `streams:
|
},
|
||||||
camera1:
|
}
|
||||||
- url1
|
for _, tt := range tests {
|
||||||
- url2
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
camera2: url3
|
b, err := Patch([]byte(tt.src), tt.path, tt.value)
|
||||||
`, string(b))
|
require.NoError(t, err)
|
||||||
}
|
require.Equal(t, tt.expect, string(b))
|
||||||
|
})
|
||||||
func TestNoNewLineEnd1(t *testing.T) {
|
}
|
||||||
b := []byte(`streams:
|
|
||||||
camera1: url4
|
|
||||||
camera2:
|
|
||||||
- url2
|
|
||||||
- url3`)
|
|
||||||
|
|
||||||
b, err := Patch(b, "camera2", "url5", "streams")
|
|
||||||
require.Nil(t, err)
|
|
||||||
|
|
||||||
require.Equal(t, `streams:
|
|
||||||
camera1: url4
|
|
||||||
camera2: url5
|
|
||||||
`, string(b))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNoNewLineEnd2(t *testing.T) {
|
|
||||||
b := []byte(`streams:
|
|
||||||
camera1: url1
|
|
||||||
homekit:
|
|
||||||
camera1:
|
|
||||||
pin: 123-45-678`)
|
|
||||||
|
|
||||||
// 1. Add new key
|
|
||||||
pairings := []string{"client1", "client2"}
|
|
||||||
|
|
||||||
b, err := Patch(b, "pairings", pairings, "homekit", "camera1")
|
|
||||||
require.Nil(t, err)
|
|
||||||
|
|
||||||
require.Equal(t, `streams:
|
|
||||||
camera1: url1
|
|
||||||
homekit:
|
|
||||||
camera1:
|
|
||||||
pin: 123-45-678
|
|
||||||
pairings:
|
|
||||||
- client1
|
|
||||||
- client2
|
|
||||||
`, string(b))
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user