]> Sergey Matveev's repositories - public-inbox.git/blob - t/v1reindex.t
solver: check one git coderepo and inbox at a time
[public-inbox.git] / t / v1reindex.t
1 # Copyright (C) 2018-2020 all contributors <meta@public-inbox.org>
2 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
3 use strict;
4 use warnings;
5 use Test::More;
6 use PublicInbox::ContentHash qw(content_digest);
7 use File::Path qw(remove_tree);
8 use PublicInbox::TestCommon;
9 use PublicInbox::Eml;
10 require_git(2.6);
11 require_mods(qw(DBD::SQLite Search::Xapian));
12 use_ok 'PublicInbox::SearchIdx';
13 use_ok 'PublicInbox::Import';
14 use_ok 'PublicInbox::OverIdx';
15 my ($inboxdir, $for_destroy) = tmpdir();
16 my $ibx_config = {
17         inboxdir => $inboxdir,
18         name => 'test-v1reindex',
19         -primary_address => 'test@example.com',
20         indexlevel => 'full',
21 };
22 my $mime = PublicInbox::Eml->new(<<'EOF');
23 From: a@example.com
24 To: test@example.com
25 Subject: this is a subject
26 Date: Fri, 02 Oct 1993 00:00:00 +0000
27
28 hello world
29 EOF
30 my $minmax;
31 my $msgmap;
32 my ($mark1, $mark2, $mark3, $mark4);
33 {
34         my %config = %$ibx_config;
35         my $ibx = PublicInbox::Inbox->new(\%config);
36         my $im = PublicInbox::Import->new($ibx->git, undef, undef, $ibx);
37         $im->init_bare;
38         foreach my $i (1..10) {
39                 $mime->header_set('Message-Id', "<$i\@example.com>");
40                 ok($im->add($mime), "message $i added");
41                 if ($i == 4) {
42                         $mark1 = $im->get_mark($im->{tip});
43                         $im->remove($mime);
44                         $mark2 = $im->get_mark($im->{tip});
45                 }
46         }
47
48         if ('test remove later') {
49                 $mark3 = $im->get_mark($im->{tip});
50                 $mime->header_set('Message-Id', "<5\@example.com>");
51                 $im->remove($mime);
52                 $mark4 = $im->get_mark($im->{tip});
53         }
54
55         $im->done;
56         my $rw = PublicInbox::SearchIdx->new($ibx, 1);
57         eval { $rw->index_sync() };
58         is($@, '', 'no error from indexing');
59
60         $minmax = [ $ibx->mm->minmax ];
61         ok(defined $minmax->[0] && defined $minmax->[1], 'minmax defined');
62         is_deeply($minmax, [ 1, 10 ], 'minmax as expected');
63         is($ibx->mm->num_highwater, 10, 'num_highwater as expected');
64
65         my ($min, $max) = @$minmax;
66         $msgmap = $ibx->mm->msg_range(\$min, $max);
67         is_deeply($msgmap, [
68                           [1, '1@example.com' ],
69                           [2, '2@example.com' ],
70                           [3, '3@example.com' ],
71                           [6, '6@example.com' ],
72                           [7, '7@example.com' ],
73                           [8, '8@example.com' ],
74                           [9, '9@example.com' ],
75                           [10, '10@example.com' ],
76                   ], 'msgmap as expected');
77 }
78
79 {
80         my %config = %$ibx_config;
81         my $ibx = PublicInbox::Inbox->new(\%config);
82         my $im = PublicInbox::Import->new($ibx->git, undef, undef, $ibx);
83         my $rw = PublicInbox::SearchIdx->new($ibx, 1);
84         eval { $rw->index_sync({reindex => 1}) };
85         is($@, '', 'no error from reindexing');
86         $im->done;
87
88         is($ibx->mm->num_highwater, 10, 'num_highwater as expected');
89
90         my ($min, $max) = $ibx->mm->minmax;
91         is_deeply($ibx->mm->msg_range(\$min, $max), $msgmap, 'msgmap unchanged');
92 }
93
94 my $xap = "$inboxdir/public-inbox/xapian".PublicInbox::Search::SCHEMA_VERSION();
95 remove_tree($xap);
96 ok(!-d $xap, 'Xapian directories removed');
97 {
98         my %config = %$ibx_config;
99         my $ibx = PublicInbox::Inbox->new(\%config);
100         my $im = PublicInbox::Import->new($ibx->git, undef, undef, $ibx);
101         my $rw = PublicInbox::SearchIdx->new($ibx, 1);
102
103         eval { $rw->index_sync({reindex => 1}) };
104         is($@, '', 'no error from reindexing');
105         $im->done;
106         ok(-d $xap, 'Xapian directories recreated');
107
108         delete $ibx->{mm};
109         is_deeply([ $ibx->mm->minmax ], $minmax, 'minmax unchanged');
110         is($ibx->mm->num_highwater, 10, 'num_highwater as expected');
111
112         my ($min, $max) = $ibx->mm->minmax;
113         is_deeply($ibx->mm->msg_range(\$min, $max), $msgmap, 'msgmap unchanged');
114 }
115
116 ok(unlink "$inboxdir/public-inbox/msgmap.sqlite3", 'remove msgmap');
117 remove_tree($xap);
118 ok(!-d $xap, 'Xapian directories removed again');
119 {
120         my @warn;
121         local $SIG{__WARN__} = sub { push @warn, @_ };
122         my %config = %$ibx_config;
123         my $ibx = PublicInbox::Inbox->new(\%config);
124         my $im = PublicInbox::Import->new($ibx->git, undef, undef, $ibx);
125         my $rw = PublicInbox::SearchIdx->new($ibx, 1);
126         eval { $rw->index_sync({reindex => 1}) };
127         is($@, '', 'no error from reindexing without msgmap');
128         is(scalar(@warn), 0, 'no warnings from reindexing');
129         $im->done;
130         ok(-d $xap, 'Xapian directories recreated');
131         delete $ibx->{mm};
132         is_deeply([ $ibx->mm->minmax ], $minmax, 'minmax unchanged');
133         is($ibx->mm->num_highwater, 10, 'num_highwater as expected');
134
135         my ($min, $max) = $ibx->mm->minmax;
136         is_deeply($ibx->mm->msg_range(\$min, $max), $msgmap, 'msgmap unchanged');
137 }
138
139 ok(unlink "$inboxdir/public-inbox/msgmap.sqlite3", 'remove msgmap');
140 remove_tree($xap);
141 ok(!-d $xap, 'Xapian directories removed again');
142 {
143         my @warn;
144         local $SIG{__WARN__} = sub { push @warn, @_ };
145         my %config = %$ibx_config;
146         my $ibx = PublicInbox::Inbox->new(\%config);
147         my $im = PublicInbox::Import->new($ibx->git, undef, undef, $ibx);
148         my $rw = PublicInbox::SearchIdx->new($ibx, 1);
149         eval { $rw->index_sync({reindex => 1}) };
150         is($@, '', 'no error from reindexing without msgmap');
151         is_deeply(\@warn, [], 'no warnings');
152         $im->done;
153         ok(-d $xap, 'Xapian directories recreated');
154         delete $ibx->{mm};
155         is_deeply([ $ibx->mm->minmax ], $minmax, 'minmax unchanged');
156         is($ibx->mm->num_highwater, 10, 'num_highwater as expected');
157
158         my ($min, $max) = @$minmax;
159         is_deeply($ibx->mm->msg_range(\$min, $max), $msgmap, 'msgmap unchanged');
160 }
161
162 ok(unlink "$inboxdir/public-inbox/msgmap.sqlite3", 'remove msgmap');
163 remove_tree($xap);
164 ok(!-d $xap, 'Xapian directories removed again');
165 {
166         my @warn;
167         local $SIG{__WARN__} = sub { push @warn, @_ };
168         my %config = %$ibx_config;
169         $config{indexlevel} = 'medium';
170         my $ibx = PublicInbox::Inbox->new(\%config);
171         my $im = PublicInbox::Import->new($ibx->git, undef, undef, $ibx);
172         my $rw = PublicInbox::SearchIdx->new($ibx, 1);
173         eval { $rw->index_sync({reindex => 1}) };
174         is($@, '', 'no error from reindexing without msgmap');
175         is_deeply(\@warn, [], 'no warnings');
176         $im->done;
177         ok(-d $xap, 'Xapian directories recreated');
178         delete $ibx->{mm};
179         is_deeply([ $ibx->mm->minmax ], $minmax, 'minmax unchanged');
180         is($ibx->mm->num_highwater, 10, 'num_highwater as expected');
181         my $mset = $ibx->search->mset('hello world');
182         isnt($mset->size, 0, 'got Xapian search results');
183
184         my ($min, $max) = $ibx->mm->minmax;
185         is_deeply($ibx->mm->msg_range(\$min, $max), $msgmap, 'msgmap unchanged');
186 }
187
188 ok(unlink "$inboxdir/public-inbox/msgmap.sqlite3", 'remove msgmap');
189 remove_tree($xap);
190 ok(!-d $xap, 'Xapian directories removed again');
191 {
192         my @warn;
193         local $SIG{__WARN__} = sub { push @warn, @_ };
194         my %config = %$ibx_config;
195         $config{indexlevel} = 'basic';
196         my $ibx = PublicInbox::Inbox->new(\%config);
197         my $im = PublicInbox::Import->new($ibx->git, undef, undef, $ibx);
198         my $rw = PublicInbox::SearchIdx->new($ibx, 1);
199         eval { $rw->index_sync({reindex => 1}) };
200         is($@, '', 'no error from reindexing without msgmap');
201         is_deeply(\@warn, [], 'no warnings');
202         $im->done;
203         ok(-d $xap, 'Xapian directories recreated');
204         delete $ibx->{mm};
205         is_deeply([ $ibx->mm->minmax ], $minmax, 'minmax unchanged');
206         is($ibx->mm->num_highwater, 10, 'num_highwater as expected');
207         isnt($ibx->search, 'no search for basic');
208
209         my ($min, $max) = $ibx->mm->minmax;
210         is_deeply($ibx->mm->msg_range(\$min, $max), $msgmap, 'msgmap unchanged');
211 }
212
213 # upgrade existing basic to medium
214 # note: changing indexlevels is not yet supported in v2,
215 # and may not be without more effort
216 # no removals
217 {
218         my @warn;
219         local $SIG{__WARN__} = sub { push @warn, @_ };
220         my %config = %$ibx_config;
221         $config{indexlevel} = 'medium';
222         my $ibx = PublicInbox::Inbox->new(\%config);
223         my $rw = PublicInbox::SearchIdx->new($ibx, 1);
224         eval { $rw->index_sync({reindex => 1}) };
225         is($@, '', 'no error from indexing');
226         is_deeply(\@warn, [], 'no warnings');
227         my $mset = $ibx->search->reopen->mset('hello world');
228         isnt($mset->size, 0, 'search OK after basic -> medium');
229
230         is($ibx->mm->num_highwater, 10, 'num_highwater as expected');
231
232         my ($min, $max) = $ibx->mm->minmax;
233         is_deeply($ibx->mm->msg_range(\$min, $max), $msgmap, 'msgmap unchanged');
234 }
235
236 # An incremental indexing test
237 ok(unlink "$inboxdir/public-inbox/msgmap.sqlite3", 'remove msgmap');
238 remove_tree($xap);
239 ok(!-d $xap, 'Xapian directories removed again');
240 {
241         my @warn;
242         local $SIG{__WARN__} = sub { push @warn, @_ };
243         my %config = %$ibx_config;
244         my $ibx = PublicInbox::Inbox->new(\%config);
245         my $im = PublicInbox::Import->new($ibx->git, undef, undef, $ibx);
246         my $rw = PublicInbox::SearchIdx->new($ibx, 1);
247         # mark1 4 simple additions in the same index_sync
248         eval { $rw->index_sync({ref => $mark1}) };
249         is($@, '', 'no error from reindexing without msgmap');
250         is_deeply(\@warn, [], 'no warnings');
251         $im->done;
252         my ($min, $max) = $ibx->mm->minmax;
253         is($min, 1, 'min as expected');
254         is($max, 4, 'max as expected');
255         is($ibx->mm->num_highwater, 4, 'num_highwater as expected');
256         is_deeply($ibx->mm->msg_range(\$min, $max),
257                   [
258                    [1, '1@example.com' ],
259                    [2, '2@example.com' ],
260                    [3, '3@example.com' ],
261                    [4, '4@example.com' ],
262                   ], 'msgmap as expected' );
263 }
264 {
265         my @warn;
266         local $SIG{__WARN__} = sub { push @warn, @_ };
267         my %config = %$ibx_config;
268         my $ibx = PublicInbox::Inbox->new(\%config);
269         my $im = PublicInbox::Import->new($ibx->git, undef, undef, $ibx);
270         my $rw = PublicInbox::SearchIdx->new($ibx, 1);
271         # mark2 A delete separated form and add in the same index_sync
272         eval { $rw->index_sync({ref => $mark2}) };
273         is($@, '', 'no error from reindexing without msgmap');
274         is_deeply(\@warn, [], 'no warnings');
275         $im->done;
276         my ($min, $max) = $ibx->mm->minmax;
277         is($min, 1, 'min as expected');
278         is($max, 3, 'max as expected');
279         is($ibx->mm->num_highwater, 4, 'num_highwater as expected');
280         is_deeply($ibx->mm->msg_range(\$min, $max),
281                   [
282                    [1, '1@example.com' ],
283                    [2, '2@example.com' ],
284                    [3, '3@example.com' ],
285                   ], 'msgmap as expected' );
286 }
287 {
288         my @warn;
289         local $SIG{__WARN__} = sub { push @warn, @_ };
290         my %config = %$ibx_config;
291         my $ibx = PublicInbox::Inbox->new(\%config);
292         my $im = PublicInbox::Import->new($ibx->git, undef, undef, $ibx);
293         my $rw = PublicInbox::SearchIdx->new($ibx, 1);
294         # mark3 adds following the delete at mark2
295         eval { $rw->index_sync({ref => $mark3}) };
296         is($@, '', 'no error from reindexing without msgmap');
297         is_deeply(\@warn, [], 'no warnings');
298         $im->done;
299         my ($min, $max) = $ibx->mm->minmax;
300         is($min, 1, 'min as expected');
301         is($max, 10, 'max as expected');
302         is($ibx->mm->num_highwater, 10, 'num_highwater as expected');
303         is_deeply($ibx->mm->msg_range(\$min, $max),
304                   [
305                    [1, '1@example.com' ],
306                    [2, '2@example.com' ],
307                    [3, '3@example.com' ],
308                    [5, '5@example.com' ],
309                    [6, '6@example.com' ],
310                    [7, '7@example.com' ],
311                    [8, '8@example.com' ],
312                    [9, '9@example.com' ],
313                    [10, '10@example.com' ],
314                   ], 'msgmap as expected' );
315 }
316 {
317         my @warn;
318         local $SIG{__WARN__} = sub { push @warn, @_ };
319         my %config = %$ibx_config;
320         my $ibx = PublicInbox::Inbox->new(\%config);
321         my $im = PublicInbox::Import->new($ibx->git, undef, undef, $ibx);
322         my $rw = PublicInbox::SearchIdx->new($ibx, 1);
323         # mark4 A delete of an older message
324         eval { $rw->index_sync({ref => $mark4}) };
325         is($@, '', 'no error from reindexing without msgmap');
326         is_deeply(\@warn, [], 'no warnings');
327         $im->done;
328         my ($min, $max) = $ibx->mm->minmax;
329         is($min, 1, 'min as expected');
330         is($max, 10, 'max as expected');
331         is($ibx->mm->num_highwater, 10, 'num_highwater as expected');
332         is_deeply($ibx->mm->msg_range(\$min, $max),
333                   [
334                    [1, '1@example.com' ],
335                    [2, '2@example.com' ],
336                    [3, '3@example.com' ],
337                    [6, '6@example.com' ],
338                    [7, '7@example.com' ],
339                    [8, '8@example.com' ],
340                    [9, '9@example.com' ],
341                    [10, '10@example.com' ],
342                   ], 'msgmap as expected' );
343 }
344
345
346 # Another incremental indexing test
347 ok(unlink "$inboxdir/public-inbox/msgmap.sqlite3", 'remove msgmap');
348 remove_tree($xap);
349 ok(!-d $xap, 'Xapian directories removed again');
350 {
351         my @warn;
352         local $SIG{__WARN__} = sub { push @warn, @_ };
353         my %config = %$ibx_config;
354         my $ibx = PublicInbox::Inbox->new(\%config);
355         my $im = PublicInbox::Import->new($ibx->git, undef, undef, $ibx);
356         my $rw = PublicInbox::SearchIdx->new($ibx, 1);
357         # mark2 an add and it's delete in the same index_sync
358         eval { $rw->index_sync({ref => $mark2}) };
359         is($@, '', 'no error from reindexing without msgmap');
360         is_deeply(\@warn, [], 'no warnings');
361         $im->done;
362         my ($min, $max) = $ibx->mm->minmax;
363         is($min, 1, 'min as expected');
364         is($max, 3, 'max as expected');
365         is($ibx->mm->num_highwater, 4, 'num_highwater as expected');
366         is_deeply($ibx->mm->msg_range(\$min, $max),
367                   [
368                    [1, '1@example.com' ],
369                    [2, '2@example.com' ],
370                    [3, '3@example.com' ],
371                   ], 'msgmap as expected' );
372 }
373 {
374         my @warn;
375         local $SIG{__WARN__} = sub { push @warn, @_ };
376         my %config = %$ibx_config;
377         my $ibx = PublicInbox::Inbox->new(\%config);
378         my $im = PublicInbox::Import->new($ibx->git, undef, undef, $ibx);
379         my $rw = PublicInbox::SearchIdx->new($ibx, 1);
380         # mark3 adds following the delete at mark2
381         eval { $rw->index_sync({ref => $mark3}) };
382         is($@, '', 'no error from reindexing without msgmap');
383         is_deeply(\@warn, [], 'no warnings');
384         $im->done;
385         my ($min, $max) = $ibx->mm->minmax;
386         is($min, 1, 'min as expected');
387         is($max, 10, 'max as expected');
388         is($ibx->mm->num_highwater, 10, 'num_highwater as expected');
389         is_deeply($ibx->mm->msg_range(\$min, $max),
390                   [
391                    [1, '1@example.com' ],
392                    [2, '2@example.com' ],
393                    [3, '3@example.com' ],
394                    [5, '5@example.com' ],
395                    [6, '6@example.com' ],
396                    [7, '7@example.com' ],
397                    [8, '8@example.com' ],
398                    [9, '9@example.com' ],
399                    [10, '10@example.com' ],
400                   ], 'msgmap as expected' );
401 }
402 {
403         my @warn;
404         local $SIG{__WARN__} = sub { push @warn, @_ };
405         my %config = %$ibx_config;
406         my $ibx = PublicInbox::Inbox->new(\%config);
407         my $im = PublicInbox::Import->new($ibx->git, undef, undef, $ibx);
408         my $rw = PublicInbox::SearchIdx->new($ibx, 1);
409         # mark4 A delete of an older message
410         eval { $rw->index_sync({ref => $mark4}) };
411         is($@, '', 'no error from reindexing without msgmap');
412         is_deeply(\@warn, [], 'no warnings');
413         $im->done;
414         my ($min, $max) = $ibx->mm->minmax;
415         is($min, 1, 'min as expected');
416         is($max, 10, 'max as expected');
417         is($ibx->mm->num_highwater, 10, 'num_highwater as expected');
418         is_deeply($ibx->mm->msg_range(\$min, $max),
419                   [
420                    [1, '1@example.com' ],
421                    [2, '2@example.com' ],
422                    [3, '3@example.com' ],
423                    [6, '6@example.com' ],
424                    [7, '7@example.com' ],
425                    [8, '8@example.com' ],
426                    [9, '9@example.com' ],
427                    [10, '10@example.com' ],
428                   ], 'msgmap as expected' );
429 }
430
431 {
432         my @warn;
433         local $SIG{__WARN__} = sub { push @warn, @_ };
434         my $ibx = PublicInbox::Inbox->new({ %$ibx_config });
435         my $f = $ibx->over->{dbh}->sqlite_db_filename;
436         my $over = PublicInbox::OverIdx->new($f);
437         my $dbh = $over->dbh;
438         my $non_ghost_tids = sub {
439                 $dbh->selectall_arrayref(<<'');
440 SELECT tid FROM over WHERE num > 0 ORDER BY tid ASC
441
442         };
443         my $before = $non_ghost_tids->();
444
445         # mess up threading:
446         my $tid = PublicInbox::OverIdx::get_counter($dbh, 'thread');
447         my $nr = $dbh->do('UPDATE over SET tid = ?', undef, $tid);
448
449         my $rw = PublicInbox::SearchIdx->new($ibx, 1);
450         my @pr;
451         my $pr = sub { push @pr, @_ };
452         $rw->index_sync({reindex => 1, rethread => 1, -progress => $pr });
453         my @n = $dbh->selectrow_array(<<EOS, undef, $tid);
454 SELECT COUNT(*) FROM over WHERE tid <= ?
455 EOS
456         is_deeply(\@n, [ 0 ], 'rethread dropped old threadids');
457         my $after = $non_ghost_tids->();
458         ok($after->[0]->[0] > $before->[-1]->[0],
459                 'all tids greater than before');
460         is(scalar @$after, scalar @$before, 'thread count unchanged');
461         is_deeply([], \@warn, 'no warnings');
462         # diag "@pr"; # XXX do we care?
463 }
464
465 done_testing();