]> git.vanrenterghem.biz Git - git.ikiwiki.info.git/blob - t/git-cgi.t
git-cgi.t: Add a failing test for a blog with a non-ASCII rootpage
[git.ikiwiki.info.git] / t / git-cgi.t
1 #!/usr/bin/perl
2 use warnings;
3 use strict;
5 use Test::More;
7 BEGIN {
8         my $git = `which git`;
9         chomp $git;
10         plan(skip_all => 'git not available') unless -x $git;
12         plan(skip_all => "CGI not available")
13                 unless eval q{
14                         use CGI qw();
15                         1;
16                 };
18         plan(skip_all => "IPC::Run not available")
19                 unless eval q{
20                         use IPC::Run qw(run);
21                         1;
22                 };
24         use_ok('IkiWiki');
25         use_ok('YAML::XS');
26 }
28 # We check for English error messages
29 $ENV{LC_ALL} = 'C';
31 use Cwd qw(getcwd);
32 use Errno qw(ENOENT);
34 my $installed = $ENV{INSTALLED_TESTS};
36 my @command;
37 if ($installed) {
38         @command = qw(ikiwiki --plugin inline);
39 }
40 else {
41         ok(! system("make -s ikiwiki.out"));
42         @command = ("perl", "-I".getcwd."/blib/lib", './ikiwiki.out',
43                 '--underlaydir='.getcwd.'/underlays/basewiki',
44                 '--set', 'underlaydirbase='.getcwd.'/underlays',
45                 '--templatedir='.getcwd.'/templates');
46 }
48 sub write_old_file {
49         my $name = shift;
50         my $dir = shift;
51         my $content = shift;
52         writefile($name, $dir, $content);
53         ok(utime(333333333, 333333333, "$dir/$name"));
54 }
56 sub write_setup_file {
57         my %setup = (
58                 wikiname => 'this is the name of my wiki',
59                 srcdir => getcwd.'/t/tmp/in/doc',
60                 destdir => getcwd.'/t/tmp/out',
61                 url => 'http://example.com',
62                 cgiurl => 'http://example.com/cgi-bin/ikiwiki.cgi',
63                 cgi_wrapper => getcwd.'/t/tmp/ikiwiki.cgi',
64                 cgi_wrappermode => '0751',
65                 add_plugins => [qw(anonok attachment lockedit recentchanges)],
66                 disable_plugins => [qw(emailauth openid passwordauth)],
67                 anonok_pagespec => 'writable/*',
68                 locked_pages => '!writable/*',
69                 rcs => 'git',
70                 git_wrapper => getcwd.'/t/tmp/in/.git/hooks/post-commit',
71                 git_wrappermode => '0754',
72                 gitorigin_branch => '',
73         );
74         unless ($installed) {
75                 $setup{ENV} = { 'PERL5LIB' => getcwd.'/blib/lib' };
76         }
77         writefile("test.setup", "t/tmp",
78                 "# IkiWiki::Setup::Yaml - YAML formatted setup file\n" .
79                 Dump(\%setup));
80 }
82 sub thoroughly_rebuild {
83         ok(unlink("t/tmp/ikiwiki.cgi") || $!{ENOENT});
84         ok(unlink("t/tmp/in/.git/hooks/post-commit") || $!{ENOENT});
85         ok(! system(@command, qw(--setup t/tmp/test.setup --rebuild --wrappers)));
86 }
88 sub check_cgi_mode_bits {
89         my $mode;
91         (undef, undef, $mode, undef, undef,
92                 undef, undef, undef, undef, undef,
93                 undef, undef, undef) = stat('t/tmp/ikiwiki.cgi');
94         is ($mode & 07777, 0751);
95         (undef, undef, $mode, undef, undef,
96                 undef, undef, undef, undef, undef,
97                 undef, undef, undef) = stat('t/tmp/in/.git/hooks/post-commit');
98         is ($mode & 07777, 0754);
99 }
101 sub run_cgi {
102         my (%args) = @_;
103         my ($in, $out);
104         my $method = $args{method} || 'GET';
105         my $environ = $args{environ} || {};
106         my $params = $args{params} || { do => 'prefs' };
108         my %defaults = (
109                 SCRIPT_NAME     => '/cgi-bin/ikiwiki.cgi',
110                 HTTP_HOST       => 'example.com',
111         );
113         my $cgi = CGI->new($args{params});
114         my $query_string = $cgi->query_string();
115         diag $query_string;
117         if ($method eq 'POST') {
118                 $defaults{REQUEST_METHOD} = 'POST';
119                 $in = $query_string;
120                 $defaults{CONTENT_LENGTH} = length $in;
121         } else {
122                 $defaults{REQUEST_METHOD} = 'GET';
123                 $defaults{QUERY_STRING} = $query_string;
124         }
126         my %envvars = (
127                 %defaults,
128                 %$environ,
129         );
130         run(["./t/tmp/ikiwiki.cgi"], \$in, \$out, init => sub {
131                 map {
132                         $ENV{$_} = $envvars{$_}
133                 } keys(%envvars);
134         });
136         return $out;
139 sub run_git {
140         my (undef, $filename, $line) = caller;
141         my $args = shift;
142         my $desc = shift || join(' ', 'git', @$args);
143         my ($in, $out);
144         ok(run(['git', @$args], \$in, \$out, init => sub {
145                 chdir 't/tmp/in' or die $!;
146                 my $name = 'The IkiWiki Tests';
147                 my $email = 'nobody@ikiwiki-tests.invalid';
148                 if ($args->[0] eq 'commit') {
149                         $ENV{GIT_AUTHOR_NAME} = $ENV{GIT_COMMITTER_NAME} = $name;
150                         $ENV{GIT_AUTHOR_EMAIL} = $ENV{GIT_COMMITTER_EMAIL} = $email;
151                 }
152         }), "$desc at $filename:$line");
153         return $out;
156 sub test {
157         my $content;
158         my $status;
160         ok(! system(qw(rm -rf t/tmp)));
161         ok(! system(qw(mkdir t/tmp)));
163         write_old_file('.gitignore', 't/tmp/in', "/doc/.ikiwiki/\n");
164         write_old_file('doc/writable/one.mdwn', 't/tmp/in', 'This is the first test page');
165         write_old_file('doc/writable/two.mdwn', 't/tmp/in', 'This is the second test page');
166         write_old_file('doc/writable/three.mdwn', 't/tmp/in', 'This is the third test page');
167         write_old_file('doc/writable/three.bin', 't/tmp/in', 'An attachment');
168         write_old_file('doc/writable/blog.mdwn', 't/tmp/in',
169                 '[[!inline pages="writable/blog/*" actions=yes rootpage=writable/blog postform=yes show=0]]');
170         write_old_file('doc/writable/__172__blog.mdwn', 't/tmp/in',
171                 '[[!inline pages="writable/¬blog/*" actions=yes rootpage="writable/¬blog" postform=yes show=0]]');
173         unless ($installed) {
174                 ok(! system(qw(cp -pRL doc/wikiicons t/tmp/in/doc/)));
175                 ok(! system(qw(cp -pRL doc/recentchanges.mdwn t/tmp/in/doc/)));
176         }
178         run_git(['init']);
179         run_git(['add', '.']);
180         run_git(['commit', '-m', 'Initial commit']);
182         write_setup_file();
183         thoroughly_rebuild();
184         check_cgi_mode_bits();
186         ok(-e 't/tmp/out/writable/one/index.html');
187         $content = readfile('t/tmp/out/writable/one/index.html');
188         like($content, qr{This is the first test page});
189         my $orig_sha1 = run_git(['rev-list', '--max-count=1', 'HEAD']);
191         # We have to wait 1 second here so that new writes are guaranteed
192         # to have a strictly larger mtime.
193         sleep 1;
195         # Test the git hook, which accepts git commits
196         writefile('doc/writable/one.mdwn', 't/tmp/in',
197                 'This is new content for the first test page');
198         run_git(['add', '.']);
199         run_git(['commit', '-m', 'Git commit']);
200         my $first_revertable_sha1 = run_git(['rev-list', '--max-count=1', 'HEAD']);
201         isnt($orig_sha1, $first_revertable_sha1);
203         ok(-e 't/tmp/out/writable/one/index.html');
204         $content = readfile('t/tmp/out/writable/one/index.html');
205         like($content, qr{This is new content for the first test page});
207         # Test a web commit
208         $content = run_cgi(method => 'POST',
209                 params => {
210                         do => 'edit',
211                         page => 'writable/two',
212                         type => 'mdwn',
213                         editmessage => 'Web commit',
214                         editcontent => 'Here is new content for the second page',
215                         _submit => 'Save Page',
216                         _submitted => '1',
217                 },
218         );
219         like($content, qr{^Status:\s*302\s}m);
220         like($content, qr{^Location:\s*http://example\.com/writable/two/\?updated}m);
221         my $second_revertable_sha1 = run_git(['rev-list', '--max-count=1', 'HEAD']);
222         isnt($orig_sha1, $second_revertable_sha1);
223         isnt($first_revertable_sha1, $second_revertable_sha1);
225         ok(-e 't/tmp/out/writable/two/index.html');
226         $content = readfile('t/tmp/out/writable/two/index.html');
227         like($content, qr{Here is new content for the second page});
229         # Another edit
230         writefile('doc/writable/three.mdwn', 't/tmp/in',
231                 'Also new content for the third page');
232         unlink('t/tmp/in/doc/writable/three.bin');
233         writefile('doc/writable/three.bin', 't/tmp/in',
234                 'Changed attachment');
235         run_git(['add', '.']);
236         run_git(['commit', '-m', 'Git commit']);
237         ok(-e 't/tmp/out/writable/three/index.html');
238         $content = readfile('t/tmp/out/writable/three/index.html');
239         like($content, qr{Also new content for the third page});
240         $content = readfile('t/tmp/out/writable/three.bin');
241         like($content, qr{Changed attachment});
242         my $third_revertable_sha1 = run_git(['rev-list', '--max-count=1', 'HEAD']);
243         isnt($orig_sha1, $third_revertable_sha1);
244         isnt($second_revertable_sha1, $third_revertable_sha1);
246         run_git(['mv', 'doc/writable/one.mdwn', 'doc/one.mdwn']);
247         run_git(['mv', 'doc/writable/two.mdwn', 'two.mdwn']);
248         run_git(['commit', '-m', 'Rename files to test CVE-2016-10026']);
249         ok(! -e 't/tmp/out/writable/two/index.html');
250         ok(! -e 't/tmp/out/writable/one/index.html');
251         ok(-e 't/tmp/out/one/index.html');
252         my $sha1_before_revert = run_git(['rev-list', '--max-count=1', 'HEAD']);
253         isnt($sha1_before_revert, $third_revertable_sha1);
255         $content = run_cgi(method => 'post',
256                 params => {
257                         do => 'revert',
258                         revertmessage => 'CVE-2016-10026',
259                         rev => $first_revertable_sha1,
260                         _submit => 'Revert',
261                         _submitted_revert => '1',
262                 },
263         );
264         like($content, qr{is locked and cannot be edited});
265         # The tree is left clean
266         run_git(['diff', '--exit-code']);
267         run_git(['diff', '--cached', '--exit-code']);
268         my $sha1 = run_git(['rev-list', '--max-count=1', 'HEAD']);
269         is($sha1, $sha1_before_revert);
271         ok(-e 't/tmp/out/one/index.html');
272         ok(! -e 't/tmp/in/doc/writable/one.mdwn');
273         ok(-e 't/tmp/in/doc/one.mdwn');
274         $content = readfile('t/tmp/out/one/index.html');
275         like($content, qr{This is new content for the first test page});
277         $content = run_cgi(method => 'post',
278                 params => {
279                         do => 'revert',
280                         revertmessage => 'CVE-2016-10026',
281                         rev => $second_revertable_sha1,
282                         _submit => 'Revert',
283                         _submitted_revert => '1',
284                 },
285         );
286         like($content, qr{you are not allowed to change two\.mdwn});
287         run_git(['diff', '--exit-code']);
288         run_git(['diff', '--cached', '--exit-code']);
289         $sha1 = run_git(['rev-list', '--max-count=1', 'HEAD']);
290         is($sha1, $sha1_before_revert);
292         ok(! -e 't/tmp/out/writable/two/index.html');
293         ok(! -e 't/tmp/out/two/index.html');
294         ok(! -e 't/tmp/in/doc/writable/two.mdwn');
295         ok(-e 't/tmp/in/two.mdwn');
296         $content = readfile('t/tmp/in/two.mdwn');
297         like($content, qr{Here is new content for the second page});
299         # We have to wait 1 second here so that new writes are guaranteed
300         # to have a strictly larger mtime.
301         sleep 1;
303         # This one can legitimately be reverted
304         $content = run_cgi(method => 'post',
305                 params => {
306                         do => 'revert',
307                         revertmessage => 'not CVE-2016-10026',
308                         rev => $third_revertable_sha1,
309                         _submit => 'Revert',
310                         _submitted_revert => '1',
311                 },
312         );
313         like($content, qr{^Status:\s*302\s}m);
314         like($content, qr{^Location:\s*http://example\.com/recentchanges/}m);
315         run_git(['diff', '--exit-code']);
316         run_git(['diff', '--cached', '--exit-code']);
317         ok(-e 't/tmp/out/writable/three/index.html');
318         $content = readfile('t/tmp/out/writable/three/index.html');
319         like($content, qr{This is the third test page});
320         $content = readfile('t/tmp/out/writable/three.bin');
321         like($content, qr{An attachment});
323         $content = readfile('t/tmp/out/writable/blog/index.html');
324         like($content, qr{<input type="hidden" name="from" value="writable/blog"});
325         $content = run_cgi(method => 'get',
326                 params => {
327                         do => 'blog',
328                         from => 'writable/blog',
329                         subpage => '1',
330                         title => 'hello',
331                 },
332         );
333         like($content, qr{<option selected="selected" value="writable/blog/hello">writable/blog/hello</option>});
335         # This attempts to reproduce the bug from
336         # doc/bugs/About___37__2F_problem, in which you can't make new posts
337         # to a blog with a non-ASCII rootpage.
338         $content = readfile('t/tmp/out/writable/__172__blog/index.html');
339         like($content, qr{<input type="hidden" name="from" value="writable/¬blog"});
340         TODO: {
341         local $TODO = 'doc/bugs/About___37__2F_problem';
342         $content = run_cgi(method => 'get',
343                 params => {
344                         do => 'blog',
345                         from => 'writable/¬blog',
346                         subpage => '1',
347                         title => 'hello',
348                 },
349         );
350         like($content, qr{<option selected="selected" value="writable/¬blog/hello">writable/¬blog/hello</option>});
351         unlike($content, qr{Error: bad page name});
352         }
355 test();
357 done_testing();