Trog-Config.t 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. use 5.040;
  2. use re '/aasx';
  3. use warnings;
  4. use Test::More;
  5. use Cwd();
  6. use File::Path();
  7. use File::Temp();
  8. use Path::Tiny();
  9. use FindBin;
  10. use lib "$FindBin::Bin/../lib";
  11. # $Trog::Config::default is read exactly once below, which is enough for perl
  12. # to suspect a typo.
  13. no warnings qw{once};
  14. require_ok('Trog::Config') or BAIL_OUT("Can't find SUT");
  15. # Every path in this app is cwd-relative, so exercise it the way the app sees
  16. # it: from inside an instance root. CLEANUP => 0 plus an explicit END, because
  17. # a tempdir cannot tidy itself up while we are standing inside it.
  18. our $ROOT = File::Temp::tempdir( 'tcms-config-XXXXXX', TMPDIR => 1, CLEANUP => 0 );
  19. our $OLDCWD = Cwd::getcwd();
  20. END {
  21. chdir($OLDCWD) if $OLDCWD;
  22. File::Path::remove_tree($ROOT) if $ROOT && -d $ROOT;
  23. }
  24. File::Path::make_path("$ROOT/config");
  25. chdir($ROOT) or die $!;
  26. subtest 'the defaults are where the app actually keeps them' => sub {
  27. # The whole bug this test exists for: get() used to hardcode the 'config/'
  28. # prefix internally, so $home_cfg was a bare basename and every writer that
  29. # used it put the file somewhere get() would never look.
  30. is( $Trog::Config::home_cfg, 'config/main.cfg', 'home_cfg is a usable path' );
  31. is( $Trog::Config::default, 'config/default.cfg', 'and so is the default' );
  32. like( $Trog::Config::home_cfg, qr{^config/}, 'home_cfg is under config/' );
  33. unlike( $Trog::Config::home_cfg, qr{^/}, 'and is relative, so it follows the instance root' );
  34. };
  35. subtest 'a config written to home_cfg is the one that gets read back' => sub {
  36. Path::Tiny->new('config/default.cfg')->spew_utf8("[general]\ndata_model=FlatFile\ntitle=Stock\n");
  37. # This is the round trip that was broken: write where the writers write,
  38. # read where the readers read, and check they agree.
  39. require Config::Simple;
  40. my $written = Config::Simple->new( syntax => 'ini' );
  41. $written->param( 'general.data_model', 'FlatFile' );
  42. $written->param( 'general.title', 'Instance' );
  43. $written->param( 'general.hostname', 'spec.example.com' );
  44. ok( $written->write($Trog::Config::home_cfg), 'wrote to home_cfg' );
  45. ok( -f 'config/main.cfg', 'which landed under config/, not the instance root' );
  46. ok( !-f 'main.cfg', 'and not loose in the root where nothing would read it' );
  47. my $read = Trog::Config::get();
  48. is( $read->param('general.title'), 'Instance', 'get() picked up the written config' );
  49. is( $read->param('general.hostname'), 'spec.example.com', 'including a key the defaults never had' );
  50. };
  51. subtest 'the shipped defaults are left alone' => sub {
  52. # get() is memoized, so this is the same object as above -- which is the
  53. # point: bin/tcms-hostname used to call save() on it, and save() writes back
  54. # to whichever file it was constructed from.
  55. is(
  56. Path::Tiny->new('config/default.cfg')->slurp_utf8,
  57. "[general]\ndata_model=FlatFile\ntitle=Stock\n",
  58. 'default.cfg is untouched'
  59. );
  60. };
  61. subtest 'the defaults describe what is editable' => sub {
  62. # The /config editor is built out of this, so the parse has to keep the
  63. # comments (they are the help text) and the file order (they are the form).
  64. Path::Tiny->new('config/schema.cfg')->spew_utf8(<<'CFG');
  65. [general]
  66. # FlatFile or SQLite.
  67. # Pick one.
  68. data_model=FlatFile
  69. title=Stock
  70. [security]
  71. # Where embeds may come from.
  72. allow_embeds_from=vimeo.com *.vimeo.com
  73. CFG
  74. my $schema = Trog::Config::schema('config/schema.cfg');
  75. is_deeply( [ map { $_->{section} } @$schema ], [qw{general security}], 'the sections come back in file order' );
  76. my $general = $schema->[0]{fields};
  77. is_deeply( [ map { $_->{key} } @$general ], [qw{data_model title}], 'and so do the keys in them' );
  78. is( $general->[0]{name}, 'general.data_model', 'a field knows its full name' );
  79. is( $general->[0]{default}, 'FlatFile', 'and what the defaults ship' );
  80. is( $general->[0]{comment}, 'FlatFile or SQLite. Pick one.', 'a comment block above a key is that key\'s help' );
  81. is( $general->[1]{comment}, '', 'and does not leak onto the key after it' );
  82. is( $schema->[1]{fields}[0]{default}, 'vimeo.com *.vimeo.com', 'values with spaces survive intact' );
  83. };
  84. done_testing();