functions_test.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
  2. // SPDX-License-Identifier: Apache-2.0
  3. package template // import "miniflux.app/v2/internal/template"
  4. import (
  5. "strings"
  6. "testing"
  7. "time"
  8. "miniflux.app/v2/internal/locale"
  9. "miniflux.app/v2/internal/model"
  10. )
  11. func TestDict(t *testing.T) {
  12. d, err := dict("k1", "v1", "k2", "v2")
  13. if err != nil {
  14. t.Fatalf(`The dict should be valid: %v`, err)
  15. }
  16. if value, found := d["k1"]; found {
  17. if value != "v1" {
  18. t.Fatalf(`Unexpected value for k1: got %q`, value)
  19. }
  20. }
  21. if value, found := d["k2"]; found {
  22. if value != "v2" {
  23. t.Fatalf(`Unexpected value for k2: got %q`, value)
  24. }
  25. }
  26. }
  27. func TestDictWithInvalidNumberOfArguments(t *testing.T) {
  28. _, err := dict("k1")
  29. if err == nil {
  30. t.Fatal(`An error should be returned if the number of arguments are not even`)
  31. }
  32. }
  33. func TestDictWithInvalidMap(t *testing.T) {
  34. _, err := dict(1, 2)
  35. if err == nil {
  36. t.Fatal(`An error should be returned if the dict keys are not string`)
  37. }
  38. }
  39. func TestTruncateWithShortTexts(t *testing.T) {
  40. scenarios := []string{"Short text", "Короткий текст"}
  41. for _, input := range scenarios {
  42. result := truncate(input, 25)
  43. if result != input {
  44. t.Fatalf(`Unexpected output, got %q instead of %q`, result, input)
  45. }
  46. result = truncate(input, len(input))
  47. if result != input {
  48. t.Fatalf(`Unexpected output, got %q instead of %q`, result, input)
  49. }
  50. }
  51. }
  52. func TestTruncateWithLongTexts(t *testing.T) {
  53. scenarios := map[string]string{
  54. "This is a really pretty long English text": "This is a really pretty l…",
  55. "Это реально очень длинный русский текст": "Это реально очень длинный…",
  56. }
  57. for input, expected := range scenarios {
  58. result := truncate(input, 25)
  59. if result != expected {
  60. t.Fatalf(`Unexpected output, got %q instead of %q`, result, expected)
  61. }
  62. }
  63. }
  64. func TestIsEmail(t *testing.T) {
  65. if !isEmail("user@domain.tld") {
  66. t.Fatal(`This email is valid and should returns true`)
  67. }
  68. if isEmail("invalid") {
  69. t.Fatal(`This email is not valid and should returns false`)
  70. }
  71. }
  72. func TestDuration(t *testing.T) {
  73. now := time.Now()
  74. var dt = []struct {
  75. in time.Time
  76. out string
  77. }{
  78. {time.Time{}, ""},
  79. {now.Add(time.Hour), "1h0m0s"},
  80. {now.Add(time.Minute), "1m0s"},
  81. {now.Add(time.Minute * 40), "40m0s"},
  82. {now.Add(time.Millisecond * 40), "0s"},
  83. {now.Add(time.Millisecond * 80), "0s"},
  84. {now.Add(time.Millisecond * 400), "0s"},
  85. {now.Add(time.Millisecond * 800), "1s"},
  86. {now.Add(time.Millisecond * 4321), "4s"},
  87. {now.Add(time.Millisecond * 8765), "9s"},
  88. {now.Add(time.Microsecond * 12345678), "12s"},
  89. {now.Add(time.Microsecond * 87654321), "1m28s"},
  90. }
  91. for i, tt := range dt {
  92. if out := durationImpl(tt.in, now); out != tt.out {
  93. t.Errorf(`%d. content mismatch for "%v": expected=%q got=%q`, i, tt.in, tt.out, out)
  94. }
  95. }
  96. }
  97. func TestElapsedTime(t *testing.T) {
  98. printer := locale.NewPrinter("en_US")
  99. var dt = []struct {
  100. in time.Time
  101. out string
  102. }{
  103. {time.Time{}, printer.Print("time_elapsed.not_yet")},
  104. {time.Now().Add(time.Hour), printer.Print("time_elapsed.not_yet")},
  105. {time.Now(), printer.Print("time_elapsed.now")},
  106. {time.Now().Add(-time.Minute), printer.Plural("time_elapsed.minutes", 1, 1)},
  107. {time.Now().Add(-time.Minute * 40), printer.Plural("time_elapsed.minutes", 40, 40)},
  108. {time.Now().Add(-time.Hour), printer.Plural("time_elapsed.hours", 1, 1)},
  109. {time.Now().Add(-time.Hour * 3), printer.Plural("time_elapsed.hours", 3, 3)},
  110. {time.Now().Add(-time.Hour * 32), printer.Print("time_elapsed.yesterday")},
  111. {time.Now().Add(-time.Hour * 24 * 3), printer.Plural("time_elapsed.days", 3, 3)},
  112. {time.Now().Add(-time.Hour * 24 * 14), printer.Plural("time_elapsed.days", 14, 14)},
  113. {time.Now().Add(-time.Hour * 24 * 15), printer.Plural("time_elapsed.days", 15, 15)},
  114. {time.Now().Add(-time.Hour * 24 * 21), printer.Plural("time_elapsed.weeks", 3, 3)},
  115. {time.Now().Add(-time.Hour * 24 * 32), printer.Plural("time_elapsed.months", 1, 1)},
  116. {time.Now().Add(-time.Hour * 24 * 60), printer.Plural("time_elapsed.months", 2, 2)},
  117. {time.Now().Add(-time.Hour * 24 * 366), printer.Plural("time_elapsed.years", 1, 1)},
  118. {time.Now().Add(-time.Hour * 24 * 365 * 3), printer.Plural("time_elapsed.years", 3, 3)},
  119. }
  120. for i, tt := range dt {
  121. if out := elapsedTime(printer, "Local", tt.in); out != tt.out {
  122. t.Errorf(`%d. content mismatch for "%v": expected=%q got=%q`, i, tt.in, tt.out, out)
  123. }
  124. }
  125. }
  126. func TestFormatFileSize(t *testing.T) {
  127. scenarios := []struct {
  128. input int64
  129. expected string
  130. }{
  131. {0, "0 B"},
  132. {1, "1 B"},
  133. {500, "500 B"},
  134. {1024, "1.0 KiB"},
  135. {43520, "42.5 KiB"},
  136. {5000 * 1024 * 1024, "4.9 GiB"},
  137. }
  138. for _, scenario := range scenarios {
  139. result := formatFileSize(scenario.input)
  140. if result != scenario.expected {
  141. t.Errorf(`Unexpected result, got %q instead of %q for %d`, result, scenario.expected, scenario.input)
  142. }
  143. }
  144. }
  145. func TestCSPExternalFont(t *testing.T) {
  146. want := []string{
  147. `default-src 'none';`,
  148. `img-src * data:;`,
  149. `media-src *;`,
  150. `frame-src *;`,
  151. `style-src 'nonce-1234';`,
  152. `script-src 'nonce-1234'`,
  153. `'strict-dynamic';`,
  154. `font-src test.com;`,
  155. `require-trusted-types-for 'script';`,
  156. `trusted-types html url;`,
  157. `manifest-src 'self';`,
  158. }
  159. got := csp(&model.User{ExternalFontHosts: "test.com"}, "1234")
  160. for _, value := range want {
  161. if !strings.Contains(got, value) {
  162. t.Errorf(`Unexpected result, didn't find %q in %q`, value, got)
  163. }
  164. }
  165. }
  166. func TestCSPNoUser(t *testing.T) {
  167. want := []string{
  168. `default-src 'none';`,
  169. `img-src * data:;`,
  170. `media-src *;`,
  171. `frame-src *;`,
  172. `style-src 'nonce-1234';`,
  173. `script-src 'nonce-1234'`,
  174. `'strict-dynamic';`,
  175. `require-trusted-types-for 'script';`,
  176. `trusted-types html url;`,
  177. `manifest-src 'self';`,
  178. }
  179. got := csp(nil, "1234")
  180. for _, value := range want {
  181. if !strings.Contains(got, value) {
  182. t.Errorf(`Unexpected result, didn't find %q in %q`, value, got)
  183. }
  184. }
  185. }
  186. func TestCSPCustomJSExternalFont(t *testing.T) {
  187. want := []string{
  188. `default-src 'none';`,
  189. `img-src * data:;`,
  190. `media-src *;`,
  191. `frame-src *;`,
  192. `style-src 'nonce-1234';`,
  193. `script-src 'nonce-1234'`,
  194. `'strict-dynamic';`,
  195. `require-trusted-types-for 'script';`,
  196. `trusted-types html url;`,
  197. `manifest-src 'self';`,
  198. }
  199. got := csp(&model.User{ExternalFontHosts: "test.com", CustomJS: "alert(1)"}, "1234")
  200. for _, value := range want {
  201. if !strings.Contains(got, value) {
  202. t.Errorf(`Unexpected result, didn't find %q in %q`, value, got)
  203. }
  204. }
  205. }
  206. func TestCSPExternalFontStylesheet(t *testing.T) {
  207. want := []string{
  208. `default-src 'none';`,
  209. `img-src * data:;`,
  210. `media-src *;`,
  211. `frame-src *;`,
  212. `style-src 'nonce-1234' test.com;`,
  213. `script-src 'nonce-1234'`,
  214. `'strict-dynamic';`,
  215. `require-trusted-types-for 'script';`,
  216. `trusted-types html url;`,
  217. `manifest-src 'self';`,
  218. }
  219. got := csp(&model.User{ExternalFontHosts: "test.com", Stylesheet: "a {color: red;}"}, "1234")
  220. for _, value := range want {
  221. if !strings.Contains(got, value) {
  222. t.Errorf(`Unexpected result, didn't find %q in %q`, value, got)
  223. }
  224. }
  225. }