Trog-Data-FlatFile-read.t 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. use 5.040;
  2. use warnings;
  3. use Test::More;
  4. use Test::Fatal qw{exception};
  5. use File::Path ();
  6. use FindBin;
  7. use lib "$FindBin::Bin/../lib";
  8. my ( $ROOT, $OLDCWD );
  9. BEGIN {
  10. require File::Temp;
  11. require Cwd;
  12. $OLDCWD = Cwd::getcwd();
  13. $ROOT = File::Temp::tempdir( 'tcms-flatfile-XXXXXX', TMPDIR => 1, CLEANUP => 1 );
  14. # Text::Xslate and the loggers both compute paths from $HOME or the cwd at
  15. # load time, and $datastore is relative, so both have to be set before the
  16. # SUT is loaded.
  17. $ENV{HOME} = $ROOT;
  18. File::Path::make_path("$ROOT/data/files");
  19. File::Path::make_path("$ROOT/schema");
  20. # Trog::SQLite::TagIndex builds the tag index at load time and wants this
  21. # path, hardcoded and relative, before the SUT will even compile.
  22. require File::Copy;
  23. File::Copy::copy( "$FindBin::Bin/../schema/flatfile.schema", "$ROOT/schema/flatfile.schema" ) or die $!;
  24. chdir($ROOT) or die "could not chdir to the sandbox: $!";
  25. }
  26. END {
  27. chdir($OLDCWD) if $OLDCWD;
  28. }
  29. require_ok('Trog::Data::FlatFile') or BAIL_OUT("Can't find SUT");
  30. require_ok('Trog::Log') or BAIL_OUT("Can't find SUT");
  31. my $model = bless( {}, 'Trog::Data::FlatFile' );
  32. # Collect whatever the SUT says rather than letting it reach the terminal, so
  33. # that "did this warn" is a thing the test can assert on.
  34. my @said;
  35. local $SIG{__WARN__} = sub { push @said, $_[0] };
  36. sub reads {
  37. my ($id) = @_;
  38. @said = ();
  39. my $posts = $model->read( { id => $id, raw => 1 } );
  40. return ( $posts, join( '', @said ) );
  41. }
  42. open( my $fh, '>', 'data/files/good' ) or die $!;
  43. print {$fh} '[{"id":"good","title":"Good","tags":["public"],"visibility":"public"}]';
  44. close $fh;
  45. subtest 'a post that is there' => sub {
  46. my ( $posts, $noise ) = reads('good');
  47. is( scalar(@$posts), 1, "the post comes back" );
  48. is( $posts->[0]{id}, 'good', "and it is the one asked for" );
  49. is( $noise, '', "quietly" );
  50. };
  51. subtest 'a post that is not there' => sub {
  52. # add() asks this before every single insert, to choose between version 0
  53. # and a bump. It used to name the file regardless of whether it existed,
  54. # so every new post reported itself as a failed read.
  55. my ( $posts, $noise ) = reads('no-such-post');
  56. is( scalar(@$posts), 0, "a miss is an empty list" );
  57. is( $noise, '', "and says nothing -- a miss is not a failure" );
  58. };
  59. subtest 'a post that cannot be parsed' => sub {
  60. open( my $bad, '>', 'data/files/corrupt' ) or die $!;
  61. print {$bad} '[{"id":"corrupt"';
  62. close $bad;
  63. my ( $posts, $noise ) = reads('corrupt');
  64. is( scalar(@$posts), 0, "nothing comes back" );
  65. like( $noise, qr/corrupt/, "but it says which post, because this one is a real failure" );
  66. };
  67. subtest 'a post that cannot be read' => sub {
  68. SKIP: {
  69. skip( "running as root, which can read anything", 2 ) if $> == 0;
  70. open( my $no, '>', 'data/files/unreadable' ) or die $!;
  71. print {$no} 'x';
  72. close $no;
  73. chmod( 0000, 'data/files/unreadable' ) or die $!;
  74. my ( $posts, $noise ) = reads('unreadable');
  75. is( scalar(@$posts), 0, "nothing comes back" );
  76. like( $noise, qr/unreadable/, "and it says which post" );
  77. chmod( 0644, 'data/files/unreadable' );
  78. }
  79. };
  80. subtest 'the log works before log_init()' => sub {
  81. # Logging is what you reach for when something has already gone wrong, so
  82. # dying because nobody set it up first turns a diagnostic into an outage.
  83. no warnings qw{once};
  84. is( $Trog::Log::log, undef, "no logger is configured in this process" );
  85. @said = ();
  86. is( exception { Trog::Log::WARN('a warning') }, undef, "WARN does not die" );
  87. like( join( '', @said ), qr/\[WARN\].*a warning/, "it lands on stderr instead" );
  88. @said = ();
  89. is( exception { Trog::Log::INFO('a note') }, undef, "INFO does not die" );
  90. is( exception { Trog::Log::DEBUG('a detail') }, undef, "DEBUG does not die" );
  91. # An uninitialized request id used to be spliced into the line as an
  92. # uninitialized-value warning.
  93. unlike( join( '', @said ), qr/uninitialized/, "and no uninitialized value creeps in" );
  94. like( join( '', @said ), qr/RequestId NONE/, "the request id says it has none" );
  95. # FATAL is fatal either way, or the caller's error handling disappears.
  96. like( exception { Trog::Log::FATAL('the roof is on fire') }, qr/the roof is on fire/, "FATAL still dies" );
  97. };
  98. done_testing();