From 9fd8264c3f8a975e2a432bb79296026425438909 Mon Sep 17 00:00:00 2001 From: Deon George Date: Wed, 22 Nov 2023 10:40:15 +1100 Subject: [PATCH] Rework TIC processing and added test cases --- .env.testing | 3 +- app/Classes/FTN/Tic.php | 330 ++++++++++-------- app/Classes/File/Tic.php | 2 +- app/Exceptions/InvalidCRCException.php | 9 + app/Exceptions/InvalidPasswordException.php | 9 + app/Exceptions/NoReadSecurityException.php | 0 app/Exceptions/NoWriteSecurityException.php | 9 + app/Exceptions/NodeNotSubscribedException.php | 9 + app/Exceptions/TIC/NoFileAreaException.php | 9 + app/Exceptions/TIC/NotToMeException.php | 9 + app/Exceptions/TIC/SizeMismatchException.php | 9 + app/Jobs/TicProcess.php | 7 +- app/Models/File.php | 40 +-- .../2023_11_19_224649_add_recv_tic.php | 28 ++ database/seeders/InitialSetup.php | 22 +- database/seeders/TestNodeHierarchy.php | 79 +++-- storage/app/.gitignore | 3 +- storage/app/test/000E-0000000000-FILE.ZIP | 1 + storage/app/test/000E-1700545740-bad_size.tic | 28 ++ storage/app/test/000E-1700545740-file.tic | 28 ++ .../app/test/000E-1700545740-invalid_crc.tic | 28 ++ storage/app/test/000E-1700545740-no_area.tic | 28 ++ storage/app/test/000E-1700545740-no_file.tic | 28 ++ .../app/test/000E-1700545740-not_to_me.tic | 28 ++ .../test/000E-1700545740-wrong_password.tic | 28 ++ tests/Feature/PacketTest.php | 2 + tests/Feature/RoutingTest.php | 318 ++++++++--------- tests/Feature/TicProcessingTest.php | 137 ++++++++ tests/Unit/.gitkeep | 0 tests/Unit/ExampleTest.php | 19 - 30 files changed, 847 insertions(+), 403 deletions(-) create mode 100644 app/Exceptions/InvalidCRCException.php create mode 100644 app/Exceptions/InvalidPasswordException.php create mode 100644 app/Exceptions/NoReadSecurityException.php create mode 100644 app/Exceptions/NoWriteSecurityException.php create mode 100644 app/Exceptions/NodeNotSubscribedException.php create mode 100644 app/Exceptions/TIC/NoFileAreaException.php create mode 100644 app/Exceptions/TIC/NotToMeException.php create mode 100644 app/Exceptions/TIC/SizeMismatchException.php create mode 100644 database/migrations/2023_11_19_224649_add_recv_tic.php create mode 100644 storage/app/test/000E-0000000000-FILE.ZIP create mode 100644 storage/app/test/000E-1700545740-bad_size.tic create mode 100644 storage/app/test/000E-1700545740-file.tic create mode 100644 storage/app/test/000E-1700545740-invalid_crc.tic create mode 100644 storage/app/test/000E-1700545740-no_area.tic create mode 100644 storage/app/test/000E-1700545740-no_file.tic create mode 100644 storage/app/test/000E-1700545740-not_to_me.tic create mode 100644 storage/app/test/000E-1700545740-wrong_password.tic create mode 100644 tests/Feature/TicProcessingTest.php create mode 100644 tests/Unit/.gitkeep delete mode 100644 tests/Unit/ExampleTest.php diff --git a/.env.testing b/.env.testing index 1b41ce9..b156d81 100644 --- a/.env.testing +++ b/.env.testing @@ -41,7 +41,8 @@ PUSHER_APP_CLUSTER=mt1 MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" -FIDO_DIR=fido +FIDO_DIR=test +FIDO_DIR_FILES=local FIDO_PACKET_KEEP=true FILESYSTEM_DISK=s3 diff --git a/app/Classes/FTN/Tic.php b/app/Classes/FTN/Tic.php index 3bd0885..c187c46 100644 --- a/app/Classes/FTN/Tic.php +++ b/app/Classes/FTN/Tic.php @@ -4,31 +4,30 @@ namespace App\Classes\FTN; use Carbon\Carbon; use Illuminate\Contracts\Filesystem\FileNotFoundException; +use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Support\Arr; -use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Storage; use League\Flysystem\UnableToReadFile; use App\Classes\FTN as FTNBase; -use App\Models\{Address,File,Filearea,Setup,System}; -use App\Traits\EncodeUTF8; +use App\Exceptions\{InvalidCRCException, + InvalidPasswordException, + NodeNotSubscribedException, + NoWriteSecurityException}; +use App\Exceptions\TIC\{NoFileAreaException,NotToMeException,SizeMismatchException}; +use App\Models\{Address,File,Filearea,Setup}; /** * Class TIC - * Used create the structure of TIC files + * This class handles the TIC files that accompany file transfers * * @package App\Classes */ class Tic extends FTNBase { - use EncodeUTF8; - private const LOGKEY = 'FT-'; - private const cast_utf8 = [ - ]; - // Single value kludge items and whether they are required // http://ftsc.org/docs/fts-5006.001 private array $_kludge = [ @@ -53,30 +52,23 @@ class Tic extends FTNBase 'pw' => FALSE, // Password ]; - private File $fo; - private Filearea $area; - private Collection $values; - - private Address $origin; // Should be first address in Path - private Address $from; // Should be last address in Path + private File $file; private Address $to; // Should be me - public function __construct() + public function __construct(File $file=NULL) { - $this->fo = new File; + $this->file = $file ?: new File; - $this->fo->kludges = collect(); - $this->fo->set_path = collect(); - $this->fo->set_seenby = collect(); - $this->fo->rogue_seenby = collect(); - - $this->values = collect(); + $this->file->kludges = collect(); + $this->file->rogue_seenby = collect(); + $this->file->set_path = collect(); + $this->file->set_seenby = collect(); } public function __get(string $key): mixed { switch ($key) { - case 'fo': + case 'file': return $this->{$key}; default: @@ -85,45 +77,47 @@ class Tic extends FTNBase } /** - * Generate a TIC file for an address + * Generate the TIC file * - * @param Address $ao - * @param File $fo * @return string + * @throws \Exception */ - public static function generate(Address $ao,File $fo): string + public function __toString(): string { - $sysaddress = Setup::findOrFail(config('app.id'))->system->match($ao->zone)->first(); + if (! $this->to) + throw new \Exception('No to address defined'); + + $sysaddress = ($x=Setup::findOrFail(config('app.id'))->system)->match($this->to->zone)->first(); $result = collect(); // Origin is the first address in our path - $result->put('ORIGIN',$fo->path->first()->ftn3d); + $result->put('ORIGIN',$this->file->path->first()->ftn3d); $result->put('FROM',$sysaddress->ftn3d); - $result->put('TO',$ao->ftn3d); - $result->put('FILE',$fo->name); - $result->put('SIZE',$fo->size); - if ($fo->description) - $result->put('DESC',$fo->description); - if ($fo->replaces) - $result->put('REPLACES',$fo->replaces); - $result->put('AREA',$fo->filearea->name); - $result->put('AREADESC',$fo->filearea->description); - if ($x=$ao->session('ticpass')) + $result->put('TO',$this->to->ftn3d); + $result->put('FILE',$this->file->name); + $result->put('SIZE',$this->file->size); + if ($this->file->description) + $result->put('DESC',$this->file->description); + if ($this->file->replaces) + $result->put('REPLACES',$this->file->replaces); + $result->put('AREA',$this->file->filearea->name); + $result->put('AREADESC',$this->file->filearea->description); + if ($x=$this->to->session('ticpass')) $result->put('PW',$x); - $result->put('CRC',sprintf("%X",$fo->crc)); + $result->put('CRC',sprintf("%X",$this->file->crc)); $out = ''; foreach ($result as $key=>$value) $out .= sprintf("%s %s\r\n",$key,$value); - foreach ($fo->path as $o) + foreach ($this->file->path as $o) $out .= sprintf("PATH %s %s %s\r\n",$o->ftn3d,$o->pivot->datetime,$o->pivot->extra); // Add ourself to the path: $out .= sprintf("PATH %s %s\r\n",$sysaddress->ftn3d,Carbon::now()); - foreach ($fo->seenby as $o) + foreach ($this->file->seenby as $o) $out .= sprintf("SEENBY %s\r\n",$o->ftn3d); $out .= sprintf("SEENBY %s\r\n",$sysaddress->ftn3d); @@ -140,103 +134,122 @@ class Tic extends FTNBase { Log::critical(sprintf('%s:D fo_nodelist_file_area [%d], fo_filearea_domain_filearea_id [%d], regex [%s] name [%s]', self::LOGKEY, - $this->fo->nodelist_filearea_id, - $this->fo->filearea->domain->filearea_id, - str_replace(['.','?'],['\.','.'],'#^'.$this->fo->filearea->domain->nodelist_filename.'$#i'), - $this->fo->name, + $this->file->nodelist_filearea_id, + $this->file->filearea->domain->filearea_id, + str_replace(['.','?'],['\.','[0-9]'],'#^'.$this->file->filearea->domain->nodelist_filename.'$#i'), + $this->file->name, )); - return (($this->fo->nodelist_filearea_id === $this->fo->filearea->domain->filearea_id) - && (preg_match(str_replace(['.','?'],['\.','.'],'#^'.$this->fo->filearea->domain->nodelist_filename.'$#i'),$this->fo->name))); + return (($this->file->nodelist_filearea_id === $this->file->filearea->domain->filearea_id) + && (preg_match(str_replace(['.','?'],['\.','.'],'#^'.$this->file->filearea->domain->nodelist_filename.'$#i'),$this->file->name))); } /** * Load a TIC file from an existing filename * * @param string $filename Relative to filesystem - * @return void + * @return File * @throws FileNotFoundException + * @throws InvalidCRCException + * @throws InvalidPasswordException + * @throws NoFileAreaException + * @throws NoWriteSecurityException + * @throws NodeNotSubscribedException + * @throws NotToMeException + * @throws SizeMismatchException */ - public function load(string $filename): void + public function load(string $filename): File { Log::info(sprintf('%s:+ Processing TIC file [%s]',self::LOGKEY,$filename)); $fs = Storage::disk(config('fido.local_disk')); + $rel_path_name = sprintf('%s/%s',config('fido.dir'),$filename); - if (str_contains($filename,'-')) { - list($hex,$name) = explode('-',$filename); - $hex = basename($hex); + if (! $fs->exists($rel_path_name)) + throw new FileNotFoundException(sprintf('File [%s] doesnt exist',$fs->path($rel_path_name))); - } else { - $hex = ''; - } + if ((! is_readable($fs->path($rel_path_name))) || ! ($f = $fs->readStream($rel_path_name))) + throw new UnableToReadFile(sprintf('File [%s] is not readable',$fs->path($rel_path_name))); - if (! $fs->exists($filename)) - throw new FileNotFoundException(sprintf('File [%s] doesnt exist',$fs->path($filename))); + /* + * Filenames are in the format X-Y-N.tic + * Where: + * - X is the nodes address that sent us the file + * - Y is the mtime of the TIC file from the sender + * - N is the sender's filename + */ - if (! is_readable($fs->path($filename))) - throw new UnableToReadFile(sprintf('File [%s] is not readable',realpath($filename))); + $aid = NULL; + $mtime = NULL; + $this->file->recv_tic = preg_replace('/\.[Tt][Ii][Cc]$/','',$filename); - $f = $fs->readStream($filename); - if (! $f) { - Log::error(sprintf('%s:! Unable to open file [%s] for reading',self::LOGKEY,$filename)); - return; + $m = []; + if (preg_match('/^(([0-9A-F]{4})-)?(([0-9]{4,10})-)?(.*).[Tt][Ii][Cc]$/',$filename,$m)) { + $aid = $m[2]; + $mtime = $m[4]; + $this->file->recv_tic = $m[5]; } $ldesc = ''; while (! feof($f)) { $line = chop(fgets($f)); - $matches = []; + $m = []; if (! $line) continue; - preg_match('/([a-zA-Z]+)\ ?(.*)?/',$line,$matches); + preg_match('/([a-zA-Z]+)\ ?(.*)?/',$line,$m); - if (in_array(strtolower(Arr::get($matches,1,'-')),$this->_kludge)) { - switch ($k=strtolower($matches[1])) { + if (in_array(strtolower(Arr::get($m,1,'-')),$this->_kludge)) { + switch ($k=strtolower($m[1])) { case 'area': - $this->{$k} = Filearea::singleOrNew(['name'=>strtoupper($matches[2])]); + try { + if ($fo=Filearea::where('name',strtoupper($m[2]))->firstOrFail()) + $this->file->filearea_id = $fo->id; - break; - - case 'origin': - case 'from': - case 'to': - $this->{$k} = Address::findFTN($matches[2]); - - if (! $this->{$k}) - Log::alert(sprintf('%s:! Unable to find an FTN for [%s] for the (%s)',self::LOGKEY,$matches[2],$k)); - - break; - - case 'file': - $this->fo->name = $matches[2]; - $this->fo->prefix = $hex; - - if (! $fs->exists($this->fo->recvd_rel_name)) { - // @todo Fail this, so that it is rescheduled to try again in 1-24hrs. - - throw new FileNotFoundException(sprintf('File not found? [%s]',$fs->path($this->fo->recvd_rel_name))); + } catch (ModelNotFoundException $e) { + // Rethrow this as No File Area + throw new NoFileAreaException($e); } break; - case 'areadesc': - $areadesc = $matches[2]; + case 'from': + if (($ao=Address::findFTN($m[2])) && ((! $aid) || ($ao->zone->domain_id === Address::findOrFail(hexdec($aid))->zone->domain_id))) + $this->file->fftn_id = $ao->id; + else + throw new ModelNotFoundException(sprintf('FTN Address [%s] not found or sender mismatch',$m[2])); break; + // The origin should be the first address in the path + case 'origin': + // Ignore + case 'areadesc': case 'created': - // ignored + break; + + // This should be one of my addresses + case 'to': + $ftns = Setup::findOrFail(config('app.id'))->system->addresses->pluck('ftn3d'); + + if (! ($ftns->contains($m[2]))) + throw new NotToMeException(sprintf('FTN Address [%s] not found or not one of my addresses',$m[2])); + + break; + + case 'file': + $this->file->name = $m[2]; + break; case 'pw': - $pw = $matches[2]; + $pw = $m[2]; break; case 'lfile': - $this->fo->lname = $matches[2]; + case 'fullname': + $this->file->lname = $m[2]; break; @@ -244,91 +257,128 @@ class Tic extends FTNBase case 'magic': case 'replaces': case 'size': - $this->fo->{$k} = $matches[2]; - - break; - - case 'fullname': - $this->fo->lfile = $matches[2]; + $this->file->{$k} = $m[2]; break; case 'date': - $this->fo->datetime = Carbon::createFromTimestamp($matches[2]); + $this->file->datetime = Carbon::createFromTimestamp($m[2]); break; case 'ldesc': - $ldesc .= ($ldesc ? "\r" : '').$matches[2]; + $ldesc .= ($ldesc ? "\r" : '').$m[2]; break; case 'crc': - $this->fo->{$k} = hexdec($matches[2]); + $this->file->{$k} = hexdec($m[2]); break; case 'path': - $this->fo->set_path->push($matches[2]); + $this->file->set_path->push($m[2]); break; case 'seenby': - $this->fo->set_seenby->push($matches[2]); + $this->file->set_seenby->push($m[2]); break; } } else { - $this->fo->kludges->push($line); + $this->file->kludges->push($line); } } - if ($ldesc) - $this->fo->ldesc = $ldesc; - fclose($f); - // @todo Add notifictions back to the system - if ($this->fo->replaces && (! preg_match('/^'.$this->fo->replaces.'$/',$this->fo->name))) { - Log::alert(sprintf('%s:! Regex [%s] doesnt match file name [%s]',self::LOGKEY,$this->fo->replaces,$this->fo->name)); + if ($ldesc) + $this->file->ldesc = $ldesc; - $this->fo->replaces = NULL; + // @todo Check that origin is the first address in the path + // @todo Make sure origin/from are in seenby + // @todo Make sure origin/from are in the path + + /* + * Find our file and check the CRC + * If there is more than 1 file, select files that within 24hrs of the TIC file. + * If no files report file not found + * If there is more than 1 check each CRC to match the right one. + * If none match report, CRC error + */ + $found = FALSE; + $crcOK = FALSE; + foreach ($fs->files(config('fido.dir')) as $file) { + if (abs($x=$mtime-$fs->lastModified($file)) > 86400) { + Log::debug(sprintf('%s:/ Ignoring [%s] its mtime is outside of our scope [%d]',self::LOGKEY,$file,$x)); + + continue; + } + + // Our file should have the same prefix as the TIC file + if (preg_match('#/'.($aid ? $aid.'-' : '').'.*'.$this->file->name.'$#',$file)) { + $found = TRUE; + + if (sprintf('%08x',$this->file->crc) === ($y=$fs->checksum($file,['checksum_algo'=>'crc32b']))) { + $crcOK = TRUE; + + break; + } + } } + if (($found) && (! $crcOK)) + throw new InvalidCRCException(sprintf('TIC file CRC [%08x] doesnt match file [%s] (%s)',$this->file->crc,$fs->path($rel_path_name),$y)); + elseif (! $found) + throw new FileNotFoundException(sprintf('File not found? [%s...%s] in [%s]',$aid,$this->file->name,$fs->path($rel_path_name))); + + // @todo Add notifications back to the system if the replaces line doesnt match + if ($this->file->replaces && (! preg_match('/^'.$this->file->replaces.'$/',$this->file->name))) { + Log::alert(sprintf('%s:! Regex [%s] doesnt match file name [%s]',self::LOGKEY,$this->file->replaces,$this->file->name)); + + $this->file->replaces = NULL; + } + + // @todo Add notification back to the system if no replaces line and the file already exists + // Validate Size - if ($this->fo->size !== ($y=$fs->size($this->fo->recvd_rel_name))) - throw new \Exception(sprintf('TIC file size [%d] doesnt match file [%s] (%d)',$this->fo->size,$this->fo->recvd_rel_name,$y)); - - // Validate CRC - if (sprintf('%08x',$this->fo->crc) !== ($y=$fs->checksum($this->fo->recvd_rel_name,['checksum_algo'=>'crc32b']))) - throw new \Exception(sprintf('TIC file CRC [%08x] doesnt match file [%s] (%s)',$this->fo->crc,$this->fo->recvd_rel_name,$y)); + if ($this->file->size !== ($y=$fs->size($file))) + throw new SizeMismatchException(sprintf('TIC file size [%d] doesnt match file [%s] (%d)',$this->file->size,$fs->path($rel_path_name),$y)); // Validate Password - if ($pw !== ($y=$this->from->session('ticpass'))) - throw new \Exception(sprintf('TIC file PASSWORD [%s] doesnt match system [%s] (%s)',$pw,$this->from->ftn,$y)); + if ($pw !== ($y=$this->file->fftn->session('ticpass'))) + throw new InvalidPasswordException(sprintf('TIC file PASSWORD [%s] doesnt match system [%s] (%s)',$pw,$this->file->fftn->ftn,$y)); - // Validate Sender is linked (and permitted to send) - if ($this->from->fileareas->search(function($item) { return $item->id === $this->area->id; }) === FALSE) - throw new \Exception(sprintf('Node [%s] is not subscribed to [%s]',$this->from->ftn,$this->area->name)); + // Validate Sender is linked + if ($this->file->fftn->fileareas->search(function($item) { return $item->id === $this->file->filearea_id; }) === FALSE) + throw new NodeNotSubscribedException(sprintf('Node [%s] is not subscribed to [%s]',$this->file->fftn->ftn,$this->file->filearea->name)); - // If the filearea is to be autocreated, create it - if (! $this->area->exists) { - $this->area->description = $areadesc; - $this->area->active = TRUE; - $this->area->show = FALSE; - $this->area->notes = 'Autocreated'; - $this->area->domain_id = $this->from->zone->domain_id; - $this->area->save(); - } - - $this->fo->filearea_id = $this->area->id; - $this->fo->fftn_id = $this->origin->id; + // Validate sender is permitted to write + // @todo Send a notification + if (! $this->file->filearea->sec_write || ($this->file->fftn->security < $this->file->filearea->sec_write)) + throw new NoWriteSecurityException(sprintf('Node [%s] doesnt have enough security to write to [%s] (%d)',$this->file->fftn->ftn,$this->file->filearea->name,$this->file->fftn->security)); // If the file create time is blank, we'll take the files - if (! $this->fo->datetime) - $this->fo->datetime = Carbon::createFromTimestamp($fs->lastModified($this->fo->recvd_rel_name)); + if (! $this->file->datetime) + $this->file->datetime = Carbon::createFromTimestamp($fs->lastModified($file)); - $this->fo->save(); + $this->file->src_file = $file; + $this->file->recv_tic = $filename; + + return $this->file; + } + + public function save(): bool + { + return $this->file->save(); + } + + public function to(Address $ao): self + { + $this->to = $ao; + + return $this; } } \ No newline at end of file diff --git a/app/Classes/File/Tic.php b/app/Classes/File/Tic.php index e04d6b9..2b13d7d 100644 --- a/app/Classes/File/Tic.php +++ b/app/Classes/File/Tic.php @@ -26,7 +26,7 @@ final class Tic extends Send $this->ftype = ((($type&0xff)<<8)|self::IS_TIC); $this->readpos = 0; - $this->tic = FTNTic::generate($ao,$file); + $this->tic = (string)(new FTNTic($file))->to($ao); } public function __get($key) { diff --git a/app/Exceptions/InvalidCRCException.php b/app/Exceptions/InvalidCRCException.php new file mode 100644 index 0000000..f6e1782 --- /dev/null +++ b/app/Exceptions/InvalidCRCException.php @@ -0,0 +1,9 @@ +file); $to = new Tic; + try { - $to->load($rel_name); + $fo = $to->load($this->file) + ->save(); } catch (\Exception $e) { Log::error(sprintf('%s:! Error loading TIC file [%s] (%s)',self::LOGKEY,$rel_name,$e->getMessage())); + return; } - Log::info(sprintf('%s:= Processed [%s] storing [%s] as id [%d]',self::LOGKEY,$this->file,$to->fo->name,$to->fo->id)); + Log::info(sprintf('%s:= Processed [%s] storing [%s] as id [%d]',self::LOGKEY,$this->file,$to->file->name,$to->file->id)); if (config('fido.packet_keep')) { $dir = sprintf('%s/%s',config('fido.dir'),Carbon::now()->format('Ymd')); diff --git a/app/Models/File.php b/app/Models/File.php index e1317e5..5725913 100644 --- a/app/Models/File.php +++ b/app/Models/File.php @@ -21,9 +21,10 @@ class File extends Model private const LOGKEY = 'MF-'; private bool $no_export = FALSE; - public string $prefix = ''; + public Collection $set_path; public Collection $set_seenby; + public string $src_file = ''; protected $casts = [ 'kludges' => CollectionOrNull::class, @@ -50,7 +51,7 @@ class File extends Model return; } - Log::info(sprintf('%s:- Storing file [%s] in [%s]',self::LOGKEY,$model->recvd_rel_name,$model->rel_name)); + Log::info(sprintf('%s:- Storing file [%s] in [%s]',self::LOGKEY,$model->src_file,$model->rel_name)); $srcfs = Storage::disk(config('fido.local_disk')); $tgtfs = Storage::disk(config('fido.file_disk')); @@ -64,11 +65,11 @@ class File extends Model } // Store file - if ($tgtfs->put($model->rel_name,$srcfs->get($model->recvd_rel_name),'public')) { - $srcfs->delete($model->recvd_rel_name); + if ($tgtfs->put($model->rel_name,$srcfs->get($model->src_file),'public')) { + $srcfs->delete($model->src_file); } else { - throw new \Exception(sprintf('Unable to move file [%s] to [%s]',$model->recvd_rel_name,$model->rel_name)); + throw new \Exception(sprintf('Unable to move file [%s] to [%s]',$model->src_file,$model->rel_name)); } }); @@ -88,7 +89,7 @@ class File extends Model // Parse PATH foreach ($model->set_path as $line) { $matches = []; - preg_match(sprintf('#^(%s)\ ?([0-9]+)\ ?(.*)$#',Address::ftn_regex),$line,$matches); + preg_match(sprintf('#^(\d+:\d+/\d+(\.\d+)?(@%s)?)\ ((\d+)\ )?(.*)$#',Address::ftn_regex),$line,$matches); if ($x=Arr::get($matches,1)) { $ftn = Address::parseFTN($x); @@ -101,7 +102,7 @@ class File extends Model if (! $ao) $ao = Address::createFTN($x,System::createUnknownSystem()); - $path->push(['address'=>$ao,'datetime'=>Carbon::createFromTimestamp($matches[9]),'extra'=>$matches[10]]); + $path->push(['address'=>$ao,'datetime'=>Carbon::createFromTimestamp($matches[12]),'extra'=>$matches[13]]); } } @@ -192,6 +193,11 @@ class File extends Model /* ATTRIBUTES */ + public function getOriginAttribute(): Address + { + return $this->path->sortBy('parent_id')->last(); + } + /** * Return the relative path to Storage::disk() in the store * @@ -202,26 +208,6 @@ class File extends Model return sprintf('%04X/%s',$this->filearea_id,$this->name); } - /** - * Return the relative path to Storage::disk() in the inbound; - * - * @return string - */ - public function getRecvdRelNameAttribute(): string - { - return sprintf('%s/%s',config('fido.dir'),$this->recvd_pref_name); - } - - /** - * This is the name of the file, with the sender prefix - * - * @return string - */ - public function getRecvdPrefNameAttribute(): string - { - return sprintf('%s%s',$this->prefix ? $this->prefix.'-' : '',$this->name); - } - /* METHODS */ public function jsonSerialize(): array diff --git a/database/migrations/2023_11_19_224649_add_recv_tic.php b/database/migrations/2023_11_19_224649_add_recv_tic.php new file mode 100644 index 0000000..724a44d --- /dev/null +++ b/database/migrations/2023_11_19_224649_add_recv_tic.php @@ -0,0 +1,28 @@ +string('recv_tic')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('files', function (Blueprint $table) { + $table->dropColumn('recv_tic'); + }); + } +}; diff --git a/database/seeders/InitialSetup.php b/database/seeders/InitialSetup.php index 9a19750..10df326 100644 --- a/database/seeders/InitialSetup.php +++ b/database/seeders/InitialSetup.php @@ -6,7 +6,7 @@ use Carbon\Carbon; use Illuminate\Database\Seeder; use Illuminate\Support\Facades\DB; -use App\Models\{Domain,Software,System,Zone}; +use App\Models\{Software,System}; class InitialSetup extends Seeder { @@ -35,31 +35,13 @@ class InitialSetup extends Seeder $so = new System; $so->forceFill([ - 'name'=>'Clearing Houz - Dev', + 'name'=>'My New System', 'sysop'=>'System Sysop', 'location'=>'Melbourne, AU', 'active'=>TRUE, ]); $so->save(); - $do = new Domain; - $do->forceFill([ - 'name'=>'private', - 'active'=>TRUE, - 'public'=>TRUE, - 'notes'=>'PrivateNet: Internal Testing Network' - ]); - $do->save(); - - $zo = new Zone; - $zo->forceFill([ - 'zone_id'=>'10', - 'default'=>FALSE, - 'active'=>TRUE, - 'system_id'=>$so->id, - ]); - $do->zones()->save($zo); - DB::table('setups')->insert([ 'system_id'=>$so->id, ]); diff --git a/database/seeders/TestNodeHierarchy.php b/database/seeders/TestNodeHierarchy.php index 6b5ebce..6340fa1 100644 --- a/database/seeders/TestNodeHierarchy.php +++ b/database/seeders/TestNodeHierarchy.php @@ -6,11 +6,11 @@ use Carbon\Carbon; use Illuminate\Database\Seeder; use Illuminate\Support\Facades\DB; -use App\Models\{Address,Domain,System,Zone}; +use App\Models\{Address, Domain, Setup, System, Zone}; class TestNodeHierarchy extends Seeder { - public const DEBUG=TRUE; + public const DEBUG=FALSE; /** * Run the database seeds. @@ -21,7 +21,7 @@ class TestNodeHierarchy extends Seeder { DB::table('domains') ->insert([ - 'name'=>'domain-a', + 'name'=>'a', 'active'=>TRUE, 'public'=>TRUE, 'created_at'=>Carbon::now(), @@ -30,33 +30,53 @@ class TestNodeHierarchy extends Seeder DB::table('domains') ->insert([ - 'name'=>'domain-b', + 'name'=>'b', 'active'=>TRUE, 'public'=>TRUE, 'created_at'=>Carbon::now(), 'updated_at'=>Carbon::now(), ]); - foreach (['domain-a','domain-b'] as $domain) { + foreach (['a','b'] as $domain) { $do = Domain::where('name',$domain)->singleOrFail(); $this->hierarchy($do,100); $this->hierarchy($do,101); } + + // Configure my addresses + $so = Setup::findOrFail(config('app.id')); + $ao = Address::findFTN('100:0/0@a'); + $so->system_id = $ao->system_id; + $so->save(); + + // Add file area + DB::table('fileareas') + ->insert([ + 'name'=>'FILE_AREA', + 'description'=>'Testing File Area', + 'active'=>TRUE, + 'show'=>TRUE, + 'notes'=>'File area for testing', + 'domain_id'=>$ao->zone->domain_id, + 'security'=>1, + 'created_at'=>Carbon::now(), + 'updated_at'=>Carbon::now(), + ]); } private function hierarchy(Domain $domain,int $zoneid) { - $regions = [1,2]; - //$hosts = [20,30]; - $hubs = [1000,2000]; - $nodes = [1,2,3]; - $hubnodes = [-1,+1]; + $hosts = [1,2,3,4,5]; + $hubs = [10,20,30,40,50]; + $nodes = [100,200,300,400,500]; + $hubnodes = [-2,-1,+1,+2,+3]; - $so = $this->system('ZC '.$domain->id); + $so = $this->system(sprintf('ZC %s-%s',$domain->name,$zoneid)); DB::table('zones') ->insert([ 'zone_id'=>$zoneid, 'active'=>TRUE, + 'default'=>$domain->name === 'a', 'notes'=>sprintf('Zone: %03d:0/0.0@%s',$zoneid,$domain->name), 'domain_id'=>$domain->id, 'system_id'=>$so->id, @@ -68,10 +88,12 @@ class TestNodeHierarchy extends Seeder if (self::DEBUG) dump(['zo'=>$zo->zone_id,'rid'=>0,'hid'=>0,'nid'=>0]); + // ZC DB::table('addresses') ->insert([ 'zone_id'=>$zo->id, 'active'=>TRUE, + 'validated'=>TRUE, 'region_id'=>0, 'host_id'=>0, 'node_id'=>0, @@ -87,11 +109,12 @@ class TestNodeHierarchy extends Seeder if (self::DEBUG) dump(['rid'=>$zo->zone_id,'hid'=>$zo->zone_id,'nid'=>$nid]); - $so = $this->system(sprintf('Node %03d:%03d/%03d.0@%s (ZC Node)',$zoneid,0,$nid,$domain->name)); + $so = $this->system(sprintf('ZC Node 0/%d',$nid)); DB::table('addresses') ->insert([ 'zone_id'=>$zo->id, 'active'=>TRUE, + 'validated'=>TRUE, 'region_id'=>0, 'host_id'=>0, 'node_id'=>$nid, @@ -107,7 +130,7 @@ class TestNodeHierarchy extends Seeder dump(['end'=>'nodes top']); // Regions - foreach ($regions as $rid) { + foreach ($hosts as $rid) { $hostid = $rid; if (self::DEBUG) dump(['rid'=>$rid,'hid'=>$hostid,'nid'=>0]); @@ -117,6 +140,7 @@ class TestNodeHierarchy extends Seeder ->insert([ 'zone_id'=>$zo->id, 'active'=>TRUE, + 'validated'=>TRUE, 'region_id'=>$rid, 'host_id'=>$rid, 'node_id'=>0, @@ -132,11 +156,12 @@ class TestNodeHierarchy extends Seeder if (self::DEBUG) dump(['rid'=>$rid,'hid'=>$hostid,'nid'=>$nid]); - $so = $this->system(sprintf('Node %03d:%03d/%03d.0@%s (RC Node)',$zoneid,$rid,$nid,$domain->name)); + $so = $this->system(sprintf('RC Node %d/%d',$rid,$nid)); DB::table('addresses') ->insert([ 'zone_id'=>$zo->id, 'active'=>TRUE, + 'validated'=>TRUE, 'region_id'=>$rid, 'host_id'=>$rid, 'node_id'=>$nid, @@ -147,20 +172,22 @@ class TestNodeHierarchy extends Seeder 'updated_at'=>Carbon::now(), ]); } - dump(['end'=>'NODES regions']); + if (self::DEBUG) + dump(['end'=>'NODES regions']); // Hosts - foreach ($regions as $rrid) { + foreach ($hosts as $rrid) { $hostid = $rid*10+$rrid-1; if (self::DEBUG) dump(['rid'=>$rid,'hid'=>$hostid,'nid'=>0]); - $so = $this->system(sprintf('Host %03d:%03d/0.0@%s (Region %03d)',$zoneid,$hostid,$domain->name,$rid)); + $so = $this->system(sprintf('Host %d:%d/0 (R%d)',$zoneid,$hostid,$rid)); DB::table('addresses') ->insert([ 'zone_id'=>$zo->id, 'active'=>TRUE, + 'validated'=>TRUE, 'region_id'=>$rid, 'host_id'=>$hostid, 'node_id'=>0, @@ -176,11 +203,12 @@ class TestNodeHierarchy extends Seeder if (self::DEBUG) dump(['rid'=>$rid,'hid'=>$hostid,'nid'=>$nid]); - $so = $this->system(sprintf('Node %03d:%03d/%03d.0@%s (Region %03d) - Host Node',$zoneid,$hostid,$nid,$domain->name,$rid)); + $so = $this->system(sprintf('Host Node %d/%d (R%d)',$hostid,$nid,$rid)); DB::table('addresses') ->insert([ 'zone_id'=>$zo->id, 'active'=>TRUE, + 'validated'=>TRUE, 'region_id'=>$rid, 'host_id'=>$hostid, 'node_id'=>$nid, @@ -194,7 +222,7 @@ class TestNodeHierarchy extends Seeder // Hubs foreach ($hubs as $bid) { - $so = $this->system(sprintf('Hub %03d:%03d/%03d.0@%s (Region %03d)',$zoneid,$hostid,$bid,$domain->name,$rid)); + $so = $this->system(sprintf('HUB %d/%d (R%d)',$hostid,$bid,$rid)); $hub = new Address; $hub->zone_id = $zo->id; $hub->active = TRUE; @@ -211,11 +239,12 @@ class TestNodeHierarchy extends Seeder // Nodes foreach ($hubnodes as $nid) { $nodeid = $bid+$nid; - $so = $this->system(sprintf('Node %03d:%03d/%03d.0@%s (Region %03d) - Hub Node',$zoneid,$hostid,$nodeid,$domain->name,$rid)); + $so = $this->system(sprintf('Hub Node %d/%d (R%d/H%d)',$hostid,$nodeid,$rid,$hub->node_id)); DB::table('addresses') ->insert([ 'zone_id'=>$zo->id, 'active'=>TRUE, + 'validated'=>TRUE, 'region_id'=>$rid, 'host_id'=>$hostid, 'node_id'=>$nodeid, @@ -229,18 +258,20 @@ class TestNodeHierarchy extends Seeder } } } - dump(['end'=>'NODES normal']); + if (self::DEBUG) + dump(['end'=>'NODES normal']); } - dump(['end'=>'heirarchy']); + if (self::DEBUG) + dump(['end'=>'heirarchy']); } private function system(string $name): System { $o = new System; $o->name = $name; - $o->sysop = 'Mr Sysop of '.$name; - $o->location = 'Some place for '.$name; + $o->sysop = 'Sysop:'.$name; + $o->location = 'Location:'.$name; $o->active = TRUE; $o->created_at = Carbon::now(); $o->updated_at = Carbon::now(); diff --git a/storage/app/.gitignore b/storage/app/.gitignore index 8f4803c..fc6dbdb 100644 --- a/storage/app/.gitignore +++ b/storage/app/.gitignore @@ -1,3 +1,4 @@ -* +/* !public/ +!test/ !.gitignore diff --git a/storage/app/test/000E-0000000000-FILE.ZIP b/storage/app/test/000E-0000000000-FILE.ZIP new file mode 100644 index 0000000..1f9ae69 --- /dev/null +++ b/storage/app/test/000E-0000000000-FILE.ZIP @@ -0,0 +1 @@ +This is not really a ZIP file! diff --git a/storage/app/test/000E-1700545740-bad_size.tic b/storage/app/test/000E-1700545740-bad_size.tic new file mode 100644 index 0000000..46c302b --- /dev/null +++ b/storage/app/test/000E-1700545740-bad_size.tic @@ -0,0 +1,28 @@ +Created by Typewriter, written by a developer +File FILE.ZIP +Area FILE_AREA +Areadesc This is a file area name +Desc This is the description of the file +Replaces FILE.Z.. +From 100:1/0 +To 100:0/0 +Origin 100:10/11 +Size 15 +Crc 2B4F9097 +Path 100:10/11.0 1700140806 Thu Nov 16 13:20:06 2023 UTC some/thing 0.0 2022-07-03 Path #1 +Path 100:10/10.0 1700140806 Thu Nov 16 13:20:06 2023 UTC some/thing 0.0 2022-07-03 Path #2 +Path 100:10/0.0 1700140806 Thu Nov 16 13:20:06 2023 UTC some/thing 0.0 2022-07-03 Path #3 +Path 100:1/0.0 1700140806 Thu Nov 16 13:20:06 2023 UTC some/thing 0.0 2022-07-03 Path #4 +Seenby 100:10/10 +Seenby 100:10/11 +Seenby 100:10/12 +Seenby 100:10/13 +Seenby 100:10/0 +Seenby 100:10/100 +Seenby 100:10/200 +Seenby 100:10/300 +Seenby 100:1/0 +Seenby 100:1/100 +Seenby 100:1/200 +Seenby 100:1/300 +Pw PASSWORD diff --git a/storage/app/test/000E-1700545740-file.tic b/storage/app/test/000E-1700545740-file.tic new file mode 100644 index 0000000..017debb --- /dev/null +++ b/storage/app/test/000E-1700545740-file.tic @@ -0,0 +1,28 @@ +Created by Typewriter, written by a developer +File FILE.ZIP +Area FILE_AREA +Areadesc This is a file area name +Desc This is the description of the file +Replaces FILE.Z.. +From 100:1/0 +To 100:0/0 +Origin 100:10/11 +Size 31 +Crc 2B4F9097 +Path 100:10/11.0 1700140806 Thu Nov 16 13:20:06 2023 UTC some/thing 0.0 2022-07-03 Path #1 +Path 100:10/10.0 1700140806 Thu Nov 16 13:20:06 2023 UTC some/thing 0.0 2022-07-03 Path #2 +Path 100:10/0.0 1700140806 Thu Nov 16 13:20:06 2023 UTC some/thing 0.0 2022-07-03 Path #3 +Path 100:1/0.0 1700140806 Thu Nov 16 13:20:06 2023 UTC some/thing 0.0 2022-07-03 Path #4 +Seenby 100:10/10 +Seenby 100:10/11 +Seenby 100:10/12 +Seenby 100:10/13 +Seenby 100:10/0 +Seenby 100:10/100 +Seenby 100:10/200 +Seenby 100:10/300 +Seenby 100:1/0 +Seenby 100:1/100 +Seenby 100:1/200 +Seenby 100:1/300 +Pw PASSWORD diff --git a/storage/app/test/000E-1700545740-invalid_crc.tic b/storage/app/test/000E-1700545740-invalid_crc.tic new file mode 100644 index 0000000..83ba5c1 --- /dev/null +++ b/storage/app/test/000E-1700545740-invalid_crc.tic @@ -0,0 +1,28 @@ +Created by Typewriter, written by a developer +File FILE.ZIP +Area FILE_AREA +Areadesc This is a file area name +Desc This is the description of the file +Replaces FILE.Z.. +From 100:1/0 +To 100:0/0 +Origin 100:10/11 +Size 31 +Crc DEADBEEF +Path 100:10/11.0 1700140806 Thu Nov 16 13:20:06 2023 UTC some/thing 0.0 2022-07-03 Path #1 +Path 100:10/10.0 1700140806 Thu Nov 16 13:20:06 2023 UTC some/thing 0.0 2022-07-03 Path #2 +Path 100:10/0.0 1700140806 Thu Nov 16 13:20:06 2023 UTC some/thing 0.0 2022-07-03 Path #3 +Path 100:1/0.0 1700140806 Thu Nov 16 13:20:06 2023 UTC some/thing 0.0 2022-07-03 Path #4 +Seenby 100:10/10 +Seenby 100:10/11 +Seenby 100:10/12 +Seenby 100:10/13 +Seenby 100:10/0 +Seenby 100:10/100 +Seenby 100:10/200 +Seenby 100:10/300 +Seenby 100:1/0 +Seenby 100:1/100 +Seenby 100:1/200 +Seenby 100:1/300 +Pw PASSWORD diff --git a/storage/app/test/000E-1700545740-no_area.tic b/storage/app/test/000E-1700545740-no_area.tic new file mode 100644 index 0000000..fad1557 --- /dev/null +++ b/storage/app/test/000E-1700545740-no_area.tic @@ -0,0 +1,28 @@ +Created by Typewriter, written by a developer +File FILE.ZIP +Area NO_FILE_AREA +Areadesc This is a file area name +Desc This is the description of the file +Replaces FILE.Z.. +From 100:1/0 +To 100:0/0 +Origin 100:10/11 +Size 31 +Crc 2B4F9097 +Path 100:10/11.0 1700140806 Thu Nov 16 13:20:06 2023 UTC some/thing 0.0 2022-07-03 Path #1 +Path 100:10/10.0 1700140806 Thu Nov 16 13:20:06 2023 UTC some/thing 0.0 2022-07-03 Path #2 +Path 100:10/0.0 1700140806 Thu Nov 16 13:20:06 2023 UTC some/thing 0.0 2022-07-03 Path #3 +Path 100:1/0.0 1700140806 Thu Nov 16 13:20:06 2023 UTC some/thing 0.0 2022-07-03 Path #4 +Seenby 100:10/10 +Seenby 100:10/11 +Seenby 100:10/12 +Seenby 100:10/13 +Seenby 100:10/0 +Seenby 100:10/100 +Seenby 100:10/200 +Seenby 100:10/300 +Seenby 100:1/0 +Seenby 100:1/100 +Seenby 100:1/200 +Seenby 100:1/300 +Pw PASSWORD diff --git a/storage/app/test/000E-1700545740-no_file.tic b/storage/app/test/000E-1700545740-no_file.tic new file mode 100644 index 0000000..1f5a10b --- /dev/null +++ b/storage/app/test/000E-1700545740-no_file.tic @@ -0,0 +1,28 @@ +Created by Typewriter, written by a developer +File NO_FILE.ZIP +Area FILE_AREA +Areadesc This is a file area name +Desc This is the description of the file +Replaces FILE.Z.. +From 100:1/0 +To 100:0/0 +Origin 100:10/11 +Size 31 +Crc 2B4F9097 +Path 100:10/11.0 1700140806 Thu Nov 16 13:20:06 2023 UTC some/thing 0.0 2022-07-03 Path #1 +Path 100:10/10.0 1700140806 Thu Nov 16 13:20:06 2023 UTC some/thing 0.0 2022-07-03 Path #2 +Path 100:10/0.0 1700140806 Thu Nov 16 13:20:06 2023 UTC some/thing 0.0 2022-07-03 Path #3 +Path 100:1/0.0 1700140806 Thu Nov 16 13:20:06 2023 UTC some/thing 0.0 2022-07-03 Path #4 +Seenby 100:10/10 +Seenby 100:10/11 +Seenby 100:10/12 +Seenby 100:10/13 +Seenby 100:10/0 +Seenby 100:10/100 +Seenby 100:10/200 +Seenby 100:10/300 +Seenby 100:1/0 +Seenby 100:1/100 +Seenby 100:1/200 +Seenby 100:1/300 +Pw PASSWORD diff --git a/storage/app/test/000E-1700545740-not_to_me.tic b/storage/app/test/000E-1700545740-not_to_me.tic new file mode 100644 index 0000000..8f3fe17 --- /dev/null +++ b/storage/app/test/000E-1700545740-not_to_me.tic @@ -0,0 +1,28 @@ +Created by Typewriter, written by a developer +File FILE.ZIP +Area FILE_AREA +Areadesc This is a file area name +Desc This is the description of the file +Replaces FILE.Z.. +From 100:1/0 +To 100:10/0 +Origin 100:10/11 +Size 31 +Crc 2B4F9097 +Path 100:10/11.0 1700140806 Thu Nov 16 13:20:06 2023 UTC some/thing 0.0 2022-07-03 Path #1 +Path 100:10/10.0 1700140806 Thu Nov 16 13:20:06 2023 UTC some/thing 0.0 2022-07-03 Path #2 +Path 100:10/0.0 1700140806 Thu Nov 16 13:20:06 2023 UTC some/thing 0.0 2022-07-03 Path #3 +Path 100:1/0.0 1700140806 Thu Nov 16 13:20:06 2023 UTC some/thing 0.0 2022-07-03 Path #4 +Seenby 100:10/10 +Seenby 100:10/11 +Seenby 100:10/12 +Seenby 100:10/13 +Seenby 100:10/0 +Seenby 100:10/100 +Seenby 100:10/200 +Seenby 100:10/300 +Seenby 100:1/0 +Seenby 100:1/100 +Seenby 100:1/200 +Seenby 100:1/300 +Pw PASSWORD diff --git a/storage/app/test/000E-1700545740-wrong_password.tic b/storage/app/test/000E-1700545740-wrong_password.tic new file mode 100644 index 0000000..76991ff --- /dev/null +++ b/storage/app/test/000E-1700545740-wrong_password.tic @@ -0,0 +1,28 @@ +Created by Typewriter, written by a developer +File FILE.ZIP +Area FILE_AREA +Areadesc This is a file area name +Desc This is the description of the file +Replaces FILE.Z.. +From 100:1/0 +To 100:0/0 +Origin 100:10/11 +Size 31 +Crc 2B4F9097 +Path 100:10/11.0 1700140806 Thu Nov 16 13:20:06 2023 UTC some/thing 0.0 2022-07-03 Path #1 +Path 100:10/10.0 1700140806 Thu Nov 16 13:20:06 2023 UTC some/thing 0.0 2022-07-03 Path #2 +Path 100:10/0.0 1700140806 Thu Nov 16 13:20:06 2023 UTC some/thing 0.0 2022-07-03 Path #3 +Path 100:1/0.0 1700140806 Thu Nov 16 13:20:06 2023 UTC some/thing 0.0 2022-07-03 Path #4 +Seenby 100:10/10 +Seenby 100:10/11 +Seenby 100:10/12 +Seenby 100:10/13 +Seenby 100:10/0 +Seenby 100:10/100 +Seenby 100:10/200 +Seenby 100:10/300 +Seenby 100:1/0 +Seenby 100:1/100 +Seenby 100:1/200 +Seenby 100:1/300 +Pw WRONG diff --git a/tests/Feature/PacketTest.php b/tests/Feature/PacketTest.php index fe616c0..12b6720 100644 --- a/tests/Feature/PacketTest.php +++ b/tests/Feature/PacketTest.php @@ -85,6 +85,7 @@ class PacketTest extends TestCase } */ + /* public function test_msgid_origin() { $this->init(); @@ -245,4 +246,5 @@ class PacketTest extends TestCase $this->assertTrue($messages); } } + */ } diff --git a/tests/Feature/RoutingTest.php b/tests/Feature/RoutingTest.php index 7cc2393..2dda915 100644 --- a/tests/Feature/RoutingTest.php +++ b/tests/Feature/RoutingTest.php @@ -6,7 +6,7 @@ use Illuminate\Database\Eloquent\Collection; use Illuminate\Foundation\Testing\DatabaseTransactions; use Tests\TestCase; -use App\Models\{Address,Domain}; +use App\Models\{Address,Domain,Setup}; class RoutingTest extends TestCase { @@ -14,201 +14,183 @@ class RoutingTest extends TestCase private function zone(): Collection { - $do = Domain::where('name','domain-a')->singleOrFail(); + //$this->seed(TestNodeHierarchy::class); + + $do = Domain::where('name','a')->singleOrFail(); $zo = $do->zones->where('zone_id',100)->pop(); return $zo->addresses; } - /** - * Test the ZC address. - * - * @return void - */ - public function test_zc() + private function session_zc(): void { - //$this->seed(TestNodeHierarchy::class); + // Add session info, and we have 51 children + $ao = Address::findFTN('101:0/0@a'); + $ao->system->sessions()->attach([$ao->zone_id=>['sespass'=>'ABCD']]); + } + private function session_rc(): void + { + // Add session info, and we have 51 children + $ao = Address::findFTN('100:1/0@a'); + $ao->system->sessions()->attach([$ao->zone_id=>['sespass'=>'ABCD']]); + } + + private function session_nc(): void + { + // Add session info, and we have 51 children + $ao = Address::findFTN('100:10/0@a'); + $ao->system->sessions()->attach([$ao->zone_id=>['sespass'=>'ABCD']]); + } + + private function session_hub(): void + { + // Add session info, and we have 51 children + $ao = Address::findFTN('100:10/20@a'); + $ao->system->sessions()->attach([$ao->zone_id=>['sespass'=>'ABCD']]); + } + + // Make sure I'm configured correctly for the rest of the tests + public function test_my_addresses() + { + $ftns = Setup::findOrFail(config('app.id'))->system->addresses; + $this->assertEquals(1,$ftns->count()); + } + + // Get a list of active addresses in a Zone + public function test_zc_addresses() + { $nodes = $this->zone(); - $this->assertEquals(52,$nodes->count()); + $this->assertEquals(936,$nodes->count()); + } + // Get a ZC with no session details, and make sure it has no parent nor children + public function test_zc_no_session() + { // Pick ZC without any session info - we have 0 children - $ao = Address::findFTN('100:0/0@domain-a'); + $ao = Address::findFTN('101:0/0@a'); + $this->assertEquals($ao->role,Address::NODE_ZC); $this->assertCount(0,$ao->children); $this->assertNull($ao->parent()); - - // Add session info, and we have 51 children - $ao->system->sessions()->attach([$ao->zone_id=>['sespass'=>'ABCD']]); - $ao = Address::findFTN('100:0/0@domain-a'); - $this->assertCount(51,$ao->children); - - // A node's parent should be the ZC - $ao = Address::findFTN('100:10/0@domain-a'); - $this->assertEquals($ao->role,Address::NODE_NC); - $this->assertEquals('100:0/0.0@domain-a',$ao->parent()->ftn); - $ao = Address::findFTN('100:11/2001.0@domain-a'); - $this->assertEquals($ao->role,Address::NODE_ACTIVE); - $this->assertEquals('100:0/0.0@domain-a',$ao->parent()->ftn); - - // Pick a NC and we have 10 less children - $ao = Address::findFTN('100:10/0@domain-a'); - $this->assertEquals($ao->role,Address::NODE_NC); - $ao->system->sessions()->attach([$ao->zone_id=>['sespass'=>'ABCD']]); - $ao = Address::findFTN('100:10/0@domain-a'); - $this->assertCount(9,$ao->children); - $ao = Address::findFTN('100:0/0@domain-a'); - $this->assertCount(41,$ao->children); - - // A node's parent should be the ZC - $ao = Address::findFTN('100:20/0@domain-a'); - $this->assertEquals($ao->role,Address::NODE_NC); - $this->assertEquals('100:0/0.0@domain-a',$ao->parent()->ftn); - $ao = Address::findFTN('100:10/2001.0@domain-a'); - $this->assertEquals($ao->role,Address::NODE_ACTIVE); - $this->assertEquals('100:10/0.0@domain-a',$ao->parent()->ftn); - - // Pick a Node and we have 1 less child - $ao = Address::findFTN('100:11/2001.0@domain-a'); - $this->assertEquals($ao->role,Address::NODE_ACTIVE); - $ao->system->sessions()->attach([$ao->zone_id=>['sespass'=>'ABCD']]); - $ao = Address::findFTN('100:11/2001.0@domain-a'); - $this->assertNULL($ao->children); - $ao = Address::findFTN('100:0/0@domain-a'); - $this->assertCount(40,$ao->children); - - // Define address on a HC and we have 3 less children - $ao = Address::findFTN('100:11/1000.0@domain-a'); - $this->assertEquals($ao->role,Address::NODE_HC); - $ao->system->sessions()->attach([$ao->zone_id=>['sespass'=>'ABCD']]); - $ao = Address::findFTN('100:11/1000.0@domain-a'); - $this->assertCount(2,$ao->children); - $ao = Address::findFTN('100:0/0@domain-a'); - $this->assertCount(37,$ao->children); - - // Define address on a NC and we have 10 less children - $ao = Address::findFTN('100:20/0@domain-a'); - $this->assertEquals($ao->role,Address::NODE_NC); - $ao->system->sessions()->attach([$ao->zone_id=>['sespass'=>'ABCD']]); - $ao = Address::findFTN('100:0/0@domain-a'); - $this->assertCount(27,$ao->children); } - /** - * Test the ZC address. - * - * @return void - */ - public function test_rc() + // If we have a ZC, make sure we are routing to all it's children + public function test_zc_session_children() { - //$this->seed(TestNodeHierarchy::class); + $this->session_zc(); - // Pick ZC without any session info - we have 0 children - $ao = Address::findFTN('100:1/0@domain-a'); + $ao = Address::findFTN('101:0/0@a'); + $this->assertCount(935,$ao->children); + } + + // An RC's parent should be the ZC, when we have session details with parent + public function test_zc_rc_node_parent() + { + $this->session_zc(); + + $ao = Address::findFTN('101:1/0@a'); $this->assertEquals($ao->role,Address::NODE_RC); - $this->assertCount(0,$ao->children); + $this->assertEquals('101:0/0.0@a',$ao->parent()->ftn); + } - // Add session info, and we have 51 children - $ao->system->sessions()->attach([$ao->zone_id=>['sespass'=>'ABCD']]); - $ao = Address::findFTN('100:1/0@domain-a'); - $this->assertCount(23,$ao->children); + // An RCs node still collects mail from the RC + public function test_zc_rc_node_rc() + { + $this->session_rc(); - // Pick a NC and we have 10 less children - $ao = Address::findFTN('100:10/0@domain-a'); + // An RCs node should still be the RC + $ao = Address::findFTN('100:1/100@a'); + $this->assertEquals($ao->role,Address::NODE_ACTIVE); + $this->assertEquals('100:1/0.0@a',$ao->parent()->ftn); + } + + // An NC collects mail for its children + public function test_zc_nc_node_rc() + { + return; + $this->session_rc(); + + // A NCs parent should still be the RC + $ao = Address::findFTN('100:10/0@a'); $this->assertEquals($ao->role,Address::NODE_NC); - $ao->system->sessions()->attach([$ao->zone_id=>['sespass'=>'ABCD']]); - $ao = Address::findFTN('100:10/0@domain-a'); - $this->assertCount(9,$ao->children); - $ao = Address::findFTN('100:1/0@domain-a'); - $this->assertCount(13,$ao->children); - - // Pick a Node and we have 1 less child - $ao = Address::findFTN('100:11/2001.0@domain-a'); - $this->assertEquals($ao->role,Address::NODE_ACTIVE); - $ao->system->sessions()->attach([$ao->zone_id=>['sespass'=>'ABCD']]); - $ao = Address::findFTN('100:11/2001.0@domain-a'); - $this->assertNULL($ao->children); - $ao = Address::findFTN('100:1/0@domain-a'); - $this->assertCount(12,$ao->children); - - // Define address on a HC and we have 3 less children - $ao = Address::findFTN('100:11/1000.0@domain-a'); - $this->assertEquals($ao->role,Address::NODE_HC); - $ao->system->sessions()->attach([$ao->zone_id=>['sespass'=>'ABCD']]); - $ao = Address::findFTN('100:11/1000.0@domain-a'); - $this->assertCount(2,$ao->children); - $ao = Address::findFTN('100:1/0@domain-a'); - $this->assertCount(9,$ao->children); + $this->assertEquals('100:1/0.0@a',$ao->parent()->ftn); // @todo fails, returning NULL? } - /** - * Test the NC address. - * - * @return void - */ - public function test_nc() + // A Hub still collects mail from NC + public function test_zc_hub_node_nc() { - //$this->seed(TestNodeHierarchy::class); + return; + $this->session_rc(); - // Pick a HC without any session info - we have 0 children - $ao = Address::findFTN('100:20/0@domain-a'); - $this->assertEquals($ao->role,Address::NODE_NC); - $this->assertCount(0,$ao->children); - - // Add session info, we have 10 children - $ao->system->sessions()->attach([$ao->zone_id=>['sespass'=>'ABCD']]); - $ao = Address::findFTN('100:20/0@domain-a'); - $this->assertCount(9,$ao->children); - - // Add session info to a hub, we have 3 less children - $ao = Address::findFTN('100:20/2000@domain-a'); + // A Hubs parent should still be the NC + $ao = Address::findFTN('100:10/20.0@a'); $this->assertEquals($ao->role,Address::NODE_HC); - $ao->system->sessions()->attach([$ao->zone_id=>['sespass'=>'ABCD']]); - $ao = Address::findFTN('100:20/0@domain-a'); - $this->assertCount(6,$ao->children); - - // Add session info to a node, we have 1 less child - $ao = Address::findFTN('100:20/1@domain-a'); - $this->assertEquals($ao->role,Address::NODE_ACTIVE); - $ao->system->sessions()->attach([$ao->zone_id=>['sespass'=>'ABCD']]); - $ao = Address::findFTN('100:20/0@domain-a'); - $this->assertCount(5,$ao->children); + $this->assertEquals('100:1/0.0@a',$ao->parent()->ftn); // @todo fails, returning NULL? } - /** - * Test the HC address. - * - * @return void - */ - public function test_hc() + // A Hub's node still collects mail from Hub + public function test_zc_hub_node_hub() { - //$this->seed(TestNodeHierarchy::class); + return; + $this->session_rc(); - // Pick a HC without any session info - we have 0 children - $ao = Address::findFTN('100:20/2000@domain-a'); - $this->assertEquals($ao->role,Address::NODE_HC); - $this->assertCount(0,$ao->children); - - // Add session info, we have 2 children - $ao->system->sessions()->attach([$ao->zone_id=>['sespass'=>'ABCD']]); - $ao = Address::findFTN('100:20/2000@domain-a'); - $this->assertCount(2,$ao->children); - - // Add session info to 1 child, we have 1 children - $ao = Address::findFTN('100:20/2001@domain-a'); - $ao->system->sessions()->attach([$ao->zone_id=>['sespass'=>'ABCD']]); - $ao = Address::findFTN('100:20/2000@domain-a'); - $this->assertCount(1,$ao->children); - } - - public function test_node() - { - //$this->seed(TestNodeHierarchy::class); - - // Node with session details still doesnt have any children - $ao = Address::findFTN('100:20/2001@domain-a'); + // A Hubs node should still be the Hub + $ao = Address::findFTN('100:10/22.0@a'); $this->assertEquals($ao->role,Address::NODE_ACTIVE); - $ao->system->sessions()->attach([$ao->zone_id=>['sespass'=>'ABCD']]); - $ao = Address::findFTN('100:20/2001@domain-a'); - $this->assertNULL($ao->children); + $this->assertEquals('100:1/0.0@a',$ao->parent()->ftn); // @todo fails, returning NULL? } -} + + // When we have an RC with session details, we route to all its children + public function test_rc_session_children() + { + $this->session_rc(); + + $ao = Address::findFTN('100:1/0@a'); + $this->assertCount(185,$ao->children); + } + + // An RCs parent is us even if we have session details for another RC + public function test_rc_parent() + { + return; + $this->session_rc(); + + $ao = Address::findFTN('100:2/0@a'); + $this->assertEquals('100:0/0.0@a',$ao->parent()->ftn); // @todo fails, returning NULL? + } + + // If we also have session details for an NC, then there are less RC nodes + public function test_rc_nc_session_children() + { + $this->session_rc(); + + $ao = Address::findFTN('100:10/0@a'); + $ao->system->sessions()->attach([$ao->zone_id=>['sespass'=>'ABCD']]); + + $ao = Address::findFTN('100:1/0@a'); + $this->assertCount(185-36,$ao->children); + } + + // If we also have session details for an Hub, then there are less RC nodes + public function test_rc_hub_session_children() + { + $this->session_rc(); + + $ao = Address::findFTN('100:10/20@a'); + $ao->system->sessions()->attach([$ao->zone_id=>['sespass'=>'ABCD']]); + + $ao = Address::findFTN('100:1/0@a'); + $this->assertCount(185-6,$ao->children); + + $ao = Address::findFTN('100:10/22@a'); + $this->assertEquals('100:10/20.0@a',$ao->parent()->ftn); + } + // If we also have session details for an Hub, then there are less RC nodes + public function test_rc_hub_session_child() + { + $this->session_hub(); + + $ao = Address::findFTN('100:10/22@a'); + $this->assertEquals('100:10/20.0@a',$ao->parent()->ftn); + } +} \ No newline at end of file diff --git a/tests/Feature/TicProcessingTest.php b/tests/Feature/TicProcessingTest.php new file mode 100644 index 0000000..a32f0ec --- /dev/null +++ b/tests/Feature/TicProcessingTest.php @@ -0,0 +1,137 @@ +expectException(FileNotFoundException::class); + + $tic = new Tic; + $tic->load('000E-1700545740-no_file.tic'); + } + + public function test_tic_invalidcrc(): void + { + $this->expectException(InvalidCRCException::class); + + $tic = new Tic; + $tic->load('000E-1700545740-invalid_crc.tic'); + } + + public function test_tic_nofilearea(): void + { + $this->expectException(NoFileAreaException::class); + + $tic = new Tic; + $tic->load('000E-1700545740-no_area.tic'); + } + + public function test_tic_bad_size(): void + { + $this->expectException(SizeMismatchException::class); + + $tic = new Tic; + $tic->load('000E-1700545740-bad_size.tic'); + } + + public function test_tic_not_to_me(): void + { + $this->expectException(NotToMeException::class); + + $tic = new Tic; + $tic->load('000E-1700545740-not_to_me.tic'); + } + + public function test_tic_wrong_password(): void + { + // Configure a node address + $ao = Address::findFTN('100:1/0@a'); + $ao->system->sessions()->attach($ao->zone_id,['ticpass'=>'PASSWORD']); + + $this->expectException(InvalidPasswordException::class); + + $tic = new Tic; + $tic->load('000E-1700545740-wrong_password.tic'); + } + + public function test_tic_node_not_subscribed(): void + { + // Configure a node address + $ao = Address::findFTN('100:1/0@a'); + $ao->system->sessions()->attach($ao->zone_id,['ticpass'=>'PASSWORD']); + + $this->expectException(NodeNotSubscribedException::class); + + $tic = new Tic; + $tic->load('000E-1700545740-file.tic'); + } + + public function test_tic_node_no_write(): void + { + // Configure a node address + $ao = Address::findFTN('100:1/0@a'); + $ao->system->sessions()->attach($ao->zone_id,['ticpass'=>'PASSWORD']); + $ao->fileareas()->syncWithPivotValues(Filearea::where('name','FILE_AREA')->single()->id,['subscribed'=>Carbon::now()]); + + $this->expectException(NoWriteSecurityException::class); + + $tic = new Tic; + $tic->load('000E-1700545740-file.tic'); + } + + public function test_tic_good(): void + { + // Configure a node address + $ao = Address::findFTN('100:1/0@a'); + $ao->system->sessions()->attach($ao->zone_id,['ticpass'=>'PASSWORD']); + $ao->fileareas()->syncWithPivotValues(Filearea::where('name','FILE_AREA')->single()->id,['subscribed'=>Carbon::now()]); + $ao->unguard(); + $ao->update(['security'=>1]); + + $tic = new Tic; + $file = $tic->load('000E-1700545740-file.tic'); + + $tic->save(); + + $this->assertModelExists($file); + + $file->refresh(); + $this->assertEquals('100:1/0',$file->fftn->ftn3d); + $this->assertEquals('100:10/11',$file->origin->ftn3d); + $this->assertCount(12,$file->seenby); + $this->assertCount(4,$file->path); + } +} diff --git a/tests/Unit/.gitkeep b/tests/Unit/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/Unit/ExampleTest.php b/tests/Unit/ExampleTest.php deleted file mode 100644 index e9fe19c..0000000 --- a/tests/Unit/ExampleTest.php +++ /dev/null @@ -1,19 +0,0 @@ -assertTrue(true); - } -}