wallabag_test.go 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
  2. // SPDX-License-Identifier: Apache-2.0
  3. package wallabag
  4. import (
  5. "encoding/json"
  6. "io"
  7. "net/http"
  8. "net/http/httptest"
  9. "strings"
  10. "testing"
  11. )
  12. func TestCreateEntry(t *testing.T) {
  13. entryURL := "https://example.com"
  14. entryTitle := "title"
  15. entryContent := "content"
  16. tags := "tag1,tag2,tag3"
  17. tests := []struct {
  18. name string
  19. username string
  20. password string
  21. clientID string
  22. clientSecret string
  23. tags string
  24. onlyURL bool
  25. entryURL string
  26. entryTitle string
  27. entryContent string
  28. serverResponse func(w http.ResponseWriter, r *http.Request)
  29. wantErr bool
  30. errContains string
  31. }{
  32. {
  33. name: "successful entry creation with url only",
  34. wantErr: false,
  35. onlyURL: true,
  36. username: "username",
  37. password: "password",
  38. clientID: "clientId",
  39. clientSecret: "clientSecret",
  40. tags: tags,
  41. entryURL: entryURL,
  42. entryTitle: entryTitle,
  43. entryContent: entryContent,
  44. serverResponse: func(w http.ResponseWriter, r *http.Request) {
  45. if strings.Contains(r.URL.Path, "/oauth/v2/token") {
  46. // Return success response
  47. w.WriteHeader(http.StatusOK)
  48. json.NewEncoder(w).Encode(map[string]any{
  49. "access_token": "test-token",
  50. "expires_in": 3600,
  51. "refresh_token": "token",
  52. "scope": "scope",
  53. "token_type": "token_type",
  54. })
  55. return
  56. }
  57. // Verify authorization header
  58. auth := r.Header.Get("Authorization")
  59. if auth != "Bearer test-token" {
  60. t.Errorf("Expected Authorization header 'Bearer test-token', got %s", auth)
  61. }
  62. // Verify content type
  63. contentType := r.Header.Get("Content-Type")
  64. if contentType != "application/json" {
  65. t.Errorf("Expected Content-Type 'application/json', got %s", contentType)
  66. }
  67. // Parse and verify request
  68. body, _ := io.ReadAll(r.Body)
  69. var req map[string]any
  70. if err := json.Unmarshal(body, &req); err != nil {
  71. t.Errorf("Failed to parse request body: %v", err)
  72. }
  73. if requstEntryURL := req["url"]; requstEntryURL != entryURL {
  74. t.Errorf("Expected entryURL %s, got %s", entryURL, requstEntryURL)
  75. }
  76. if requestEntryTitle := req["title"]; requestEntryTitle != entryTitle {
  77. t.Errorf("Expected entryTitle %s, got %s", entryTitle, requestEntryTitle)
  78. }
  79. if _, ok := req["content"]; ok {
  80. t.Errorf("Expected entryContent to be empty, got value")
  81. }
  82. if requestTags := req["tags"]; requestTags != tags {
  83. t.Errorf("Expected tags %s, got %s", tags, requestTags)
  84. } // Return success response
  85. w.WriteHeader(http.StatusOK)
  86. },
  87. errContains: "",
  88. },
  89. {
  90. name: "successful entry creation with content",
  91. wantErr: false,
  92. onlyURL: false,
  93. username: "username",
  94. password: "password",
  95. clientID: "clientId",
  96. clientSecret: "clientSecret",
  97. tags: tags,
  98. entryURL: entryURL,
  99. entryTitle: entryTitle,
  100. entryContent: entryContent,
  101. serverResponse: func(w http.ResponseWriter, r *http.Request) {
  102. if strings.Contains(r.URL.Path, "/oauth/v2/token") {
  103. // Return success response
  104. w.WriteHeader(http.StatusOK)
  105. json.NewEncoder(w).Encode(map[string]any{
  106. "access_token": "test-token",
  107. "expires_in": 3600,
  108. "refresh_token": "token",
  109. "scope": "scope",
  110. "token_type": "token_type",
  111. })
  112. return
  113. }
  114. // Verify authorization header
  115. auth := r.Header.Get("Authorization")
  116. if auth != "Bearer test-token" {
  117. t.Errorf("Expected Authorization header 'Bearer test-token', got %s", auth)
  118. }
  119. // Verify content type
  120. contentType := r.Header.Get("Content-Type")
  121. if contentType != "application/json" {
  122. t.Errorf("Expected Content-Type 'application/json', got %s", contentType)
  123. }
  124. // Parse and verify request
  125. body, _ := io.ReadAll(r.Body)
  126. var req map[string]any
  127. if err := json.Unmarshal(body, &req); err != nil {
  128. t.Errorf("Failed to parse request body: %v", err)
  129. }
  130. if requstEntryURL := req["url"]; requstEntryURL != entryURL {
  131. t.Errorf("Expected entryURL %s, got %s", entryURL, requstEntryURL)
  132. }
  133. if requestEntryTitle := req["title"]; requestEntryTitle != entryTitle {
  134. t.Errorf("Expected entryTitle %s, got %s", entryTitle, requestEntryTitle)
  135. }
  136. if requestEntryContent := req["content"]; requestEntryContent != entryContent {
  137. t.Errorf("Expected entryContent %s, got %s", entryContent, requestEntryContent)
  138. }
  139. if requestTags := req["tags"]; requestTags != tags {
  140. t.Errorf("Expected tags %s, got %s", tags, requestTags)
  141. } // Return success response
  142. w.WriteHeader(http.StatusOK)
  143. },
  144. errContains: "",
  145. },
  146. {
  147. name: "failed when unable to decode accessToken response",
  148. wantErr: true,
  149. onlyURL: true,
  150. username: "username",
  151. password: "password",
  152. clientID: "clientId",
  153. clientSecret: "clientSecret",
  154. tags: tags,
  155. entryURL: entryURL,
  156. entryTitle: entryTitle,
  157. entryContent: entryContent,
  158. serverResponse: func(w http.ResponseWriter, r *http.Request) {
  159. if strings.Contains(r.URL.Path, "/oauth/v2/token") {
  160. // Return success response
  161. w.WriteHeader(http.StatusOK)
  162. w.Write([]byte("invalid json"))
  163. return
  164. }
  165. t.Error("Server should not be called when failed to get accessToken")
  166. },
  167. errContains: "unable to decode token response",
  168. },
  169. {
  170. name: "failed when saving entry",
  171. wantErr: true,
  172. onlyURL: true,
  173. username: "username",
  174. password: "password",
  175. clientID: "clientId",
  176. clientSecret: "clientSecret",
  177. tags: tags,
  178. entryURL: entryURL,
  179. entryTitle: entryTitle,
  180. entryContent: entryContent,
  181. serverResponse: func(w http.ResponseWriter, r *http.Request) {
  182. if strings.Contains(r.URL.Path, "/oauth/v2/token") {
  183. // Return success response
  184. w.WriteHeader(http.StatusOK)
  185. json.NewEncoder(w).Encode(map[string]any{
  186. "access_token": "test-token",
  187. "expires_in": 3600,
  188. "refresh_token": "token",
  189. "scope": "scope",
  190. "token_type": "token_type",
  191. })
  192. return
  193. }
  194. w.WriteHeader(http.StatusUnauthorized)
  195. },
  196. errContains: "unable to get save entry",
  197. },
  198. {
  199. name: "failure due to no accessToken",
  200. wantErr: true,
  201. onlyURL: false,
  202. username: "username",
  203. password: "password",
  204. clientID: "clientId",
  205. clientSecret: "clientSecret",
  206. tags: tags,
  207. entryURL: entryURL,
  208. entryTitle: entryTitle,
  209. entryContent: entryContent,
  210. serverResponse: func(w http.ResponseWriter, r *http.Request) {
  211. if strings.Contains(r.URL.Path, "/oauth/v2/token") {
  212. // Return error response
  213. w.WriteHeader(http.StatusUnauthorized)
  214. return
  215. }
  216. t.Error("Server should not be called when failed to get accessToken")
  217. },
  218. errContains: "unable to get access token",
  219. },
  220. {
  221. name: "failure due to missing client parameters",
  222. wantErr: true,
  223. onlyURL: false,
  224. tags: tags,
  225. entryURL: entryURL,
  226. entryTitle: entryTitle,
  227. entryContent: entryContent,
  228. serverResponse: func(w http.ResponseWriter, r *http.Request) {
  229. t.Error("Server should not be called when failed to get accessToken")
  230. },
  231. errContains: "wallabag: missing base URL, client ID, client secret, username or password",
  232. },
  233. }
  234. for _, tt := range tests {
  235. t.Run(tt.name, func(t *testing.T) {
  236. // Create test server if we have a server response function
  237. var serverURL string
  238. if tt.serverResponse != nil {
  239. server := httptest.NewServer(http.HandlerFunc(tt.serverResponse))
  240. defer server.Close()
  241. serverURL = server.URL
  242. }
  243. // Create client with test server URL
  244. client := NewClient(serverURL, tt.clientID, tt.clientSecret, tt.username, tt.password, tt.tags, tt.onlyURL)
  245. // Call CreateBookmark
  246. err := client.CreateEntry(tt.entryURL, tt.entryTitle, tt.entryContent)
  247. // Check error expectations
  248. if tt.wantErr {
  249. if err == nil {
  250. t.Errorf("Expected error but got none")
  251. } else if tt.errContains != "" && !strings.Contains(err.Error(), tt.errContains) {
  252. t.Errorf("Expected error containing '%s', got '%s'", tt.errContains, err.Error())
  253. }
  254. } else {
  255. if err != nil {
  256. t.Errorf("Unexpected error: %v", err)
  257. }
  258. }
  259. })
  260. }
  261. }
  262. func TestNewClient(t *testing.T) {
  263. tests := []struct {
  264. name string
  265. baseURL string
  266. clientID string
  267. clientSecret string
  268. username string
  269. password string
  270. tags string
  271. onlyURL bool
  272. }{
  273. {
  274. name: "with all parameters",
  275. baseURL: "https://wallabag.example.com",
  276. clientID: "clientID",
  277. clientSecret: "clientSecret",
  278. username: "wallabag",
  279. password: "wallabag",
  280. tags: "",
  281. onlyURL: true,
  282. },
  283. }
  284. for _, tt := range tests {
  285. t.Run(tt.name, func(t *testing.T) {
  286. client := NewClient(tt.baseURL, tt.clientID, tt.clientSecret, tt.username, tt.password, tt.tags, tt.onlyURL)
  287. if client.baseURL != tt.baseURL {
  288. t.Errorf("Expected.baseURL %s, got %s", tt.baseURL, client.baseURL)
  289. }
  290. if client.username != tt.username {
  291. t.Errorf("Expected username %s, got %s", tt.username, client.username)
  292. }
  293. if client.password != tt.password {
  294. t.Errorf("Expected password %s, got %s", tt.password, client.password)
  295. }
  296. if client.clientID != tt.clientID {
  297. t.Errorf("Expected clientID %s, got %s", tt.clientID, client.clientID)
  298. }
  299. if client.clientSecret != tt.clientSecret {
  300. t.Errorf("Expected clientSecret %s, got %s", tt.clientSecret, client.clientSecret)
  301. }
  302. if client.tags != tt.tags {
  303. t.Errorf("Expected tags %s, got %s", tt.tags, client.tags)
  304. }
  305. if client.onlyURL != tt.onlyURL {
  306. t.Errorf("Expected onlyURL %v, got %v", tt.onlyURL, client.onlyURL)
  307. }
  308. })
  309. }
  310. }