1// Copyright 2011 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package multipart
6
7import (
8	"bytes"
9	"io/ioutil"
10	"net/textproto"
11	"strings"
12	"testing"
13)
14
15func TestWriter(t *testing.T) {
16	fileContents := []byte("my file contents")
17
18	var b bytes.Buffer
19	w := NewWriter(&b)
20	{
21		part, err := w.CreateFormFile("myfile", "my-file.txt")
22		if err != nil {
23			t.Fatalf("CreateFormFile: %v", err)
24		}
25		part.Write(fileContents)
26		err = w.WriteField("key", "val")
27		if err != nil {
28			t.Fatalf("WriteField: %v", err)
29		}
30		part.Write([]byte("val"))
31		err = w.Close()
32		if err != nil {
33			t.Fatalf("Close: %v", err)
34		}
35		s := b.String()
36		if len(s) == 0 {
37			t.Fatal("String: unexpected empty result")
38		}
39		if s[0] == '\r' || s[0] == '\n' {
40			t.Fatal("String: unexpected newline")
41		}
42	}
43
44	r := NewReader(&b, w.Boundary())
45
46	part, err := r.NextPart()
47	if err != nil {
48		t.Fatalf("part 1: %v", err)
49	}
50	if g, e := part.FormName(), "myfile"; g != e {
51		t.Errorf("part 1: want form name %q, got %q", e, g)
52	}
53	slurp, err := ioutil.ReadAll(part)
54	if err != nil {
55		t.Fatalf("part 1: ReadAll: %v", err)
56	}
57	if e, g := string(fileContents), string(slurp); e != g {
58		t.Errorf("part 1: want contents %q, got %q", e, g)
59	}
60
61	part, err = r.NextPart()
62	if err != nil {
63		t.Fatalf("part 2: %v", err)
64	}
65	if g, e := part.FormName(), "key"; g != e {
66		t.Errorf("part 2: want form name %q, got %q", e, g)
67	}
68	slurp, err = ioutil.ReadAll(part)
69	if err != nil {
70		t.Fatalf("part 2: ReadAll: %v", err)
71	}
72	if e, g := "val", string(slurp); e != g {
73		t.Errorf("part 2: want contents %q, got %q", e, g)
74	}
75
76	part, err = r.NextPart()
77	if part != nil || err == nil {
78		t.Fatalf("expected end of parts; got %v, %v", part, err)
79	}
80}
81
82func TestWriterSetBoundary(t *testing.T) {
83	tests := []struct {
84		b  string
85		ok bool
86	}{
87		{"abc", true},
88		{"", false},
89		{"ungültig", false},
90		{"!", false},
91		{strings.Repeat("x", 70), true},
92		{strings.Repeat("x", 71), false},
93		{"bad!ascii!", false},
94		{"my-separator", true},
95		{"with space", true},
96		{"badspace ", false},
97	}
98	for i, tt := range tests {
99		var b bytes.Buffer
100		w := NewWriter(&b)
101		err := w.SetBoundary(tt.b)
102		got := err == nil
103		if got != tt.ok {
104			t.Errorf("%d. boundary %q = %v (%v); want %v", i, tt.b, got, err, tt.ok)
105		} else if tt.ok {
106			got := w.Boundary()
107			if got != tt.b {
108				t.Errorf("boundary = %q; want %q", got, tt.b)
109			}
110			w.Close()
111			wantSub := "\r\n--" + tt.b + "--\r\n"
112			if got := b.String(); !strings.Contains(got, wantSub) {
113				t.Errorf("expected %q in output. got: %q", wantSub, got)
114			}
115		}
116	}
117}
118
119func TestWriterBoundaryGoroutines(t *testing.T) {
120	// Verify there's no data race accessing any lazy boundary if it's used by
121	// different goroutines. This was previously broken by
122	// https://codereview.appspot.com/95760043/ and reverted in
123	// https://codereview.appspot.com/117600043/
124	w := NewWriter(ioutil.Discard)
125	done := make(chan int)
126	go func() {
127		w.CreateFormField("foo")
128		done <- 1
129	}()
130	w.Boundary()
131	<-done
132}
133
134func TestSortedHeader(t *testing.T) {
135	var buf bytes.Buffer
136	w := NewWriter(&buf)
137	if err := w.SetBoundary("MIMEBOUNDARY"); err != nil {
138		t.Fatalf("Error setting mime boundary: %v", err)
139	}
140
141	header := textproto.MIMEHeader{
142		"A": {"2"},
143		"B": {"5", "7", "6"},
144		"C": {"4"},
145		"M": {"3"},
146		"Z": {"1"},
147	}
148
149	part, err := w.CreatePart(header)
150	if err != nil {
151		t.Fatalf("Unable to create part: %v", err)
152	}
153	part.Write([]byte("foo"))
154
155	w.Close()
156
157	want := "--MIMEBOUNDARY\r\nA: 2\r\nB: 5\r\nB: 7\r\nB: 6\r\nC: 4\r\nM: 3\r\nZ: 1\r\n\r\nfoo\r\n--MIMEBOUNDARY--\r\n"
158	if want != buf.String() {
159		t.Fatalf("\n got: %q\nwant: %q\n", buf.String(), want)
160	}
161}
162