ShaarliHtmlClientTest.swift 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. //
  2. // ShaarliHtmlClientTest.swift
  3. // ShaarliOSTests
  4. //
  5. // Created by Marcus Rohrmoser on 09.06.19.
  6. // Copyright © 2019-2022 Marcus Rohrmoser mobile Software http://mro.name/me. All rights reserved.
  7. //
  8. // This program is free software: you can redistribute it and/or modify
  9. // it under the terms of the GNU General Public License as published by
  10. // the Free Software Foundation, either version 3 of the License, or
  11. // (at your option) any later version.
  12. //
  13. // This program is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. // GNU General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU General Public License
  19. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. //
  21. import XCTest
  22. private let AGENT = "test"
  23. private let TO : TimeInterval = 4.0
  24. class ShaarliHtmlClientTest: XCTestCase {
  25. override func setUp() {
  26. // Put setup code here. This method is called before the invocation of each test method in the class.
  27. }
  28. override func tearDown() {
  29. URLSession.shared.reset() { print("URLSession.reset() done.")}
  30. }
  31. func testUrlEmpty () {
  32. XCTAssertEqual("", URLEmpty.absoluteString)
  33. }
  34. func testUrl() {
  35. let url = URL(string: "https://uid:pwd@example.com/foo")!
  36. XCTAssertEqual("https://uid:pwd@example.com/foo", url.absoluteString)
  37. XCTAssertEqual("https", url.scheme)
  38. XCTAssertEqual("example.com", url.host)
  39. XCTAssertEqual("uid", url.user)
  40. XCTAssertEqual("pwd", url.password)
  41. XCTAssertEqual("/foo", url.path)
  42. XCTAssertEqual(nil, url.query)
  43. XCTAssertEqual(nil, url.fragment)
  44. var b = URLComponents(string:url.absoluteString)!
  45. b.user = "foo"
  46. let u2 = b.url!
  47. XCTAssertEqual("foo", u2.user)
  48. XCTAssertEqual("pwd", u2.password)
  49. let url1 = URL(string: "a.b")!
  50. XCTAssertNil(url1.host)
  51. XCTAssertNil(url1.scheme)
  52. }
  53. func testIssue61() {
  54. let ur = issue61("http://example.com?a=b c")
  55. XCTAssertEqual("http://example.com?a=b+c", ur.absoluteString)
  56. }
  57. func testUrlCredential() {
  58. let cre = URLCredential(user:"", password:"", persistence:.permanent)
  59. XCTAssertEqual(true, cre.hasPassword)
  60. XCTAssertEqual("", cre.user)
  61. XCTAssertEqual("", cre.password)
  62. }
  63. func testBasic() {
  64. let str = httpBasic(URLCredential(user:"usr", password:"pwd", persistence:.forSession))
  65. XCTAssertEqual("Basic dXNyOnB3ZA==", str)
  66. guard let cre = httpBasic(str) else {
  67. XCTFail()
  68. return
  69. }
  70. XCTAssertTrue(cre.hasPassword)
  71. XCTAssertEqual("usr", cre.user)
  72. XCTAssertEqual("pwd", cre.password)
  73. let c : URLCredential? = nil
  74. XCTAssertNil(httpBasic(c))
  75. XCTAssertNil(httpBasic(URLCredential(user:"", password:"uhu", persistence:.permanent)))
  76. XCTAssertEqual("Basic dWh1Og==", httpBasic(URLCredential(user:"uhu", password:"", persistence:.forSession)))
  77. XCTAssertEqual("Basic ZGVtTzpkZW1PZGVtT2RlbU8=", httpBasic(URLCredential(user:"demO", password:"demOdemOdemO", persistence:.forSession)))
  78. }
  79. func testFormData() {
  80. XCTAssertEqual("1=a%26b", String(data: formData(["1":"a&b"]), encoding: .ascii))
  81. XCTAssertEqual("2%3D2=c%3Dc", String(data: formData(["2=2":"c=c"]), encoding: .ascii))
  82. XCTAssertEqual("3=d%0Ad", String(data: formData(["3":"d\nd"]), encoding: .ascii))
  83. var frm = ["3":"d\nd", LF_PRI:"on"]
  84. frm[LF_PRI] = nil
  85. XCTAssertEqual("3=d%0Ad", String(data: formData(frm), encoding: .ascii))
  86. }
  87. func testFormDataIssue59() {
  88. let pwd = ":8-$;(B%Z_rM]]?i?p{'+]1|xQk008]$,L}\\z2HxTB^%YpEl"
  89. let pw1 = ":8-$;(B%Z_rM]]?i?p{\'+]1|xQk008]$,L}\\z2HxTB^%YpEl"
  90. XCTAssertEqual(pw1, pwd, "literal escaping")
  91. // $ curl --trace-ascii - --data-urlencode password=':8-$;(B%Z_rM]]?i?p{\'+]1|xQk008]$,L}\\z2HxTB^%YpEl' https://demo.mro.name/
  92. let pwcurl = "%3A8-%24%3B%28B%25Z_rM%5D%5D%3Fi%3Fp%7B%27%2B%5D1%7CxQk008%5D%24%2CL%7D%5Cz2HxTB%5E%25YpEl"
  93. XCTAssertEqual("password=\(pwcurl)", String(data: formData(["password":pwd]), encoding: .ascii))
  94. }
  95. func testEncoding() {
  96. let str = "Hello, wörld!"
  97. let byt = str.data(using: .utf8, allowLossyConversion: false)!
  98. XCTAssertEqual(str, String(bytes: byt, encoding:.utf8))
  99. XCTAssertEqual("Hello, wörld!", String(bytes: byt, encoding:.isoLatin1))
  100. }
  101. func testUrlEscaping() {
  102. var uc = URLComponents(string: "scheme://uid:pwd@host:123/path?q=s#frag")!
  103. uc.user = "my uid_with_:_/_@_?_&_$_ä_🚀_end"
  104. uc.password = "my pwd_with_:_/_@_?_&_$_ä_🚀_end"
  105. XCTAssertEqual("scheme://my%20uid_with_%3A_%2F_%40_%3F_&_$_%C3%A4_%F0%9F%9A%80_end:my%20pwd_with_%3A_%2F_%40_%3F_&_$_%C3%A4_%F0%9F%9A%80_end@host:123/path?q=s#frag", uc.url?.absoluteString)
  106. XCTAssertEqual("my pwd_with_:_/_@_?_&_$_ä_🚀_end", uc.password)
  107. uc.scheme = HTTP_HTTPS
  108. uc.host = "demo.mro.name"
  109. uc.port = 8443
  110. uc.user = "demo"
  111. uc.password = "demo -/:;&@\"$#%"
  112. uc.path = "/shaarli-v0.11.1-issue28/"
  113. uc.query = nil
  114. uc.fragment = nil
  115. XCTAssertEqual("https://demo:demo%20-%2F%3A;&%40%22$%23%25@demo.mro.name:8443/shaarli-v0.11.1-issue28/", uc.url?.absoluteString)
  116. let u2 = uc.url!
  117. XCTAssertEqual(uc.scheme, u2.scheme)
  118. XCTAssertEqual(uc.host, u2.host)
  119. XCTAssertEqual(uc.port, u2.port)
  120. XCTAssertEqual(uc.user, u2.user)
  121. XCTAssertEqual("demo%20-%2F%3A;&%40%22$%23%25", u2.password) // URL.password is raw!
  122. XCTAssertEqual("/shaarli-v0.11.1-issue28", u2.path)
  123. XCTAssertEqual(uc.query, u2.query)
  124. XCTAssertEqual(uc.fragment, u2.fragment)
  125. }
  126. // https://nshipster.com/swift-regular-expressions/
  127. func testRegex() {
  128. let ra0 = "Fancy a game of Cluedo™️?".range(of: "Clue(do)?™️?", options:.regularExpression)
  129. XCTAssertEqual(16, ra0?.lowerBound.encodedOffset)
  130. let msg = "<script>alert(\"foo\"); // bar \");"
  131. let ra1 = msg.range(of: PAT_WRONG_LOGIN, options:.regularExpression)!
  132. XCTAssertEqual("<script>alert(\"foo\");", msg[ra1])
  133. }
  134. func testResponseLoginFormSunshine() {
  135. let raw = dataWithContentsOfFixture(me:self, fileName: "login.0", extensio:"html")
  136. let re = HTTPURLResponse(url: URLEmpty, statusCode: 200, httpVersion: "1.1", headerFields:[
  137. "Content-Type":"text/html; charset=utf-8",
  138. "Content-Length":"\(raw.count)",
  139. ])!
  140. XCTAssertEqual(200, re.statusCode)
  141. XCTAssertEqual("text/html", re.mimeType)
  142. XCTAssertEqual("utf-8", re.textEncodingName)
  143. XCTAssertEqual(2509, re.expectedContentLength)
  144. let res = check(raw, re, nil)
  145. XCTAssertEqual("", res.1)
  146. XCTAssertEqual(1, res.0.count)
  147. guard let lifo = res.0[LOGIN_FORM] else {
  148. XCTFail()
  149. return
  150. }
  151. XCTAssertEqual(3, lifo.count)
  152. XCTAssertEqual("http://links.mro.name/", lifo["returnurl"])
  153. XCTAssertEqual("Login", lifo[""])
  154. XCTAssertEqual("20119241badf78a3dcfa55ae58eab429a5d24bad", lifo["token"])
  155. }
  156. func testResponseLoginFormBanned() {
  157. let raw = dataWithContentsOfFixture(me:self, fileName: "login.banned", extensio:"html")
  158. let re = HTTPURLResponse(url: URLEmpty, statusCode: 200, httpVersion: "1.1", headerFields:[
  159. "Content-Type":"text/html; charset=utf-8",
  160. "Content-Length":"\(raw.count)",
  161. ])!
  162. XCTAssertEqual(200, re.statusCode)
  163. XCTAssertEqual("text/html", re.mimeType)
  164. XCTAssertEqual("utf-8", re.textEncodingName)
  165. XCTAssertEqual(1982, re.expectedContentLength)
  166. let res = check(raw, re, nil)
  167. XCTAssertEqual("You have been banned from login after too many failed attempts. Try later.", res.1)
  168. XCTAssertEqual(0, res.0.count)
  169. }
  170. func testProbeSunshine() {
  171. let demo = URL(string:"https://demo:demo@demo.shaarli.org/")! // credentials are public
  172. // let demo = URL(string:"https://demo:demodemodemo@demo.mro.name:8443/shaarli-v0.10.2/")! // credentials are public
  173. // let demo = URL(string:"https://demo:demo@shaarli-next.hoa.ro/")! // credentials are public
  174. // let demo = URL(string:"https://demo:demodemodemo@demo.mro.name/shaarli-v0.41b/")! // credentials are public
  175. // let demo = URL(string:"https://demo:demodemodemo@demo.mro.name/shaarligo/")! // credentials are public
  176. let exp = self.expectation(description: "Probing") // https://medium.com/@johnsundell/unit-testing-asynchronous-swift-code-9805d1d0ac5e
  177. let srv = ShaarliHtmlClient(AGENT)
  178. srv.probe(demo, nil, TO) { (url, tit, pride, tizo, err) in
  179. XCTAssertEqual("", err)
  180. // XCTAssertEqual("https://demo.shaarli.org/", url.absoluteString)
  181. XCTAssertEqual("Shaarli demo", tit)
  182. //XCTAssertEqual("https://demo:demodemodemo@demo.mro.name:8443/shaarli-v0.10.2/", url.absoluteString)
  183. XCTAssertEqual("https://demo:demo@demo.shaarli.org/", url.absoluteString)
  184. // XCTAssertEqual("https://demo:demo@shaarli-next.hoa.ro/", url.absoluteString)
  185. // XCTAssertEqual("https://demo:demodemodemo@demo.mro.name:8443/shaarli-v0.41b/", url.absoluteString)
  186. // XCTAssertEqual("https://demo:demodemodemo@demo.mro.name/shaarligo/shaarligo.cgi", url.absoluteString)
  187. // XCTAssertEqual("ShaarliGo 🚀", tit)
  188. XCTAssertEqual(false, pride)
  189. XCTAssertEqual(nil, tizo?.identifier)
  190. // XCTAssertEqual("Europe/Paris", tizo?.identifier)
  191. exp.fulfill()
  192. return
  193. }
  194. waitForExpectations(timeout: 3, handler: nil)
  195. }
  196. func testProbeSunshineIssue28() {
  197. var uc = URLComponents(string: "")!
  198. uc.scheme = HTTP_HTTPS
  199. uc.host = "demo.mro.name"
  200. uc.port = 8443
  201. uc.path = "/shaarli-v0.11.1-issue28/"
  202. // credentials are public
  203. uc.user = "demo"
  204. uc.password = "demo -/:;&@\"$#%"
  205. XCTAssertEqual("https://demo:demo%20-%2F%3A;&%40%22$%23%25@demo.mro.name:8443/shaarli-v0.11.1-issue28/", uc.url?.absoluteString)
  206. let demo = uc.url!
  207. XCTAssertEqual("demo", demo.user)
  208. XCTAssertEqual("demo%20-%2F%3A;&%40%22$%23%25", demo.password)
  209. let exp = self.expectation(description: "Probing") // https://medium.com/@johnsundell/unit-testing-asynchronous-swift-code-9805d1d0ac5e
  210. let srv = ShaarliHtmlClient(AGENT)
  211. srv.probe(demo, nil, TO) { (url, tit, pride, tizo, err) in
  212. XCTAssertEqual("", err)
  213. XCTAssertEqual("https://demo:demo%20-%2F%3A;&%40%22$%23%25@demo.mro.name:8443/shaarli-v0.11.1-issue28/", url.absoluteString)
  214. XCTAssertEqual(false, pride)
  215. XCTAssertEqual("Europe/Berlin", tizo?.identifier)
  216. exp.fulfill()
  217. return
  218. }
  219. waitForExpectations(timeout: 5, handler: nil)
  220. }
  221. func testProbe403() {
  222. // let demo = URL(string:"https://demo:foo@demo.shaarli.org/")! // credentials are public
  223. let demo = URL(string:"https://tast:foo@demo.mro.name/shaarli-v0.10.2/")! // credentials are public
  224. // let demo = URL(string:"https://tast:foo@demo.mro.name/shaarli-v0.41b/")! // credentials are public
  225. let exp = self.expectation(description: "Probing") // https://medium.com/@johnsundell/unit-testing-asynchronous-swift-code-9805d1d0ac5e
  226. let srv = ShaarliHtmlClient(AGENT)
  227. srv.probe(demo, nil, TO) { (url, pong, pride, tizo, err) in
  228. XCTAssertEqual(URLEmpty, url)
  229. XCTAssertEqual("", pong)
  230. XCTAssertEqual("Wrong login/password.", err)
  231. XCTAssertEqual(false, pride)
  232. XCTAssertEqual(nil, tizo?.identifier)
  233. // XCTAssertEqual(ShaarliHtmlClient.STR_BANNED, err)
  234. exp.fulfill()
  235. return
  236. }
  237. waitForExpectations(timeout: 2, handler: nil)
  238. }
  239. func testProbe404() {
  240. // let demo = URL(string:"https://demo:foo@demo.shaarli.org/hgr/")! // credentials are public
  241. // let demo = URL(string:"https://tast:foo@demo.mro.name/shaarli-v0.10.2/")! // credentials are public
  242. let demo = URL(string:"https://demo.mro.name/bogus")! // credentials are public
  243. let exp = self.expectation(description: "Probing") // https://medium.com/@johnsundell/unit-testing-asynchronous-swift-code-9805d1d0ac5e
  244. let srv = ShaarliHtmlClient(AGENT)
  245. srv.probe(demo, nil, TO) { (url, pong, pride, tizo, err) in
  246. XCTAssertEqual(URLEmpty, url)
  247. XCTAssertEqual("", pong)
  248. XCTAssertEqual("Expected response HTTP status '200 Ok' but got '404 not found'", err)
  249. XCTAssertEqual(false, pride)
  250. XCTAssertEqual(nil, tizo?.identifier)
  251. exp.fulfill()
  252. return
  253. }
  254. waitForExpectations(timeout: 2, handler: nil)
  255. }
  256. func testGetSunshine() {
  257. // let end = URL(string:"https://demo:demo@demo.shaarli.org/")! // credentials are public
  258. // let end = URL(string:"https://demo:demodemodemo@demo.mro.name/shaarli-v0.11.1/")! // credentials are public
  259. // let end = URL(string:"https://demo:demodemodemo@demo.mro.name/shaarli-v0.10.4/")! // credentials are public
  260. // let end = URL(string:"https://demo:demodemodemo@demo.mro.name/shaarli-v0.10.2/")! // credentials are public
  261. // let end = URL(string:"https://demo:demodemodemo@demo.mro.name:8443/shaarli-v0.10.2/")! // credentials are public
  262. let end = URL(string:"https://demo:demodemodemo@demo.mro.name/shaarli-v0.41b/")! // credentials are public
  263. // let end = URL(string:"https://demo:demodemodemo@demo.mro.name/shaarligo")! // credentials are public
  264. let url = URL(string:"https://shaarli.readthedocs.io")!
  265. let cre = URLCredential(user:"demO", password:"demOdemOdemO", persistence:.forSession)
  266. let exp = self.expectation(description: "Reading") // https://medium.com/@johnsundell/unit-testing-asynchronous-swift-code-9805d1d0ac5e
  267. let srv = ShaarliHtmlClient(AGENT)
  268. srv.get(end, cre, TO, url) { (_, act, frm, url, tit, dsc, tgs, pri, tim, seti, err) in
  269. XCTAssertEqual("https://demo.mro.name/shaarli-v0.41b/?post=https%3A%2F%2Fshaarli.readthedocs.io", act.absoluteString)
  270. XCTAssertEqual("https://shaarli.readthedocs.io", url.absoluteString)
  271. XCTAssertEqual("", tit)
  272. XCTAssertEqual([], tgs)
  273. XCTAssertFalse(pri)
  274. XCTAssertEqual("", err)
  275. // XCTAssertEqual([:], frm)
  276. exp.fulfill()
  277. }
  278. waitForExpectations(timeout: 2, handler: nil)
  279. }
  280. func testPostSunshine() {
  281. // let end = URL(string:"https://demo:demo@demo.shaarli.org/")! // credentials are public
  282. // let end = URL(string:"https://demo:demodemodemo@demo.mro.name/shaarli-v0.11.1/")! // credentials are public
  283. // let end = URL(string:"https://demo:demodemodemo@demo.mro.name/shaarli-v0.10.4/")! // credentials are public
  284. // let end = URL(string:"https://demo:demo@shaarli-next.hoa.ro/")! // credentials are public
  285. // let end = URL(string:"https://demo:demodemodemo@demo.mro.name/shaarli-v0.10.2/")! // credentials are public
  286. // let end = URL(string:"https://demo:demodemodemo@demo.mro.name:8443/shaarli-v0.10.2/")! // credentials are public
  287. let end = URL(string:"https://demo:demodemodemo@demo.mro.name/shaarli-v0.41b/")! // credentials are public
  288. let url = URL(string:"http://idlewords.com/talks/website_obesity.htm?foo=bar#minimalism")!
  289. let exp0 = self.expectation(description: "Reading") // https://medium.com/@johnsundell/unit-testing-asynchronous-swift-code-9805d1d0ac5e
  290. let exp1 = self.expectation(description: "Posting")
  291. let srv = ShaarliHtmlClient(AGENT)
  292. srv.get(end, nil, TO, url) { (ses, act, frm, url, tit, dsc, tgs, pri, tim, seti, err0) in
  293. XCTAssertEqual("", err0)
  294. // XCTAssertEqual("http://idlewords.com/talks/website_obesity.htm#minimalism", url.absoluteString)
  295. XCTAssertEqual("http://idlewords.com/talks/website_obesity.htm?foo=bar#minimalism", url.absoluteString)
  296. // XCTAssertEqual("The Website Obesity Crisis", tit)
  297. XCTAssertEqual("", dsc, "why is dsc empty?")
  298. XCTAssertEqual([], tgs)
  299. //XCTAssertFalse(pri)
  300. //XCTAssertNil(ctx[LF_PRI])
  301. exp0.fulfill()
  302. srv.add(ses, act, frm, url, tit, dsc, tgs, pri) { err1 in
  303. XCTAssertEqual("", err1)
  304. exp1.fulfill()
  305. }
  306. }
  307. waitForExpectations(timeout: 3, handler: nil)
  308. }
  309. func testPostUrlIssue61() {
  310. let end = URL(string:"https://demo:demodemodemo@demo.mro.name/shaarli-v0.11.1/")! // credentials are public
  311. let url0 = URL(string:"https://example.com?a=b+c")!
  312. let exp0 = self.expectation(description: "Reading")
  313. let exp1 = self.expectation(description: "Posting")
  314. let srv = ShaarliHtmlClient(AGENT)
  315. srv.get(end, nil, TO, url0) { (ses, act, frm, url, tit, dsc, tgs, pri, tim, seti, err0) in
  316. XCTAssertEqual("", err0)
  317. XCTAssertEqual("https://example.com?a=b+c", url.absoluteString)
  318. // XCTAssertEqual("The Website Obesity Crisis", tit)
  319. XCTAssertEqual("", dsc, "why is dsc empty?")
  320. XCTAssertEqual([], tgs)
  321. exp0.fulfill()
  322. srv.add(ses, act, frm, url, tit, dsc, tgs, pri) { err1 in
  323. XCTAssertEqual("", err1)
  324. exp1.fulfill()
  325. }
  326. }
  327. waitForExpectations(timeout: 3, handler: nil)
  328. }
  329. func testTime() {
  330. let cli = ShaarliHtmlClient(AGENT)
  331. let tz = TimeZone(secondsFromGMT:2*60*60)!
  332. XCTAssertNil(cli.timeShaarli(tz, nil))
  333. XCTAssertNil(cli.timeShaarli(tz, "bogus"))
  334. XCTAssertNil(cli.timeShaarli(tz, ""))
  335. XCTAssertEqual("2021-04-07 14:15:06 +0000", cli.timeShaarli(tz, "20210407_161506")?.description)
  336. }
  337. }