1// Copyright 2017 Frank Schroeder. 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 properties
6
7import (
8	"fmt"
9	"io/ioutil"
10	"net/http"
11	"net/http/httptest"
12	"os"
13	"strings"
14	"testing"
15
16	"github.com/magiconair/properties/assert"
17)
18
19func TestLoadFailsWithNotExistingFile(t *testing.T) {
20	_, err := LoadFile("doesnotexist.properties", ISO_8859_1)
21	assert.Equal(t, err != nil, true, "")
22	assert.Matches(t, err.Error(), "open.*no such file or directory")
23}
24
25func TestLoadFilesFailsOnNotExistingFile(t *testing.T) {
26	_, err := LoadFile("doesnotexist.properties", ISO_8859_1)
27	assert.Equal(t, err != nil, true, "")
28	assert.Matches(t, err.Error(), "open.*no such file or directory")
29}
30
31func TestLoadFilesDoesNotFailOnNotExistingFileAndIgnoreMissing(t *testing.T) {
32	p, err := LoadFiles([]string{"doesnotexist.properties"}, ISO_8859_1, true)
33	assert.Equal(t, err, nil)
34	assert.Equal(t, p.Len(), 0)
35}
36
37func TestLoadString(t *testing.T) {
38	x := "key=äüö"
39	p1 := MustLoadString(x)
40	p2 := must(Load([]byte(x), UTF8))
41	assert.Equal(t, p1, p2)
42}
43
44func TestLoadMap(t *testing.T) {
45	// LoadMap does not guarantee the same import order
46	// of keys every time since map access is randomized.
47	// Therefore, we need to compare the generated maps.
48	m := map[string]string{"key": "value", "abc": "def"}
49	assert.Equal(t, LoadMap(m).Map(), m)
50}
51
52func TestLoadFile(t *testing.T) {
53	tf := make(tempFiles, 0)
54	defer tf.removeAll()
55
56	filename := tf.makeFile("key=value")
57	p := MustLoadFile(filename, ISO_8859_1)
58
59	assert.Equal(t, p.Len(), 1)
60	assertKeyValues(t, "", p, "key", "value")
61}
62
63func TestLoadFiles(t *testing.T) {
64	tf := make(tempFiles, 0)
65	defer tf.removeAll()
66
67	filename := tf.makeFile("key=value")
68	filename2 := tf.makeFile("key2=value2")
69	p := MustLoadFiles([]string{filename, filename2}, ISO_8859_1, false)
70	assertKeyValues(t, "", p, "key", "value", "key2", "value2")
71}
72
73func TestLoadExpandedFile(t *testing.T) {
74	tf := make(tempFiles, 0)
75	defer tf.removeAll()
76
77	if err := os.Setenv("_VARX", "some-value"); err != nil {
78		t.Fatal(err)
79	}
80	filename := tf.makeFilePrefix(os.Getenv("_VARX"), "key=value")
81	filename = strings.Replace(filename, os.Getenv("_VARX"), "${_VARX}", -1)
82	p := MustLoadFile(filename, ISO_8859_1)
83	assertKeyValues(t, "", p, "key", "value")
84}
85
86func TestLoadFilesAndIgnoreMissing(t *testing.T) {
87	tf := make(tempFiles, 0)
88	defer tf.removeAll()
89
90	filename := tf.makeFile("key=value")
91	filename2 := tf.makeFile("key2=value2")
92	p := MustLoadFiles([]string{filename, filename + "foo", filename2, filename2 + "foo"}, ISO_8859_1, true)
93	assertKeyValues(t, "", p, "key", "value", "key2", "value2")
94}
95
96func TestLoadURL(t *testing.T) {
97	srv := testServer()
98	defer srv.Close()
99	p := MustLoadURL(srv.URL + "/a")
100	assertKeyValues(t, "", p, "key", "value")
101}
102
103func TestLoadURLs(t *testing.T) {
104	srv := testServer()
105	defer srv.Close()
106	p := MustLoadURLs([]string{srv.URL + "/a", srv.URL + "/b"}, false)
107	assertKeyValues(t, "", p, "key", "value", "key2", "value2")
108}
109
110func TestLoadURLsAndFailMissing(t *testing.T) {
111	srv := testServer()
112	defer srv.Close()
113	p, err := LoadURLs([]string{srv.URL + "/a", srv.URL + "/c"}, false)
114	assert.Equal(t, p, (*Properties)(nil))
115	assert.Matches(t, err.Error(), ".*returned 404.*")
116}
117
118func TestLoadURLsAndIgnoreMissing(t *testing.T) {
119	srv := testServer()
120	defer srv.Close()
121	p := MustLoadURLs([]string{srv.URL + "/a", srv.URL + "/b", srv.URL + "/c"}, true)
122	assertKeyValues(t, "", p, "key", "value", "key2", "value2")
123}
124
125func TestLoadURLEncoding(t *testing.T) {
126	srv := testServer()
127	defer srv.Close()
128
129	uris := []string{"/none", "/utf8", "/plain", "/latin1", "/iso88591"}
130	for i, uri := range uris {
131		p := MustLoadURL(srv.URL + uri)
132		assert.Equal(t, p.GetString("key", ""), "äöü", fmt.Sprintf("%d", i))
133	}
134}
135
136func TestLoadURLFailInvalidEncoding(t *testing.T) {
137	srv := testServer()
138	defer srv.Close()
139
140	p, err := LoadURL(srv.URL + "/json")
141	assert.Equal(t, p, (*Properties)(nil))
142	assert.Matches(t, err.Error(), ".*invalid content type.*")
143}
144
145func TestLoadAll(t *testing.T) {
146	tf := make(tempFiles, 0)
147	defer tf.removeAll()
148
149	filename := tf.makeFile("key=value")
150	filename2 := tf.makeFile("key2=value3")
151	filename3 := tf.makeFile("key=value4")
152	srv := testServer()
153	defer srv.Close()
154	p := MustLoadAll([]string{filename, filename2, srv.URL + "/a", srv.URL + "/b", filename3}, UTF8, false)
155	assertKeyValues(t, "", p, "key", "value4", "key2", "value2")
156}
157
158type tempFiles []string
159
160func (tf *tempFiles) removeAll() {
161	for _, path := range *tf {
162		err := os.Remove(path)
163		if err != nil {
164			fmt.Printf("os.Remove: %v", err)
165		}
166	}
167}
168
169func (tf *tempFiles) makeFile(data string) string {
170	return tf.makeFilePrefix("properties", data)
171}
172
173func (tf *tempFiles) makeFilePrefix(prefix, data string) string {
174	f, err := ioutil.TempFile("", prefix)
175	if err != nil {
176		panic("ioutil.TempFile: " + err.Error())
177	}
178
179	// remember the temp file so that we can remove it later
180	*tf = append(*tf, f.Name())
181
182	n, err := fmt.Fprint(f, data)
183	if err != nil {
184		panic("fmt.Fprintln: " + err.Error())
185	}
186	if n != len(data) {
187		panic(fmt.Sprintf("Data size mismatch. expected=%d wrote=%d\n", len(data), n))
188	}
189
190	err = f.Close()
191	if err != nil {
192		panic("f.Close: " + err.Error())
193	}
194
195	return f.Name()
196}
197
198func testServer() *httptest.Server {
199	return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
200		send := func(data []byte, contentType string) {
201			w.Header().Set("Content-Type", contentType)
202			if _, err := w.Write(data); err != nil {
203				panic(err)
204			}
205		}
206
207		utf8 := []byte("key=äöü")
208		iso88591 := []byte{0x6b, 0x65, 0x79, 0x3d, 0xe4, 0xf6, 0xfc} // key=äöü
209
210		switch r.RequestURI {
211		case "/a":
212			send([]byte("key=value"), "")
213		case "/b":
214			send([]byte("key2=value2"), "")
215		case "/none":
216			send(utf8, "")
217		case "/utf8":
218			send(utf8, "text/plain; charset=utf-8")
219		case "/json":
220			send(utf8, "application/json; charset=utf-8")
221		case "/plain":
222			send(iso88591, "text/plain")
223		case "/latin1":
224			send(iso88591, "text/plain; charset=latin1")
225		case "/iso88591":
226			send(iso88591, "text/plain; charset=iso-8859-1")
227		default:
228			w.WriteHeader(404)
229		}
230	}))
231}
232