Trog-Routes-HTML-config.t 4.8 KB

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