1
0
Fork 0
golang-github-twin-deepmerge/json_test.go
Daniel Baumann 0759e85aad
Adding upstream version 0.2.2.
Signed-off-by: Daniel Baumann <daniel@debian.org>
2025-05-16 22:24:59 +02:00

285 lines
6.3 KiB
Go

package deepmerge_test
import (
"encoding/json"
"errors"
"testing"
"github.com/TwiN/deepmerge"
)
func TestJSON(t *testing.T) {
scenarios := []struct {
name string
config deepmerge.Config
dst string
src string
expected string
expectedErr error
}{
{
name: "invalid-dst",
dst: ``,
src: `{}`,
expected: `{}`,
expectedErr: errors.New("unexpected end of JSON input"),
},
{
name: "invalid-src",
dst: `{}`,
src: ``,
expected: `{}`,
expectedErr: errors.New("unexpected end of JSON input"),
},
{
name: "simple-endpoint-merge",
dst: `{
"endpoints": [
{
"name": "one",
"url": "https://example.com",
"client": {
"timeout": "5s"
},
"conditions": [
"[CONNECTED] == true",
"[STATUS] == 200"
],
"alerts": [
{
"type": "slack",
"failure-threshold": 5
}
]
},
{
"name": "two",
"url": "https://example.org",
"conditions": [
"len([BODY]) > 0"
]
}
]
}`,
src: `{
"endpoints": [
{
"name": "three",
"url": "https://twin.sh/health",
"conditions": [
"[STATUS] == 200",
"[BODY].status == UP"
]
}
]
}`,
expected: `{
"endpoints": [
{
"name": "one",
"url": "https://example.com",
"client": {
"timeout": "5s"
},
"conditions": [
"[CONNECTED] == true",
"[STATUS] == 200"
],
"alerts": [
{
"type": "slack",
"failure-threshold": 5
}
]
},
{
"name": "two",
"url": "https://example.org",
"conditions": [
"len([BODY]) > 0"
]
},
{
"name": "three",
"url": "https://twin.sh/health",
"conditions": [
"[STATUS] == 200",
"[BODY].status == UP"
]
}
]
}`,
},
{
name: "deep-merge-with-map-slice-and-primitive",
dst: `{
"metrics": true,
"alerting": {
"slack": {
"webhook-url": "https://hooks.slack.com/services/xxx/yyy/zzz",
"default-alert": {
"description": "health check failed",
"send-on-resolved": true,
"failure-threshold": 5,
"success-threshold": 5
}
}
},
"endpoints": [
{
"name": "example",
"url": "https://example.org",
"interval": "5s"
}
]
}`,
src: `{
"debug": true,
"alerting": {
"discord": {
"webhook-url": "https://discord.com/api/webhooks/xxx/yyy"
}
},
"endpoints": [
{
"name": "frontend",
"url": "https://example.com"
}
]
}`,
expected: `{
"metrics": true,
"debug": true,
"alerting": {
"discord": {
"webhook-url": "https://discord.com/api/webhooks/xxx/yyy"
},
"slack": {
"webhook-url": "https://hooks.slack.com/services/xxx/yyy/zzz",
"default-alert": {
"description": "health check failed",
"send-on-resolved": true,
"failure-threshold": 5,
"success-threshold": 5
}
}
},
"endpoints": [
{
"interval": "5s",
"name": "example",
"url": "https://example.org"
},
{
"name": "frontend",
"url": "https://example.com"
}
]
}`,
},
{ // only maps and slices can be merged. If there are duplicate keys that have a primitive value, then that's an error.
name: "duplicate-key-with-primitive-value",
config: deepmerge.Config{PreventMultipleDefinitionsOfKeysWithPrimitiveValue: true}, // NOTE: true is the default
dst: `{"metrics": true}`,
src: `{"metrics": false}`,
expectedErr: deepmerge.ErrKeyWithPrimitiveValueDefinedMoreThanOnce,
},
{
name: "duplicate-key-with-primitive-value-with-PreventMultipleDefinitionsOfKeysWithPrimitiveValue-set-to-false",
config: deepmerge.Config{PreventMultipleDefinitionsOfKeysWithPrimitiveValue: false},
dst: `{"metrics": true, "debug": true}`,
src: `{"metrics": false}`,
expected: `{"metrics": false, "debug": true}`,
},
{
name: "readme-example",
dst: `{
"debug": true,
"client": {
"insecure": true
},
"users": [
{
"id": 1,
"firstName": "John",
"lastName": "Doe"
},
{
"id": 2,
"firstName": "Jane",
"lastName": "Doe"
}
]
}`,
src: `{
"client": {
"timeout": "5s"
},
"users": [
{
"id": 3,
"firstName": "Bob",
"lastName": "Smith"
}
]
}`,
expected: `{
"client": {
"insecure": true,
"timeout": "5s"
},
"debug": true,
"users": [
{
"firstName": "John",
"id": 1,
"lastName": "Doe"
},
{
"firstName": "Jane",
"id": 2,
"lastName": "Doe"
},
{
"firstName": "Bob",
"id": 3,
"lastName": "Smith"
}
]
}`,
},
}
for _, scenario := range scenarios {
t.Run(scenario.name, func(t *testing.T) {
output, err := deepmerge.JSON([]byte(scenario.dst), []byte(scenario.src), scenario.config)
if !errors.Is(err, scenario.expectedErr) && !(scenario.expectedErr != nil && err.Error() == scenario.expectedErr.Error()) {
t.Errorf("[%s] expected error %v, got %v", scenario.name, scenario.expectedErr, err)
}
// Just so we don't have to worry about the formatting, we'll unmarshal the output and marshal it again.
expectedAsMap, outputAsMap := make(map[string]interface{}), make(map[string]interface{})
if len(output) > 0 {
if err := json.Unmarshal(output, &outputAsMap); err != nil {
t.Errorf("[%s] failed to unmarshal output: %v", scenario.name, err)
}
}
if len(scenario.expected) > 0 {
if err := json.Unmarshal([]byte(scenario.expected), &expectedAsMap); err != nil {
t.Errorf("[%s] failed to unmarshal expected: %v", scenario.name, err)
}
}
formattedOutput, err := json.Marshal(outputAsMap)
if err != nil {
t.Errorf("[%s] should've been able to re-marshal output: %v", scenario.name, err)
}
formattedExpected, err := json.Marshal(expectedAsMap)
if err != nil {
t.Errorf("[%s] should've been able to re-marshal expected: %v", scenario.name, err)
}
// Compare what we got vs what we expected
if string(formattedOutput) != string(formattedExpected) {
t.Errorf("[%s] expected:\n%s\n\ngot:\n%s", scenario.name, string(formattedExpected), string(formattedOutput))
}
})
}
}