1package testing
2
3import (
4	"crypto/md5"
5	"fmt"
6	"io"
7	"net/http"
8	"testing"
9	"time"
10
11	"github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects"
12	th "github.com/gophercloud/gophercloud/testhelper"
13	fake "github.com/gophercloud/gophercloud/testhelper/client"
14)
15
16// HandleDownloadObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux that
17// responds with a `Download` response.
18func HandleDownloadObjectSuccessfully(t *testing.T) {
19	th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
20		date := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
21		th.TestMethod(t, r, "GET")
22		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
23		th.TestHeader(t, r, "Accept", "application/json")
24		w.Header().Set("Date", date.Format(time.RFC1123))
25		w.Header().Set("X-Static-Large-Object", "True")
26
27		unModifiedSince := r.Header.Get("If-Unmodified-Since")
28		modifiedSince := r.Header.Get("If-Modified-Since")
29		if unModifiedSince != "" {
30			ums, _ := time.Parse(time.RFC1123, unModifiedSince)
31			if ums.Before(date) || ums.Equal(date) {
32				w.WriteHeader(http.StatusPreconditionFailed)
33				return
34			}
35		}
36		if modifiedSince != "" {
37			ms, _ := time.Parse(time.RFC1123, modifiedSince)
38			if ms.After(date) {
39				w.WriteHeader(http.StatusNotModified)
40				return
41			}
42		}
43		w.Header().Set("Last-Modified", date.Format(time.RFC1123))
44		w.WriteHeader(http.StatusOK)
45		fmt.Fprintf(w, "Successful download with Gophercloud")
46	})
47}
48
49// ExpectedListInfo is the result expected from a call to `List` when full
50// info is requested.
51var ExpectedListInfo = []objects.Object{
52	{
53		Hash:         "451e372e48e0f6b1114fa0724aa79fa1",
54		LastModified: time.Date(2016, time.August, 17, 22, 11, 58, 602650000, time.UTC),
55		Bytes:        14,
56		Name:         "goodbye",
57		ContentType:  "application/octet-stream",
58	},
59	{
60		Hash:         "451e372e48e0f6b1114fa0724aa79fa1",
61		LastModified: time.Date(2016, time.August, 17, 22, 11, 58, 602650000, time.UTC),
62		Bytes:        14,
63		Name:         "hello",
64		ContentType:  "application/octet-stream",
65	},
66}
67
68// ExpectedListSubdir is the result expected from a call to `List` when full
69// info is requested.
70var ExpectedListSubdir = []objects.Object{
71	{
72		Subdir: "directory/",
73	},
74}
75
76// ExpectedListNames is the result expected from a call to `List` when just
77// object names are requested.
78var ExpectedListNames = []string{"hello", "goodbye"}
79
80// HandleListObjectsInfoSuccessfully creates an HTTP handler at `/testContainer` on the test handler mux that
81// responds with a `List` response when full info is requested.
82func HandleListObjectsInfoSuccessfully(t *testing.T) {
83	th.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) {
84		th.TestMethod(t, r, "GET")
85		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
86		th.TestHeader(t, r, "Accept", "application/json")
87
88		w.Header().Set("Content-Type", "application/json")
89		r.ParseForm()
90		marker := r.Form.Get("marker")
91		switch marker {
92		case "":
93			fmt.Fprintf(w, `[
94      {
95        "hash": "451e372e48e0f6b1114fa0724aa79fa1",
96        "last_modified": "2016-08-17T22:11:58.602650",
97        "bytes": 14,
98        "name": "goodbye",
99        "content_type": "application/octet-stream"
100      },
101      {
102        "hash": "451e372e48e0f6b1114fa0724aa79fa1",
103        "last_modified": "2016-08-17T22:11:58.602650",
104        "bytes": 14,
105        "name": "hello",
106        "content_type": "application/octet-stream"
107      }
108    ]`)
109		case "hello":
110			fmt.Fprintf(w, `[]`)
111		default:
112			t.Fatalf("Unexpected marker: [%s]", marker)
113		}
114	})
115}
116
117// HandleListSubdirSuccessfully creates an HTTP handler at `/testContainer` on the test handler mux that
118// responds with a `List` response when full info is requested.
119func HandleListSubdirSuccessfully(t *testing.T) {
120	th.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) {
121		th.TestMethod(t, r, "GET")
122		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
123		th.TestHeader(t, r, "Accept", "application/json")
124
125		w.Header().Set("Content-Type", "application/json")
126		r.ParseForm()
127		marker := r.Form.Get("marker")
128		switch marker {
129		case "":
130			fmt.Fprintf(w, `[
131      {
132        "subdir": "directory/"
133      }
134    ]`)
135		case "directory/":
136			fmt.Fprintf(w, `[]`)
137		default:
138			t.Fatalf("Unexpected marker: [%s]", marker)
139		}
140	})
141}
142
143// HandleListObjectNamesSuccessfully creates an HTTP handler at `/testContainer` on the test handler mux that
144// responds with a `List` response when only object names are requested.
145func HandleListObjectNamesSuccessfully(t *testing.T) {
146	th.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) {
147		th.TestMethod(t, r, "GET")
148		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
149		th.TestHeader(t, r, "Accept", "text/plain")
150
151		w.Header().Set("Content-Type", "text/plain")
152		r.ParseForm()
153		marker := r.Form.Get("marker")
154		switch marker {
155		case "":
156			fmt.Fprintf(w, "hello\ngoodbye\n")
157		case "goodbye":
158			fmt.Fprintf(w, "")
159		default:
160			t.Fatalf("Unexpected marker: [%s]", marker)
161		}
162	})
163}
164
165// HandleCreateTextObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux
166// that responds with a `Create` response. A Content-Type of "text/plain" is expected.
167func HandleCreateTextObjectSuccessfully(t *testing.T, content string) {
168	th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
169		th.TestMethod(t, r, "PUT")
170		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
171		th.TestHeader(t, r, "Content-Type", "text/plain")
172		th.TestHeader(t, r, "Accept", "application/json")
173		th.TestBody(t, r, `Did gyre and gimble in the wabe`)
174
175		hash := md5.New()
176		io.WriteString(hash, content)
177		localChecksum := hash.Sum(nil)
178
179		w.Header().Set("ETag", fmt.Sprintf("%x", localChecksum))
180		w.WriteHeader(http.StatusCreated)
181	})
182}
183
184// HandleCreateTextWithCacheControlSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler
185// mux that responds with a `Create` response. A Cache-Control of `max-age="3600", public` is expected.
186func HandleCreateTextWithCacheControlSuccessfully(t *testing.T, content string) {
187	th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
188		th.TestMethod(t, r, "PUT")
189		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
190		th.TestHeader(t, r, "Cache-Control", `max-age="3600", public`)
191		th.TestHeader(t, r, "Accept", "application/json")
192		th.TestBody(t, r, `All mimsy were the borogoves`)
193
194		hash := md5.New()
195		io.WriteString(hash, content)
196		localChecksum := hash.Sum(nil)
197
198		w.Header().Set("ETag", fmt.Sprintf("%x", localChecksum))
199		w.WriteHeader(http.StatusCreated)
200	})
201}
202
203// HandleCreateTypelessObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler
204// mux that responds with a `Create` response. No Content-Type header may be present in the request, so that server-
205// side content-type detection will be triggered properly.
206func HandleCreateTypelessObjectSuccessfully(t *testing.T, content string) {
207	th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
208		th.TestMethod(t, r, "PUT")
209		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
210		th.TestHeader(t, r, "Accept", "application/json")
211		th.TestBody(t, r, `The sky was the color of television, tuned to a dead channel.`)
212
213		if contentType, present := r.Header["Content-Type"]; present {
214			t.Errorf("Expected Content-Type header to be omitted, but was %#v", contentType)
215		}
216
217		hash := md5.New()
218		io.WriteString(hash, content)
219		localChecksum := hash.Sum(nil)
220
221		w.Header().Set("ETag", fmt.Sprintf("%x", localChecksum))
222		w.WriteHeader(http.StatusCreated)
223	})
224}
225
226// HandleCopyObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux that
227// responds with a `Copy` response.
228func HandleCopyObjectSuccessfully(t *testing.T) {
229	th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
230		th.TestMethod(t, r, "COPY")
231		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
232		th.TestHeader(t, r, "Accept", "application/json")
233		th.TestHeader(t, r, "Destination", "/newTestContainer/newTestObject")
234		w.WriteHeader(http.StatusCreated)
235	})
236}
237
238// HandleDeleteObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux that
239// responds with a `Delete` response.
240func HandleDeleteObjectSuccessfully(t *testing.T) {
241	th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
242		th.TestMethod(t, r, "DELETE")
243		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
244		th.TestHeader(t, r, "Accept", "application/json")
245		w.WriteHeader(http.StatusNoContent)
246	})
247}
248
249const bulkDeleteResponse = `
250{
251    "Response Status": "foo",
252    "Response Body": "bar",
253    "Errors": [],
254    "Number Deleted": 2,
255    "Number Not Found": 0
256}
257`
258
259// HandleBulkDeleteSuccessfully creates an HTTP handler at `/` on the test
260// handler mux that responds with a `BulkDelete` response.
261func HandleBulkDeleteSuccessfully(t *testing.T) {
262	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
263		th.TestMethod(t, r, "POST")
264		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
265		th.TestHeader(t, r, "Accept", "application/json")
266		th.TestHeader(t, r, "Content-Type", "text/plain")
267		th.TestFormValues(t, r, map[string]string{
268			"bulk-delete": "true",
269		})
270		th.TestBody(t, r, "testContainer/testObject1\ntestContainer/testObject2\n")
271
272		w.Header().Set("Content-Type", "application/json")
273		w.WriteHeader(http.StatusOK)
274		fmt.Fprintf(w, bulkDeleteResponse)
275	})
276}
277
278// HandleUpdateObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux that
279// responds with a `Update` response.
280func HandleUpdateObjectSuccessfully(t *testing.T) {
281	th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
282		th.TestMethod(t, r, "POST")
283		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
284		th.TestHeader(t, r, "Accept", "application/json")
285		th.TestHeader(t, r, "X-Object-Meta-Gophercloud-Test", "objects")
286		th.TestHeader(t, r, "X-Remove-Object-Meta-Gophercloud-Test-Remove", "remove")
287		w.WriteHeader(http.StatusAccepted)
288	})
289}
290
291// HandleGetObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux that
292// responds with a `Get` response.
293func HandleGetObjectSuccessfully(t *testing.T) {
294	th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
295		th.TestMethod(t, r, "HEAD")
296		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
297		th.TestHeader(t, r, "Accept", "application/json")
298		w.Header().Add("X-Object-Meta-Gophercloud-Test", "objects")
299		w.Header().Add("X-Static-Large-Object", "true")
300		w.WriteHeader(http.StatusNoContent)
301	})
302}
303