فهرست منبع

Keep "0" a value where it is one

The last commit made "0" count as absent wherever it simplified a test
to the value itself.  That is right for most of those fields, and wrong
for four kinds of them, which go back to ( $x // q{} ) ne q{}:

- a vault secret, which can be any string, "0" included
- a like query, in the four data sources and DataSource::searchable_match,
  where a search for "0" is a search and not the absence of one
- a wizard field's placeholder, which for a number field can be "0"
- a path under a DirIndex root, where a file named 0 has the relative
  path "0", and would otherwise resolve to the root

The new tests store a secret of "0" and search for "0", and both fail
against the commit before this one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
troglodyne-bot 1 هفته پیش
والد
کامیت
49d1d52ce7
8فایلهای تغییر یافته به همراه13 افزوده شده و 8 حذف شده
  1. 1 1
      lib/Trog/Data/SQLite.pm
  2. 2 2
      lib/Trog/DataSource.pm
  3. 2 2
      lib/Trog/DataSource/DirIndex.pm
  4. 1 1
      lib/Trog/DataSource/Virt.pm
  5. 1 1
      lib/Trog/Routes/HTML.pm
  6. 1 1
      lib/Trog/Vault.pm
  7. 1 0
      t/Trog-Data-SQLite.t
  8. 4 0
      t/Trog-Vault.t

+ 1 - 1
lib/Trog/Data/SQLite.pm

@@ -242,7 +242,7 @@ sub _where ($query) {
         push( @bind,  $query->{author} );
     }
 
-    if ( $query->{like} ) {
+    if ( ( $query->{like} // q{} ) ne q{} ) {
         my ( $clause, @like_bind ) = _like_clause( $query->{like} );
         push( @where, $clause );
         push( @bind,  @like_bind );

+ 2 - 2
lib/Trog/DataSource.pm

@@ -102,7 +102,7 @@ rather than by hoping this one covers it.
 sub filter ( $query, @posts ) {
     $query //= {};
 
-    if ( $query->{like} ) {
+    if ( ( $query->{like} // q{} ) ne q{} ) {
         my $like = $query->{like};
         @posts = grep { searchable_match( $like, $_->{title}, $_->{data} ) } @posts;
     }
@@ -138,7 +138,7 @@ or a nested structure is passed over rather than matched against its address.
 =cut
 
 sub searchable_match ( $like, @values ) {
-    return 0 unless $like;
+    return 0 unless ( $like // q{} ) ne q{};
 
     foreach my $value (@values) {
         next     if !defined $value || ref $value;

+ 2 - 2
lib/Trog/DataSource/DirIndex.pm

@@ -189,7 +189,7 @@ sub _resolve ($directory) {
     my $relative = $absolute;
     $relative =~ s{^\Q$base\E/?}{};
 
-    return { absolute => $absolute, relative => $relative ? "$root/$relative" : $root };
+    return { absolute => $absolute, relative => $relative ne q{} ? "$root/$relative" : $root };
 }
 
 # Path containment, done on the resolved paths rather than on the strings the
@@ -303,7 +303,7 @@ under it.
 sub filter ( $query, @posts ) {
     $query //= {};
 
-    if ( $query->{like} ) {
+    if ( ( $query->{like} // q{} ) ne q{} ) {
         my $like = $query->{like};
         @posts = grep { Trog::DataSource::searchable_match( $like, $_->{name}, $_->{path}, $_->{extension} ) } @posts;
     }

+ 1 - 1
lib/Trog/DataSource/Virt.pm

@@ -210,7 +210,7 @@ Trog::DataSource::filter.
 sub filter ( $query, @posts ) {
     $query //= {};
 
-    if ( $query->{like} ) {
+    if ( ( $query->{like} // q{} ) ne q{} ) {
         my $like = $query->{like};
         @posts = grep {
             Trog::DataSource::searchable_match(

+ 1 - 1
lib/Trog/Routes/HTML.pm

@@ -3250,7 +3250,7 @@ sub _wizard_sidecar ( $name, $fields, $includes, $body_form, $datasource = '', $
         my $property = { %{ $wizard_field_types{ $field->{type} } } };
         $property->{'x-tcms-input'}       = $field->{type};
         $property->{'x-tcms-label'}       = $field->{label};
-        $property->{'x-tcms-placeholder'} = $field->{placeholder} if $field->{placeholder};
+        $property->{'x-tcms-placeholder'} = $field->{placeholder} if ( $field->{placeholder} // q{} ) ne q{};
 
         # Only written when it is true: a field is public unless it says so,
         # and a sidecar full of x-tcms-private:false reads like the opposite.

+ 1 - 1
lib/Trog/Vault.pm

@@ -204,7 +204,7 @@ Remember one thing for one user, replacing whatever was there under that name.
 sub set ( $user, $name, $secret ) {
     return ( 0, 'this installation has no vault key, so there is nowhere to keep it' ) unless has_key();
     return ( 0, 'a secret needs a name' )                                              unless _safe_name($name);
-    return ( 0, 'nothing to store' )                                                   unless $secret;
+    return ( 0, 'nothing to store' )                                                   unless ( $secret // q{} ) ne q{};
 
     my $salt = Crypt::PRNG::random_bytes(32);
     my ( $key, $iv ) = _derive($salt);

+ 1 - 0
t/Trog-Data-SQLite.t

@@ -265,6 +265,7 @@ subtest 'the like filter goes through FTS5' => sub {
         [ 'Secret Plans' => ['p-private'] ],    # title as well as body
         [ 'foxes'        => ['p-slides'] ],     # data stored as an array
         [ 'nonesuch'     => [] ],
+        [ '0'            => [] ],               # a term, not an absent one
     );
 
     foreach my $case (@cases) {

+ 4 - 0
t/Trog-Vault.t

@@ -77,6 +77,10 @@ subtest 'what a secret looks like on disk' => sub {
     is( $row->{key_id},         Trog::Vault::key_id(), 'naming the key it was sealed under' );
 
     is( Trog::Vault::get( 'bob', 'keepass' ), 'correct horse battery staple', 'and it comes back out' );
+
+    ( $ok, $why ) = Trog::Vault::set(qw{bob pin 0});
+    is( $ok,                              1,   'a secret of "0" is a secret, not nothing' ) or diag($why);
+    is( Trog::Vault::get( 'bob', 'pin' ), '0', 'and comes back out' );
 };
 
 subtest 'a fresh salt every write' => sub {