#!/usr/bin/env perl

# Regression test for checkSizeAgainstTransport()'s df-parsing bug, found running the real
# harness against dd-nas1 with a genuinely clean folder-based-transport sandbox (Step 0.6).
#
# The original code only accepted a `df -k $mountPoint` line whose "Mounted on" column was
# EXACTLY $mountPoint. That's true when transport.mountPoint is its own dataset/mount, but
# folder-based transport (Part 2's whole feature) uses a plain subdirectory - df reports the
# mountpoint of the filesystem CONTAINING that subdirectory, which never equals the subdirectory
# itself. The match always failed, $availBytes stayed 0, and the function silently reported "could
# not determine available space", which doSourceReplication treated as "insufficient space" and
# aborted the entire replication with a FATAL ERROR - even with terabytes genuinely free.
#
# sneakernet is a script, not a requireable module (unguarded top-level logic runs on load), so
# the function under test is copied here verbatim, per this project's "extract a sub into a stub
# harness" testing convention (see test_destroyedSourceDataset.pl). No live ZFS/df required - a
# fake df on PATH stands in for it.

use strict;
use warnings;
use FindBin;
use lib "$FindBin::Bin/..";
use ZFS_Utils qw(logMsg runCmd humanReadable);
use File::Temp qw(tempdir);

$ZFS_Utils::displayLogsOnConsole = 0;
$ZFS_Utils::logFileName = '/tmp/test_checkSizeAgainstTransport.log';
unlink $ZFS_Utils::logFileName if -f $ZFS_Utils::logFileName;
$ZFS_Utils::verboseLoggingLevel = 3;

my $passed = 0;
my $failed = 0;

sub ok {
   my ($cond, $desc) = @_;
   if ($cond) { print "  PASS: $desc\n"; $passed++; }
   else       { print "  FAIL: $desc\n"; $failed++; }
}

# checkSizeAgainstTransport() references the top-level $config and cleanup() only on the
# "doesn't fit" fatalError path, which these tests never reach.
my $config;
sub cleanup { }
my @fatalErrors;
sub fatalError { push @fatalErrors, $_[0]; die "fatalError: $_[0]"; }

# --- verbatim copy of checkSizeAgainstTransport, sneakernet (post-fix) ---
sub checkSizeAgainstTransport {
   my ($sizeBytes, $mountPoint) = @_;
   my @lines = runCmd( 'df -k', $mountPoint );
   my $availBytes = 0;
   foreach my $line ( @lines ) {
      next if $line =~ /^Filesystem/;
      my @fields = split( /\s+/, $line );
      if ( @fields >= 4 ) {
         $availBytes = $fields[3] * 1024;
         last;
      }
   }
   logMsg("checkSizeAgainstTransport: Available space on $mountPoint: " . humanReadable($availBytes) . " bytes") if $ZFS_Utils::verboseLoggingLevel >= 2;
   if ($availBytes && defined($sizeBytes) && $sizeBytes =~ /^\d+$/) {
      if ($sizeBytes < $availBytes) {
         logMsg("checkSizeAgainstTransport: Estimated size " . humanReadable($sizeBytes) . " fits on $mountPoint") if $ZFS_Utils::verboseLoggingLevel >= 2;
         return 1;
      } else {
         fatalError("checkSizeAgainstTransport: Estimated size " . humanReadable($sizeBytes) . " does NOT fit on $mountPoint (available: " . humanReadable($availBytes) . ")", $config, \&cleanup);
         return 0;
      }
   } else {
      logMsg("checkSizeAgainstTransport: Could not determine available space or size");
      return 0;
   }
}
# --- end copy ---

my $bindir = tempdir( CLEANUP => 1 );
open my $fh, '>', "$bindir/df" or die $!;
print $fh <<'EOF';
#!/bin/sh
# Mimics real df -k behavior for a folder-based transport path: the "Mounted on" column is the
# CONTAINING filesystem's mountpoint, not the subdirectory passed as the argument.
echo "Filesystem      1024-blocks Used       Avail Capacity  Mounted on"
echo "storage/testing 12766478156 4169 12766473986     0%    /storage/testing"
EOF
close $fh;
chmod 0755, "$bindir/df";
local $ENV{PATH} = "$bindir:$ENV{PATH}";

{
   my $result = checkSizeAgainstTransport( 1024, '/storage/testing/transport' );
   ok( $result == 1, "a folder-based transport.mountPoint (a subdirectory, not its own mount) is still recognized as having space" );
   ok( scalar(@fatalErrors) == 0, "no fatalError was raised" );
}

{
   # Sanity: the fix must still correctly refuse when the estimated size genuinely exceeds
   # the real available space, not just always return true now.
   @fatalErrors = ();
   my $hugeSize = 12766473986 * 1024 + 1; # one byte more than the fake df's Avail (in bytes)
   my $result = eval { checkSizeAgainstTransport( $hugeSize, '/storage/testing/transport' ) };
   ok( !$result, "an estimated size that genuinely exceeds available space still fails" );
   ok( scalar(@fatalErrors) == 1, "and still raises exactly one fatalError, not silently swallowed" );
}

print "\n" . "=" x 70 . "\n";
print "Test Summary: $passed passed, $failed failed\n";
print "=" x 70 . "\n";

exit( $failed == 0 ? 0 : 1 );
