10 plan(skip_all => 'git not available') unless -x $git;
12 plan(skip_all => "CGI not available")
18 plan(skip_all => "IPC::Run not available")
28 # We check for English error messages
34 my $installed = $ENV{INSTALLED_TESTS};
38 @command = qw(ikiwiki --plugin inline);
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');
52 writefile($name, $dir, $content);
53 ok(utime(333333333, 333333333, "$dir/$name"));
56 sub write_setup_file {
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/*',
70 git_wrapper => getcwd.'/t/tmp/in/.git/hooks/post-commit',
71 git_wrappermode => '0754',
72 gitorigin_branch => '',
75 $setup{ENV} = { 'PERL5LIB' => getcwd.'/blib/lib' };
77 writefile("test.setup", "t/tmp",
78 "# IkiWiki::Setup::Yaml - YAML formatted setup file\n" .
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)));
88 sub check_cgi_mode_bits {
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);
104 my $method = $args{method} || 'GET';
105 my $environ = $args{environ} || {};
106 my $params = $args{params} || { do => 'prefs' };
109 SCRIPT_NAME => '/cgi-bin/ikiwiki.cgi',
110 HTTP_HOST => 'example.com',
113 my $cgi = CGI->new($args{params});
114 my $query_string = $cgi->query_string();
117 if ($method eq 'POST') {
118 $defaults{REQUEST_METHOD} = 'POST';
120 $defaults{CONTENT_LENGTH} = length $in;
122 $defaults{REQUEST_METHOD} = 'GET';
123 $defaults{QUERY_STRING} = $query_string;
130 run(["./t/tmp/ikiwiki.cgi"], \$in, \$out, init => sub {
132 $ENV{$_} = $envvars{$_}
140 my (undef, $filename, $line) = caller;
142 my $desc = shift || join(' ', 'git', @$args);
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;
152 }), "$desc at $filename:$line");
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]]');
171 unless ($installed) {
172 ok(! system(qw(cp -pRL doc/wikiicons t/tmp/in/doc/)));
173 ok(! system(qw(cp -pRL doc/recentchanges.mdwn t/tmp/in/doc/)));
177 run_git(['add', '.']);
178 run_git(['commit', '-m', 'Initial commit']);
181 thoroughly_rebuild();
182 check_cgi_mode_bits();
184 ok(-e 't/tmp/out/writable/one/index.html');
185 $content = readfile('t/tmp/out/writable/one/index.html');
186 like($content, qr{This is the first test page});
187 my $orig_sha1 = run_git(['rev-list', '--max-count=1', 'HEAD']);
189 # We have to wait 1 second here so that new writes are guaranteed
190 # to have a strictly larger mtime.
193 # Test the git hook, which accepts git commits
194 writefile('doc/writable/one.mdwn', 't/tmp/in',
195 'This is new content for the first test page');
196 run_git(['add', '.']);
197 run_git(['commit', '-m', 'Git commit']);
198 my $first_revertable_sha1 = run_git(['rev-list', '--max-count=1', 'HEAD']);
199 isnt($orig_sha1, $first_revertable_sha1);
201 ok(-e 't/tmp/out/writable/one/index.html');
202 $content = readfile('t/tmp/out/writable/one/index.html');
203 like($content, qr{This is new content for the first test page});
206 $content = run_cgi(method => 'POST',
209 page => 'writable/two',
211 editmessage => 'Web commit',
212 editcontent => 'Here is new content for the second page',
213 _submit => 'Save Page',
217 like($content, qr{^Status:\s*302\s}m);
218 like($content, qr{^Location:\s*http://example\.com/writable/two/\?updated}m);
219 my $second_revertable_sha1 = run_git(['rev-list', '--max-count=1', 'HEAD']);
220 isnt($orig_sha1, $second_revertable_sha1);
221 isnt($first_revertable_sha1, $second_revertable_sha1);
223 ok(-e 't/tmp/out/writable/two/index.html');
224 $content = readfile('t/tmp/out/writable/two/index.html');
225 like($content, qr{Here is new content for the second page});
228 writefile('doc/writable/three.mdwn', 't/tmp/in',
229 'Also new content for the third page');
230 unlink('t/tmp/in/doc/writable/three.bin');
231 writefile('doc/writable/three.bin', 't/tmp/in',
232 'Changed attachment');
233 run_git(['add', '.']);
234 run_git(['commit', '-m', 'Git commit']);
235 ok(-e 't/tmp/out/writable/three/index.html');
236 $content = readfile('t/tmp/out/writable/three/index.html');
237 like($content, qr{Also new content for the third page});
238 $content = readfile('t/tmp/out/writable/three.bin');
239 like($content, qr{Changed attachment});
240 my $third_revertable_sha1 = run_git(['rev-list', '--max-count=1', 'HEAD']);
241 isnt($orig_sha1, $third_revertable_sha1);
242 isnt($second_revertable_sha1, $third_revertable_sha1);
244 run_git(['mv', 'doc/writable/one.mdwn', 'doc/one.mdwn']);
245 run_git(['mv', 'doc/writable/two.mdwn', 'two.mdwn']);
246 run_git(['commit', '-m', 'Rename files to test CVE-2016-10026']);
247 ok(! -e 't/tmp/out/writable/two/index.html');
248 ok(! -e 't/tmp/out/writable/one/index.html');
249 ok(-e 't/tmp/out/one/index.html');
250 my $sha1_before_revert = run_git(['rev-list', '--max-count=1', 'HEAD']);
251 isnt($sha1_before_revert, $third_revertable_sha1);
253 $content = run_cgi(method => 'post',
256 revertmessage => 'CVE-2016-10026',
257 rev => $first_revertable_sha1,
259 _submitted_revert => '1',
262 like($content, qr{is locked and cannot be edited});
263 # The tree is left clean
264 run_git(['diff', '--exit-code']);
265 run_git(['diff', '--cached', '--exit-code']);
266 my $sha1 = run_git(['rev-list', '--max-count=1', 'HEAD']);
267 is($sha1, $sha1_before_revert);
269 ok(-e 't/tmp/out/one/index.html');
270 ok(! -e 't/tmp/in/doc/writable/one.mdwn');
271 ok(-e 't/tmp/in/doc/one.mdwn');
272 $content = readfile('t/tmp/out/one/index.html');
273 like($content, qr{This is new content for the first test page});
275 $content = run_cgi(method => 'post',
278 revertmessage => 'CVE-2016-10026',
279 rev => $second_revertable_sha1,
281 _submitted_revert => '1',
284 like($content, qr{you are not allowed to change two\.mdwn});
285 run_git(['diff', '--exit-code']);
286 run_git(['diff', '--cached', '--exit-code']);
287 $sha1 = run_git(['rev-list', '--max-count=1', 'HEAD']);
288 is($sha1, $sha1_before_revert);
290 ok(! -e 't/tmp/out/writable/two/index.html');
291 ok(! -e 't/tmp/out/two/index.html');
292 ok(! -e 't/tmp/in/doc/writable/two.mdwn');
293 ok(-e 't/tmp/in/two.mdwn');
294 $content = readfile('t/tmp/in/two.mdwn');
295 like($content, qr{Here is new content for the second page});
297 # We have to wait 1 second here so that new writes are guaranteed
298 # to have a strictly larger mtime.
301 # This one can legitimately be reverted
302 $content = run_cgi(method => 'post',
305 revertmessage => 'not CVE-2016-10026',
306 rev => $third_revertable_sha1,
308 _submitted_revert => '1',
311 like($content, qr{^Status:\s*302\s}m);
312 like($content, qr{^Location:\s*http://example\.com/recentchanges/}m);
313 run_git(['diff', '--exit-code']);
314 run_git(['diff', '--cached', '--exit-code']);
315 ok(-e 't/tmp/out/writable/three/index.html');
316 $content = readfile('t/tmp/out/writable/three/index.html');
317 like($content, qr{This is the third test page});
318 $content = readfile('t/tmp/out/writable/three.bin');
319 like($content, qr{An attachment});
321 $content = readfile('t/tmp/out/writable/blog/index.html');
322 like($content, qr{<input type="hidden" name="from" value="writable/blog"});
323 $content = run_cgi(method => 'get',
326 from => 'writable/blog',
331 like($content, qr{<option selected="selected" value="writable/blog/hello">writable/blog/hello</option>});