1
2
3
4
5 package multipart
6
7 import (
8 "bytes"
9 "io"
10 "mime"
11 "net/textproto"
12 "strings"
13 "testing"
14 )
15
16 func TestWriter(t *testing.T) {
17 fileContents := []byte("my file contents")
18
19 var b bytes.Buffer
20 w := NewWriter(&b)
21 {
22 part, err := w.CreateFormFile("myfile", "my-file.txt")
23 if err != nil {
24 t.Fatalf("CreateFormFile: %v", err)
25 }
26 part.Write(fileContents)
27 err = w.WriteField("key", "val")
28 if err != nil {
29 t.Fatalf("WriteField: %v", err)
30 }
31 part.Write([]byte("val"))
32 err = w.Close()
33 if err != nil {
34 t.Fatalf("Close: %v", err)
35 }
36 s := b.String()
37 if len(s) == 0 {
38 t.Fatal("String: unexpected empty result")
39 }
40 if s[0] == '\r' || s[0] == '\n' {
41 t.Fatal("String: unexpected newline")
42 }
43 }
44
45 r := NewReader(&b, w.Boundary())
46
47 part, err := r.NextPart()
48 if err != nil {
49 t.Fatalf("part 1: %v", err)
50 }
51 if g, e := part.FormName(), "myfile"; g != e {
52 t.Errorf("part 1: want form name %q, got %q", e, g)
53 }
54 slurp, err := io.ReadAll(part)
55 if err != nil {
56 t.Fatalf("part 1: ReadAll: %v", err)
57 }
58 if e, g := string(fileContents), string(slurp); e != g {
59 t.Errorf("part 1: want contents %q, got %q", e, g)
60 }
61
62 part, err = r.NextPart()
63 if err != nil {
64 t.Fatalf("part 2: %v", err)
65 }
66 if g, e := part.FormName(), "key"; g != e {
67 t.Errorf("part 2: want form name %q, got %q", e, g)
68 }
69 slurp, err = io.ReadAll(part)
70 if err != nil {
71 t.Fatalf("part 2: ReadAll: %v", err)
72 }
73 if e, g := "val", string(slurp); e != g {
74 t.Errorf("part 2: want contents %q, got %q", e, g)
75 }
76
77 part, err = r.NextPart()
78 if part != nil || err == nil {
79 t.Fatalf("expected end of parts; got %v, %v", part, err)
80 }
81 }
82
83 func TestWriterSetBoundary(t *testing.T) {
84 tests := []struct {
85 b string
86 ok bool
87 }{
88 {"abc", true},
89 {"", false},
90 {"ungültig", false},
91 {"!", false},
92 {strings.Repeat("x", 70), true},
93 {strings.Repeat("x", 71), false},
94 {"bad!ascii!", false},
95 {"my-separator", true},
96 {"with space", true},
97 {"badspace ", false},
98 {"(boundary)", true},
99 }
100 for i, tt := range tests {
101 var b strings.Builder
102 w := NewWriter(&b)
103 err := w.SetBoundary(tt.b)
104 got := err == nil
105 if got != tt.ok {
106 t.Errorf("%d. boundary %q = %v (%v); want %v", i, tt.b, got, err, tt.ok)
107 } else if tt.ok {
108 got := w.Boundary()
109 if got != tt.b {
110 t.Errorf("boundary = %q; want %q", got, tt.b)
111 }
112
113 ct := w.FormDataContentType()
114 mt, params, err := mime.ParseMediaType(ct)
115 if err != nil {
116 t.Errorf("could not parse Content-Type %q: %v", ct, err)
117 } else if mt != "multipart/form-data" {
118 t.Errorf("unexpected media type %q; want %q", mt, "multipart/form-data")
119 } else if b := params["boundary"]; b != tt.b {
120 t.Errorf("unexpected boundary parameter %q; want %q", b, tt.b)
121 }
122
123 w.Close()
124 wantSub := "\r\n--" + tt.b + "--\r\n"
125 if got := b.String(); !strings.Contains(got, wantSub) {
126 t.Errorf("expected %q in output. got: %q", wantSub, got)
127 }
128 }
129 }
130 }
131
132 func TestWriterBoundaryGoroutines(t *testing.T) {
133
134
135
136
137 w := NewWriter(io.Discard)
138 done := make(chan int)
139 go func() {
140 w.CreateFormField("foo")
141 done <- 1
142 }()
143 w.Boundary()
144 <-done
145 }
146
147 func TestSortedHeader(t *testing.T) {
148 var buf strings.Builder
149 w := NewWriter(&buf)
150 if err := w.SetBoundary("MIMEBOUNDARY"); err != nil {
151 t.Fatalf("Error setting mime boundary: %v", err)
152 }
153
154 header := textproto.MIMEHeader{
155 "A": {"2"},
156 "B": {"5", "7", "6"},
157 "C": {"4"},
158 "M": {"3"},
159 "Z": {"1"},
160 }
161
162 part, err := w.CreatePart(header)
163 if err != nil {
164 t.Fatalf("Unable to create part: %v", err)
165 }
166 part.Write([]byte("foo"))
167
168 w.Close()
169
170 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"
171 if want != buf.String() {
172 t.Fatalf("\n got: %q\nwant: %q\n", buf.String(), want)
173 }
174 }
175
View as plain text