1package httpheaders
2
3import (
4 "testing"
5
6 "github.com/stretchr/testify/assert"
7 "github.com/stretchr/testify/require"
8)
9
10func TestDefaultUserAgent(t *testing.T) {
11 t.Parallel()
12
13 tests := []struct {
14 name string
15 version string
16 want string
17 }{
18 {name: "basic version", version: "0.11.0", want: "Charm Fantasy/0.11.0"},
19 {name: "another version", version: "1.0.0", want: "Charm Fantasy/1.0.0"},
20 }
21
22 for _, tt := range tests {
23 t.Run(tt.name, func(t *testing.T) {
24 t.Parallel()
25 got := DefaultUserAgent(tt.version)
26 assert.Equal(t, tt.want, got)
27 })
28 }
29}
30
31func TestResolveHeaders_Precedence(t *testing.T) {
32 t.Parallel()
33
34 t.Run("explicit UA wins over headers and default", func(t *testing.T) {
35 t.Parallel()
36 headers := map[string]string{"User-Agent": "from-headers"}
37 got := ResolveHeaders(headers, "explicit-ua", "default-ua")
38 assert.Equal(t, "explicit-ua", got["User-Agent"])
39 })
40
41 t.Run("header UA wins over default", func(t *testing.T) {
42 t.Parallel()
43 headers := map[string]string{"User-Agent": "from-headers"}
44 got := ResolveHeaders(headers, "", "default-ua")
45 assert.Equal(t, "from-headers", got["User-Agent"])
46 })
47
48 t.Run("default UA used when nothing else set", func(t *testing.T) {
49 t.Parallel()
50 got := ResolveHeaders(nil, "", "default-ua")
51 assert.Equal(t, "default-ua", got["User-Agent"])
52 })
53
54 t.Run("explicit UA wins over case-insensitive header key", func(t *testing.T) {
55 t.Parallel()
56 headers := map[string]string{"user-agent": "from-headers"}
57 got := ResolveHeaders(headers, "explicit-ua", "default-ua")
58 assert.Equal(t, "explicit-ua", got["User-Agent"])
59 _, hasLower := got["user-agent"]
60 assert.False(t, hasLower, "old case-insensitive key should be removed")
61 })
62
63 t.Run("case-insensitive header key canonicalized when no explicit UA", func(t *testing.T) {
64 t.Parallel()
65 headers := map[string]string{"user-agent": "from-headers"}
66 got := ResolveHeaders(headers, "", "default-ua")
67 assert.Equal(t, "from-headers", got["User-Agent"])
68 _, hasLower := got["user-agent"]
69 assert.False(t, hasLower, "non-canonical key should be removed")
70 })
71}
72
73func TestResolveHeaders_NoMutation(t *testing.T) {
74 t.Parallel()
75
76 original := map[string]string{"X-Custom": "value"}
77 _ = ResolveHeaders(original, "explicit", "default")
78
79 _, hasUA := original["User-Agent"]
80 require.False(t, hasUA, "input map must not be mutated")
81 assert.Equal(t, "value", original["X-Custom"])
82}
83
84func TestResolveHeaders_PreservesOtherHeaders(t *testing.T) {
85 t.Parallel()
86
87 headers := map[string]string{
88 "X-Custom": "custom-value",
89 "Authorization": "Bearer token",
90 }
91 got := ResolveHeaders(headers, "", "Charm Fantasy/1.0.0")
92 assert.Equal(t, "custom-value", got["X-Custom"])
93 assert.Equal(t, "Bearer token", got["Authorization"])
94 assert.Equal(t, "Charm Fantasy/1.0.0", got["User-Agent"])
95}
96
97func TestResolveHeaders_DuplicateCaseInsensitiveKeys(t *testing.T) {
98 t.Parallel()
99
100 t.Run("explicit UA removes all variants", func(t *testing.T) {
101 t.Parallel()
102 headers := map[string]string{
103 "User-Agent": "canonical",
104 "user-agent": "lowercase",
105 }
106 got := ResolveHeaders(headers, "explicit", "default")
107 assert.Equal(t, "explicit", got["User-Agent"])
108 _, hasLower := got["user-agent"]
109 assert.False(t, hasLower, "all case-insensitive UA keys must be removed")
110 })
111
112 t.Run("no explicit UA collapses to single canonical key", func(t *testing.T) {
113 t.Parallel()
114 headers := map[string]string{
115 "User-Agent": "canonical",
116 "user-agent": "lowercase",
117 }
118 got := ResolveHeaders(headers, "", "default")
119 _, hasLower := got["user-agent"]
120 hasCanonical := got["User-Agent"]
121 assert.False(t, hasLower, "non-canonical key should be removed")
122 assert.NotEmpty(t, hasCanonical, "canonical User-Agent key must exist")
123 })
124}
125
126func TestCallUserAgent(t *testing.T) {
127 t.Parallel()
128
129 tests := []struct {
130 name string
131 callUA string
132 wantUA string
133 wantOK bool
134 }{
135 {name: "no override", callUA: "", wantUA: "", wantOK: false},
136 {name: "explicit UA", callUA: "MyAgent/1.0", wantUA: "MyAgent/1.0", wantOK: true},
137 }
138 for _, tt := range tests {
139 t.Run(tt.name, func(t *testing.T) {
140 t.Parallel()
141 ua, ok := CallUserAgent(tt.callUA)
142 assert.Equal(t, tt.wantOK, ok)
143 assert.Equal(t, tt.wantUA, ua)
144 })
145 }
146}