You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

282 lines
5.2 KiB

package httpheader
import (
"fmt"
"net/http"
"reflect"
"testing"
"time"
)
func TestHeader_types(t *testing.T) {
str := "string"
strPtr := &str
timeVal := time.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC)
tests := []struct {
in interface{}
want http.Header
}{
{
// basic primitives
struct {
A string
B int
C uint
D float32
E bool
}{},
http.Header{
"A": []string{""},
"B": []string{"0"},
"C": []string{"0"},
"D": []string{"0"},
"E": []string{"false"},
},
},
{
// pointers
struct {
A *string
B *int
C **string
D *time.Time
}{
A: strPtr,
C: &strPtr,
D: &timeVal,
},
http.Header{
"A": []string{str},
"B": []string{""},
"C": []string{str},
"D": []string{"Sat, 01 Jan 2000 12:34:56 GMT"},
},
},
{
// slices and arrays
struct {
A []string
B []*string
C [2]string
D []bool `header:",int"`
}{
A: []string{"a", "b"},
B: []*string{&str, &str},
C: [2]string{"a", "b"},
D: []bool{true, false},
},
http.Header{
"A": []string{"a", "b"},
"B": {"string", "string"},
"C": []string{"a", "b"},
"D": {"1", "0"},
},
},
{
// other types
struct {
A time.Time
B time.Time `header:",unix"`
C bool `header:",int"`
D bool `header:",int"`
E http.Header
}{
A: time.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC),
B: time.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC),
C: true,
D: false,
E: http.Header{
"F": []string{"f1"},
"G": []string{"gg"},
},
},
http.Header{
"A": []string{"Sat, 01 Jan 2000 12:34:56 GMT"},
"B": []string{"946730096"},
"C": []string{"1"},
"D": []string{"0"},
"F": []string{"f1"},
"G": []string{"gg"},
},
},
{
nil,
http.Header{},
},
{
&struct {
A string
}{"test"},
http.Header{
"A": []string{"test"},
},
},
}
for i, tt := range tests {
v, err := Header(tt.in)
if err != nil {
t.Errorf("%d. Header(%q) returned error: %v", i, tt.in, err)
}
if !reflect.DeepEqual(tt.want, v) {
t.Errorf("%d. Header(%q) returned %#v, want %#v", i, tt.in, v, tt.want)
}
}
}
func TestHeader_omitEmpty(t *testing.T) {
str := ""
s := struct {
a string
A string
B string `header:",omitempty"`
C string `header:"-"`
D string `header:"omitempty"` // actually named omitempty, not an option
E *string `header:",omitempty"`
F bool `header:",omitempty"`
G int `header:",omitempty"`
H uint `header:",omitempty"`
I float32 `header:",omitempty"`
J time.Time `header:",omitempty"`
K struct{} `header:",omitempty"`
}{E: &str}
v, err := Header(s)
if err != nil {
t.Errorf("Header(%#v) returned error: %v", s, err)
}
want := http.Header{
"A": []string{""},
"Omitempty": []string{""},
"E": []string{""}, // E is included because the pointer is not empty, even though the string being pointed to is
}
if !reflect.DeepEqual(want, v) {
t.Errorf("Header(%#v) returned %v, want %v", s, v, want)
}
}
type A struct {
B
}
type B struct {
C string
}
type D struct {
B
C string
}
type e struct {
B
C string
}
type F struct {
e
}
func TestHeader_embeddedStructs(t *testing.T) {
tests := []struct {
in interface{}
want http.Header
}{
{
A{B{C: "foo"}},
http.Header{"C": []string{"foo"}},
},
{
D{B: B{C: "bar"}, C: "foo"},
http.Header{"C": []string{"foo", "bar"}},
},
{
F{e{B: B{C: "bar"}, C: "foo"}}, // With unexported embed
http.Header{"C": []string{"foo", "bar"}},
},
}
for i, tt := range tests {
v, err := Header(tt.in)
if err != nil {
t.Errorf("%d. Header(%q) returned error: %v", i, tt.in, err)
}
if !reflect.DeepEqual(tt.want, v) {
t.Errorf("%d. Header(%q) returned %v, want %v", i, tt.in, v, tt.want)
}
}
}
func TestHeader_invalidInput(t *testing.T) {
_, err := Header("")
if err == nil {
t.Errorf("expected Header() to return an error on invalid input")
}
}
type EncodedArgs []string
func (m EncodedArgs) EncodeHeader(key string, v *http.Header) error {
for i, arg := range m {
v.Set(fmt.Sprintf("%s.%d", key, i), arg)
}
return nil
}
func TestHeader_Marshaler(t *testing.T) {
s := struct {
Args EncodedArgs `header:"arg"`
}{[]string{"a", "b", "c"}}
v, err := Header(s)
if err != nil {
t.Errorf("Header(%q) returned error: %v", s, err)
}
want := http.Header{
"Arg.0": []string{"a"},
"Arg.1": []string{"b"},
"Arg.2": []string{"c"},
}
if !reflect.DeepEqual(want, v) {
t.Errorf("Header(%q) returned %v, want %v", s, v, want)
}
}
func TestHeader_MarshalerWithNilPointer(t *testing.T) {
s := struct {
Args *EncodedArgs `header:"arg"`
}{}
v, err := Header(s)
if err != nil {
t.Errorf("Header(%q) returned error: %v", s, err)
}
want := http.Header{}
if !reflect.DeepEqual(want, v) {
t.Errorf("Header(%q) returned %v, want %v", s, v, want)
}
}
func TestTagParsing(t *testing.T) {
name, opts := parseTag("field,foobar,foo")
if name != "field" {
t.Fatalf("name = %q, want field", name)
}
for _, tt := range []struct {
opt string
want bool
}{
{"foobar", true},
{"foo", true},
{"bar", false},
{"field", false},
} {
if opts.Contains(tt.opt) != tt.want {
t.Errorf("Contains(%q) = %v", tt.opt, !tt.want)
}
}
}