From 47415935793f0fb0f12225bccbc38749dad1d136 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Mon, 28 Oct 2024 16:19:02 +0100 Subject: [PATCH 01/10] Add `libs/dyn/jsonsaver` This package can be used to marshal a `dyn.Value` as JSON and retain the ordering of keys in a mapping. The output does not encode HTML characters as opposed to the default behavior of `json.Marshal`. Otherwise this is no different from using `json.Marshal` with `v.AsAny()`. --- libs/dyn/jsonsaver/encoder.go | 33 ++++++++++++ libs/dyn/jsonsaver/encoder_test.go | 39 ++++++++++++++ libs/dyn/jsonsaver/saver.go | 83 ++++++++++++++++++++++++++++++ libs/dyn/jsonsaver/saver_test.go | 58 +++++++++++++++++++++ 4 files changed, 213 insertions(+) create mode 100644 libs/dyn/jsonsaver/encoder.go create mode 100644 libs/dyn/jsonsaver/encoder_test.go create mode 100644 libs/dyn/jsonsaver/saver.go create mode 100644 libs/dyn/jsonsaver/saver_test.go diff --git a/libs/dyn/jsonsaver/encoder.go b/libs/dyn/jsonsaver/encoder.go new file mode 100644 index 0000000000..f26e774aee --- /dev/null +++ b/libs/dyn/jsonsaver/encoder.go @@ -0,0 +1,33 @@ +package jsonsaver + +import ( + "bytes" + "encoding/json" +) + +// The encoder type encapsulates a [json.Encoder] and its target buffer. +// Escaping of HTML characters in the output is disabled. +type encoder struct { + *json.Encoder + *bytes.Buffer +} + +func newEncoder() encoder { + var buf bytes.Buffer + enc := json.NewEncoder(&buf) + enc.SetEscapeHTML(false) + return encoder{enc, &buf} +} + +func marshalNoEscape(v any) ([]byte, error) { + enc := newEncoder() + err := enc.Encode(v) + return enc.Bytes(), err +} + +func marshalIndentNoEscape(v any, prefix, indent string) ([]byte, error) { + enc := newEncoder() + enc.SetIndent(prefix, indent) + err := enc.Encode(v) + return enc.Bytes(), err +} diff --git a/libs/dyn/jsonsaver/encoder_test.go b/libs/dyn/jsonsaver/encoder_test.go new file mode 100644 index 0000000000..101481d399 --- /dev/null +++ b/libs/dyn/jsonsaver/encoder_test.go @@ -0,0 +1,39 @@ +package jsonsaver + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestEncoder_marshalNoEscape(t *testing.T) { + out, err := marshalNoEscape("1 < 2") + require.NoError(t, err) + + // Confirm the output. + assert.JSONEq(t, `"1 < 2"`, string(out)) + + // Confirm that HTML escaping is disabled. + assert.False(t, strings.Contains(string(out), "\\u003c")) + + // Confirm that the encoder writes a trailing newline. + assert.True(t, strings.HasSuffix(string(out), "\n")) +} + +func TestEncoder_marshalIndentNoEscape(t *testing.T) { + out, err := marshalIndentNoEscape([]string{"1 < 2", "2 < 3"}, "", " ") + require.NoError(t, err) + + // Confirm the output. + assert.JSONEq(t, `["1 < 2", "2 < 3"]`, string(out)) + + // Confirm that HTML escaping is disabled. + assert.False(t, strings.Contains(string(out), "\\u003c")) + + // Confirm that the encoder performs indenting and writes a trailing newline. + assert.True(t, strings.HasPrefix(string(out), "[\n")) + assert.True(t, strings.Contains(string(out), " \"1 < 2\",\n")) + assert.True(t, strings.HasSuffix(string(out), "]\n")) +} diff --git a/libs/dyn/jsonsaver/saver.go b/libs/dyn/jsonsaver/saver.go new file mode 100644 index 0000000000..538776e6a9 --- /dev/null +++ b/libs/dyn/jsonsaver/saver.go @@ -0,0 +1,83 @@ +package jsonsaver + +import ( + "bytes" + "fmt" + + "github.com/databricks/cli/libs/dyn" +) + +// Marshal is a version of [json.Marshal] for [dyn.Value]. +// +// The output does not escape HTML characters in strings. +func Marshal(v dyn.Value) ([]byte, error) { + return marshalNoEscape(wrap{v}) +} + +// MarshalIndent is a version of [json.MarshalIndent] for [dyn.Value]. +// +// The output does not escape HTML characters in strings. +func MarshalIndent(v dyn.Value, prefix, indent string) ([]byte, error) { + return marshalIndentNoEscape(wrap{v}, prefix, indent) +} + +// Wrapper type for [dyn.Value] to expose the [json.Marshaler] interface. +type wrap struct { + v dyn.Value +} + +// MarshalJSON implements the [json.Marshaler] interface for the [dyn.Value] wrapper type. +func (w wrap) MarshalJSON() ([]byte, error) { + var buf bytes.Buffer + if err := marshalValue(&buf, w.v); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// marshalValue recursively writes JSON for a [dyn.Value] to the buffer. +func marshalValue(buf *bytes.Buffer, v dyn.Value) error { + switch v.Kind() { + case dyn.KindString, dyn.KindBool, dyn.KindInt, dyn.KindFloat, dyn.KindTime, dyn.KindNil: + out, err := marshalNoEscape(v.AsAny()) + if err != nil { + return err + } + + // The encoder writes a trailing newline, so we need to remove it + // to avoid adding extra newlines when embedding this JSON. + out = out[:len(out)-1] + buf.Write(out) + case dyn.KindMap: + buf.WriteByte('{') + for i, pair := range v.MustMap().Pairs() { + if i > 0 { + buf.WriteByte(',') + } + // Marshal the key + if err := marshalValue(buf, pair.Key); err != nil { + return err + } + buf.WriteByte(':') + // Marshal the value + if err := marshalValue(buf, pair.Value); err != nil { + return err + } + } + buf.WriteByte('}') + case dyn.KindSequence: + buf.WriteByte('[') + for i, item := range v.MustSequence() { + if i > 0 { + buf.WriteByte(',') + } + if err := marshalValue(buf, item); err != nil { + return err + } + } + buf.WriteByte(']') + default: + return fmt.Errorf("unsupported kind: %d", v.Kind()) + } + return nil +} diff --git a/libs/dyn/jsonsaver/saver_test.go b/libs/dyn/jsonsaver/saver_test.go new file mode 100644 index 0000000000..7355dce8cb --- /dev/null +++ b/libs/dyn/jsonsaver/saver_test.go @@ -0,0 +1,58 @@ +package jsonsaver + +import ( + "testing" + + "github.com/databricks/cli/libs/dyn" + "github.com/stretchr/testify/require" +) + +func TestMarshalString(t *testing.T) { + b, err := Marshal(dyn.V("string")) + require.NoError(t, err) + require.JSONEq(t, `"string"`, string(b)) +} + +func TestMarshalBool(t *testing.T) { + b, err := Marshal(dyn.V(true)) + require.NoError(t, err) + require.JSONEq(t, `true`, string(b)) +} + +func TestMarshalInt(t *testing.T) { + b, err := Marshal(dyn.V(42)) + require.NoError(t, err) + require.JSONEq(t, `42`, string(b)) +} + +func TestMarshalFloat(t *testing.T) { + b, err := Marshal(dyn.V(42.1)) + require.NoError(t, err) + require.JSONEq(t, `42.1`, string(b)) +} + +func TestMarshalTime(t *testing.T) { + b, err := Marshal(dyn.V(dyn.MustTime("2021-01-01T00:00:00Z"))) + require.NoError(t, err) + require.JSONEq(t, `"2021-01-01T00:00:00Z"`, string(b)) +} + +func TestMarshalMap(t *testing.T) { + m := dyn.NewMapping() + m.Set(dyn.V("key1"), dyn.V("value1")) + m.Set(dyn.V("key2"), dyn.V("value2")) + + b, err := Marshal(dyn.V(m)) + require.NoError(t, err) + require.JSONEq(t, `{"key1":"value1","key2":"value2"}`, string(b)) +} + +func TestMarshalSequence(t *testing.T) { + var s []dyn.Value + s = append(s, dyn.V("value1")) + s = append(s, dyn.V("value2")) + + b, err := Marshal(dyn.V(s)) + require.NoError(t, err) + require.JSONEq(t, `["value1","value2"]`, string(b)) +} From 3c3ef4158821c1263dc455166f66be5eca1c5c12 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Mon, 28 Oct 2024 16:21:32 +0100 Subject: [PATCH 02/10] Comment about key order --- libs/dyn/jsonsaver/saver.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/dyn/jsonsaver/saver.go b/libs/dyn/jsonsaver/saver.go index 538776e6a9..4a7d9d7bfd 100644 --- a/libs/dyn/jsonsaver/saver.go +++ b/libs/dyn/jsonsaver/saver.go @@ -9,6 +9,7 @@ import ( // Marshal is a version of [json.Marshal] for [dyn.Value]. // +// Objects in the output retain the order of keys as they appear in the underlying [dyn.Value]. // The output does not escape HTML characters in strings. func Marshal(v dyn.Value) ([]byte, error) { return marshalNoEscape(wrap{v}) @@ -16,6 +17,7 @@ func Marshal(v dyn.Value) ([]byte, error) { // MarshalIndent is a version of [json.MarshalIndent] for [dyn.Value]. // +// Objects in the output retain the order of keys as they appear in the underlying [dyn.Value]. // The output does not escape HTML characters in strings. func MarshalIndent(v dyn.Value, prefix, indent string) ([]byte, error) { return marshalIndentNoEscape(wrap{v}, prefix, indent) From 3b3bdeef909ef3acb45023e44c56b699a23035bb Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Mon, 28 Oct 2024 16:25:53 +0100 Subject: [PATCH 03/10] Fix dynassert test --- libs/dyn/dynassert/assert.go | 4 ++++ libs/dyn/jsonsaver/encoder_test.go | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/libs/dyn/dynassert/assert.go b/libs/dyn/dynassert/assert.go index dc6676ca27..f667b08c72 100644 --- a/libs/dyn/dynassert/assert.go +++ b/libs/dyn/dynassert/assert.go @@ -111,3 +111,7 @@ func PanicsWithError(t assert.TestingT, errString string, f func(), msgAndArgs . func NotPanics(t assert.TestingT, f func(), msgAndArgs ...interface{}) bool { return assert.NotPanics(t, f, msgAndArgs...) } + +func JSONEq(t assert.TestingT, expected string, actual string, msgAndArgs ...interface{}) bool { + return assert.JSONEq(t, expected, actual, msgAndArgs...) +} diff --git a/libs/dyn/jsonsaver/encoder_test.go b/libs/dyn/jsonsaver/encoder_test.go index 101481d399..9ee09c8d12 100644 --- a/libs/dyn/jsonsaver/encoder_test.go +++ b/libs/dyn/jsonsaver/encoder_test.go @@ -4,7 +4,7 @@ import ( "strings" "testing" - "github.com/stretchr/testify/assert" + assert "github.com/databricks/cli/libs/dyn/dynassert" "github.com/stretchr/testify/require" ) From 31d1eb7377012131307c15c844676ca9f9646cd8 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Tue, 29 Oct 2024 12:29:39 +0100 Subject: [PATCH 04/10] Use Contains and NotContains --- libs/dyn/jsonsaver/encoder_test.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/libs/dyn/jsonsaver/encoder_test.go b/libs/dyn/jsonsaver/encoder_test.go index 9ee09c8d12..6657df6978 100644 --- a/libs/dyn/jsonsaver/encoder_test.go +++ b/libs/dyn/jsonsaver/encoder_test.go @@ -1,7 +1,6 @@ package jsonsaver import ( - "strings" "testing" assert "github.com/databricks/cli/libs/dyn/dynassert" @@ -16,10 +15,10 @@ func TestEncoder_marshalNoEscape(t *testing.T) { assert.JSONEq(t, `"1 < 2"`, string(out)) // Confirm that HTML escaping is disabled. - assert.False(t, strings.Contains(string(out), "\\u003c")) + assert.NotContains(t, string(out), "\\u003c") // Confirm that the encoder writes a trailing newline. - assert.True(t, strings.HasSuffix(string(out), "\n")) + assert.Contains(t, string(out), "\n") } func TestEncoder_marshalIndentNoEscape(t *testing.T) { @@ -30,10 +29,10 @@ func TestEncoder_marshalIndentNoEscape(t *testing.T) { assert.JSONEq(t, `["1 < 2", "2 < 3"]`, string(out)) // Confirm that HTML escaping is disabled. - assert.False(t, strings.Contains(string(out), "\\u003c")) + assert.NotContains(t, string(out), "\\u003c") // Confirm that the encoder performs indenting and writes a trailing newline. - assert.True(t, strings.HasPrefix(string(out), "[\n")) - assert.True(t, strings.Contains(string(out), " \"1 < 2\",\n")) - assert.True(t, strings.HasSuffix(string(out), "]\n")) + assert.Contains(t, string(out), "[\n") + assert.Contains(t, string(out), " \"1 < 2\",\n") + assert.Contains(t, string(out), "]\n") } From bfb967628a5eab834ba708677061718b9f3f7498 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Tue, 29 Oct 2024 12:36:38 +0100 Subject: [PATCH 05/10] Add comment why we disable escaping --- libs/dyn/jsonsaver/encoder.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libs/dyn/jsonsaver/encoder.go b/libs/dyn/jsonsaver/encoder.go index f26e774aee..66997e96de 100644 --- a/libs/dyn/jsonsaver/encoder.go +++ b/libs/dyn/jsonsaver/encoder.go @@ -15,6 +15,12 @@ type encoder struct { func newEncoder() encoder { var buf bytes.Buffer enc := json.NewEncoder(&buf) + + // By default, json.Encoder escapes HTML characters, converting symbols like '<' to '\u003c'. + // This behavior helps prevent XSS attacks when JSON is embedded within HTML. + // However, we disable this feature since we're not dealing with HTML context. + // Keeping the escapes enabled would result in unnecessary differences when processing JSON payloads + // that already contain escaped characters. enc.SetEscapeHTML(false) return encoder{enc, &buf} } From 71b2da2479d68c2b17b1d519ebd9dffa33e96b3a Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Tue, 29 Oct 2024 12:38:45 +0100 Subject: [PATCH 06/10] Add key check --- libs/dyn/jsonsaver/saver.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libs/dyn/jsonsaver/saver.go b/libs/dyn/jsonsaver/saver.go index 4a7d9d7bfd..a78a68f299 100644 --- a/libs/dyn/jsonsaver/saver.go +++ b/libs/dyn/jsonsaver/saver.go @@ -56,6 +56,10 @@ func marshalValue(buf *bytes.Buffer, v dyn.Value) error { if i > 0 { buf.WriteByte(',') } + // Require keys to be strings. + if pair.Key.Kind() != dyn.KindString { + return fmt.Errorf("map key must be a string, got %s", pair.Key.Kind()) + } // Marshal the key if err := marshalValue(buf, pair.Key); err != nil { return err From 0662b84a3551af6986300d7e33e6aa5889cfe209 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Tue, 29 Oct 2024 12:44:13 +0100 Subject: [PATCH 07/10] Add test for complex value --- libs/dyn/jsonsaver/saver_test.go | 33 ++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/libs/dyn/jsonsaver/saver_test.go b/libs/dyn/jsonsaver/saver_test.go index 7355dce8cb..06c4542b13 100644 --- a/libs/dyn/jsonsaver/saver_test.go +++ b/libs/dyn/jsonsaver/saver_test.go @@ -56,3 +56,36 @@ func TestMarshalSequence(t *testing.T) { require.NoError(t, err) require.JSONEq(t, `["value1","value2"]`, string(b)) } + +func TestMarshalComplex(t *testing.T) { + map1 := dyn.NewMapping() + map1.Set(dyn.V("str1"), dyn.V("value1")) + map1.Set(dyn.V("str2"), dyn.V("value2")) + + seq1 := []dyn.Value{} + seq1 = append(seq1, dyn.V("value1")) + seq1 = append(seq1, dyn.V("value2")) + + root := dyn.NewMapping() + root.Set(dyn.V("map1"), dyn.V(map1)) + root.Set(dyn.V("seq1"), dyn.V(seq1)) + + // Marshal without indent. + b, err := Marshal(dyn.V(root)) + require.NoError(t, err) + require.Equal(t, `{"map1":{"str1":"value1","str2":"value2"},"seq1":["value1","value2"]}`+"\n", string(b)) + + // Marshal with indent. + b, err = MarshalIndent(dyn.V(root), "", " ") + require.NoError(t, err) + require.Equal(t, `{ + "map1": { + "str1": "value1", + "str2": "value2" + }, + "seq1": [ + "value1", + "value2" + ] +}`+"\n", string(b)) +} From 9aefbaa2d66389fc59b14553a8b34181f2c729f2 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Tue, 29 Oct 2024 12:46:40 +0100 Subject: [PATCH 08/10] Exclusively use assert package --- libs/dyn/dynassert/assert.go | 4 --- libs/dyn/jsonsaver/encoder_test.go | 9 ++++-- libs/dyn/jsonsaver/saver_test.go | 47 ++++++++++++++++++------------ 3 files changed, 34 insertions(+), 26 deletions(-) diff --git a/libs/dyn/dynassert/assert.go b/libs/dyn/dynassert/assert.go index f667b08c72..dc6676ca27 100644 --- a/libs/dyn/dynassert/assert.go +++ b/libs/dyn/dynassert/assert.go @@ -111,7 +111,3 @@ func PanicsWithError(t assert.TestingT, errString string, f func(), msgAndArgs . func NotPanics(t assert.TestingT, f func(), msgAndArgs ...interface{}) bool { return assert.NotPanics(t, f, msgAndArgs...) } - -func JSONEq(t assert.TestingT, expected string, actual string, msgAndArgs ...interface{}) bool { - return assert.JSONEq(t, expected, actual, msgAndArgs...) -} diff --git a/libs/dyn/jsonsaver/encoder_test.go b/libs/dyn/jsonsaver/encoder_test.go index 6657df6978..3a4e74a402 100644 --- a/libs/dyn/jsonsaver/encoder_test.go +++ b/libs/dyn/jsonsaver/encoder_test.go @@ -4,12 +4,13 @@ import ( "testing" assert "github.com/databricks/cli/libs/dyn/dynassert" - "github.com/stretchr/testify/require" ) func TestEncoder_marshalNoEscape(t *testing.T) { out, err := marshalNoEscape("1 < 2") - require.NoError(t, err) + if !assert.NoError(t, err) { + return + } // Confirm the output. assert.JSONEq(t, `"1 < 2"`, string(out)) @@ -23,7 +24,9 @@ func TestEncoder_marshalNoEscape(t *testing.T) { func TestEncoder_marshalIndentNoEscape(t *testing.T) { out, err := marshalIndentNoEscape([]string{"1 < 2", "2 < 3"}, "", " ") - require.NoError(t, err) + if !assert.NoError(t, err) { + return + } // Confirm the output. assert.JSONEq(t, `["1 < 2", "2 < 3"]`, string(out)) diff --git a/libs/dyn/jsonsaver/saver_test.go b/libs/dyn/jsonsaver/saver_test.go index 06c4542b13..eaee20d5bd 100644 --- a/libs/dyn/jsonsaver/saver_test.go +++ b/libs/dyn/jsonsaver/saver_test.go @@ -4,37 +4,42 @@ import ( "testing" "github.com/databricks/cli/libs/dyn" - "github.com/stretchr/testify/require" + assert "github.com/databricks/cli/libs/dyn/dynassert" ) func TestMarshalString(t *testing.T) { b, err := Marshal(dyn.V("string")) - require.NoError(t, err) - require.JSONEq(t, `"string"`, string(b)) + if assert.NoError(t, err) { + assert.JSONEq(t, `"string"`, string(b)) + } } func TestMarshalBool(t *testing.T) { b, err := Marshal(dyn.V(true)) - require.NoError(t, err) - require.JSONEq(t, `true`, string(b)) + if assert.NoError(t, err) { + assert.JSONEq(t, `true`, string(b)) + } } func TestMarshalInt(t *testing.T) { b, err := Marshal(dyn.V(42)) - require.NoError(t, err) - require.JSONEq(t, `42`, string(b)) + if assert.NoError(t, err) { + assert.JSONEq(t, `42`, string(b)) + } } func TestMarshalFloat(t *testing.T) { b, err := Marshal(dyn.V(42.1)) - require.NoError(t, err) - require.JSONEq(t, `42.1`, string(b)) + if assert.NoError(t, err) { + assert.JSONEq(t, `42.1`, string(b)) + } } func TestMarshalTime(t *testing.T) { b, err := Marshal(dyn.V(dyn.MustTime("2021-01-01T00:00:00Z"))) - require.NoError(t, err) - require.JSONEq(t, `"2021-01-01T00:00:00Z"`, string(b)) + if assert.NoError(t, err) { + assert.JSONEq(t, `"2021-01-01T00:00:00Z"`, string(b)) + } } func TestMarshalMap(t *testing.T) { @@ -43,8 +48,9 @@ func TestMarshalMap(t *testing.T) { m.Set(dyn.V("key2"), dyn.V("value2")) b, err := Marshal(dyn.V(m)) - require.NoError(t, err) - require.JSONEq(t, `{"key1":"value1","key2":"value2"}`, string(b)) + if assert.NoError(t, err) { + assert.JSONEq(t, `{"key1":"value1","key2":"value2"}`, string(b)) + } } func TestMarshalSequence(t *testing.T) { @@ -53,8 +59,9 @@ func TestMarshalSequence(t *testing.T) { s = append(s, dyn.V("value2")) b, err := Marshal(dyn.V(s)) - require.NoError(t, err) - require.JSONEq(t, `["value1","value2"]`, string(b)) + if assert.NoError(t, err) { + assert.JSONEq(t, `["value1","value2"]`, string(b)) + } } func TestMarshalComplex(t *testing.T) { @@ -72,13 +79,14 @@ func TestMarshalComplex(t *testing.T) { // Marshal without indent. b, err := Marshal(dyn.V(root)) - require.NoError(t, err) - require.Equal(t, `{"map1":{"str1":"value1","str2":"value2"},"seq1":["value1","value2"]}`+"\n", string(b)) + if assert.NoError(t, err) { + assert.Equal(t, `{"map1":{"str1":"value1","str2":"value2"},"seq1":["value1","value2"]}`+"\n", string(b)) + } // Marshal with indent. b, err = MarshalIndent(dyn.V(root), "", " ") - require.NoError(t, err) - require.Equal(t, `{ + if assert.NoError(t, err) { + assert.Equal(t, `{ "map1": { "str1": "value1", "str2": "value2" @@ -88,4 +96,5 @@ func TestMarshalComplex(t *testing.T) { "value2" ] }`+"\n", string(b)) + } } From a4a2b5d3a96f4dc5ca0ebe56db19684f133860f5 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Tue, 29 Oct 2024 12:47:07 +0100 Subject: [PATCH 09/10] Fix --- libs/dyn/dynassert/assert.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libs/dyn/dynassert/assert.go b/libs/dyn/dynassert/assert.go index dc6676ca27..f667b08c72 100644 --- a/libs/dyn/dynassert/assert.go +++ b/libs/dyn/dynassert/assert.go @@ -111,3 +111,7 @@ func PanicsWithError(t assert.TestingT, errString string, f func(), msgAndArgs . func NotPanics(t assert.TestingT, f func(), msgAndArgs ...interface{}) bool { return assert.NotPanics(t, f, msgAndArgs...) } + +func JSONEq(t assert.TestingT, expected string, actual string, msgAndArgs ...interface{}) bool { + return assert.JSONEq(t, expected, actual, msgAndArgs...) +} From ec5f9180e469e2187fc97b95522aea7a40033691 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Tue, 29 Oct 2024 12:48:23 +0100 Subject: [PATCH 10/10] Move around --- libs/dyn/jsonsaver/encoder_test.go | 4 ++-- libs/dyn/jsonsaver/{saver.go => marshal.go} | 0 .../jsonsaver/{saver_test.go => marshal_test.go} | 16 ++++++++-------- 3 files changed, 10 insertions(+), 10 deletions(-) rename libs/dyn/jsonsaver/{saver.go => marshal.go} (100%) rename libs/dyn/jsonsaver/{saver_test.go => marshal_test.go} (85%) diff --git a/libs/dyn/jsonsaver/encoder_test.go b/libs/dyn/jsonsaver/encoder_test.go index 3a4e74a402..d1b7d01774 100644 --- a/libs/dyn/jsonsaver/encoder_test.go +++ b/libs/dyn/jsonsaver/encoder_test.go @@ -6,7 +6,7 @@ import ( assert "github.com/databricks/cli/libs/dyn/dynassert" ) -func TestEncoder_marshalNoEscape(t *testing.T) { +func TestEncoder_MarshalNoEscape(t *testing.T) { out, err := marshalNoEscape("1 < 2") if !assert.NoError(t, err) { return @@ -22,7 +22,7 @@ func TestEncoder_marshalNoEscape(t *testing.T) { assert.Contains(t, string(out), "\n") } -func TestEncoder_marshalIndentNoEscape(t *testing.T) { +func TestEncoder_MarshalIndentNoEscape(t *testing.T) { out, err := marshalIndentNoEscape([]string{"1 < 2", "2 < 3"}, "", " ") if !assert.NoError(t, err) { return diff --git a/libs/dyn/jsonsaver/saver.go b/libs/dyn/jsonsaver/marshal.go similarity index 100% rename from libs/dyn/jsonsaver/saver.go rename to libs/dyn/jsonsaver/marshal.go diff --git a/libs/dyn/jsonsaver/saver_test.go b/libs/dyn/jsonsaver/marshal_test.go similarity index 85% rename from libs/dyn/jsonsaver/saver_test.go rename to libs/dyn/jsonsaver/marshal_test.go index eaee20d5bd..0b6a342831 100644 --- a/libs/dyn/jsonsaver/saver_test.go +++ b/libs/dyn/jsonsaver/marshal_test.go @@ -7,42 +7,42 @@ import ( assert "github.com/databricks/cli/libs/dyn/dynassert" ) -func TestMarshalString(t *testing.T) { +func TestMarshal_String(t *testing.T) { b, err := Marshal(dyn.V("string")) if assert.NoError(t, err) { assert.JSONEq(t, `"string"`, string(b)) } } -func TestMarshalBool(t *testing.T) { +func TestMarshal_Bool(t *testing.T) { b, err := Marshal(dyn.V(true)) if assert.NoError(t, err) { assert.JSONEq(t, `true`, string(b)) } } -func TestMarshalInt(t *testing.T) { +func TestMarshal_Int(t *testing.T) { b, err := Marshal(dyn.V(42)) if assert.NoError(t, err) { assert.JSONEq(t, `42`, string(b)) } } -func TestMarshalFloat(t *testing.T) { +func TestMarshal_Float(t *testing.T) { b, err := Marshal(dyn.V(42.1)) if assert.NoError(t, err) { assert.JSONEq(t, `42.1`, string(b)) } } -func TestMarshalTime(t *testing.T) { +func TestMarshal_Time(t *testing.T) { b, err := Marshal(dyn.V(dyn.MustTime("2021-01-01T00:00:00Z"))) if assert.NoError(t, err) { assert.JSONEq(t, `"2021-01-01T00:00:00Z"`, string(b)) } } -func TestMarshalMap(t *testing.T) { +func TestMarshal_Map(t *testing.T) { m := dyn.NewMapping() m.Set(dyn.V("key1"), dyn.V("value1")) m.Set(dyn.V("key2"), dyn.V("value2")) @@ -53,7 +53,7 @@ func TestMarshalMap(t *testing.T) { } } -func TestMarshalSequence(t *testing.T) { +func TestMarshal_Sequence(t *testing.T) { var s []dyn.Value s = append(s, dyn.V("value1")) s = append(s, dyn.V("value2")) @@ -64,7 +64,7 @@ func TestMarshalSequence(t *testing.T) { } } -func TestMarshalComplex(t *testing.T) { +func TestMarshal_Complex(t *testing.T) { map1 := dyn.NewMapping() map1.Set(dyn.V("str1"), dyn.V("value1")) map1.Set(dyn.V("str2"), dyn.V("value2"))