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