utils_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. package utils
  2. import (
  3. "encoding/base64"
  4. "net/http"
  5. "net/http/httptest"
  6. "net/url"
  7. "os"
  8. "strings"
  9. "testing"
  10. "time"
  11. )
  12. func TestSendTextResponse(t *testing.T) {
  13. w := httptest.NewRecorder()
  14. SendTextResponse(w, "Hello, World!")
  15. if w.Body.String() != "Hello, World!" {
  16. t.Errorf("Expected: 'Hello, World!', Got: '%s'", w.Body.String())
  17. }
  18. }
  19. func TestSendJSONResponse(t *testing.T) {
  20. w := httptest.NewRecorder()
  21. SendJSONResponse(w, `{"key": "value"}`)
  22. expectedBody := `{"key": "value"}`
  23. if w.Body.String() != expectedBody {
  24. t.Errorf("Expected: '%s', Got: '%s'", expectedBody, w.Body.String())
  25. }
  26. if w.Header().Get("Content-Type") != "application/json" {
  27. t.Error("Content-Type header should be set to 'application/json'")
  28. }
  29. }
  30. func TestSendErrorResponse(t *testing.T) {
  31. w := httptest.NewRecorder()
  32. SendErrorResponse(w, "Something went wrong")
  33. expectedBody := `{"error":"Something went wrong"}`
  34. if w.Body.String() != expectedBody {
  35. t.Errorf("Expected: '%s', Got: '%s'", expectedBody, w.Body.String())
  36. }
  37. if w.Header().Get("Content-Type") != "application/json" {
  38. t.Error("Content-Type header should be set to 'application/json'")
  39. }
  40. }
  41. func TestSendOK(t *testing.T) {
  42. w := httptest.NewRecorder()
  43. SendOK(w)
  44. expectedBody := `"OK"`
  45. if w.Body.String() != expectedBody {
  46. t.Errorf("Expected: '%s', Got: '%s'", expectedBody, w.Body.String())
  47. }
  48. if w.Header().Get("Content-Type") != "application/json" {
  49. t.Error("Content-Type header should be set to 'application/json'")
  50. }
  51. }
  52. func TestTimeToString(t *testing.T) {
  53. testTime := time.Date(2022, 2, 3, 12, 30, 0, 0, time.UTC)
  54. result := TimeToString(testTime)
  55. expectedResult := "2022-02-03 12:30:00"
  56. if result != expectedResult {
  57. t.Errorf("Expected: '%s', Got: '%s'", expectedResult, result)
  58. }
  59. }
  60. func TestFileExists(t *testing.T) {
  61. // Create a temporary file for testing
  62. tempFile, err := os.CreateTemp("", "testfile.txt")
  63. tempFile.Close()
  64. if err != nil {
  65. t.Fatal(err)
  66. }
  67. defer os.Remove(tempFile.Name())
  68. t.Log(tempFile.Name())
  69. // Test case 1: Existing file
  70. exists := FileExists(tempFile.Name())
  71. if !exists {
  72. t.Errorf("Test case 1 failed. Expected: true, Got: false")
  73. }
  74. // Test case 2: Non-existing file
  75. err = os.Remove(tempFile.Name())
  76. if err != nil {
  77. t.Errorf("OS Remove failed %v", err.Error())
  78. }
  79. exists = FileExists(tempFile.Name())
  80. if exists {
  81. t.Errorf("Test case 2 failed. Expected: false, Got: true")
  82. }
  83. }
  84. // --- GetPara ---
  85. func TestGetPara(t *testing.T) {
  86. req := httptest.NewRequest(http.MethodGet, "/?foo=bar", nil)
  87. val, err := GetPara(req, "foo")
  88. if err != nil {
  89. t.Fatalf("unexpected error: %v", err)
  90. }
  91. if val != "bar" {
  92. t.Errorf("expected 'bar', got '%s'", val)
  93. }
  94. _, err = GetPara(req, "missing")
  95. if err == nil {
  96. t.Error("expected error for missing key, got nil")
  97. }
  98. }
  99. // --- GetBool ---
  100. func TestGetBool(t *testing.T) {
  101. cases := []struct {
  102. query string
  103. key string
  104. expected bool
  105. wantErr bool
  106. }{
  107. {"?flag=true", "flag", true, false},
  108. {"?flag=1", "flag", true, false},
  109. {"?flag=false", "flag", false, false},
  110. {"?flag=0", "flag", false, false},
  111. {"?flag=yes", "flag", false, true},
  112. {"", "flag", false, true},
  113. }
  114. for _, tc := range cases {
  115. req := httptest.NewRequest(http.MethodGet, "/"+tc.query, nil)
  116. got, err := GetBool(req, tc.key)
  117. if tc.wantErr {
  118. if err == nil {
  119. t.Errorf("query=%q key=%q: expected error, got nil", tc.query, tc.key)
  120. }
  121. } else {
  122. if err != nil {
  123. t.Errorf("query=%q key=%q: unexpected error: %v", tc.query, tc.key, err)
  124. }
  125. if got != tc.expected {
  126. t.Errorf("query=%q key=%q: expected %v, got %v", tc.query, tc.key, tc.expected, got)
  127. }
  128. }
  129. }
  130. }
  131. // --- GetInt ---
  132. func TestGetInt(t *testing.T) {
  133. req := httptest.NewRequest(http.MethodGet, "/?n=42", nil)
  134. val, err := GetInt(req, "n")
  135. if err != nil {
  136. t.Fatalf("unexpected error: %v", err)
  137. }
  138. if val != 42 {
  139. t.Errorf("expected 42, got %d", val)
  140. }
  141. req2 := httptest.NewRequest(http.MethodGet, "/?n=abc", nil)
  142. _, err = GetInt(req2, "n")
  143. if err == nil {
  144. t.Error("expected error for non-integer value")
  145. }
  146. req3 := httptest.NewRequest(http.MethodGet, "/", nil)
  147. _, err = GetInt(req3, "n")
  148. if err == nil {
  149. t.Error("expected error for missing key")
  150. }
  151. }
  152. // --- PostPara ---
  153. func TestPostPara(t *testing.T) {
  154. form := url.Values{}
  155. form.Set("name", "alice")
  156. req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(form.Encode()))
  157. req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  158. val, err := PostPara(req, "name")
  159. if err != nil {
  160. t.Fatalf("unexpected error: %v", err)
  161. }
  162. if val != "alice" {
  163. t.Errorf("expected 'alice', got '%s'", val)
  164. }
  165. _, err = PostPara(req, "missing")
  166. if err == nil {
  167. t.Error("expected error for missing key, got nil")
  168. }
  169. }
  170. // --- PostBool ---
  171. func TestPostBool(t *testing.T) {
  172. cases := []struct {
  173. formVal string
  174. expected bool
  175. wantErr bool
  176. }{
  177. {"true", true, false},
  178. {"1", true, false},
  179. {"false", false, false},
  180. {"0", false, false},
  181. {"maybe", false, true},
  182. }
  183. for _, tc := range cases {
  184. form := url.Values{}
  185. form.Set("flag", tc.formVal)
  186. req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(form.Encode()))
  187. req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  188. got, err := PostBool(req, "flag")
  189. if tc.wantErr {
  190. if err == nil {
  191. t.Errorf("formVal=%q: expected error, got nil", tc.formVal)
  192. }
  193. } else {
  194. if err != nil {
  195. t.Errorf("formVal=%q: unexpected error: %v", tc.formVal, err)
  196. }
  197. if got != tc.expected {
  198. t.Errorf("formVal=%q: expected %v, got %v", tc.formVal, tc.expected, got)
  199. }
  200. }
  201. }
  202. // Missing key
  203. req := httptest.NewRequest(http.MethodPost, "/", nil)
  204. _, err := PostBool(req, "flag")
  205. if err == nil {
  206. t.Error("expected error for missing key")
  207. }
  208. }
  209. // --- PostInt ---
  210. func TestPostInt(t *testing.T) {
  211. form := url.Values{}
  212. form.Set("count", "7")
  213. req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(form.Encode()))
  214. req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  215. val, err := PostInt(req, "count")
  216. if err != nil {
  217. t.Fatalf("unexpected error: %v", err)
  218. }
  219. if val != 7 {
  220. t.Errorf("expected 7, got %d", val)
  221. }
  222. // Non-integer value
  223. form2 := url.Values{}
  224. form2.Set("count", "notanint")
  225. req2 := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(form2.Encode()))
  226. req2.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  227. _, err = PostInt(req2, "count")
  228. if err == nil {
  229. t.Error("expected error for non-integer value")
  230. }
  231. // Missing key
  232. req3 := httptest.NewRequest(http.MethodPost, "/", nil)
  233. _, err = PostInt(req3, "count")
  234. if err == nil {
  235. t.Error("expected error for missing key")
  236. }
  237. }
  238. // --- IsDir ---
  239. func TestIsDir(t *testing.T) {
  240. dir, err := os.MkdirTemp("", "testdir")
  241. if err != nil {
  242. t.Fatal(err)
  243. }
  244. defer os.RemoveAll(dir)
  245. if !IsDir(dir) {
  246. t.Errorf("expected IsDir=true for directory %s", dir)
  247. }
  248. tmpFile, err := os.CreateTemp(dir, "file")
  249. if err != nil {
  250. t.Fatal(err)
  251. }
  252. tmpFile.Close()
  253. if IsDir(tmpFile.Name()) {
  254. t.Errorf("expected IsDir=false for regular file %s", tmpFile.Name())
  255. }
  256. if IsDir("/nonexistent/path/xyz") {
  257. t.Error("expected IsDir=false for non-existent path")
  258. }
  259. }
  260. // --- LoadImageAsBase64 ---
  261. func TestLoadImageAsBase64(t *testing.T) {
  262. // Write some bytes to a temp file and verify roundtrip
  263. content := []byte{0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A} // PNG magic bytes
  264. tmpFile, err := os.CreateTemp("", "img*.png")
  265. if err != nil {
  266. t.Fatal(err)
  267. }
  268. defer os.Remove(tmpFile.Name())
  269. if _, err := tmpFile.Write(content); err != nil {
  270. t.Fatal(err)
  271. }
  272. tmpFile.Close()
  273. encoded, err := LoadImageAsBase64(tmpFile.Name())
  274. if err != nil {
  275. t.Fatalf("unexpected error: %v", err)
  276. }
  277. decoded, err := base64.StdEncoding.DecodeString(encoded)
  278. if err != nil {
  279. t.Fatalf("failed to decode base64: %v", err)
  280. }
  281. if string(decoded) != string(content) {
  282. t.Errorf("decoded content mismatch: expected %v, got %v", content, decoded)
  283. }
  284. // Non-existent file
  285. _, err = LoadImageAsBase64("/nonexistent/image.png")
  286. if err == nil {
  287. t.Error("expected error for non-existent file")
  288. }
  289. }
  290. // --- ConstructRelativePathFromRequestURL ---
  291. func TestConstructRelativePathFromRequestURL(t *testing.T) {
  292. cases := []struct {
  293. requestURI string
  294. location string
  295. expected string
  296. }{
  297. // Root level: only one slash, no prepend
  298. {"/page", "index.html", "index.html"},
  299. // One level deep: one extra slash --> one "../"
  300. {"/section/page", "index.html", "../index.html"},
  301. // Two levels deep: two extra slashes --> two "../"
  302. {"/a/b/page", "index.html", "../../index.html"},
  303. }
  304. for _, tc := range cases {
  305. got := ConstructRelativePathFromRequestURL(tc.requestURI, tc.location)
  306. if got != tc.expected {
  307. t.Errorf("requestURI=%q location=%q: expected %q, got %q",
  308. tc.requestURI, tc.location, tc.expected, got)
  309. }
  310. }
  311. }
  312. // --- StringInArray ---
  313. func TestStringInArray(t *testing.T) {
  314. arr := []string{"apple", "banana", "cherry"}
  315. if !StringInArray(arr, "banana") {
  316. t.Error("expected 'banana' to be found in array")
  317. }
  318. if StringInArray(arr, "Banana") {
  319. t.Error("expected case-sensitive check to fail for 'Banana'")
  320. }
  321. if StringInArray(arr, "mango") {
  322. t.Error("expected 'mango' not to be found in array")
  323. }
  324. if StringInArray([]string{}, "apple") {
  325. t.Error("expected false for empty array")
  326. }
  327. }
  328. // --- StringInArrayIgnoreCase ---
  329. func TestStringInArrayIgnoreCase(t *testing.T) {
  330. arr := []string{"Apple", "Banana", "Cherry"}
  331. if !StringInArrayIgnoreCase(arr, "apple") {
  332. t.Error("expected 'apple' to be found (case-insensitive)")
  333. }
  334. if !StringInArrayIgnoreCase(arr, "BANANA") {
  335. t.Error("expected 'BANANA' to be found (case-insensitive)")
  336. }
  337. if StringInArrayIgnoreCase(arr, "mango") {
  338. t.Error("expected 'mango' not to be found")
  339. }
  340. if StringInArrayIgnoreCase([]string{}, "apple") {
  341. t.Error("expected false for empty array")
  342. }
  343. }
  344. // --- Templateload ---
  345. func TestTemplateload(t *testing.T) {
  346. content := "Hello, {{name}}! You are {{age}} years old."
  347. tmpFile, err := os.CreateTemp("", "template*.html")
  348. if err != nil {
  349. t.Fatal(err)
  350. }
  351. defer os.Remove(tmpFile.Name())
  352. if _, err := tmpFile.WriteString(content); err != nil {
  353. t.Fatal(err)
  354. }
  355. tmpFile.Close()
  356. data := map[string]string{
  357. "name": "Alice",
  358. "age": "30",
  359. }
  360. result, err := Templateload(tmpFile.Name(), data)
  361. if err != nil {
  362. t.Fatalf("unexpected error: %v", err)
  363. }
  364. expected := "Hello, Alice! You are 30 years old."
  365. if result != expected {
  366. t.Errorf("expected %q, got %q", expected, result)
  367. }
  368. // Non-existent template file
  369. _, err = Templateload("/nonexistent/template.html", data)
  370. if err == nil {
  371. t.Error("expected error for non-existent template file")
  372. }
  373. }
  374. // --- TemplateApply ---
  375. func TestTemplateApply(t *testing.T) {
  376. tmpl := "Dear {{title}} {{surname}}, welcome to {{place}}."
  377. data := map[string]string{
  378. "title": "Dr.",
  379. "surname": "Smith",
  380. "place": "ArozOS",
  381. }
  382. result := TemplateApply(tmpl, data)
  383. expected := "Dear Dr. Smith, welcome to ArozOS."
  384. if result != expected {
  385. t.Errorf("expected %q, got %q", expected, result)
  386. }
  387. // No replacements needed
  388. plain := "No placeholders here."
  389. result2 := TemplateApply(plain, map[string]string{})
  390. if result2 != plain {
  391. t.Errorf("expected unchanged string %q, got %q", plain, result2)
  392. }
  393. // Placeholder that doesn't exist in data is left intact
  394. partial := "Hello {{name}}, your code is {{code}}."
  395. result3 := TemplateApply(partial, map[string]string{"name": "Bob"})
  396. if result3 != "Hello Bob, your code is {{code}}." {
  397. t.Errorf("unexpected result for partial replacement: %q", result3)
  398. }
  399. }
  400. // --- FilenameIsWebSafe ---
  401. func TestFilenameIsWebSafe(t *testing.T) {
  402. safeNames := []string{
  403. "myfile.txt",
  404. "image-001.png",
  405. "document_v2.pdf",
  406. "report 2024.docx",
  407. }
  408. for _, name := range safeNames {
  409. if !FilenameIsWebSafe(name) {
  410. t.Errorf("expected %q to be web-safe", name)
  411. }
  412. }
  413. unsafeNames := []string{
  414. "file/with/slashes.txt",
  415. "back\\slash.txt",
  416. "query?param=1",
  417. "percent%20encoded",
  418. "wild*card",
  419. "colon:name",
  420. "pipe|name",
  421. `quote"name`,
  422. "less<than",
  423. "greater>than",
  424. }
  425. for _, name := range unsafeNames {
  426. if FilenameIsWebSafe(name) {
  427. t.Errorf("expected %q to be NOT web-safe", name)
  428. }
  429. }
  430. }