Trog-Routes-HTML-config.t 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. use 5.040;
  2. use re '/aasx';
  3. use warnings FATAL => 'all';
  4. use Test::More;
  5. use Cwd();
  6. use File::Path();
  7. use File::Temp();
  8. use Path::Tiny();
  9. use FindBin::libs;
  10. # The config editor is built out of config/default.cfg rather than a hardcoded
  11. # list of fields, so what it can edit is whatever that file describes. These
  12. # tests stand inside an instance root, since every path in this app is
  13. # cwd-relative -- and they stand there before the SUT is loaded, because reading
  14. # the configuration is memoized and would otherwise be the one in this checkout.
  15. our $ROOT = File::Temp::tempdir( 'tcms-config-route-XXXXXX', TMPDIR => 1, CLEANUP => 0 );
  16. our $OLDCWD = Cwd::getcwd();
  17. END {
  18. chdir($OLDCWD) if $OLDCWD;
  19. File::Path::remove_tree($ROOT) if $ROOT && -d $ROOT;
  20. }
  21. File::Path::make_path("$ROOT/config");
  22. File::Path::make_path("$ROOT/www/themes/spec");
  23. chdir($ROOT) or die $!;
  24. Path::Tiny->new('config/default.cfg')->spew_utf8(<<'CFG');
  25. [general]
  26. # FlatFile or SQLite.
  27. data_model=FlatFile
  28. # Which directory under www/themes to render with.
  29. theme=
  30. title=Stock
  31. hostname=stock.example.com
  32. [security]
  33. allow_embeds_from=vimeo.com *.vimeo.com youtube.com *.youtube.com www.youtube-nocookie.com
  34. CFG
  35. # What this instance is actually running with, which is not what ships -- and
  36. # one setting it deliberately cleared.
  37. Path::Tiny->new('config/main.cfg')->spew_utf8(<<'CFG');
  38. [general]
  39. theme=spec
  40. hostname=spec.example.com
  41. title=
  42. CFG
  43. require_ok('Trog::Routes::HTML') or BAIL_OUT("Can't find SUT");
  44. my %field;
  45. my $sections = Trog::Routes::HTML::config_sections();
  46. foreach my $section (@$sections) {
  47. $field{ $_->{name} } = $_ foreach @{ $section->{fields} };
  48. }
  49. subtest 'the form is the shape of the defaults' => sub {
  50. is_deeply( [ map { $_->{section} } @$sections ], [qw{general security}], 'a details block per section, in file order' );
  51. is_deeply( [ map { $_->{title} } @$sections ], [ 'General', 'Security' ], 'each with a legible summary' );
  52. is_deeply(
  53. [ map { $_->{name} } @{ $sections->[0]{fields} } ],
  54. [qw{general.data_model general.theme general.title general.hostname}],
  55. 'and the keys that section describes, in order'
  56. );
  57. is( $field{'general.data_model'}{title}, 'Data Model', 'a key gets a legible label' );
  58. is( $field{'general.data_model'}{comment}, 'FlatFile or SQLite.', '...and the comment above it for help' );
  59. };
  60. subtest 'fields carry what the instance is running with' => sub {
  61. is( $field{'general.hostname'}{value}, 'spec.example.com', 'a setting this instance overrode shows its value' );
  62. is( $field{'general.data_model'}{value}, 'FlatFile', '...and one it never touched falls back to the default' );
  63. # Blanking a setting is how you turn it off, so the defaults must not creep
  64. # back in and get re-saved on the next commit.
  65. is( $field{'general.title'}{value}, '', '...while one cleared on purpose stays cleared' );
  66. };
  67. subtest 'the settings which are a choice are menus' => sub {
  68. # default.cfg can say general.theme exists; it cannot say it has to be one
  69. # of the themes on disk, so that part is not read off the file.
  70. my $themes = $field{'general.theme'}{options};
  71. is_deeply( [ map { $_->{value} } @$themes ], [ '', 'spec' ], 'the theme menu is what is installed, plus none' );
  72. is( $themes->[0]{label}, 'default', 'the empty theme is named rather than blank' );
  73. is_deeply( [ map { $_->{selected} } @$themes ], [ 0, 1 ], 'and the running theme is the selected one' );
  74. ok( scalar @{ $field{'general.data_model'}{options} }, 'the data model is a menu too' );
  75. ok( !$field{'general.title'}{options}, 'while a free text setting is not' );
  76. ok( $field{'security.allow_embeds_from'}{multiline}, 'a long value gets a textarea' );
  77. ok( !$field{'general.hostname'}{multiline}, '...and a short one does not' );
  78. };
  79. subtest 'saving only writes what the defaults describe' => sub {
  80. require Config::Simple;
  81. my $conf = Config::Simple->new( syntax => 'ini' );
  82. Trog::Routes::HTML::config_apply(
  83. $conf,
  84. {
  85. 'general.title' => 'Renamed',
  86. 'general.theme' => '',
  87. # Neither of these is in the schema: one is a leftover from the
  88. # form this replaced, the other is somebody trying it on.
  89. 'theme' => 'nope',
  90. 'general.pwnage' => 'nope',
  91. }
  92. );
  93. is( $conf->param('general.title'), 'Renamed', 'a posted setting is saved' );
  94. is( $conf->param('general.theme'), '', 'including one cleared back to empty' );
  95. is_deeply( [ $conf->param('general.pwnage') ], [], 'a key the defaults never named is ignored' );
  96. is_deeply( [ $conf->param('theme') ], [], '...as is the bare name the old form used' );
  97. };
  98. done_testing();