1package query
2
3import (
4	"bytes"
5	"fmt"
6	smithytesting "github.com/aws/smithy-go/testing"
7	"testing"
8)
9
10func TestEncode(t *testing.T) {
11	cases := map[string]struct {
12		Encode func(*Encoder) error
13		Expect []byte
14	}{
15		"object": {
16			Encode: func(e *Encoder) error {
17				e.Object().Key("foo").String("bar")
18				return e.Encode()
19			},
20			Expect: []byte(`foo=bar`),
21		},
22		"nested object": {
23			Encode: func(e *Encoder) error {
24				e.Object().Key("foo").Object().Key("bar").String("baz")
25				return e.Encode()
26			},
27			Expect: []byte(`foo.bar=baz`),
28		},
29		"list": {
30			Encode: func(e *Encoder) error {
31				list := e.Object().Key("list").Array("spam")
32				list.Value().String("spam")
33				list.Value().String("eggs")
34				return e.Encode()
35			},
36			Expect: []byte(`list.spam.1=spam&list.spam.2=eggs`),
37		},
38		"flat list": {
39			Encode: func(e *Encoder) error {
40				list := e.Object().FlatKey("list").Array("spam")
41				list.Value().String("spam")
42				list.Value().String("eggs")
43				return e.Encode()
44			},
45			Expect: []byte(`list.1=spam&list.2=eggs`),
46		},
47		"map": {
48			Encode: func(e *Encoder) error {
49				mapValue := e.Object().Key("map").Map("key", "value")
50				mapValue.Key("bar").String("baz")
51				mapValue.Key("foo").String("bin")
52				return e.Encode()
53			},
54			Expect: []byte(`map.entry.1.key=bar&map.entry.1.value=baz&map.entry.2.key=foo&map.entry.2.value=bin`),
55		},
56		"flat map": {
57			Encode: func(e *Encoder) error {
58				mapValue := e.Object().FlatKey("map").Map("key", "value")
59				mapValue.Key("bar").String("baz")
60				mapValue.Key("foo").String("bin")
61				return e.Encode()
62			},
63			Expect: []byte(`map.1.key=bar&map.1.value=baz&map.2.key=foo&map.2.value=bin`),
64		},
65	}
66
67	for name, c := range cases {
68		t.Run(name, func(t *testing.T) {
69			var buff bytes.Buffer
70			encoder := NewEncoder(&buff)
71			if err := c.Encode(encoder); err != nil {
72				t.Fatalf("failed to encode, %v", err)
73			}
74			smithytesting.AssertURLFormEqual(t, c.Expect, buff.Bytes())
75		})
76	}
77}
78
79// limitedWriter exists to isolate WriteString to ensure that any writer
80// can actually be used
81type limitedWriter struct {
82	writer *bytes.Buffer
83}
84
85func (lw limitedWriter) Write(v []byte) (int, error) {
86	return lw.writer.Write(v)
87}
88
89func TestEncodeHandlesBareIoWriter(t *testing.T) {
90	buff := limitedWriter{writer: bytes.NewBuffer(nil)}
91	encoder := NewEncoder(buff)
92	encoder.Object().Key("foo").String("bar")
93	if err := encoder.Encode(); err != nil {
94		t.Fatal(err)
95	}
96	smithytesting.AssertURLFormEqual(t, []byte(`foo=bar`), buff.writer.Bytes())
97}
98
99// stringWriter exists to ensure that WriteString is called when
100// available.
101type stringWriter struct {
102	writer *bytes.Buffer
103}
104
105func (w stringWriter) Write(v []byte) (int, error) {
106	return 0, fmt.Errorf("the WriteString method should be used when available")
107}
108
109func (w stringWriter) WriteString(v string) (int, error) {
110	return w.writer.WriteString(v)
111}
112
113func TestEncodeUsesWriteString(t *testing.T) {
114	buff := stringWriter{writer: bytes.NewBuffer(nil)}
115	encoder := NewEncoder(buff)
116	encoder.Object().Key("foo").String("bar")
117	if err := encoder.Encode(); err != nil {
118		t.Fatal(err)
119	}
120	smithytesting.AssertURLFormEqual(t, []byte(`foo=bar`), buff.writer.Bytes())
121}
122