From bee063e1e547bd08a72c33f4956b4fbbf8d1ffbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20BIDON?= Date: Fri, 14 Nov 2025 20:57:14 +0100 Subject: [PATCH] test: improved test coverage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactored to remove unreachable (untestable) code * added tests for edge cases Signed-off-by: Frédéric BIDON --- pointer.go | 62 +++++++++++++++++++++++++++---------------------- pointer_test.go | 46 +++++++++++++++++++++++++++++++++++- 2 files changed, 79 insertions(+), 29 deletions(-) diff --git a/pointer.go b/pointer.go index 35c8eff..4fbb652 100644 --- a/pointer.go +++ b/pointer.go @@ -114,18 +114,18 @@ func (p *Pointer) Offset(document string) (int64, error) { // "Constructor", parses the given string JSON pointer func (p *Pointer) parse(jsonPointerString string) error { - var err error - - if jsonPointerString != emptyPointer { - if !strings.HasPrefix(jsonPointerString, pointerSeparator) { - err = errors.Join(ErrInvalidStart, ErrPointer) - } else { - referenceTokens := strings.Split(jsonPointerString, pointerSeparator) - p.referenceTokens = append(p.referenceTokens, referenceTokens[1:]...) - } + if jsonPointerString == emptyPointer { + return nil + } + + if !strings.HasPrefix(jsonPointerString, pointerSeparator) { + return errors.Join(ErrInvalidStart, ErrPointer) } - return err + referenceTokens := strings.Split(jsonPointerString, pointerSeparator) + p.referenceTokens = append(p.referenceTokens, referenceTokens[1:]...) + + return nil } func (p *Pointer) get(node any, nameProvider *jsonname.NameProvider) (any, reflect.Kind, error) { @@ -166,33 +166,36 @@ func (p *Pointer) set(node, data any, nameProvider *jsonname.NameProvider) error ) } - if nameProvider == nil { - nameProvider = jsonname.DefaultJSONNameProvider - } + l := len(p.referenceTokens) // full document when empty - if len(p.referenceTokens) == 0 { + if l == 0 { return nil } - lastI := len(p.referenceTokens) - 1 - for i, token := range p.referenceTokens { - isLastToken := i == lastI - decodedToken := Unescape(token) + if nameProvider == nil { + nameProvider = jsonname.DefaultJSONNameProvider + } - if isLastToken { - return setSingleImpl(node, data, decodedToken, nameProvider) - } + var decodedToken string + lastIndex := l - 1 - next, err := p.resolveNodeForToken(node, decodedToken, nameProvider) - if err != nil { - return err - } + if lastIndex > 0 { // skip if we only have one token in pointer + for _, token := range p.referenceTokens[:lastIndex] { + decodedToken = Unescape(token) + next, err := p.resolveNodeForToken(node, decodedToken, nameProvider) + if err != nil { + return err + } - node = next + node = next + } } - return nil + // last token + decodedToken = Unescape(p.referenceTokens[lastIndex]) + + return setSingleImpl(node, data, decodedToken, nameProvider) } func (p *Pointer) resolveNodeForToken(node any, decodedToken string, nameProvider *jsonname.NameProvider) (next any, err error) { @@ -420,6 +423,7 @@ func offsetSingleObject(dec *json.Decoder, decodedToken string) (int64, error) { return 0, fmt.Errorf("invalid token %#v: %w", tk, ErrPointer) } } + return 0, fmt.Errorf("token reference %q not found: %w", decodedToken, ErrPointer) } @@ -452,6 +456,7 @@ func offsetSingleArray(dec *json.Decoder, decodedToken string) (int64, error) { if !dec.More() { return 0, fmt.Errorf("token reference %q not found: %w", decodedToken, ErrPointer) } + return dec.InputOffset(), nil } @@ -477,10 +482,11 @@ func drainSingle(dec *json.Decoder) error { } } - // Consumes the ending delim + // consumes the ending delim if _, err := dec.Token(); err != nil { return err } + return nil } diff --git a/pointer_test.go b/pointer_test.go index 06ff043..0c917d3 100644 --- a/pointer_test.go +++ b/pointer_test.go @@ -102,6 +102,27 @@ func TestFullDocument(t *testing.T) { asMap, ok := result.(map[string]any) require.True(t, ok) require.Lenf(t, asMap, testDocumentNBItems(), "Get(%v) = %v, expect full document", in, result) + + t.Run("should set value in doc, with nil name provider", func(t *testing.T) { + setter, err := New("/foo/0") + require.NoErrorf(t, err, "New(%v) error %v", in, err) + + const value = "hey" + require.NoError(t, setter.set(asMap, value, nil)) + + foos, ok := asMap["foo"] + require.True(t, ok) + + asArray, ok := foos.([]any) + require.True(t, ok) + require.Len(t, asArray, 2) + + foo := asArray[0] + bar, ok := foo.(string) + require.True(t, ok) + + require.Equal(t, value, bar) + }) }) }) } @@ -367,7 +388,7 @@ func TestOtherThings(t *testing.T) { require.Error(t, err) }) - t.Run("resolving pointer against an unsupport type (int) should error", func(t *testing.T) { + t.Run("resolving pointer against an unsupported type (int) should error", func(t *testing.T) { p, err := New("/invalid") require.NoError(t, err) _, _, err = p.Get(1234) @@ -861,3 +882,26 @@ func TestOffset(t *testing.T) { }) } } + +func TestEdgeCases(t *testing.T) { + t.Parallel() + + t.Run("set at pointer against an unsupported type (int) should error", func(t *testing.T) { + p, err := New("/invalid") + require.NoError(t, err) + _, err = p.Set(1, 1234) + require.Error(t, err) + require.ErrorIs(t, err, ErrUnsupportedValueType) + }) + + t.Run("set with empty pointer", func(t *testing.T) { + p, err := New("") + require.NoError(t, err) + + doc := testDocumentJSON(t) + newDoc, err := p.Set(doc, 1) + require.NoError(t, err) + + require.Equal(t, doc, newDoc) + }) +}