Add API Framework Revel Source Files
[iec.git] / src / foundation / api / revel / i18n_test.go
1 // Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
2 // Revel Framework source code and usage is governed by a MIT style
3 // license that can be found in the LICENSE file.
4
5 package revel
6
7 import (
8         "html/template"
9         "net/http"
10         "testing"
11         "time"
12
13         "github.com/revel/config"
14         "github.com/revel/revel/logger"
15 )
16
17 const (
18         testDataPath   string = "testdata/i18n"
19         testConfigPath string = "testdata/i18n/config"
20         testConfigName string = "test_app.conf"
21 )
22
23 func TestI18nLoadMessages(t *testing.T) {
24         loadMessages(testDataPath)
25
26         // Assert that we have the expected number of languages
27         if len(MessageLanguages()) != 2 {
28                 t.Fatalf("Expected messages to contain no more or less than 2 languages, instead there are %d languages", len(MessageLanguages()))
29         }
30 }
31
32 func TestI18nMessage(t *testing.T) {
33         loadMessages(testDataPath)
34         loadTestI18nConfig(t)
35
36         // Assert that we can get a message and we get the expected return value
37         if message := Message("nl", "greeting"); message != "Hallo" {
38                 t.Errorf("Message 'greeting' for locale 'nl' (%s) does not have the expected value", message)
39         }
40         if message := Message("en", "greeting"); message != "Hello" {
41                 t.Errorf("Message 'greeting' for locale 'en' (%s) does not have the expected value", message)
42         }
43         if message := Message("en", "folded"); message != "Greeting is 'Hello'" {
44                 t.Error("Unexpected unfolded message: ", message)
45         }
46         if message := Message("en", "arguments.string", "Vincent Hanna"); message != "My name is Vincent Hanna" {
47                 t.Errorf("Message 'arguments.string' for locale 'en' (%s) does not have the expected value", message)
48         }
49         if message := Message("en", "arguments.hex", 1234567, 1234567); message != "The number 1234567 in hexadecimal notation would be 12d687" {
50                 t.Errorf("Message 'arguments.hex' for locale 'en' (%s) does not have the expected value", message)
51         }
52         if message := Message("en", "folded.arguments", 12345); message != "Rob is 12345 years old" {
53                 t.Errorf("Message 'folded.arguments' for locale 'en' (%s) does not have the expected value", message)
54         }
55         if message := Message("en-AU", "greeting"); message != "G'day" {
56                 t.Errorf("Message 'greeting' for locale 'en-AU' (%s) does not have the expected value", message)
57         }
58         if message := Message("en-AU", "only_exists_in_default"); message != "Default" {
59                 t.Errorf("Message 'only_exists_in_default' for locale 'en-AU' (%s) does not have the expected value", message)
60         }
61
62         // Assert that message merging works
63         if message := Message("en", "greeting2"); message != "Yo!" {
64                 t.Errorf("Message 'greeting2' for locale 'en' (%s) does not have the expected value", message)
65         }
66
67         // Assert that we get the expected return value for a locale that doesn't exist
68         if message := Message("unknown locale", "message"); message != "??? message ???" {
69                 t.Error("Locale 'unknown locale' is not supposed to exist")
70         }
71         // Assert that we get the expected return value for a message that doesn't exist
72         if message := Message("nl", "unknown message"); message != "??? unknown message ???" {
73                 t.Error("Message 'unknown message' is not supposed to exist")
74         }
75         // XSS
76         if message := Message("en", "arguments.string", "<img src=a onerror=alert(1) />"); message != "My name is &lt;img src=a onerror=alert(1) /&gt;" {
77                 t.Error("XSS protection for messages is broken:", message)
78         }
79         // Avoid escaping HTML
80         if message := Message("en", "arguments.string", template.HTML("<img src=a onerror=alert(1) />")); message != "My name is <img src=a onerror=alert(1) />" {
81                 t.Error("Passing safe HTML to message is broken:", message)
82         }
83 }
84
85 func TestI18nMessageWithDefaultLocale(t *testing.T) {
86         loadMessages(testDataPath)
87         loadTestI18nConfig(t)
88
89         if message := Message("doesn't exist", "greeting"); message != "Hello" {
90                 t.Errorf("Expected message '%s' for unknown locale to be default '%s' but was '%s'", "greeting", "Hello", message)
91         }
92         if message := Message("doesn't exist", "unknown message"); message != "??? unknown message ???" {
93                 t.Error("Message 'unknown message' is not supposed to exist in the default language")
94         }
95 }
96
97 func TestHasLocaleCookie(t *testing.T) {
98         loadTestI18nConfig(t)
99
100         if found, value := hasLocaleCookie(buildRequestWithCookie("APP_LANG", "en").Request); !found {
101                 t.Errorf("Expected %s cookie with value '%s' but found nothing or unexpected value '%s'", "APP_LANG", "en", value)
102         }
103         if found, value := hasLocaleCookie(buildRequestWithCookie("APP_LANG", "en-US").Request); !found {
104                 t.Errorf("Expected %s cookie with value '%s' but found nothing or unexpected value '%s'", "APP_LANG", "en-US", value)
105         }
106         if found, _ := hasLocaleCookie(buildRequestWithCookie("DOESNT_EXIST", "en-US").Request); found {
107                 t.Errorf("Expected %s cookie to not exist, but apparently it does", "DOESNT_EXIST")
108         }
109 }
110
111 func TestHasLocaleCookieWithInvalidConfig(t *testing.T) {
112         loadTestI18nConfigWithoutLanguageCookieOption(t)
113         if found, _ := hasLocaleCookie(buildRequestWithCookie("APP_LANG", "en-US").Request); found {
114                 t.Errorf("Expected %s cookie to not exist because the configured name is missing", "APP_LANG")
115         }
116         if found, _ := hasLocaleCookie(buildRequestWithCookie("REVEL_LANG", "en-US").Request); !found {
117                 t.Errorf("Expected %s cookie to exist", "REVEL_LANG")
118         }
119 }
120
121 func TestHasAcceptLanguageHeader(t *testing.T) {
122         if found, value := hasAcceptLanguageHeader(buildRequestWithAcceptLanguages("en-US").Request); !found && value != "en-US" {
123                 t.Errorf("Expected to find Accept-Language header with value '%s', found '%s' instead", "en-US", value)
124         }
125         if found, value := hasAcceptLanguageHeader(buildRequestWithAcceptLanguages("en-GB", "en-US", "nl").Request); !found && value != "en-GB" {
126                 t.Errorf("Expected to find Accept-Language header with value '%s', found '%s' instead", "en-GB", value)
127         }
128 }
129
130 func TestBeforeRequest(t *testing.T) {
131         loadTestI18nConfig(t)
132
133         c := buildEmptyRequest()
134         if I18nFilter(c, NilChain); c.Request.Locale != "" {
135                 t.Errorf("Expected to find current language '%s' in controller, found '%s' instead", "", c.Request.Locale)
136         }
137
138         c = buildRequestWithCookie("APP_LANG", "en-US")
139         if I18nFilter(c, NilChain); c.Request.Locale != "en-US" {
140                 t.Errorf("Expected to find current language '%s' in controller, found '%s' instead", "en-US", c.Request.Locale)
141         }
142
143         c = buildRequestWithAcceptLanguages("en-GB", "en-US")
144         if I18nFilter(c, NilChain); c.Request.Locale != "en-GB" {
145                 t.Errorf("Expected to find current language '%s' in controller, found '%s' instead", "en-GB", c.Request.Locale)
146         }
147 }
148
149 func TestI18nMessageUnknownValueFormat(t *testing.T) {
150         loadMessages(testDataPath)
151         loadTestI18nConfigWithUnknowFormatOption(t)
152
153         // Assert that we can get a message and we get the expected return value
154         if message := Message("en", "greeting"); message != "Hello" {
155                 t.Errorf("Message 'greeting' for locale 'en' (%s) does not have the expected value", message)
156         }
157
158         // Assert that we get the expected return value with original format
159         if message := Message("unknown locale", "message"); message != "*** message ***" {
160                 t.Error("Locale 'unknown locale' is not supposed to exist")
161         }
162         if message := Message("nl", "unknown message"); message != "*** unknown message ***" {
163                 t.Error("Message 'unknown message' is not supposed to exist")
164         }
165 }
166
167 func BenchmarkI18nLoadMessages(b *testing.B) {
168         excludeFromTimer(b, func() {
169                 RevelLog.SetHandler(logger.FuncHandler(func(r *logger.Record) error {
170                         return nil
171                 }))
172         })
173
174         for i := 0; i < b.N; i++ {
175                 loadMessages(testDataPath)
176         }
177 }
178
179 func BenchmarkI18nMessage(b *testing.B) {
180         for i := 0; i < b.N; i++ {
181                 Message("nl", "greeting")
182         }
183 }
184
185 func BenchmarkI18nMessageWithArguments(b *testing.B) {
186         excludeFromTimer(b, func() {
187                 RevelLog.SetHandler(logger.FuncHandler(func(r *logger.Record) error {
188                         return nil
189                 }))
190         })
191
192
193         for i := 0; i < b.N; i++ {
194                 Message("en", "arguments.string", "Vincent Hanna")
195         }
196 }
197
198 func BenchmarkI18nMessageWithFoldingAndArguments(b *testing.B) {
199         excludeFromTimer(b, func() {
200                 RevelLog.SetHandler(logger.FuncHandler(func(r *logger.Record) error {
201                         return nil
202                 }))
203         })
204
205
206         for i := 0; i < b.N; i++ {
207                 Message("en", "folded.arguments", 12345)
208         }
209 }
210
211 // Exclude whatever operations the given function performs from the benchmark timer.
212 func excludeFromTimer(b *testing.B, f func()) {
213         b.StopTimer()
214         f()
215         b.StartTimer()
216 }
217
218 func loadTestI18nConfig(t *testing.T) {
219         ConfPaths = append(ConfPaths, testConfigPath)
220         testConfig, err := config.LoadContext(testConfigName, ConfPaths)
221         if err != nil {
222                 t.Fatalf("Unable to load test config '%s': %s", testConfigName, err.Error())
223         }
224         Config = testConfig
225         CookiePrefix = Config.StringDefault("cookie.prefix", "REVEL")
226 }
227
228 func loadTestI18nConfigWithoutLanguageCookieOption(t *testing.T) {
229         loadTestI18nConfig(t)
230         Config.Raw().RemoveOption("DEFAULT", "i18n.cookie")
231 }
232
233 func loadTestI18nConfigWithUnknowFormatOption(t *testing.T) {
234         loadTestI18nConfig(t)
235         Config.Raw().AddOption("DEFAULT", "i18n.unknown_format", "*** %s ***")
236 }
237
238 func buildRequestWithCookie(name, value string) *Controller {
239         httpRequest, _ := http.NewRequest("GET", "/", nil)
240         controller := NewTestController(nil, httpRequest)
241
242         httpRequest.AddCookie(&http.Cookie{
243                 Name:       name,
244                 Value:      value,
245                 Path:       "",
246                 Domain:     "",
247                 Expires:    time.Now(),
248                 RawExpires: "",
249                 MaxAge:     0,
250                 Secure:     false,
251                 HttpOnly:   false,
252                 Raw:        "",
253                 Unparsed:   nil,
254         })
255         return controller
256 }
257
258 func buildRequestWithAcceptLanguages(acceptLanguages ...string) *Controller {
259         httpRequest, _ := http.NewRequest("GET", "/", nil)
260         controller := NewTestController(nil, httpRequest)
261
262         request := controller.Request
263         for _, acceptLanguage := range acceptLanguages {
264                 request.AcceptLanguages = append(request.AcceptLanguages, AcceptLanguage{acceptLanguage, 1})
265         }
266         return controller
267 }
268
269 func buildEmptyRequest() *Controller {
270         httpRequest, _ := http.NewRequest("GET", "/", nil)
271         controller := NewTestController(nil, httpRequest)
272         return controller
273 }