Home | History | Annotate | Download | only in multipart
      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 
      5 package multipart
      6 
      7 import (
      8 	"bytes"
      9 	"io/ioutil"
     10 	"net/textproto"
     11 	"strings"
     12 	"testing"
     13 )
     14 
     15 func 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 
     82 func TestWriterSetBoundary(t *testing.T) {
     83 	tests := []struct {
     84 		b  string
     85 		ok bool
     86 	}{
     87 		{"abc", true},
     88 		{"", false},
     89 		{"ungltig", 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 
    119 func 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 
    134 func 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