]> Sergey Matveev's repositories - public-inbox.git/blob - t/v2reindex.t
remove most internal Email::MIME usage
[public-inbox.git] / t / v2reindex.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::Eml;
7 use PublicInbox::ContentId qw(content_digest);
8 use File::Path qw(remove_tree);
9 use PublicInbox::TestCommon;
10 require_git(2.6);
11 require_mods(qw(DBD::SQLite Search::Xapian));
12 use_ok 'PublicInbox::V2Writable';
13 my ($inboxdir, $for_destroy) = tmpdir();
14 my $ibx_config = {
15         inboxdir => $inboxdir,
16         name => 'test-v2writable',
17         version => 2,
18         -primary_address => 'test@example.com',
19         indexlevel => 'full',
20 };
21 my $agpl = do {
22         open my $fh, '<', 'COPYING' or die "can't open COPYING: $!";
23         local $/;
24         <$fh>;
25 };
26 my $phrase = q("defending all users' freedom");
27 my $mime = PublicInbox::Eml->new(<<'EOF'.$agpl);
28 From: a@example.com
29 To: test@example.com
30 Subject: this is a subject
31 Date: Fri, 02 Oct 1993 00:00:00 +0000
32
33 EOF
34 my $minmax;
35 my $msgmap;
36 my ($mark1, $mark2, $mark3, $mark4);
37 {
38         my %config = %$ibx_config;
39         my $ibx = PublicInbox::Inbox->new(\%config);
40         my $im = PublicInbox::V2Writable->new($ibx, {nproc => 1});
41         my $im0 = $im->importer();
42         foreach my $i (1..10) {
43                 $mime->header_set('Message-Id', "<$i\@example.com>");
44                 ok($im->add($mime), "message $i added");
45                 if ($i == 4) {
46                         $mark1 = $im0->get_mark($im0->{tip});
47                         $im->remove($mime);
48                         $mark2 = $im0->get_mark($im0->{tip});
49                 }
50         }
51
52         if ('test remove later') {
53                 $mark3 = $im0->get_mark($im0->{tip});
54                 $mime->header_set('Message-Id', "<5\@example.com>");
55                 $im->remove($mime);
56                 $mark4 = $im0->get_mark($im0->{tip});
57         }
58
59         $im->done;
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::V2Writable->new($ibx, 1);
83         eval { $im->index_sync({reindex => 1}) };
84         is($@, '', 'no error from reindexing');
85         $im->done;
86
87         delete $ibx->{mm};
88         is_deeply([ $ibx->mm->minmax ], $minmax, 'minmax unchanged');
89         is($ibx->mm->num_highwater, 10, 'num_highwater as expected');
90
91         my ($min, $max) = $ibx->mm->minmax;
92         is_deeply($ibx->mm->msg_range(\$min, $max), $msgmap, 'msgmap unchanged');
93 }
94
95 my $xap = "$inboxdir/xap".PublicInbox::Search::SCHEMA_VERSION();
96 remove_tree($xap);
97 ok(!-d $xap, 'Xapian directories removed');
98 {
99         my %config = %$ibx_config;
100         my $ibx = PublicInbox::Inbox->new(\%config);
101         my $im = PublicInbox::V2Writable->new($ibx, 1);
102         eval { $im->index_sync({reindex => 1}) };
103         is($@, '', 'no error from reindexing');
104         $im->done;
105         ok(-d $xap, 'Xapian directories recreated');
106
107         delete $ibx->{mm};
108         is_deeply([ $ibx->mm->minmax ], $minmax, 'minmax unchanged');
109         is($ibx->mm->num_highwater, 10, 'num_highwater as expected');
110
111         my ($min, $max) = $ibx->mm->minmax;
112         is_deeply($ibx->mm->msg_range(\$min, $max), $msgmap, 'msgmap unchanged');
113 }
114
115 ok(unlink "$inboxdir/msgmap.sqlite3", 'remove msgmap');
116 remove_tree($xap);
117 ok(!-d $xap, 'Xapian directories removed again');
118 {
119         my @warn;
120         local $SIG{__WARN__} = sub { push @warn, @_ };
121         my %config = %$ibx_config;
122         my $ibx = PublicInbox::Inbox->new(\%config);
123         my $im = PublicInbox::V2Writable->new($ibx, 1);
124         eval { $im->index_sync({reindex => 1}) };
125         is($@, '', 'no error from reindexing without msgmap');
126         is(scalar(@warn), 0, 'no warnings from reindexing');
127         $im->done;
128         ok(-d $xap, 'Xapian directories recreated');
129         delete $ibx->{mm};
130         is_deeply([ $ibx->mm->minmax ], $minmax, 'minmax unchanged');
131         is($ibx->mm->num_highwater, 10, 'num_highwater as expected');
132
133         my ($min, $max) = $ibx->mm->minmax;
134         is_deeply($ibx->mm->msg_range(\$min, $max), $msgmap, 'msgmap unchanged');
135 }
136
137 my %sizes;
138 ok(unlink "$inboxdir/msgmap.sqlite3", 'remove msgmap');
139 remove_tree($xap);
140 ok(!-d $xap, 'Xapian directories removed again');
141 {
142         my @warn;
143         local $SIG{__WARN__} = sub { push @warn, @_ };
144         my %config = %$ibx_config;
145         my $ibx = PublicInbox::Inbox->new(\%config);
146         my $im = PublicInbox::V2Writable->new($ibx, 1);
147         eval { $im->index_sync({reindex => 1}) };
148         is($@, '', 'no error from reindexing without msgmap');
149         is_deeply(\@warn, [], 'no warnings');
150         $im->done;
151         ok(-d $xap, 'Xapian directories recreated');
152         delete $ibx->{mm};
153         is_deeply([ $ibx->mm->minmax ], $minmax, 'minmax unchanged');
154         is($ibx->mm->num_highwater, 10, 'num_highwater as expected');
155         my $mset = $ibx->search->query($phrase, {mset=>1});
156         isnt($mset->size, 0, "phrase search succeeds on indexlevel=full");
157         for (glob("$xap/*/*")) { $sizes{$ibx->{indexlevel}} += -s _ if -f $_ }
158
159         my ($min, $max) = $ibx->mm->minmax;
160         is_deeply($ibx->mm->msg_range(\$min, $max), $msgmap, 'msgmap unchanged');
161 }
162
163 ok(unlink "$inboxdir/msgmap.sqlite3", 'remove msgmap');
164 remove_tree($xap);
165 ok(!-d $xap, 'Xapian directories removed again');
166 {
167         my @warn;
168         local $SIG{__WARN__} = sub { push @warn, @_ };
169         my %config = %$ibx_config;
170         $config{indexlevel} = 'medium';
171         my $ibx = PublicInbox::Inbox->new(\%config);
172         my $im = PublicInbox::V2Writable->new($ibx);
173         eval { $im->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
182         if (0) {
183                 # not sure why, but Xapian seems to fallback to terms and
184                 # phrase searches still work
185                 delete $ibx->{search};
186                 my $mset = $ibx->search->query($phrase, {mset=>1});
187                 is($mset->size, 0, 'phrase search does not work on medium');
188         }
189         my $words = $phrase;
190         $words =~ tr/"'//d;
191         my $mset = $ibx->search->query($words, {mset=>1});
192         isnt($mset->size, 0, "normal search works on indexlevel=medium");
193         for (glob("$xap/*/*")) { $sizes{$ibx->{indexlevel}} += -s _ if -f $_ }
194
195         ok($sizes{full} > $sizes{medium}, 'medium is smaller than full');
196
197
198         my ($min, $max) = $ibx->mm->minmax;
199         is_deeply($ibx->mm->msg_range(\$min, $max), $msgmap, 'msgmap unchanged');
200 }
201
202 ok(unlink "$inboxdir/msgmap.sqlite3", 'remove msgmap');
203 remove_tree($xap);
204 ok(!-d $xap, 'Xapian directories removed again');
205 {
206         my @warn;
207         local $SIG{__WARN__} = sub { push @warn, @_ };
208         my %config = %$ibx_config;
209         $config{indexlevel} = 'basic';
210         my $ibx = PublicInbox::Inbox->new(\%config);
211         my $im = PublicInbox::V2Writable->new($ibx);
212         eval { $im->index_sync({reindex => 1}) };
213         is($@, '', 'no error from reindexing without msgmap');
214         is_deeply(\@warn, [], 'no warnings');
215         $im->done;
216         ok(-d $xap, 'Xapian directories recreated');
217         delete $ibx->{mm};
218         is_deeply([ $ibx->mm->minmax ], $minmax, 'minmax unchanged');
219         is($ibx->mm->num_highwater, 10, 'num_highwater as expected');
220
221         isnt($ibx->search, 'no search for basic');
222
223         for (glob("$xap/*/*")) { $sizes{$ibx->{indexlevel}} += -s _ if -f $_ }
224         ok($sizes{medium} > $sizes{basic}, 'basic is smaller than medium');
225
226         my ($min, $max) = $ibx->mm->minmax;
227         is_deeply($ibx->mm->msg_range(\$min, $max), $msgmap, 'msgmap unchanged');
228 }
229
230
231 # An incremental indexing test
232 ok(unlink "$inboxdir/msgmap.sqlite3", 'remove msgmap');
233 remove_tree($xap);
234 ok(!-d $xap, 'Xapian directories removed again');
235 {
236         my @warn;
237         local $SIG{__WARN__} = sub { push @warn, @_ };
238         my %config = %$ibx_config;
239         my $ibx = PublicInbox::Inbox->new(\%config);
240         # mark1 4 simple additions in the same index_sync
241         $ibx->{ref_head} = $mark1;
242         my $im = PublicInbox::V2Writable->new($ibx);
243         eval { $im->index_sync() };
244         is($@, '', 'no error from reindexing without msgmap');
245         is_deeply(\@warn, [], 'no warnings');
246         $im->done;
247         my ($min, $max) = $ibx->mm->minmax;
248         is($min, 1, 'min as expected');
249         is($max, 4, 'max as expected');
250         is($ibx->mm->num_highwater, 4, 'num_highwater as expected');
251         is_deeply($ibx->mm->msg_range(\$min, $max),
252                   [
253                    [1, '1@example.com' ],
254                    [2, '2@example.com' ],
255                    [3, '3@example.com' ],
256                    [4, '4@example.com' ],
257                   ], 'msgmap as expected' );
258 }
259 {
260         my @warn;
261         local $SIG{__WARN__} = sub { push @warn, @_ };
262         my %config = %$ibx_config;
263         my $ibx = PublicInbox::Inbox->new(\%config);
264         # mark2 A delete separated from an add in the same index_sync
265         $ibx->{ref_head} = $mark2;
266         my $im = PublicInbox::V2Writable->new($ibx);
267         eval { $im->index_sync() };
268         is($@, '', 'no error from reindexing without msgmap');
269         is_deeply(\@warn, [], 'no warnings');
270         $im->done;
271         my ($min, $max) = $ibx->mm->minmax;
272         is($min, 1, 'min as expected');
273         is($max, 3, 'max as expected');
274         is($ibx->mm->num_highwater, 4, 'num_highwater as expected');
275         is_deeply($ibx->mm->msg_range(\$min, $max),
276                   [
277                    [1, '1@example.com' ],
278                    [2, '2@example.com' ],
279                    [3, '3@example.com' ],
280                   ], 'msgmap as expected' );
281 }
282 {
283         my @warn;
284         local $SIG{__WARN__} = sub { push @warn, @_ };
285         my %config = %$ibx_config;
286         my $ibx = PublicInbox::Inbox->new(\%config);
287         # mark3 adds following the delete at mark2
288         $ibx->{ref_head} = $mark3;
289         my $im = PublicInbox::V2Writable->new($ibx);
290         eval { $im->index_sync() };
291         is($@, '', 'no error from reindexing without msgmap');
292         is_deeply(\@warn, [], 'no warnings');
293         $im->done;
294         my ($min, $max) = $ibx->mm->minmax;
295         is($min, 1, 'min as expected');
296         is($max, 10, 'max as expected');
297         is($ibx->mm->num_highwater, 10, 'num_highwater as expected');
298         is_deeply($ibx->mm->msg_range(\$min, $max),
299                   [
300                    [1, '1@example.com' ],
301                    [2, '2@example.com' ],
302                    [3, '3@example.com' ],
303                    [5, '5@example.com' ],
304                    [6, '6@example.com' ],
305                    [7, '7@example.com' ],
306                    [8, '8@example.com' ],
307                    [9, '9@example.com' ],
308                    [10, '10@example.com' ],
309                   ], 'msgmap as expected' );
310 }
311 {
312         my @warn;
313         local $SIG{__WARN__} = sub { push @warn, @_ };
314         my %config = %$ibx_config;
315         my $ibx = PublicInbox::Inbox->new(\%config);
316         # mark4 A delete of an older message
317         $ibx->{ref_head} = $mark4;
318         my $im = PublicInbox::V2Writable->new($ibx);
319         eval { $im->index_sync() };
320         is($@, '', 'no error from reindexing without msgmap');
321         is_deeply(\@warn, [], 'no warnings');
322         $im->done;
323         my ($min, $max) = $ibx->mm->minmax;
324         is($min, 1, 'min as expected');
325         is($max, 10, 'max as expected');
326         is($ibx->mm->num_highwater, 10, 'num_highwater as expected');
327         is_deeply($ibx->mm->msg_range(\$min, $max),
328                   [
329                    [1, '1@example.com' ],
330                    [2, '2@example.com' ],
331                    [3, '3@example.com' ],
332                    [6, '6@example.com' ],
333                    [7, '7@example.com' ],
334                    [8, '8@example.com' ],
335                    [9, '9@example.com' ],
336                    [10, '10@example.com' ],
337                   ], 'msgmap as expected' );
338 }
339
340
341 # Another incremental indexing test
342 ok(unlink "$inboxdir/msgmap.sqlite3", 'remove msgmap');
343 remove_tree($xap);
344 ok(!-d $xap, 'Xapian directories removed again');
345 {
346         my @warn;
347         local $SIG{__WARN__} = sub { push @warn, @_ };
348         my %config = %$ibx_config;
349         my $ibx = PublicInbox::Inbox->new(\%config);
350         # mark2 an add and it's delete in the same index_sync
351         $ibx->{ref_head} = $mark2;
352         my $im = PublicInbox::V2Writable->new($ibx);
353         eval { $im->index_sync() };
354         is($@, '', 'no error from reindexing without msgmap');
355         is_deeply(\@warn, [], 'no warnings');
356         $im->done;
357         my ($min, $max) = $ibx->mm->minmax;
358         is($min, 1, 'min as expected');
359         is($max, 3, 'max as expected');
360         is($ibx->mm->num_highwater, 4, 'num_highwater as expected');
361         is_deeply($ibx->mm->msg_range(\$min, $max),
362                   [
363                    [1, '1@example.com' ],
364                    [2, '2@example.com' ],
365                    [3, '3@example.com' ],
366                   ], 'msgmap as expected' );
367 }
368 {
369         my @warn;
370         local $SIG{__WARN__} = sub { push @warn, @_ };
371         my %config = %$ibx_config;
372         my $ibx = PublicInbox::Inbox->new(\%config);
373         # mark3 adds following the delete at mark2
374         $ibx->{ref_head} = $mark3;
375         my $im = PublicInbox::V2Writable->new($ibx);
376         eval { $im->index_sync() };
377         is($@, '', 'no error from reindexing without msgmap');
378         is_deeply(\@warn, [], 'no warnings');
379         $im->done;
380         my ($min, $max) = $ibx->mm->minmax;
381         is($min, 1, 'min as expected');
382         is($max, 10, 'max as expected');
383         is($ibx->mm->num_highwater, 10, 'num_highwater as expected');
384         is_deeply($ibx->mm->msg_range(\$min, $max),
385                   [
386                    [1, '1@example.com' ],
387                    [2, '2@example.com' ],
388                    [3, '3@example.com' ],
389                    [5, '5@example.com' ],
390                    [6, '6@example.com' ],
391                    [7, '7@example.com' ],
392                    [8, '8@example.com' ],
393                    [9, '9@example.com' ],
394                    [10, '10@example.com' ],
395                   ], 'msgmap as expected' );
396 }
397 {
398         my @warn;
399         local $SIG{__WARN__} = sub { push @warn, @_ };
400         my %config = %$ibx_config;
401         my $ibx = PublicInbox::Inbox->new(\%config);
402         # mark4 A delete of an older message
403         $ibx->{ref_head} = $mark4;
404         my $im = PublicInbox::V2Writable->new($ibx);
405         eval { $im->index_sync() };
406         is($@, '', 'no error from reindexing without msgmap');
407         is_deeply(\@warn, [], 'no warnings');
408         $im->done;
409         my ($min, $max) = $ibx->mm->minmax;
410         is($min, 1, 'min as expected');
411         is($max, 10, 'max as expected');
412         is($ibx->mm->num_highwater, 10, 'num_highwater as expected');
413         is_deeply($ibx->mm->msg_range(\$min, $max),
414                   [
415                    [1, '1@example.com' ],
416                    [2, '2@example.com' ],
417                    [3, '3@example.com' ],
418                    [6, '6@example.com' ],
419                    [7, '7@example.com' ],
420                    [8, '8@example.com' ],
421                    [9, '9@example.com' ],
422                    [10, '10@example.com' ],
423                   ], 'msgmap as expected' );
424 }
425
426 # A real example from linux-renesas-soc on lore where a 3-headed monster
427 # of a message has 3 sets of common headers.  Another normal message
428 # previously existed with a single Message-ID that conflicts with one
429 # of the Message-IDs in the 3-headed monster.
430 {
431         my @warn;
432         local $SIG{__WARN__} = sub { push @warn, @_ };
433         my %config = %$ibx_config;
434         $config{indexlevel} = 'medium';
435         my $ibx = PublicInbox::Inbox->new(\%config);
436         my $im = PublicInbox::V2Writable->new($ibx);
437         my $m3 = PublicInbox::Eml->new(<<'EOF');
438 Date: Tue, 24 May 2016 14:34:22 -0700 (PDT)
439 Message-Id: <20160524.143422.552507610109476444.d@example.com>
440 To: t@example.com
441 Cc: c@example.com
442 Subject: Re: [PATCH v2 2/2] uno
443 From: <f@example.com>
444 In-Reply-To: <1463825855-7363-2-git-send-email-y@example.com>
445 References: <1463825855-7363-1-git-send-email-y@example.com>
446         <1463825855-7363-2-git-send-email-y@example.com>
447 Date: Wed, 25 May 2016 10:01:51 +0900
448 From: h@example.com
449 To: g@example.com
450 Cc: m@example.com
451 Subject: Re: [PATCH] dos
452 Message-ID: <20160525010150.GD7292@example.com>
453 References: <1463498133-23918-1-git-send-email-g+r@example.com>
454 In-Reply-To: <1463498133-23918-1-git-send-email-g+r@example.com>
455 From: s@example.com
456 To: h@example.com
457 Cc: m@example.com
458 Subject: [PATCH 12/13] tres
459 Date: Wed, 01 Jun 2016 01:32:35 +0300
460 Message-ID: <1923946.Jvi0TDUXFC@wasted.example.com>
461 In-Reply-To: <13205049.n7pM8utpHF@wasted.example.com>
462 References: <13205049.n7pM8utpHF@wasted.example.com>
463
464 Somehow we got a message with 3 sets of headers into one
465 message, could've been something broken on the archiver side.
466 EOF
467
468         my $m1 = PublicInbox::Eml->new(<<'EOF');
469 From: a@example.com
470 To: t@example.com
471 Subject: [PATCH 12/13]
472 Date: Wed, 01 Jun 2016 01:32:35 +0300
473 Message-ID: <1923946.Jvi0TDUXFC@wasted.example.com>
474 In-Reply-To: <13205049.n7pM8utpHF@wasted.example.com>
475 References: <13205049.n7pM8utpHF@wasted.example.com>
476
477 This is probably one of the original messages
478
479 EOF
480         $im->add($m1);
481         $im->add($m3);
482         $im->done;
483         remove_tree($xap);
484         eval { $im->index_sync() };
485         is($@, '', 'no error from initial indexing');
486         is_deeply(\@warn, [], 'no warnings from initial index');
487         eval { $im->index_sync({reindex=>1}) };
488         is($@, '', 'no error from reindexing after reused Message-ID (x3)');
489         is_deeply(\@warn, [], 'no warnings on reindex');
490
491         my %uniq;
492         for my $s (qw(uno dos tres)) {
493                 my $msgs = $ibx->search->query("s:$s");
494                 is(scalar(@$msgs), 1, "only one result for `$s'");
495                 $uniq{$msgs->[0]->{num}}++;
496         }
497         is_deeply([values %uniq], [3], 'search on different subjects');
498 }
499
500 done_testing();