gopher-server.t 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. # Copyright (C) 2017–2018 Alex Schroeder <alex@gnu.org>
  2. #
  3. # This program is free software; you can redistribute it and/or modify
  4. # it under the terms of the GNU General Public License as published by
  5. # the Free Software Foundation; either version 3 of the License, or
  6. # (at your option) any later version.
  7. #
  8. # This program is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. package OddMuse;
  16. use strict;
  17. use 5.10.0;
  18. use Test::More;
  19. use IO::Socket::IP;
  20. use utf8; # tests contain UTF-8 characters and it matters
  21. require './t/test.pl';
  22. add_module('tags.pl');
  23. # enable uploads
  24. our($ConfigFile);
  25. AppendStringToFile($ConfigFile, "\$UploadAllowed = 1;\n");
  26. my $port = random_port();
  27. my $pid = fork();
  28. END {
  29. # kill server
  30. if ($pid) {
  31. kill 'KILL', $pid or warn "Could not kill server $pid";
  32. }
  33. }
  34. our ($DataDir);
  35. if (!defined $pid) {
  36. die "Cannot fork: $!";
  37. } elsif ($pid == 0) {
  38. use Config;
  39. my $secure_perl_path = $Config{perlpath};
  40. exec($secure_perl_path,
  41. "stuff/gopher-server.pl",
  42. "--port=$port",
  43. "--log_level=0", # set to 4 for verbose logging
  44. "--wiki=./wiki.pl",
  45. "--wiki_dir=$DataDir",
  46. "--wiki_pages=Alex",
  47. "--wiki_pages=Berta",
  48. "--wiki_pages=Chris")
  49. or die "Cannot exec: $!";
  50. }
  51. update_page('Alex', "My best friend is [[Berta]].\n\nTags: [[tag:Friends]]\n");
  52. update_page('Berta', "This is me.\n\nTags: [[tag:Friends]]\n");
  53. update_page('Chris', "I'm Chris.\n\nTags: [[tag:Friends]]\n");
  54. update_page('Friends', "Some friends.\n");
  55. update_page('2017-12-25', 'It was a Monday.\n\nTags: [[tag:Day]]');
  56. update_page('2017-12-26', 'It was a Tuesday.\n\nTags: [[tag:Day]]');
  57. update_page('2017-12-27', 'It was a Wednesday.\n\nTags: [[tag:Day]]');
  58. update_page('Friends', "News about friends.\n", 'rewrite', 1); # minor change
  59. update_page('Friends', "News about friends:\n\n<journal search tag:friends>\n",
  60. 'add journal tag', 1); # minor change
  61. # file created using convert NULL: test.png && base64 test.png
  62. update_page('Picture',
  63. "#FILE image/png\niVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQAAAAA3bv"
  64. . "kkAAAACklEQVQI12NoAAAAggCB3UNq9AAAAABJRU5ErkJggg==");
  65. sub query_gopher {
  66. my $query = shift;
  67. my $text = shift;
  68. # create client
  69. my $socket = IO::Socket::IP->new(
  70. PeerHost => "localhost",
  71. PeerPort => $port,
  72. Type => SOCK_STREAM, )
  73. or die "Cannot construct client socket: $@";
  74. $socket->print("$query\r\n");
  75. $socket->print($text);
  76. undef $/; # slurp
  77. return <$socket>;
  78. }
  79. # main menu
  80. my $page = query_gopher("");
  81. for my $item(qw(Alex Berta Chris 2017-12-25 2017-12-26 2017-12-27)) {
  82. like($page, qr/^1$item\t$item\/menu\t/m, "main menu contains $item");
  83. }
  84. # page menu
  85. $page = query_gopher("Alex/menu");
  86. like($page, qr/^0Alex\tAlex\t/m,
  87. "Alex menu links to plain text");
  88. like($page, qr/^hAlex\tAlex\/html\t/m,
  89. "Alex menu links to HTML");
  90. like($page, qr/^1Page History\tAlex\/history\t/m,
  91. "Alex menu links to page history");
  92. like($page, qr/^1Berta\tBerta\/menu\t/m,
  93. "Alex menu links to Berta menu");
  94. like($page, qr/^1Friends\tFriends\/tag\t/m,
  95. "Alex menu links to Friends tag");
  96. # plain text
  97. $page = query_gopher("Alex");
  98. like($page, qr/^My best friend is \[\[Berta\]\]/, "Alex plain text");
  99. # HTML
  100. $page = query_gopher("Alex/html");
  101. like($page, qr/<p>My best friend is <a.*?>Berta<\/a>/, "Alex HTML");
  102. # tags
  103. $page = query_gopher("Friends/tag");
  104. like($page, qr/iThis page is about the tag Friends/, "tag menu intro");
  105. for my $item(qw(Friends Alex Berta Chris)) {
  106. like($page, qr/^1$item\t$item\/menu\t/m, "tag menu contains $item");
  107. }
  108. # tags
  109. $page = query_gopher("Day/tag");
  110. like($page, qr/2017-12-27.*2017-12-26.*2017-12-25/s,
  111. "tag menu sorted newest first");
  112. # match
  113. $page = query_gopher("do/match\t2017");
  114. for my $item(qw(2017-12-25 2017-12-26 2017-12-27)) {
  115. like($page, qr/^1$item\t$item\/menu\t/m, "match menu contains $item");
  116. }
  117. like($page, qr/2017-12-27.*2017-12-26.*2017-12-25/s,
  118. "match menu sorted newest first");
  119. # search
  120. $page = query_gopher("do/search\ttag:day");
  121. for my $item(qw(2017-12-25 2017-12-26 2017-12-27)) {
  122. like($page, qr/^1$item\t$item\/menu\t/m, "serch menu contains $item");
  123. }
  124. like($page, qr/2017-12-27.*2017-12-26.*2017-12-25/s,
  125. "search menu sorted newest first");
  126. # rc
  127. $page = query_gopher("do/rc");
  128. my $re = join(".*", "Picture", "2017-12-27", "2017-12-26", "2017-12-25",
  129. "Friends", "Chris", "Berta", "Alex");
  130. like($page, qr/$re/s, "rc in the right order");
  131. $page = query_gopher("do/rc/showedits");
  132. $re = join(".*", "Friends", "2017-12-27", "2017-12-26", "2017-12-25");
  133. like($page, qr/$re/s, "rc in the right order");
  134. # history
  135. $page = query_gopher("Friends/history");
  136. like($page, qr/^1Friends \(1\)\tFriends\/1\/menu\t/m,
  137. "Friends (1)");
  138. like($page, qr/^1Friends \(2\)\tFriends\/2\/menu\t/m,
  139. "Friends (2)");
  140. like($page, qr/^1Friends \(current\)\tFriends\/menu\t/m,
  141. "Friends (current)");
  142. like($page, qr/Friends\/menu.*Friends\/2\/menu.*Friends\/1\/menu/s,
  143. "history in the right order");
  144. # revision menu
  145. $page = query_gopher("Friends/1/menu");
  146. like($page, qr/^0Friends\tFriends\/1\t/m,
  147. "Friends/1 menu links to plain text");
  148. like($page, qr/^hFriends\tFriends\/1\/html\t/m,
  149. "Friends/1 menu links to HTML");
  150. unlike($page, qr/Search result for tag/,
  151. "Friends/1 has no journal and thus no tag search");
  152. # revision plain text
  153. $page = query_gopher("Friends/1");
  154. like($page, qr/^Some friends/m, "Friends/1 plain text");
  155. # revision html
  156. $page = query_gopher("Friends/1/html");
  157. like($page, qr/<p>Some friends/m, "Friends/1 html");
  158. # upload text
  159. my $haiku = <<EOT;
  160. Quiet disk ratling
  161. Keyboard clicking, then it stops.
  162. Rain falls and I think
  163. .
  164. EOT
  165. $page = query_gopher("Haiku/write/text", "$haiku");
  166. like($page, qr/^iPage was saved./m, "Write Haiku");
  167. like($page, qr/^1Haiku\tHaiku\/menu/m, "Link back to Haiku");
  168. my $haiku_re = quotemeta(substr($haiku, 0, -2)); # strip period and \n
  169. $page = query_gopher("Haiku");
  170. like($page, qr/^$haiku_re/, "Haiku saved");
  171. $haiku = <<"EOT";
  172. ```
  173. username: Alex
  174. minor: 1
  175. summary: typos
  176. ```
  177. Quiet disk rattling
  178. Keyboard clicking, then it stops.
  179. Rain falls and I think.
  180. .
  181. EOT
  182. $page = query_gopher("Haiku/write/text", "$haiku");
  183. like($page, qr/^iPage was saved./m, "Write haiku");
  184. $haiku_re = quotemeta(<<"EOT");
  185. Quiet disk rattling
  186. Keyboard clicking, then it stops.
  187. Rain falls and I think.
  188. EOT
  189. $page = query_gopher("Haiku");
  190. like($page, qr/^$haiku_re/, "Haiku updated");
  191. $page = query_gopher("Haiku/history");
  192. like($page, qr/^1Haiku \(current\)\tHaiku\/menu\t/m, "Haiku (current)");
  193. like($page, qr/^i\d\d:\d\d UTC by Alex: typos \(minor\)/m,
  194. "Metadata recorded");
  195. like($page, qr/^1Haiku \(1\)\tHaiku\/1\/menu\t/m, "Haiku (1)");
  196. # new page
  197. $page = query_gopher("do/new", <<"EOT");
  198. ```
  199. username: Alex
  200. summary: copy
  201. title: Haiku_Copy
  202. ```
  203. Quiet disk rattling
  204. Keyboard clicking, then it stops.
  205. Rain falls and I think.
  206. .
  207. EOT
  208. like($page, qr/^iPage was saved./m, "Write copy of haiku");
  209. $page = query_gopher("Haiku_Copy");
  210. like($page, qr/^$haiku_re/, "New copy of haiku created");
  211. # append
  212. $page = query_gopher("Haiku_Copy/append/text", "This is a comment by me!\n.\n");
  213. like($page, qr/^iPage was saved./m, "Append to copy of haiku");
  214. $page = query_gopher("Haiku_Copy");
  215. like($page, qr/^$haiku_re/, "Copy of haiku still there");
  216. like($page, qr/\n\n----\n\nThis is a comment by me!\n\n-- Anonymous/,
  217. "Comment is also there");
  218. # Image download
  219. my $image = query_gopher("Picture");
  220. like($image, qr/\211PNG\r\n/, "Image download");
  221. # Image upload
  222. $page = query_gopher("PictureCopy/write/file\t" . length($image), "$image");
  223. like($page, qr/Files of type application\/octet-stream are not allowed/m,
  224. "MIME type check");
  225. $page = query_gopher("PictureCopy/image/png/write/file\t" . length($image), "$image");
  226. like($page, qr/^iPage was saved./m, "Image upload");
  227. unlike($page, qr/^3Page was not saved/, "Messages are correct");
  228. my $copy = query_gopher("PictureCopy");
  229. like($copy, qr/\211PNG\r\n/, "Image copy download");
  230. is($copy, $image, "Image and copy are identical");
  231. # image:link
  232. $page = query_gopher("Test/write/text", "[[image:Picture]]\n.\n");
  233. like($page, qr/^iPage was saved./m, "Saved test page containing image link");
  234. $page = query_gopher("Test/menu");
  235. like($page, qr/^1Picture\tPicture\/menu/m, "Link to image page looks good");
  236. $page = query_gopher("Picture/menu");
  237. like($page, qr/^IPicture\tPicture/, "Link to image file looks good");
  238. # Test upload of large page (but note $MaxPost: 1024 * 210 > (10 * 8 + 1) * 2600)
  239. my $garbage = (("0123456789" x 8) . "\n") x 2600 . "Last Line\n";
  240. $page = query_gopher("Large/write/text", "$garbage.\n");
  241. like($page, qr/^iPage was saved./m, "Write page with "
  242. . length($garbage) . " bytes");
  243. $page = query_gopher("Large");
  244. like(substr($page, -20), qr/Last Line/, "All of large page was saved");
  245. # Test of Umlauts in the selector
  246. test_page(update_page('Zürich♥', '[[Üetliberg♥]]'), 'Zürich♥', 'Üetliberg♥');
  247. $page = query_gopher("Z%c3%bcrich%e2%99%a5");
  248. utf8::decode($page);
  249. like($page, qr/Üetliberg♥/, "UTF-8 encoded page names");
  250. $page = query_gopher("Z%c3%bcrich%e2%99%a5/menu");
  251. utf8::decode($page);
  252. like($page, qr/^0Zürich♥\tZ%c3%bcrich%e2%99%a5\t/m, "UTF-8 encoded text link");
  253. like($page, qr/^1Üetliberg♥\t%c3%9cetliberg%e2%99%a5\/menu\t/m,
  254. "UTF-8 encoded links");
  255. # gopher links
  256. update_page('Gopher', '[http://gopher.floodgap.com/gopher/gw?a=gopher%3A%2F%2Fsdf.org%3A70%2F0%2Fusers%2Fsolderpunk%2Fphlog%2Fintroducing-vf1.txt VF-1], [gopher://sdf.org:70/1/phlogs/ Phlogs]');
  257. $page = query_gopher("Gopher/menu");
  258. like($page, qr/^1Phlogs\t\/phlogs\/\tsdf\.org\t70/m, "Direct Gopher link");
  259. like($page, qr/^0VF-1\t\/users\/solderpunk\/phlog\/introducing-vf1.txt\tsdf\.org\t70/m, "Floodgap proxy link");
  260. done_testing();