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 	var b bytes.Buffer
     84 	w := NewWriter(&b)
     85 	tests := []struct {
     86 		b  string
     87 		ok bool
     88 	}{
     89 		{"abc", true},
     90 		{"", false},
     91 		{"ungltig", false},
     92 		{"!", false},
     93 		{strings.Repeat("x", 69), true},
     94 		{strings.Repeat("x", 70), false},
     95 		{"bad!ascii!", false},
     96 		{"my-separator", true},
     97 	}
     98 	for i, tt := range tests {
     99 		err := w.SetBoundary(tt.b)
    100 		got := err == nil
    101 		if got != tt.ok {
    102 			t.Errorf("%d. boundary %q = %v (%v); want %v", i, tt.b, got, err, tt.ok)
    103 		} else if tt.ok {
    104 			got := w.Boundary()
    105 			if got != tt.b {
    106 				t.Errorf("boundary = %q; want %q", got, tt.b)
    107 			}
    108 		}
    109 	}
    110 	w.Close()
    111 	if got := b.String(); !strings.Contains(got, "\r\n--my-separator--\r\n") {
    112 		t.Errorf("expected my-separator in output. got: %q", got)
    113 	}
    114 }
    115 
    116 func TestWriterBoundaryGoroutines(t *testing.T) {
    117 	// Verify there's no data race accessing any lazy boundary if it's used by
    118 	// different goroutines. This was previously broken by
    119 	// https://codereview.appspot.com/95760043/ and reverted in
    120 	// https://codereview.appspot.com/117600043/
    121 	w := NewWriter(ioutil.Discard)
    122 	done := make(chan int)
    123 	go func() {
    124 		w.CreateFormField("foo")
    125 		done <- 1
    126 	}()
    127 	w.Boundary()
    128 	<-done
    129 }
    130 
    131 func TestSortedHeader(t *testing.T) {
    132 	var buf bytes.Buffer
    133 	w := NewWriter(&buf)
    134 	if err := w.SetBoundary("MIMEBOUNDARY"); err != nil {
    135 		t.Fatalf("Error setting mime boundary: %v", err)
    136 	}
    137 
    138 	header := textproto.MIMEHeader{
    139 		"A": {"2"},
    140 		"B": {"5", "7", "6"},
    141 		"C": {"4"},
    142 		"M": {"3"},
    143 		"Z": {"1"},
    144 	}
    145 
    146 	part, err := w.CreatePart(header)
    147 	if err != nil {
    148 		t.Fatalf("Unable to create part: %v", err)
    149 	}
    150 	part.Write([]byte("foo"))
    151 
    152 	w.Close()
    153 
    154 	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"
    155 	if want != buf.String() {
    156 		t.Fatalf("\n got: %q\nwant: %q\n", buf.String(), want)
    157 	}
    158 }
    159